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