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 }