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.support.xml;
    14
    15  import java.io.IOException;
    16  import java.io.InputStream;
    17  import java.io.OutputStreamWriter;
    18  import java.io.StringReader;
    19  import java.io.StringWriter;
    20  import java.io.Writer;
    21  import java.util.ArrayList;
    22  import java.util.Collection;
    23  import java.util.HashMap;
    24  import java.util.HashSet;
    25  import java.util.Iterator;
    26  import java.util.List;
    27  import java.util.Map;
    28  import java.util.Set;
    29  import java.util.StringTokenizer;
    30
    31  import javax.xml.namespace.QName;
    32  import javax.xml.parsers.DocumentBuilder;
    33  import javax.xml.parsers.DocumentBuilderFactory;
    34  import javax.xml.parsers.ParserConfigurationException;
    35
    36  import org.apache.log4j.Logger;
    37  import org.apache.xmlbeans.XmlCursor;
    38  import org.apache.xmlbeans.XmlException;
    39  import org.apache.xmlbeans.XmlObject;
    40  import org.apache.xmlbeans.XmlOptions;
    41  import org.apache.xmlbeans.XmlCursor.TokenType;
    42  import org.w3c.dom.Attr;
    43  import org.w3c.dom.Document;
    44  import org.w3c.dom.DocumentFragment;
    45  import org.w3c.dom.Element;
    46  import org.w3c.dom.NamedNodeMap;
    47  import org.w3c.dom.Node;
    48  import org.w3c.dom.NodeList;
    49  import org.w3c.dom.Text;
    50  import org.xml.sax.InputSource;
    51  import org.xml.sax.SAXException;
    52
    53  import com.eviware.soapui.SoapUI;
    54  import com.eviware.soapui.impl.wsdl.WsdlInterface;
    55  import com.eviware.soapui.impl.wsdl.support.soap.SoapVersion;
    56  import com.eviware.soapui.support.StringUtils;
    57  import com.eviware.soapui.support.types.StringToStringMap;
    58
    59  /***
    60   * General XML-related utilities
    61   */
    62
    63  public final class XmlUtils
    64  {
    65     private static DocumentBuilder documentBuilder;
    66     private final static Logger log = Logger.getLogger( XmlUtils.class );
    67
    68     public static class XPathData
    69     {
    70        private StringToStringMap nsMap = new StringToStringMap();
    71        private List<String> pathComponents = new ArrayList<String>();
    72
    73        public XPathData(StringToStringMap nsMap, List<String> pathComponents)
    74        {
    75           this.nsMap = nsMap;
    76           this.pathComponents = pathComponents;
    77        }
    78
    79        @Override
    80        public boolean equals(Object obj)
    81        {
    82           if(obj.getClass() != XPathData.class)
    83              return false;
    84
    85           XPathData other = (XPathData) obj;
    86           return this.getCanonicalPath().equals( other.getCanonicalPath() );
    87        }
    88
    89        @Override
    90        public int hashCode()
    91        {
    92           return this.getCanonicalPath().hashCode();
    93        }
    94
    95        public String buildXPath()
    96        {
    97           return buildXPath(null);
    98        }
    99
    100       public String buildXPath(XPathModifier modifier)
    101       {
    102          StringBuffer xpath = new StringBuffer();
    103
    104          for( Iterator<String> i = nsMap.keySet().iterator(); i.hasNext(); )
    105          {
    106             String ns = i.next();
    107             xpath.append( "declare namespace " + nsMap.get( ns ) + "='" + ns + "';\n");
    108          }
    109
    110          if( modifier != null )
    111             modifier.beforeSelector( xpath );
    112
    113          xpath.append( "/" );
    114
    115          for( int c = pathComponents.size()-1; c >= 0; c-- )
    116          {
    117             xpath.append( "/" ).append( pathComponents.get( c ));
    118          }
    119
    120          if( modifier != null )
    121             modifier.afterSelector( xpath );
    122
    123          return xpath.toString();
    124       }
    125
    126       public String getPath()
    127       {
    128          StringBuffer buf = new StringBuffer();
    129          buf.append( "/" );
    130
    131          for( int c = pathComponents.size()-1; c >= 0; c-- )
    132          {
    133             buf.append( "/" ).append( pathComponents.get( c ));
    134          }
    135
    136          return buf.toString();
    137       }
    138
    139       /*** Get a path with all namespaces replaced. */
    140       public String getCanonicalPath()
    141       {
    142          HashMap<String,String> inverseNsMap = new HashMap<String,String>();
    143          for(String key : nsMap.keySet())
    144          {
    145             String value = nsMap.get(key);
    146             inverseNsMap.put(value, key);
    147          }
    148
    149          StringBuffer buf = new StringBuffer();
    150          buf.append( "/" );
    151
    152          for( int c = pathComponents.size()-1; c >= 0; c-- )
    153          {
    154             buf.append( "/" );
    155             String s = pathComponents.get( c );
    156             String[] words = s.split(":");
    157             if(words.length == 2)
    158             {
    159                String ns = inverseNsMap.get(words[0]);
    160                if(ns != null)
    161                {
    162                   buf.append( ns ).append( ":" ).append( words[1] );
    163                }
    164                else
    165                {
    166                   buf.append( s );
    167                }
    168             }
    169             else
    170             {
    171                buf.append( s );
    172             }
    173          }
    174
    175          return buf.toString();
    176       }
    177
    178       /*** Get a path with no namespaces or namespace prefixes. */
    179       public String getShortPath()
    180       {
    181          StringBuffer buf = new StringBuffer();
    182          buf.append( "/" );
    183
    184          for( int c = pathComponents.size()-1; c >= 0; c-- )
    185          {
    186             buf.append( "/" );
    187             String s = pathComponents.get( c );
    188             String[] words = s.split(":");
    189             if(words.length == 2)
    190             {
    191                buf.append( words[1] );
    192             }
    193             else
    194             {
    195                buf.append( s );
    196             }
    197          }
    198
    199          return buf.toString();
    200       }
    201
    202       public Collection<String> getNamespaces()
    203       {
    204          return nsMap.keySet();
    205       }
    206    }
    207
    208    static synchronized public Document parse( InputStream in )
    209    {
    210       try
    211       {
    212          return ensureDocumentBuilder().parse( in );
    213       }
    214       catch( Exception e )
    215       {
    216       	log.error( "Error parsing InputStream; " + e.getMessage(), e );
    217       }
    218
    219       return null;
    220    }
    221
    222    static synchronized public Document parse( String fileName ) throws IOException
    223    {
    224       try
    225       {
    226          return  ensureDocumentBuilder().parse( fileName );
    227       }
    228       catch( SAXException e )
    229       {
    230       	log.error( "Error parsing fileName [" + fileName + "]; " + e.getMessage(), e );
    231       }
    232
    233       return null;
    234    }
    235
    236    public static String entitize( String xml )
    237    {
    238    	return xml.replaceAll( "&", "&amp;" ).replaceAll( "<", "&lt;" ).replaceAll( ">", "&gt;" ).
    239    		replaceAll( "\"", "&quot;" ).replaceAll( "'", "&apos;" );
    240    }
    241
    242    static synchronized public Document parse( InputSource inputSource ) throws IOException
    243    {
    244       try
    245       {
    246          return ensureDocumentBuilder().parse( inputSource );
    247       }
    248       catch( SAXException e )
    249       {
    250       	throw new IOException( e.toString() );
    251       }
    252    }
    253
    254    private static DocumentBuilder ensureDocumentBuilder()
    255    {
    256       if( documentBuilder == null )
    257       {
    258          try
    259          {
    260             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    261             dbf.setNamespaceAware( true );
    262 				documentBuilder = dbf.newDocumentBuilder();
    263          }
    264          catch( ParserConfigurationException e )
    265          {
    266          	log.error( "Error creating DocumentBuilder; " + e.getMessage() );
    267          }
    268       }
    269
    270       return documentBuilder;
    271    }
    272
    273    public static void serializePretty( Document document )
    274    {
    275       try
    276       {
    277          serializePretty( document, new OutputStreamWriter( System.out ) );
    278       }
    279       catch( IOException e )
    280       {
    281          log.error( "Failed to seraialize: " + e );
    282       }
    283    }
    284
    285    public static void serializePretty( Document dom, Writer writer )
    286       throws IOException
    287    {
    288    	try
    289 		{
    290 			XmlObject xmlObject = XmlObject.Factory.parse(dom.getDocumentElement());
    291 			serializePretty(xmlObject, writer);
    292 		}
    293 		catch (Exception e)
    294 		{
    295 			throw new IOException( e.toString() );
    296 		}
    297    }
    298
    299    public static void serializePretty( XmlObject xmlObject, Writer writer ) throws IOException
    300 	{
    301 		XmlOptions options = new XmlOptions();
    302 		options.setSavePrettyPrint();
    303 		options.setSavePrettyPrintIndent( 3 );
    304 		options.setSaveNoXmlDecl();
    305 		options.setSaveAggressiveNamespaces();
    306 		xmlObject.save(writer, options);
    307 	}
    308
    309    public static void serialize( Document dom, Writer writer )
    310    throws IOException
    311 	{
    312    	serialize( dom.getDocumentElement(), writer );
    313 	}
    314
    315    public static void serialize( Element elm, Writer writer )
    316    throws IOException
    317 	{
    318    	try
    319 		{
    320 			XmlObject xmlObject = XmlObject.Factory.parse(elm);
    321 			xmlObject.save( writer );
    322 		}
    323 		catch (XmlException e)
    324 		{
    325 			throw new IOException( e.toString() );
    326 		}
    327 	}
    328
    329    static public void setElementText( Element elm, String text )
    330    {
    331       Node node = elm.getFirstChild();
    332       if( node == null  )
    333       {
    334       	if( text != null)
    335       		elm.appendChild( elm.getOwnerDocument().createTextNode( text ) );
    336       }
    337       else if( node.getNodeType() == Node.TEXT_NODE )
    338       {
    339          if( text == null )
    340             node.getParentNode().removeChild( node );
    341          else
    342             node.setNodeValue( text );
    343       }
    344       else if( text != null )
    345       {
    346          Text textNode = node.getOwnerDocument().createTextNode( text );
    347          elm.insertBefore( textNode, elm.getFirstChild() );
    348       }
    349    }
    350
    351    public static String getChildElementText( Element elm, String name )
    352    {
    353       Element child = getFirstChildElement( elm, name );
    354       return child == null ? null : getElementText( child );
    355    }
    356
    357    public static Element getFirstChildElement( Element elm )
    358    {
    359    	return getFirstChildElement( elm, null );
    360    }
    361
    362    public static Element getFirstChildElement( Element elm, String name )
    363    {
    364    	if( elm == null )
    365    		return null;
    366
    367       NodeList nl = elm.getChildNodes();
    368       for( int c = 0; c < nl.getLength(); c++ )
    369       {
    370          Node node = nl.item( c );
    371          if( node.getNodeType() == Node.ELEMENT_NODE && (name == null || node.getNodeName().equals( name )) )
    372             return (Element) node;
    373       }
    374
    375       return null;
    376    }
    377
    378    public static Element getFirstChildElementNS( Element elm, String tns, String localName )
    379    {
    380    	if( tns == null && localName == null )
    381    		return getFirstChildElement( elm );
    382
    383    	if( tns == null )
    384    		return getFirstChildElement( elm, localName );
    385
    386       NodeList nl = elm.getChildNodes();
    387       for( int c = 0; c < nl.getLength(); c++ )
    388       {
    389          Node node = nl.item( c );
    390          if( node.getNodeType() != Node.ELEMENT_NODE ) continue;
    391
    392          if( localName == null && tns.equals( node.getNamespaceURI() ))
    393          	return ( Element ) node;
    394
    395          if( localName != null && tns.equals( node.getNamespaceURI() ) && localName.equals( node.getLocalName() ))
    396          	return ( Element ) node;
    397       }
    398
    399       return null;
    400    }
    401
    402    static public String getElementText( Element elm )
    403    {
    404       Node node = elm.getFirstChild();
    405       if( node != null && node.getNodeType() == Node.TEXT_NODE )
    406          return node.getNodeValue();
    407
    408       return null;
    409    }
    410
    411    static public String getFragmentText( DocumentFragment elm )
    412    {
    413       Node node = elm.getFirstChild();
    414       if( node != null && node.getNodeType() == Node.TEXT_NODE )
    415          return node.getNodeValue();
    416
    417       return null;
    418    }
    419
    420    public static String getChildElementText( Element elm, String name, String defaultValue )
    421    {
    422       String result = getChildElementText( elm, name );
    423       return result == null ? defaultValue : result;
    424    }
    425
    426    static public String getNodeValue( Node node )
    427    {
    428    	if( node.getNodeType() == Node.ELEMENT_NODE )
    429    		return getElementText( (Element) node );
    430    	else if( node.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE )
    431    		return getFragmentText( (DocumentFragment) node );
    432    	else
    433    		return node.getNodeValue();
    434    }
    435
    436    public static Node createNodeFromPath( Element modelElement, String path )
    437    {
    438       Document document = modelElement.getOwnerDocument();
    439       StringTokenizer st = new StringTokenizer( path, "/" );
    440       while( st.hasMoreTokens() )
    441       {
    442          String t = st.nextToken();
    443
    444          if( st.hasMoreTokens() )
    445          {
    446             if( t.equals( ".." ) )
    447             {
    448                modelElement = (Element) modelElement.getParentNode();
    449             }
    450             else
    451             {
    452                Element elm = getFirstChildElement( modelElement, t );
    453                if( elm == null )
    454                   modelElement = (Element) modelElement.insertBefore(
    455                      document.createElement( t ),
    456                      getFirstChildElement( modelElement, t ) );
    457                else
    458                   modelElement = elm;
    459             }
    460          }
    461          else
    462          {
    463             modelElement = (Element) modelElement.insertBefore(
    464                document.createElement( t ),
    465                getFirstChildElement( modelElement, t ) );
    466          }
    467       }
    468
    469       return modelElement;
    470    }
    471
    472    public static Element addChildElement( Element element, String name, String text )
    473    {
    474       Document document = element.getOwnerDocument();
    475       Element result = (Element) element.appendChild( document.createElement( name ) );
    476       if( text != null )
    477          result.appendChild( document.createTextNode( text ) );
    478
    479       return result;
    480    }
    481
    482    public static void setChildElementText( Element element, String name, String text )
    483    {
    484       Element elm = getFirstChildElement( element, name );
    485       if( elm == null )
    486       {
    487          elm = element.getOwnerDocument().createElement( name );
    488          element.appendChild( elm );
    489       }
    490
    491       setElementText( elm, text );
    492    }
    493
    494    public static Document parseXml( String xmlString ) throws IOException
    495    {
    496       return parse( new InputSource( new StringReader( xmlString )));
    497    }
    498
    499    public static void dumpParserErrors(XmlObject xmlObject)
    500    {
    501       List errors = new ArrayList();
    502       xmlObject.validate(new XmlOptions().setErrorListener(errors));
    503       for (Iterator i = errors.iterator(); i.hasNext();)
    504       {
    505          System.out.println(i.next());
    506       }
    507    }
    508
    509 	public static String transferValues(String source, String dest)
    510 	{
    511 		if( StringUtils.isNullOrEmpty( source ) || StringUtils.isNullOrEmpty( dest ))
    512 			return dest;
    513
    514 		XmlCursor cursor = null;
    515 		try
    516 		{
    517 			XmlObject sourceXml = XmlObject.Factory.parse( source );
    518 			XmlObject destXml = XmlObject.Factory.parse( dest );
    519
    520 			cursor = sourceXml.newCursor();
    521 			cursor.toNextToken();
    522 			while( !cursor.isEnddoc() )
    523 			{
    524 				while( !cursor.isContainer() && !cursor.isEnddoc() )
    525 					cursor.toNextToken();
    526
    527 				if( cursor.isContainer() )
    528 				{
    529 					Element elm = ( Element ) cursor.getDomNode();
    530 					String path = createXPath( elm );
    531 					XmlObject[] paths = destXml.selectPath( path );
    532 					if( paths != null && paths.length > 0 )
    533 					{
    534 						Element elm2 = ( Element ) paths[0].getDomNode();
    535
    536 						// transfer attributes
    537 						NamedNodeMap attributes = elm.getAttributes();
    538 						for( int c = 0; c < attributes.getLength(); c++ )
    539 						{
    540 							Attr attr = (Attr) attributes.item( c );
    541 							elm2.setAttribute( attr.getNodeName(), attr.getNodeValue() );
    542 						}
    543
    544 						// transfer text
    545 						setElementText( elm2, getElementText( elm ));
    546 					}
    547
    548 					cursor.toNextToken();
    549 				}
    550 			}
    551
    552 			return destXml.xmlText();
    553 		}
    554 		catch (Exception e)
    555 		{
    556 			SoapUI.logError( e );
    557 		}
    558 		finally
    559 		{
    560 			if( cursor != null )
    561 				cursor.dispose();
    562 		}
    563
    564 		return dest;
    565 	}
    566
    567 	/***
    568 	 * Returns absolute xpath for specified element, ignores namespaces
    569 	 *
    570 	 * @param elm the element to create for
    571 	 * @return the elements path in its containing document
    572 	 */
    573
    574 	public static String getElementPath(Element element)
    575 	{
    576 		Node elm = element;
    577
    578 		String result = elm.getNodeName() + "[" + getElementIndex( elm ) + "]";
    579 		while( elm.getParentNode() != null && elm.getParentNode().getNodeType() != Node.DOCUMENT_NODE )
    580 		{
    581 			elm = elm.getParentNode();
    582 			result = elm.getNodeName() + "[" + getElementIndex( elm ) + "]/" + result;
    583 		}
    584
    585 		return "/" + result;
    586 	}
    587
    588 	/***
    589 	 * Gets the index of the specified element amongst elements with the same name
    590 	 *
    591 	 * @param element the element to get for
    592 	 * @return the index of the element, will be >= 1
    593 	 */
    594
    595 	public static int getElementIndex(Node element)
    596 	{
    597 		int result = 1;
    598
    599 		Node elm = element.getPreviousSibling();
    600 		while( elm != null )
    601 		{
    602 			if( elm.getNodeType() == Node.ELEMENT_NODE && elm.getNodeName().equals( element.getNodeName() ))
    603 				result++;
    604 			elm = elm.getPreviousSibling();
    605 		}
    606
    607 		return result;
    608 	}
    609
    610 	public static String declareXPathNamespaces( String xmlString ) throws XmlException
    611 	{
    612 		return declareXPathNamespaces( XmlObject.Factory.parse(xmlString));
    613 	}
    614
    615 	public static synchronized String prettyPrintXml( String xml )
    616 	{
    617       try
    618       {
    619       	if( StringUtils.isNullOrEmpty( xml ))
    620       		return xml;
    621
    622          StringWriter writer = new StringWriter();
    623          XmlUtils.serializePretty( XmlObject.Factory.parse( xml ), writer );
    624          return writer.toString();
    625       }
    626       catch( Exception e )
    627       {
    628       	log.warn( "Failed to prettyPrint xml [" + xml + "]: " + e );
    629          return xml;
    630       }
    631 	}
    632
    633 	public static synchronized String prettyPrintXml( XmlObject xml )
    634 	{
    635       try
    636       {
    637       	if( xml == null )
    638       		return null;
    639
    640          StringWriter writer = new StringWriter();
    641          XmlUtils.serializePretty( xml, writer );
    642          return writer.toString();
    643       }
    644       catch( Exception e )
    645       {
    646       	log.warn( "Failed to prettyPrint xml [" + xml + "]: " + e );
    647          return xml.xmlText();
    648       }
    649 	}
    650
    651 	public static String declareXPathNamespaces(WsdlInterface iface)
    652 	{
    653 		StringBuffer buf = new StringBuffer();
    654 		buf.append( "declare namespace soap='" );
    655 		buf.append( iface.getSoapVersion().getEnvelopeNamespace() );
    656 		buf.append( "';\n");
    657
    658 		try
    659 		{
    660 			Collection<String> namespaces = iface.getWsdlContext().getDefinedNamespaces();
    661 			int c = 1;
    662 			for (Iterator<String> i = namespaces.iterator(); i.hasNext();)
    663 			{
    664 				buf.append("declare namespace ns");
    665 				buf.append(c++);
    666 				buf.append("='");
    667 				buf.append(i.next());
    668 				buf.append("';\n");
    669 			}
    670 		}
    671 		catch (Exception e)
    672 		{
    673 			SoapUI.logError( e );
    674 		}
    675
    676 		return buf.toString();
    677 	}
    678
    679 	public static String createXPath(Node node )
    680 	{
    681 		return createXPath( node, false, false, null );
    682 	}
    683
    684 	public static String createXPath(Node node, boolean anonymous, boolean selectText, XPathModifier modifier )
    685 	{
    686       XPathData xpathData = createXPathData( node, anonymous, selectText );
    687       return xpathData.buildXPath(modifier);
    688    }
    689
    690    public static XPathData createXPathData(Node node )
    691    {
    692       return createXPathData( node, false, false );
    693    }
    694
    695    public static XPathData createXPathData(Node node, boolean anonymous, boolean selectText )
    696    {
    697       StringToStringMap nsMap = new StringToStringMap();
    698       List<String> pathComponents = new ArrayList<String>();
    699
    700       int nsCnt = 1;
    701
    702 		String namespaceURI = node.getNamespaceURI();
    703 		if( node.getNodeType() == Node.ATTRIBUTE_NODE )
    704 		{
    705 			if( namespaceURI.length() > 0 )
    706 			{
    707 				String prefix = node.getPrefix();
    708 				if( prefix == null || prefix.length() == 0 )
    709 					prefix = "ns" + nsCnt++;
    710
    711             nsMap.put( namespaceURI, prefix );
    712             pathComponents.add( "@" + prefix + ":" + node.getLocalName() );
    713 			}
    714 			else
    715 			{
    716             pathComponents.add( "@" + node.getLocalName() );
    717 			}
    718 			node = ((Attr)node).getOwnerElement();
    719 		}
    720
    721 		if( node.getNodeType() == Node.ELEMENT_NODE )
    722 		{
    723 			int index = anonymous ? 0 : findNodeIndex( node );
    724
    725 			String pc = null;
    726
    727 			namespaceURI = node.getNamespaceURI();
    728 			if( namespaceURI.length() > 0 )
    729 			{
    730 				String prefix = node.getPrefix();
    731 				if( prefix == null || prefix.length() == 0 )
    732 					prefix = "ns" + nsCnt++;
    733
    734             nsMap.put( namespaceURI, prefix );
    735 				pc = prefix + ":" + node.getLocalName();
    736 			}
    737 			else
    738 			{
    739 				pc = node.getLocalName();
    740 			}
    741
    742 			String elementText = XmlUtils.getElementText( (Element) node );
    743
    744 			// not an attribute?
    745 			if( selectText && pathComponents.isEmpty() && elementText != null && elementText.trim().length() > 0  )
    746             pathComponents.add( "text()" );
    747
    748          pathComponents.add( pc + ((index == 0 ) ? "" : "[" + index + "]" ));
    749 		}
    750 		else
    751 			return null;
    752
    753 		node = node.getParentNode();
    754 		namespaceURI = node.getNamespaceURI();
    755 		while( node != null && node.getNodeType() == Node.ELEMENT_NODE &&
    756 				!node.getNodeName().equals( "Body" ) &&
    757 				!namespaceURI.equals( SoapVersion.Soap11.getEnvelopeNamespace() ) &&
    758 				!namespaceURI.equals( SoapVersion.Soap12.getEnvelopeNamespace() ))
    759 		{
    760 			int index = anonymous ? 0 : findNodeIndex( node );
    761
    762 			String ns = nsMap.get( namespaceURI );
    763 			String pc = null;
    764
    765 			if( ns == null && namespaceURI.length() > 0 )
    766 			{
    767 				String prefix = node.getPrefix();
    768 				if( prefix == null || prefix.length() == 0 )
    769 					prefix = "ns" + nsCnt++;
    770
    771             nsMap.put( namespaceURI, prefix );
    772 				ns = nsMap.get( namespaceURI );
    773
    774 				pc = prefix + ":" + node.getLocalName();
    775 			}
    776 			else if( ns != null )
    777 			{
    778 				 pc = ns + ":" + node.getLocalName();
    779 			}
    780 			else
    781 			{
    782 				 pc = node.getLocalName();
    783 			}
    784
    785          pathComponents.add( pc + ((index == 0 ) ? "" : "[" + index + "]" ));
    786 			node = node.getParentNode();
    787 			namespaceURI = node.getNamespaceURI();
    788 		}
    789
    790       return new XPathData(nsMap, pathComponents);
    791 	}
    792
    793 	private static int findNodeIndex(Node node)
    794 	{
    795 		String nm = node.getLocalName();
    796 		String ns = node.getNamespaceURI();
    797
    798 		Node parentNode = node.getParentNode();
    799 		if( parentNode.getNodeType() != Node.ELEMENT_NODE )
    800 			return 1;
    801
    802 		NodeList nl = ((Element)parentNode).getElementsByTagNameNS( ns, nm );
    803
    804 		if( nl.getLength() == 1 )
    805 			return 0;
    806
    807 		int mod = 0;
    808 		for( int c = 0; c < nl.getLength(); c++ )
    809 		{
    810 			if( nl.item( c ).getParentNode() != node.getParentNode() )
    811 				mod++;
    812 			else if( nl.item( c ) == node )
    813 				return c+1-mod;
    814 		}
    815
    816 		throw new RuntimeException( "Child node not found in parent!?" );
    817 	}
    818
    819 	public static boolean setNodeValue( Node domNode, String string )
    820 	{
    821 		short nodeType = domNode.getNodeType();
    822 		if( nodeType == Node.ELEMENT_NODE )
    823 		{
    824 			setElementText( ( Element ) domNode, string );
    825 			return true;
    826 		}
    827 		else if( nodeType == Node.ATTRIBUTE_NODE || nodeType == Node.TEXT_NODE )
    828 		{
    829 			domNode.setNodeValue( string );
    830 			return true;
    831 		}
    832
    833 		return false;
    834 	}
    835
    836 	public static String declareXPathNamespaces( XmlObject xmlObject )
    837 	{
    838 		Map<QName,String> map = new HashMap<QName,String>();
    839 		XmlCursor cursor = xmlObject.newCursor();
    840
    841 		while( cursor.hasNextToken() )
    842 		{
    843 			if( cursor.toNextToken().isNamespace() )
    844 				map.put( cursor.getName(), cursor.getTextValue() );
    845 		}
    846
    847 		Iterator<QName> i = map.keySet().iterator();
    848 		int nsCnt = 0;
    849
    850 		StringBuffer buf = new StringBuffer();
    851 		Set<String> prefixes = new HashSet<String>();
    852 		Set<String> usedPrefixes = new HashSet<String>();
    853
    854 		while( i.hasNext() )
    855 		{
    856 			QName name = i.next();
    857 			String prefix = name.getLocalPart();
    858 			if( prefix.length() == 0 ) prefix = "ns" + Integer.toString( ++nsCnt );
    859 			else if( prefix.equals( "xsd") || prefix.equals( "xsi")) continue;
    860
    861 			if( usedPrefixes.contains( prefix ))
    862 			{
    863 				int c = 1;
    864 				while( usedPrefixes.contains( prefix + c )) c++;
    865
    866 				prefix = prefix + Integer.toString( c );
    867 			}
    868 			else prefixes.add( prefix );
    869
    870 			buf.append( "declare namespace " );
    871 			buf.append( prefix );
    872 			buf.append( "='" );
    873 			buf.append( map.get( name ));
    874 			buf.append( "';\n");
    875
    876 			usedPrefixes.add( prefix );
    877 		}
    878
    879 		return buf.toString();
    880 	}
    881
    882 	public static String setXPathContent( String emptyResponse, String string, String actor )
    883 	{
    884 		try
    885 		{
    886 			XmlObject xmlObject = XmlObject.Factory.parse( emptyResponse );
    887
    888 			String namespaces = declareXPathNamespaces( xmlObject );
    889 			if( namespaces != null && namespaces.trim().length() > 0 )
    890 				string = namespaces + string;
    891
    892 			XmlObject[] path = xmlObject.selectPath( string );
    893 			for( XmlObject xml : path )
    894 			{
    895 				setNodeValue( xml.getDomNode(), actor );
    896 			}
    897
    898 			return xmlObject.toString();
    899 		}
    900 		catch( Exception e )
    901 		{
    902 			SoapUI.logError( e );
    903 		}
    904
    905 		return emptyResponse;
    906 	}
    907
    908 	public static QName getQName( Node node )
    909 	{
    910 		if( node.getNamespaceURI() == null )
    911 			return new QName( node.getNodeName());
    912 		else
    913 			return new QName( node.getNamespaceURI(), node.getLocalName() );
    914 	}
    915
    916 	public static String removeXPathNamespaceDeclarations( String xpath )
    917 	{
    918 		while( xpath.startsWith( "declare namespace" ))
    919 		{
    920 			int ix = xpath.indexOf( ';' );
    921 			if( ix == -1 )
    922 				break;
    923
    924 			xpath = xpath.substring( ix+1 ).trim();
    925 		}
    926 		return xpath;
    927 	}
    928
    929 	public static String stripWhitespaces( String content )
    930 	{
    931 		try
    932 		{
    933 			XmlObject xml = XmlObject.Factory.parse( content, new XmlOptions().setLoadStripWhitespace().setLoadStripComments() );
    934 			content = xml.xmlText();
    935 		}
    936 		catch( Exception e )
    937 		{
    938 			SoapUI.logError( e );
    939 		}
    940
    941 		return content;
    942 	}
    943
    944 	public static NodeList getChildElements( Element elm )
    945 	{
    946 		List<Element> list = new ArrayList<Element>();
    947
    948 		NodeList nl = elm.getChildNodes();
    949 		for( int c = 0; c < nl.getLength(); c++ )
    950 		{
    951 			Node item = nl.item( c );
    952 			if( item.getParentNode() == elm && item.getNodeType() == Node.ELEMENT_NODE )
    953 				list.add(  ( Element ) item );
    954 		}
    955
    956 		return new ElementNodeList( list );
    957 	}
    958
    959 	public static NodeList getChildElementsByTagName( Element elm, String name )
    960 	{
    961 		List<Element> list = new ArrayList<Element>();
    962
    963 		NodeList nl = elm.getChildNodes();
    964 		for( int c = 0; c < nl.getLength(); c++ )
    965 		{
    966 			Node item = nl.item( c );
    967 			if( item.getParentNode() == elm && item.getNodeType() == Node.ELEMENT_NODE && name.equals( item.getNodeName()) )
    968 				list.add(  ( Element ) item );
    969 		}
    970
    971 		return new ElementNodeList( list );
    972 	}
    973
    974 	public static NodeList getChildElementsByTagNameNS( Element elm, String namespaceUri, String localName )
    975 	{
    976 		List<Element> list = new ArrayList<Element>();
    977
    978 		NodeList nl = elm.getChildNodes();
    979 		for( int c = 0; c < nl.getLength(); c++ )
    980 		{
    981 			Node item = nl.item( c );
    982 			if( item.getParentNode() == elm && item.getNodeType() == Node.ELEMENT_NODE &&
    983 						localName.equals( item.getLocalName())	&& namespaceUri.equals( item.getNamespaceURI()))
    984 				list.add(  ( Element ) item );
    985 		}
    986
    987 		return new ElementNodeList( list );
    988 	}
    989
    990 	private final static class ElementNodeList implements NodeList
    991 	{
    992 		private final List<Element> list;
    993
    994 		public ElementNodeList( List<Element> list )
    995 		{
    996 			this.list = list;
    997 		}
    998
    999 		public int getLength()
    1000 		{
    1001 			return list.size();
    1002 		}
    1003
    1004 		public Node item( int index )
    1005 		{
    1006 			return list.get( index );
    1007 		}
    1008    }
    1009
    1010 	public static boolean seemsToBeXml( String str )
    1011 	{
    1012 		try
    1013 		{
    1014 			if( StringUtils.isNullOrEmpty( str ))
    1015       		return false;
    1016
    1017 			return XmlObject.Factory.parse( str ) != null;
    1018 		}
    1019 		catch( Exception e )
    1020 		{
    1021 			return false;
    1022 		}
    1023 	}
    1024
    1025 	public static String extractNamespaces( String xpath )
    1026 	{
    1027 		String result = xpath;
    1028 		int ix = xpath.lastIndexOf( "declare namespace" );
    1029 		if( ix != -1 )
    1030 		{
    1031 			ix = xpath.indexOf( '\'', ix+1 );
    1032 			if( ix != -1 )
    1033 			{
    1034 				ix = xpath.indexOf( '\'', ix+1 );
    1035 				if( ix != -1 )
    1036 				{
    1037 					ix = xpath.indexOf( ';' );
    1038 					if( ix != -1 )
    1039 					{
    1040 						result = xpath.substring( 0, ix+1 );
    1041 					}
    1042 				}
    1043 			}
    1044 		}
    1045
    1046 		return result;
    1047 	}
    1048
    1049 	public static String removeUnneccessaryNamespaces( String xml )
    1050 	{
    1051 		if( StringUtils.isNullOrEmpty( xml ))
    1052 			return xml;
    1053
    1054 		XmlObject xmlObject = null;
    1055 		XmlCursor cursor = null;
    1056 		try
    1057 		{
    1058 			xmlObject = XmlObject.Factory.parse( xml );
    1059
    1060 			cursor = xmlObject.newCursor();
    1061 			while( cursor.currentTokenType() != TokenType.START && cursor.currentTokenType() != TokenType.ENDDOC )
    1062 			{
    1063 				cursor.toNextToken();
    1064 			}
    1065
    1066 			if( cursor.currentTokenType() == TokenType.START )
    1067 			{
    1068 				Map nsMap = new HashMap();
    1069
    1070 				cursor.getAllNamespaces( nsMap );
    1071 				nsMap.remove( cursor.getDomNode().getPrefix() );
    1072
    1073 				NamedNodeMap attributes = cursor.getDomNode().getAttributes();
    1074 				for( int c = 0; attributes != null && c < attributes.getLength(); c++ )
    1075 				{
    1076 					nsMap.remove( attributes.item( c ).getPrefix() );
    1077 				}
    1078
    1079 				if( cursor.toFirstChild() )
    1080 				{
    1081 					while( cursor.getDomNode() != xmlObject.getDomNode() )
    1082 					{
    1083 						attributes = cursor.getDomNode().getAttributes();
    1084 						for( int c = 0; attributes != null && c < attributes.getLength(); c++ )
    1085 						{
    1086 							nsMap.remove( attributes.item( c ).getPrefix() );
    1087 						}
    1088
    1089 						nsMap.remove( cursor.getDomNode().getPrefix() );
    1090 						cursor.toNextToken();
    1091 					}
    1092 				}
    1093
    1094 				xml = xmlObject.xmlText( new XmlOptions().setSaveOuter().setSavePrettyPrint()
    1095 							.setSaveImplicitNamespaces( nsMap ) );
    1096 			}
    1097 		}
    1098 		catch( XmlException e )
    1099 		{
    1100
    1101 		}
    1102 		finally
    1103 		{
    1104 			if( cursor != null )
    1105 				cursor.dispose();
    1106 		}
    1107
    1108 		return xml;
    1109 	}
    1110 }
    1111