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.teststeps;
14  
15  import java.beans.PropertyChangeEvent;
16  import java.beans.PropertyChangeListener;
17  import java.beans.PropertyChangeSupport;
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import org.apache.log4j.Logger;
22  import org.apache.xmlbeans.XmlCursor;
23  import org.apache.xmlbeans.XmlException;
24  import org.apache.xmlbeans.XmlObject;
25  import org.apache.xmlbeans.XmlOptions;
26  import org.apache.xmlbeans.XmlCursor.TokenType;
27  import org.w3c.dom.DocumentFragment;
28  import org.w3c.dom.Element;
29  import org.w3c.dom.Node;
30  
31  import com.eviware.soapui.config.ValueTransferConfig;
32  import com.eviware.soapui.impl.wsdl.submit.filters.PropertyExpansionRequestFilter;
33  import com.eviware.soapui.model.iface.SubmitContext;
34  import com.eviware.soapui.model.support.TestSuiteListenerAdapter;
35  import com.eviware.soapui.model.testsuite.TestCase;
36  import com.eviware.soapui.model.testsuite.TestStep;
37  import com.eviware.soapui.model.testsuite.TestStepProperty;
38  import com.eviware.soapui.support.PropertyChangeNotifier;
39  import com.eviware.soapui.support.xml.XmlUtils;
40  
41  /***
42   * Class for transferring a property value between 2 test steps. This class is relatively complex due
43   * to backwards compatibility issues and to gracefull handling of references test steps and properties.
44   * 
45   * @author Ole.Matzura
46   */
47  
48  public class PropertyTransfer implements PropertyChangeNotifier
49  {
50  	private final static Logger log = Logger.getLogger( PropertyTransfer.class );
51  	
52  	public final static String SOURCE_PATH_PROPERTY = PropertyTransfer.class.getName() + "@sourcePath";
53  	public final static String SOURCE_TYPE_PROPERTY = PropertyTransfer.class.getName() + "@sourceProperty";
54  	public final static String SOURCE_STEP_PROPERTY = PropertyTransfer.class.getName() + "@sourceStep";
55  	public final static String TARGET_PATH_PROPERTY = PropertyTransfer.class.getName() + "@targetPath";
56  	public final static String TARGET_TYPE_PROPERTY = PropertyTransfer.class.getName() + "@targetProperty";
57  	public final static String TARGET_STEP_PROPERTY = PropertyTransfer.class.getName() + "@targetStep";
58  	public final static String NAME_PROPERTY = PropertyTransfer.class.getName() + "@name";
59  	public final static String CONFIG_PROPERTY = PropertyTransfer.class.getName() + "@config";
60  	
61  	private TestStep testStep;
62  	
63  	// create local copies since a deleted/changed valuetransfer can be referred to from a result 
64  	private ValueTransferConfig config;
65  	private String sourcePath;
66  	private String sourceType;
67  	private String targetPath;
68  	private String name;
69  	private String targetType;
70  	private String sourceStep;
71  	private String targetStep;
72  	
73  	private TestStep currentTargetStep;
74  	private TestStep currentSourceStep;
75  	private TestStepProperty currentTargetProperty;
76  	private TestStepProperty currentSourceProperty;
77  	
78  	private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport( this );
79  	private StepNameChangeListener stepNameChangeListener = new StepNameChangeListener();
80  	private PropertyNameChangeListener propertyNameChangeListener = new PropertyNameChangeListener();
81  	private TestCase testCase;
82  
83  	private InternalTestSuiteListener testSuiteListener = new InternalTestSuiteListener();
84  
85  	public PropertyTransfer( TestStep testStep )
86  	{
87  		this( testStep, ValueTransferConfig.Factory.newInstance() );
88  	}
89  	
90  	public PropertyTransfer( TestStep testStep, ValueTransferConfig config )
91  	{
92  		this.testStep = testStep;
93  		
94  		if( testStep != null )
95  		{
96  			this.testCase = testStep.getTestCase();
97  			testCase.getTestSuite().addTestSuiteListener( testSuiteListener );
98  		}
99  		
100 		setConfig( config );
101 	}
102 	
103 	void setConfigOnMove( ValueTransferConfig config )
104 	{
105 		this.config = config;
106 	}
107 	
108 	void setConfig(ValueTransferConfig config)
109 	{
110 		releaseListeners();
111 		
112 		this.config = config;
113 		
114 		if( !config.isSetSetNullOnMissingSource() )
115 		{
116 			config.setSetNullOnMissingSource( true );
117 		}
118 		
119 		if( !config.isSetTransferTextContent() )
120 		{
121 			config.setTransferTextContent( true );
122 		}
123 		
124 		sourceStep = config.getSourceStep();
125 		if( sourceStep == null )
126 		{
127 			sourceStep = getSourceStepName();
128 			if( sourceStep != null )
129 				config.setSourceStep( sourceStep );
130 		}
131 		
132 		currentSourceStep = (sourceStep == null || testCase == null ) ? null : testCase.getTestStepByName( sourceStep );
133 
134 		sourceType = config.getSourceType();
135 		currentSourceProperty = currentSourceStep == null || sourceType == null ? null : currentSourceStep.getProperty( sourceType );
136 		
137 		sourcePath = config.getSourcePath();
138 
139 		targetStep = config.getTargetStep();
140 		if( targetStep == null )
141 		{
142 			targetStep = getTargetStepName();
143 			if( targetStep != null )
144 				config.setTargetStep( targetStep );
145 		}
146 		
147 		currentTargetStep = (targetStep == null || testCase == null ) ? null : testCase.getTestStepByName( targetStep );
148 
149 		targetType = config.getTargetType();
150 		currentTargetProperty = currentTargetStep == null || targetType == null ? null : currentTargetStep.getProperty( targetType );
151 		
152 		targetPath = config.getTargetPath();
153 		
154 		name = config.getName();
155 		initListeners();
156 		
157 		propertyChangeSupport.firePropertyChange( CONFIG_PROPERTY, null, null );
158 	}
159 	
160 	private void initListeners()
161 	{
162 		if( currentSourceStep != null )
163 		{
164 			currentSourceStep.addPropertyChangeListener( TestStep.NAME_PROPERTY, stepNameChangeListener );
165 			((WsdlTestStep)currentSourceStep).addTestStepListener( propertyNameChangeListener );
166 		}
167 
168 		if( currentTargetStep != null )
169 		{
170 			currentTargetStep.addPropertyChangeListener( TestStep.NAME_PROPERTY, stepNameChangeListener );
171 			((WsdlTestStep)currentTargetStep).addTestStepListener( propertyNameChangeListener );
172 		}
173 	}
174 	
175 	public void releaseListeners()
176 	{
177 		if( currentSourceStep != null )
178 		{
179 			currentSourceStep.removePropertyChangeListener( TestStep.NAME_PROPERTY, stepNameChangeListener );
180 			((WsdlTestStep)currentSourceStep).removeTestStepListener( propertyNameChangeListener );
181 		}
182 
183 		if( currentTargetStep != null )
184 		{
185 			currentTargetStep.removePropertyChangeListener( TestStep.NAME_PROPERTY, stepNameChangeListener );
186 			((WsdlTestStep)currentTargetStep).removeTestStepListener( propertyNameChangeListener );
187 		}
188 		
189 		PropertyChangeListener[] listeners = propertyChangeSupport.getPropertyChangeListeners();
190 		for( PropertyChangeListener listener : listeners )
191 			propertyChangeSupport.removePropertyChangeListener( listener );
192 	}
193 	
194 	public void release()
195 	{
196 		releaseListeners();
197 		testCase.getTestSuite().removeTestSuiteListener( testSuiteListener );
198 	}
199 	
200 	public ValueTransferConfig getConfig()
201 	{
202 		return config;
203 	}
204 
205 	public String getSourcePath()
206 	{
207 		return sourcePath;
208 	}
209 	
210 	public String getTargetPath()
211 	{
212 		return targetPath;
213 	}
214 	
215 	public TestStepProperty getSourceProperty()
216 	{
217 		if( sourceType == null )
218 			return null;
219 		
220 		if( currentSourceProperty != null )
221 			return currentSourceProperty;
222 		
223 		TestStep actualSourceStep = getSourceStep();
224 		return actualSourceStep == null ? null : actualSourceStep.getProperty( sourceType );
225 	}
226 	
227 	public String [] transferProperties(SubmitContext context) throws PropertyTransferException
228 	{
229 		TestStepProperty sourceProperty = getSourceProperty();
230 		TestStepProperty targetProperty = getTargetProperty();
231 		
232 		try
233 		{
234 			if (sourceProperty == null)
235 				throw new Exception("Missing source property");
236 			if (targetProperty == null)
237 				throw new Exception("Missing target property");
238 			if (sourceProperty.getValue() == null && !getSetNullOnMissingSource() && !getIgnoreEmpty() )
239 				throw new Exception("Source property is null");
240 			
241 			if (!hasSourcePath() && !hasTargetPath())
242 			{
243 				if( !getIgnoreEmpty() || (sourceProperty.getValue() != null && sourceProperty.getValue().length() > 0))
244 					return transferStringToString(sourceProperty, targetProperty);
245 			}
246 			else if (hasSourcePath() && hasTargetPath())
247 			{
248 					return transferXPathToXml(sourceProperty, targetProperty, context);
249 			}
250 			else if (hasSourcePath() && !hasTargetPath())
251 			{
252 				return new String[] { transferXPathToString(sourceProperty, targetProperty, context) };
253 			}
254 			else if (!hasSourcePath() && hasTargetPath())
255 			{
256 				if( !getIgnoreEmpty() || (sourceProperty.getValue() != null && sourceProperty.getValue().length() > 0))
257 					return transferStringToXml(sourceProperty, targetProperty, context);
258 			}
259 		}
260 		catch (Exception e)
261 		{
262 			throw new PropertyTransferException( e.getMessage(), getSourceStepName(), sourceProperty, 
263 					getTargetStepName(), targetProperty );
264 		}		
265 		
266 		return new String[0];
267 	}
268 
269 	private boolean hasTargetPath()
270 	{
271 		String path = getTargetPath();
272 		return path != null && path.trim().length() > 0 ;
273 	}
274 
275 	private boolean hasSourcePath()
276 	{
277 		String path = getSourcePath();
278 		return path != null && path.trim().length() > 0 ;
279 	}
280 
281 	protected String[] transferStringToString(TestStepProperty sourceProperty, TestStepProperty targetProperty)
282 	{
283 		String value = sourceProperty.getValue();
284 		targetProperty.setValue( value );
285 		return new String[]{ value};
286 	}
287 
288 	protected String[] transferXPathToXml( TestStepProperty sourceProperty, TestStepProperty targetProperty, SubmitContext context) throws XmlException, Exception
289 	{
290 		String sourcePropertyValue = sourceProperty.getValue();
291 		XmlObject sourceXmlObject = sourcePropertyValue == null ? null : XmlObject.Factory.parse( sourcePropertyValue );
292 		XmlCursor sourceXml = sourceXmlObject == null ? null : sourceXmlObject.newCursor();
293 		
294 		String targetPropertyValue = targetProperty.getValue();
295 		XmlObject targetXmlObject = XmlObject.Factory.parse( targetPropertyValue );
296 		XmlCursor targetXml = targetXmlObject.newCursor();
297 
298 		XmlCursor lastSource = null;
299 		
300 		try
301 		{		
302 			List<String> result = new ArrayList<String>();
303 
304 			targetXml.selectPath( PropertyExpansionRequestFilter.expandProperties( context, getTargetPath()) );
305 			
306 			if( !targetXml.hasNextSelection() )
307 				throw new Exception( "Missing match for Target path [" + getTargetPath() + "]" );
308 			
309 			if( sourceXml == null )
310 			{
311 				if( getSetNullOnMissingSource() )
312 				{
313 					while( targetXml.toNextSelection() )
314 					{
315 						result.add( setNodeValue( null, targetXml.getDomNode() ));
316 						if( !getTransferToAll())
317 							break;
318 					}
319 				}
320 			}
321 			else if( getUseXQuery() )
322 			{
323 				XmlCursor resultCursor = sourceXml.execQuery( PropertyExpansionRequestFilter.expandProperties( context, getSourcePath()) );
324 				sourceXml.dispose();
325 				sourceXml = resultCursor;
326 				
327 				if( sourceXml.toNextToken() != TokenType.START )
328 				{
329 					if( getSetNullOnMissingSource() )
330 					{
331 						while( targetXml.toNextSelection() )
332 						{
333 							result.add( setNodeValue( null, targetXml.getDomNode() ));
334 							if( !getTransferToAll())
335 								break;
336 						}
337 					}
338 					else if( !getIgnoreEmpty() )
339 						throw new Exception( "Missing match for Source XQuery [" + getSourcePath() + "]" );
340 				}
341 				
342 				boolean hasTarget = targetXml.toNextSelection();
343 				
344 				if( hasTarget )
345 				{
346 					lastSource = sourceXml.newCursor();
347 					result.add( transferXmlValue( sourceXml, targetXml ));
348 				}
349 			}
350 			else
351 			{
352 				sourceXml.selectPath( PropertyExpansionRequestFilter.expandProperties( context, getSourcePath()) );
353 				
354 				if( !sourceXml.hasNextSelection() )
355 				{
356 					if( getSetNullOnMissingSource() )
357 					{
358 						while( targetXml.toNextSelection() )
359 						{
360 							result.add( setNodeValue( null, targetXml.getDomNode() ));
361 							if( !getTransferToAll())
362 								break;
363 						}
364 					}
365 					else if( !getIgnoreEmpty() )
366 						throw new Exception( "Missing match for Source XPath [" + getSourcePath() + "]" );
367 				}
368 				else
369 				{
370 					boolean hasSource = sourceXml.toNextSelection();
371 					boolean hasTarget = targetXml.toNextSelection();
372 					
373 					while( hasSource && hasTarget )
374 					{
375 						if( lastSource != null )
376 							lastSource.dispose();
377 						
378 						lastSource = sourceXml.newCursor();
379 						result.add( transferXmlValue( sourceXml, targetXml ));
380 						
381 						hasSource = sourceXml.toNextSelection();
382 						hasTarget = targetXml.toNextSelection();
383 					}
384 				
385 					if( getTransferToAll() && !hasSource && hasTarget && lastSource != null )
386 					{
387 						while( hasTarget )
388 						{
389 							result.add( transferXmlValue( lastSource, targetXml ));
390 							hasTarget = targetXml.toNextSelection();
391 						}
392 					}
393 				}
394 			}
395 			
396 			if( result.size() > 0 )
397 			{
398 				targetProperty.setValue( targetXmlObject.xmlText( new XmlOptions().setSaveAggressiveNamespaces()) );
399 			}
400 			
401 			return result.toArray( new String[result.size()] );
402 		}
403 		finally
404 		{
405 			sourceXml.dispose();
406 			targetXml.dispose();
407 			
408 			if( lastSource != null )
409 				lastSource.dispose();
410 		}
411 	}
412 
413 	protected String [] transferStringToXml(TestStepProperty sourceProperty, TestStepProperty targetProperty, SubmitContext context) throws XmlException, Exception
414 	{
415 		XmlObject targetXml = XmlObject.Factory.parse( targetProperty.getValue() );
416 		XmlCursor targetCursor = targetXml.newCursor();
417 		
418 		try
419 		{
420 			List<String> result = new ArrayList<String>();
421 			
422 			targetCursor.selectPath( PropertyExpansionRequestFilter.expandProperties( context, 
423 					getTargetPath() ));
424 			
425 			if( !targetCursor.toNextSelection() )
426 				throw new Exception( "Missing match for Target XPath [" + getTargetPath() + "]" );
427 			
428 			String value = sourceProperty.getValue();
429 			
430 			Node targetNode = targetCursor.getDomNode();
431 			setNodeValue(value, targetNode);
432 			
433 			result.add( value );
434 			
435 			if( getTransferToAll() )
436 			{
437 				while( targetCursor.toNextSelection())
438 				{
439 					targetNode = targetCursor.getDomNode();
440 					setNodeValue(value, targetNode);
441 					
442 					result.add( value );
443 				}
444 			}
445 			
446 			targetProperty.setValue( targetXml.xmlText( new XmlOptions().setSaveAggressiveNamespaces()) );
447 			
448 			return result.toArray( new String[result.size()] );
449 		}
450 		finally
451 		{
452 			targetCursor.dispose();
453 		}
454 	}
455 
456 	private String setNodeValue(String value, Node node) throws Exception
457 	{
458 		short targetNodeType = node.getNodeType();
459 		
460 		if( targetNodeType == Node.DOCUMENT_FRAGMENT_NODE )
461 		{
462 			node = ((DocumentFragment) node ).getFirstChild();
463 			if( node != null )
464 				targetNodeType = node.getNodeType();
465 			else
466 				throw new Exception( "Missing source value for " + getSourcePropertyName() );
467 		}
468 		
469 		if( targetNodeType == Node.TEXT_NODE || targetNodeType == Node.ATTRIBUTE_NODE )
470 		{
471 			node.setNodeValue( value );
472 		}
473 		else if( targetNodeType == Node.ELEMENT_NODE )
474 		{
475 			XmlUtils.setElementText( (Element) node, value );
476 		}
477 		else
478 		{
479 			throw new Exception( "Failed to set value to node [" + node.toString() + "] of type [" + targetNodeType + "]" ); 
480 		}
481 		
482 		return value;
483 	}
484 
485 	protected String transferXPathToString( TestStepProperty sourceProperty, TestStepProperty targetProperty, SubmitContext context) throws XmlException, Exception
486 	{
487 		String sourceValue = sourceProperty.getValue();
488 		
489 		if( sourceValue == null )
490 		{
491 			if( !getIgnoreEmpty() )
492 				throw new Exception( "Missing match for Source path [" + getSourcePath() + "]" );
493 			
494 			if( getSetNullOnMissingSource() )
495 				targetProperty.setValue( null );
496 		}
497 		
498 		
499 		XmlObject sourceXml = sourceValue == null ? null : XmlObject.Factory.parse( sourceValue );
500 		XmlCursor sourceCursor = sourceValue == null ? null : sourceXml.newCursor();
501 		
502 		try
503 		{
504 			String value = null;
505 			
506 			String xquery = PropertyExpansionRequestFilter.expandProperties( context, getSourcePath());
507 			if( getUseXQuery() )
508 			{
509 				XmlCursor resultCursor = sourceCursor.execQuery( xquery );
510 				sourceCursor.dispose();
511 				sourceCursor = resultCursor;
512 			}
513 			else
514 			{
515 				sourceCursor.selectPath( xquery );
516 			}
517 			
518 			if( !getUseXQuery() && !sourceCursor.toNextSelection() )
519 			{
520 				if( !getSetNullOnMissingSource() && !getIgnoreEmpty() )
521 					throw new Exception( "Missing match for Source path [" + getSourcePath() + "]" );
522 			}
523 			else if( getUseXQuery() && sourceCursor.toNextToken() != TokenType.START )
524 			{
525 				if( !getSetNullOnMissingSource() && !getIgnoreEmpty() )
526 					throw new Exception( "Missing match for Source path [" + getSourcePath() + "]" );
527 			}
528 			else
529 			{
530 				Node sourceNode = sourceCursor.getDomNode();
531 				short sourceNodeType = sourceNode.getNodeType();
532 				
533 				if( sourceNodeType == Node.DOCUMENT_FRAGMENT_NODE )
534 				{
535 					sourceNode = ((DocumentFragment) sourceNode ).getFirstChild();
536 					if( sourceNode != null )
537 						sourceNodeType = sourceNode.getNodeType();
538 					else
539 						throw new Exception( "Missing source value for " + getSourcePropertyName() );
540 				}
541 				
542 				if( sourceNodeType == Node.TEXT_NODE || sourceNodeType == Node.ATTRIBUTE_NODE )
543 				{
544 					value = sourceNode.getNodeValue();
545 				}
546 				else if( sourceNodeType == Node.ELEMENT_NODE )
547 				{
548 					value = XmlUtils.getElementText( (Element) sourceNode );
549 					if( value == null )
550 					{
551 						value = sourceCursor.getObject().xmlText( new XmlOptions().setSaveOuter().setSaveAggressiveNamespaces().setSavePrettyPrint());
552 					}
553 				}
554 			}
555 			
556 			if( !getIgnoreEmpty() || (value != null && value.length() >  0) ) 
557 				targetProperty.setValue( value );
558 			else
559 				value = "";
560 			
561 			return value;
562 		}
563 		finally
564 		{
565 			sourceCursor.dispose();
566 		}
567 	}
568 
569 	/***
570 	 * Method called for transferring between 2 xml properties..
571 	 */
572 	
573 	private String transferXmlValue(XmlCursor source, XmlCursor dest) throws Exception
574 	{
575 		// just copy if nodes are of same type
576 		Node destNode = dest.getDomNode();
577 		Node sourceNode = source.getDomNode();
578 		short destNodeType = destNode.getNodeType();
579 		short sourceNodeType = sourceNode.getNodeType();
580 		String value = null;
581 		
582 		if( sourceNodeType == Node.DOCUMENT_FRAGMENT_NODE )
583 		{
584 			sourceNode = ((DocumentFragment) sourceNode ).getFirstChild();
585 			if( sourceNode != null )
586 				sourceNodeType = sourceNode.getNodeType();
587 			else
588 				throw new Exception( "Missing source value for " + source );
589 		}
590 		
591 		// same type of node?
592 		if( destNodeType == sourceNodeType )
593 		{
594 			if( destNodeType == Node.TEXT_NODE || destNodeType == Node.ATTRIBUTE_NODE )
595 			{
596 				value = sourceNode.getNodeValue();
597 				if( !getIgnoreEmpty() || (value != null && value.length() > 0) )
598 					destNode.setNodeValue( value );
599 			}
600 			else if( config.getTransferTextContent() && destNodeType == Node.ELEMENT_NODE )
601 			{
602 				value = XmlUtils.getElementText( (Element) sourceNode);
603 				if( value == null && sourceNode.getFirstChild() != null )
604 				{
605 					value = source.getObject().xmlText( new XmlOptions().setSaveOuter().setSaveAggressiveNamespaces().setSavePrettyPrint() );
606 					destNode.getParentNode().replaceChild( destNode.getOwnerDocument().importNode( sourceNode, true ), destNode );
607 				}
608 				else if( !getIgnoreEmpty() || (value != null && value.length() > 0) )
609 					XmlUtils.setElementText( (Element) destNode, value );
610 			}
611 			else
612 			{
613 				destNode = destNode.getParentNode().replaceChild( 
614 						destNode.getOwnerDocument().importNode( sourceNode, true ), destNode );
615 				
616 				value = dest.xmlText();
617 			}
618 		}
619 		// text to attribute?
620 		else if( (sourceNodeType == Node.TEXT_NODE && destNodeType == Node.ATTRIBUTE_NODE) ||
621 				(sourceNodeType == Node.ATTRIBUTE_NODE && destNodeType == Node.TEXT_NODE) )
622 		{
623 			value = sourceNode.getNodeValue();
624 			if( !getIgnoreEmpty() || (value != null && value.length() > 0) )
625 				destNode.setNodeValue( value );
626 		}
627 		else if( sourceNodeType == Node.ELEMENT_NODE && 
628 				destNodeType == Node.ATTRIBUTE_NODE || destNodeType == Node.TEXT_NODE )
629 		{
630 			value = XmlUtils.getElementText( (Element) sourceNode );
631 			if( !getIgnoreEmpty() || (value != null && value.length() > 0) )
632 				destNode.setNodeValue( value );
633 		}
634 		else if( destNodeType == Node.ELEMENT_NODE && 
635 				sourceNodeType == Node.ATTRIBUTE_NODE || sourceNodeType == Node.TEXT_NODE )
636 		{
637 			// hmm.. not sure xmlbeans handles this ok
638 			value = sourceNode.getNodeValue();
639 			if( !getIgnoreEmpty() || (value != null && value.length() > 0) )
640 				XmlUtils.setElementText( (Element) destNode, value );
641 		}
642 
643 		return value;
644 	}
645 	
646 	/***
647 	 * Returns the name of the source property. 
648 	 */
649 	
650 	public String getSourcePropertyName()
651 	{
652 		if( sourceType == null )
653 			return null;
654 		
655 		if( currentSourceProperty != null )
656 			return currentSourceProperty.getName();
657 		
658 		TestStep actualSourceStep = getSourceStep();
659 		if( actualSourceStep == null )
660 			return sourceType;
661 		
662 		TestStepProperty property = actualSourceStep.getProperty( sourceType );
663 		return property == null ? sourceType : property.getName();
664 	}
665 	
666 	public void setSourcePropertyName( String name )
667 	{
668 		String old = getSourcePropertyName();
669 		
670 		// check for change
671 		if( (name == null && old == null) || (name != null && old != null && name.equals( old ))) return;
672 
673 		// update
674 		sourceType = name;
675 		config.setSourceType( name );
676 		
677 		// update actual property
678 		TestStep sourceStep2 = getSourceStep();
679 		currentSourceProperty = sourceStep2 != null && sourceType != null ? 
680 				sourceStep2.getProperty( sourceType ) : null;
681 		
682 		// notify!
683 		propertyChangeSupport.firePropertyChange( SOURCE_TYPE_PROPERTY, old, name );
684 	}
685 	
686 	public TestStepProperty getTargetProperty()
687 	{
688 		if( targetType == null )
689 			return null;
690 		
691 		if( currentTargetProperty != null )
692 			return currentTargetProperty;
693 		
694 		TestStep actualTargetStep = getTargetStep();
695 		return actualTargetStep == null ? null : actualTargetStep.getProperty( targetType );
696 	}
697 	
698 	public String getTargetPropertyName()
699 	{
700 		if( targetType == null )
701 			return null;
702 		
703 		if( currentTargetProperty != null )
704 			return currentTargetProperty.getName();
705 		
706 		TestStep actualTargetStep = getTargetStep();
707 		TestStepProperty property = actualTargetStep == null ? null : actualTargetStep.getProperty( targetType );
708 		return actualTargetStep == null || property == null ? targetType: property.getName();
709 	}
710 	
711 	public void setTargetPropertyName( String name )
712 	{
713 		String old = getTargetPropertyName();
714 		
715 		// check for change
716 		if( (name == null && old == null) || (name != null && old != null && name.equals( old ))) return;
717 
718 		// update
719 		targetType = name;
720 		config.setTargetType( name );
721 		
722 		// update actual property
723 		TestStep targetStep2 = getTargetStep();
724 		
725 		currentTargetProperty = targetStep2 != null && targetType != null ? 
726 				targetStep2.getProperty( targetType ) : null;
727 		
728 		// notify!
729 		propertyChangeSupport.firePropertyChange( TARGET_TYPE_PROPERTY, old, name );
730 	}
731 	
732 	public String getName()
733 	{
734 		return config.getName();
735 	}
736 	
737 	public void setSourcePath( String path )
738 	{
739 		String old = sourcePath;
740 		sourcePath = path;
741 		config.setSourcePath( path );
742 		propertyChangeSupport.firePropertyChange( SOURCE_PATH_PROPERTY, old, path );
743 	}
744 	
745 	public void setTargetPath( String path )
746 	{
747 		String old = targetPath;
748 		targetPath = path;
749 		config.setTargetPath( path );
750 		propertyChangeSupport.firePropertyChange( TARGET_PATH_PROPERTY, old, path );
751 	}
752 	
753 	public void setName( String name )
754 	{
755 		String old = this.name;
756 		this.name = name;
757 		config.setName( name );
758 		propertyChangeSupport.firePropertyChange( NAME_PROPERTY, old, name );
759 	}
760 
761 	public TestStep getSourceStep()
762 	{
763 		String sourceStepName = getSourceStepName();
764 		return sourceStepName == null ? null : testCase.getTestStepByName( sourceStepName );
765 	}
766 
767 	public String getSourceStepName()
768 	{
769 		if( sourceStep != null )
770 			return sourceStep;
771 		
772 		if( testCase == null )
773 			return null;
774 		
775 		TestStep step = testCase.findPreviousStepOfType( this.testStep, WsdlTestRequestStep.class );
776 		return step == null ? null : step.getName();
777 	}
778 
779 	public void setSourceStepName(String sourceStep)
780 	{
781 		String old = getSourceStepName();
782 		
783 		//	 check for change
784 		if( (sourceStep == null && old == null) || (sourceStep != null && old != null && sourceStep.equals( old ))) return;
785 		
786 		if( sourceStep == null )
787 			log.debug( "Setting sourceStep for transfer [" + getName() + "] to null" );
788 		
789 		this.sourceStep = sourceStep;
790 		config.setSourceStep( sourceStep );
791 		
792 		if( currentSourceStep != null )
793 		{
794 			currentSourceStep.removePropertyChangeListener( stepNameChangeListener );
795 			((WsdlTestStep)currentSourceStep).removeTestStepListener( propertyNameChangeListener );
796 		}
797 		
798 		currentSourceStep = (WsdlTestStep) (sourceStep == null ? null : testStep.getTestCase().getTestStepByName( sourceStep ));
799 		if( currentSourceStep != null )
800 		{
801 			currentSourceStep.addPropertyChangeListener( TestStep.NAME_PROPERTY, stepNameChangeListener );
802 			((WsdlTestStep)currentSourceStep).addTestStepListener( propertyNameChangeListener );
803 		}
804 		
805 		propertyChangeSupport.firePropertyChange( SOURCE_STEP_PROPERTY, old, sourceStep );
806 		setSourcePropertyName( null );
807 	}
808 	
809 	public TestStep getTargetStep()
810 	{
811 		String targetStepName = getTargetStepName();
812 		return targetStepName == null ? null : testCase.getTestStepByName( targetStepName );
813 	}
814 
815 	public String getTargetStepName()
816 	{
817 		if( targetStep != null )
818 			return targetStep;
819 		
820 		if( testCase == null )
821 			return null;
822 		
823 		TestStep step = testCase.findNextStepOfType( this.testStep, WsdlTestRequestStep.class );
824 		return step == null ? null : step.getName();
825 	}
826 
827 	public void setTargetStepName(String targetStep)
828 	{
829 		String old = getTargetStepName();
830 		
831 		//	 check for change
832 		if( (targetStep == null && old == null) || (targetStep != null && old != null && targetStep.equals( old ))) return;
833 		
834 		if( targetStep == null )
835 			log.debug( "Setting targetStep for transfer [" + getName() + "] to null" );
836 		
837 		this.targetStep = targetStep;
838 		config.setTargetStep( targetStep );
839 		
840 		if( currentTargetStep != null )
841 		{
842 			currentTargetStep.removePropertyChangeListener( stepNameChangeListener );
843 			((WsdlTestStep)currentTargetStep).removeTestStepListener( propertyNameChangeListener );
844 		}
845 		
846 		currentTargetStep = (WsdlTestStep) (targetStep == null ? null : testStep.getTestCase().getTestStepByName( targetStep ));
847 		if( currentTargetStep != null )
848 		{
849 			currentTargetStep.addPropertyChangeListener( TestStep.NAME_PROPERTY, stepNameChangeListener );
850 			((WsdlTestStep)currentTargetStep).addTestStepListener( propertyNameChangeListener );
851 		}
852 		
853 		propertyChangeSupport.firePropertyChange( TARGET_STEP_PROPERTY, old, targetStep );
854 		setTargetPropertyName( null );
855 	}
856 
857 	public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener)
858 	{
859 		propertyChangeSupport.addPropertyChangeListener( propertyName, listener );
860 	}
861 
862 	public void addPropertyChangeListener(PropertyChangeListener listener)
863 	{
864 		propertyChangeSupport.addPropertyChangeListener( listener );
865 	}
866 
867 	public void removePropertyChangeListener(PropertyChangeListener listener)
868 	{
869 		propertyChangeSupport.removePropertyChangeListener( listener );
870 	}
871 
872 	public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener)
873 	{
874 		propertyChangeSupport.removePropertyChangeListener( propertyName, listener );
875 	}
876 	
877 	public boolean getFailOnError()
878 	{
879 		return config.getFailOnError();
880 	}
881 	
882 	public void setFailOnError( boolean failOnError )
883 	{
884 		config.setFailOnError( failOnError );
885 	}
886 	
887 	public boolean getTransferToAll()
888 	{
889 		return config.getTransferToAll();
890 	}
891 	
892 	public void setTransferToAll( boolean transferToAll )
893 	{
894 		config.setTransferToAll( transferToAll );
895 	}
896 	
897 	public boolean getUseXQuery()
898 	{
899 		return config.getUseXQuery();
900 	}
901 	
902 	public void setUseXQuery( boolean useXQuery )
903 	{
904 		config.setUseXQuery( useXQuery );
905 	}
906 	
907 	public boolean getIgnoreEmpty()
908 	{
909 		return config.getIgnoreEmpty();
910 	}
911 	
912 	public void setIgnoreEmpty( boolean ignoreEmpty )
913 	{
914 		config.setIgnoreEmpty( ignoreEmpty );
915 	}
916 	
917 	public boolean getSetNullOnMissingSource()
918 	{
919 		return config.getSetNullOnMissingSource();
920 	}
921 	
922 	public void setSetNullOnMissingSource( boolean setNullOnMissingSource )
923 	{
924 		config.setSetNullOnMissingSource( setNullOnMissingSource );
925 	}
926 	
927 	public boolean getTransferTextContent()
928 	{
929 		return config.getTransferTextContent();
930 	}
931 	
932 	public void setTransferTextContent( boolean transferTextContent )
933 	{
934 		config.setTransferTextContent( transferTextContent );
935 	}
936 	
937 	private final class InternalTestSuiteListener extends TestSuiteListenerAdapter
938 	{
939 		public void testStepRemoved(TestStep testStep, int index)
940 		{
941 			if( testStep.getTestCase() == testCase )
942 			{
943 				String stepName = testStep.getName();
944 				if( stepName.equals( sourceStep ) )
945 				{
946 					setSourceStepName( null );
947 				}
948 			
949 				if( stepName.equals( targetStep ) )
950 				{
951 					setTargetStepName( null );
952 				}
953 			}
954 		}
955 	}
956 
957 	/***
958 	 * Handle changes to source/target testStep names
959 	 * 
960 	 * @author Ole.Matzura
961 	 */
962 	
963 	private class StepNameChangeListener implements PropertyChangeListener
964 	{
965 		public void propertyChange(PropertyChangeEvent evt)
966 		{
967 			String oldName = (String) evt.getOldValue();
968 			String newValue = (String) evt.getNewValue();
969 			
970 			if( newValue == null )
971 			{
972 				log.error( "Tried to change stepname to null!" );
973 				Thread.dumpStack();
974 				return;
975 			}
976 			
977 			if( oldName.equals( sourceStep ))
978 			{
979 				sourceStep = newValue;
980 				config.setSourceStep( sourceStep );
981 				propertyChangeSupport.firePropertyChange( SOURCE_STEP_PROPERTY, oldName, sourceStep );
982 			}
983 			
984 			if( oldName.equals( targetStep ))
985 			{
986 				targetStep = newValue;
987 				config.setTargetStep( targetStep );
988 				propertyChangeSupport.firePropertyChange( TARGET_STEP_PROPERTY, oldName, targetStep );
989 			}
990 		}
991 	}
992 	
993 	/***
994 	 * Handle changes to source/target property names
995 	 * 
996 	 * @author Ole.Matzura
997 	 */
998 	
999 	private class PropertyNameChangeListener extends WsdlTestStepListenerAdapter
1000 	{
1001 		public void propertyRenamed(String oldName, String newName)
1002 		{
1003 			if( oldName.equals( sourceType ))
1004 			{
1005 				sourceType = newName;
1006 				config.setSourceType( sourceType );
1007 				propertyChangeSupport.firePropertyChange( SOURCE_TYPE_PROPERTY, oldName, sourceType );
1008 			}
1009 			
1010 			if( oldName.equals( targetType ))
1011 			{
1012 				targetType = newName;
1013 				config.setTargetType( targetType );
1014 				propertyChangeSupport.firePropertyChange( TARGET_TYPE_PROPERTY, oldName, targetType );
1015 			}
1016 		}
1017 
1018 		public void propertyRemoved(String name)
1019 		{
1020 			if( name.equals( sourceType ))
1021 			{
1022 				log.warn( "source property for transfer [" + getName() + "] in teststep [" + 
1023 						testStep.getName() + "] set to null, was [" + name + "]" );  
1024 				
1025 				setSourcePropertyName( null );
1026 			}
1027 
1028 			if( name.equals( targetType ))
1029 			{
1030 				log.warn( "target property for transfer [" + getName() + "] in teststep [" + 
1031 						testStep.getName() + "] set to null, was [" + name + "]" );  
1032 				
1033 				setTargetPropertyName( null );
1034 			}
1035 		}
1036 	}
1037 }