View Javadoc

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