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.setAttributeNodeNS( (Attr) elm2.getOwnerDocument().importNode( attr, true ) );
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 
714       switch( nodeType )
715       {
716          case Node.ELEMENT_NODE:
717          {
718             setElementText( (Element) domNode, string );
719             break;
720          }
721          case Node.ATTRIBUTE_NODE:
722          case Node.TEXT_NODE:
723          {
724             domNode.setNodeValue( string );
725             break;
726          }
727          case Node.PROCESSING_INSTRUCTION_NODE:
728          {
729             ( (ProcessingInstruction) domNode ).setData( string );
730             break;
731          }
732          case Node.CDATA_SECTION_NODE:
733          {
734             ( (CDATASection) domNode ).setData( string );
735             break;
736          }
737          default:
738          {
739             return false;
740          }
741       }
742 
743       return true;
744    }
745 
746    public static String declareXPathNamespaces( XmlObject xmlObject )
747    {
748       Map<QName, String> map = new HashMap<QName, String>();
749       XmlCursor cursor = xmlObject.newCursor();
750 
751       while( cursor.hasNextToken() )
752       {
753          if( cursor.toNextToken().isNamespace() )
754             map.put( cursor.getName(), cursor.getTextValue() );
755       }
756 
757       Iterator<QName> i = map.keySet().iterator();
758       int nsCnt = 0;
759 
760       StringBuffer buf = new StringBuffer();
761       Set<String> prefixes = new HashSet<String>();
762       Set<String> usedPrefixes = new HashSet<String>();
763 
764       while( i.hasNext() )
765       {
766          QName name = i.next();
767          String prefix = name.getLocalPart();
768          if( prefix.length() == 0 ) prefix = "ns" + Integer.toString( ++nsCnt );
769          else if( prefix.equals( "xsd" ) || prefix.equals( "xsi" ) ) continue;
770 
771          if( usedPrefixes.contains( prefix ) )
772          {
773             int c = 1;
774             while( usedPrefixes.contains( prefix + c ) ) c++;
775 
776             prefix = prefix + Integer.toString( c );
777          }
778          else prefixes.add( prefix );
779 
780          buf.append( "declare namespace " );
781          buf.append( prefix );
782          buf.append( "='" );
783          buf.append( map.get( name ) );
784          buf.append( "';\n" );
785 
786          usedPrefixes.add( prefix );
787       }
788 
789       return buf.toString();
790    }
791 
792    public static String setXPathContent( String xmlText, String xpath, String value )
793    {
794       try
795       {
796          XmlObject xmlObject = XmlObject.Factory.parse( xmlText );
797 
798          String namespaces = declareXPathNamespaces( xmlObject );
799          if( namespaces != null && namespaces.trim().length() > 0 )
800             xpath = namespaces + xpath;
801 
802          XmlObject[] path = xmlObject.selectPath( xpath );
803          for( XmlObject xml : path )
804          {
805             setNodeValue( xml.getDomNode(), value );
806          }
807 
808          return xmlObject.toString();
809       }
810       catch( Exception e )
811       {
812          SoapUI.logError( e );
813       }
814 
815       return xmlText;
816    }
817 
818    public static QName getQName( Node node )
819    {
820       if( node.getNamespaceURI() == null )
821          return new QName( node.getNodeName() );
822       else
823          return new QName( node.getNamespaceURI(), node.getLocalName() );
824    }
825 
826    public static String removeXPathNamespaceDeclarations( String xpath )
827    {
828       while( xpath.startsWith( "declare namespace" ) )
829       {
830          int ix = xpath.indexOf( ';' );
831          if( ix == -1 )
832             break;
833 
834          xpath = xpath.substring( ix + 1 ).trim();
835       }
836       return xpath;
837    }
838 
839    public static String stripWhitespaces( String content )
840    {
841       try
842       {
843          XmlObject xml = XmlObject.Factory.parse( content, new XmlOptions().setLoadStripWhitespace().setLoadStripComments() );
844          content = xml.xmlText();
845       }
846       catch( Exception e )
847       {
848          SoapUI.logError( e );
849       }
850 
851       return content;
852    }
853 
854    public static NodeList getChildElements( Element elm )
855    {
856       List<Element> list = new ArrayList<Element>();
857 
858       NodeList nl = elm.getChildNodes();
859       for( int c = 0; c < nl.getLength(); c++ )
860       {
861          Node item = nl.item( c );
862          if( item.getParentNode() == elm && item.getNodeType() == Node.ELEMENT_NODE )
863             list.add( (Element) item );
864       }
865 
866       return new ElementNodeList( list );
867    }
868 
869    public static NodeList getChildElementsByTagName( Element elm, String name )
870    {
871       List<Element> list = new ArrayList<Element>();
872 
873       NodeList nl = elm.getChildNodes();
874       for( int c = 0; c < nl.getLength(); c++ )
875       {
876          Node item = nl.item( c );
877          if( item.getParentNode() == elm && item.getNodeType() == Node.ELEMENT_NODE && name.equals( item.getNodeName() ) )
878             list.add( (Element) item );
879       }
880 
881       return new ElementNodeList( list );
882    }
883 
884    public static NodeList getChildElementsOfType( Element elm, SchemaType schemaType )
885    {
886       List<Element> list = new ArrayList<Element>();
887 
888       NodeList nl = elm.getChildNodes();
889       for( int c = 0; c < nl.getLength(); c++ )
890       {
891          Node item = nl.item( c );
892          if( item.getParentNode() == elm && item.getNodeType() == Node.ELEMENT_NODE &&
893                  ( (Element) item ).getAttributeNS( Constants.XSI_NS, "type" ).endsWith( ":" + schemaType.getName().getLocalPart() ) )
894          {
895             list.add( (Element) item );
896          }
897       }
898 
899       return new ElementNodeList( list );
900    }
901 
902    public static NodeList getChildElementsNS( Element elm, QName name )
903    {
904       return getChildElementsByTagNameNS( elm, name.getNamespaceURI(), name.getLocalPart() );
905    }
906 
907    public static NodeList getChildElementsByTagNameNS( Element elm, String namespaceUri, String localName )
908    {
909       List<Element> list = new ArrayList<Element>();
910 
911       NodeList nl = elm.getChildNodes();
912       for( int c = 0; c < nl.getLength(); c++ )
913       {
914          Node item = nl.item( c );
915          if( item.getParentNode() == elm && item.getNodeType() == Node.ELEMENT_NODE &&
916                  localName.equals( item.getLocalName() ) && namespaceUri.equals( item.getNamespaceURI() ) )
917             list.add( (Element) item );
918       }
919 
920       return new ElementNodeList( list );
921    }
922 
923    public static String serialize( Document document )
924    {
925       StringWriter writer = new StringWriter();
926       try
927       {
928          serialize( document, writer );
929       }
930       catch( IOException e )
931       {
932          e.printStackTrace();
933       }
934       return writer.toString();
935    }
936 
937    public static Element getFirstChildElementNS( Element domNode, QName name )
938    {
939       return getFirstChildElementNS( domNode, name.getNamespaceURI(), name.getLocalPart() );
940    }
941 
942    public static QName findTypeNameForXsiType( String typeName, Element elm )
943    {
944       int ix = typeName.indexOf( ':' );
945       if( ix == -1 )
946          return null;
947 
948       String prefix = typeName.substring( 0, ix );
949       String localName = typeName.substring( ix + 1 );
950       String namespaceUri = elm.getAttribute( "xmlns:" + prefix );
951 
952       if( !StringUtils.hasContent( namespaceUri ) )
953          namespaceUri = findNamespaceForPrefix( elm, prefix );
954 
955       if( StringUtils.hasContent( namespaceUri ) )
956       {
957          return new QName( namespaceUri, localName );
958       }
959 
960       return null;
961    }
962 
963    private static String findNamespaceForPrefix( Element elm, String prefix )
964    {
965       String namespaceUri = null;
966       while( StringUtils.isNullOrEmpty( namespaceUri ) && elm != null )
967       {
968          if( elm.getParentNode().getNodeType() != Node.ELEMENT_NODE )
969             break;
970 
971          elm = (Element) elm.getParentNode();
972          namespaceUri = elm.getAttribute( "xmlns:" + prefix );
973       }
974 
975       return StringUtils.isNullOrEmpty( namespaceUri ) ? null : namespaceUri;
976    }
977 
978    public static String findPrefixForNamespace( Element elm, String namespace )
979    {
980       while( elm != null )
981       {
982          NamedNodeMap attributes = elm.getAttributes();
983          for( int c = 0; c < attributes.getLength(); c++ )
984          {
985             if( attributes.item( c ).getNodeValue().equals( namespace ) &&
986                     attributes.item( c ).getNodeName().startsWith( "xmlns:" ) )
987             {
988                return attributes.item( c ).getNodeName().substring( 6 );
989             }
990          }
991 
992          if( elm.getParentNode().getNodeType() != Node.ELEMENT_NODE )
993             break;
994 
995          elm = (Element) elm.getParentNode();
996       }
997 
998       return null;
999    }
1000 
1001    public static void setXsiType( Element elm, QName name )
1002    {
1003       String prefix = findPrefixForNamespace( elm, name.getNamespaceURI() );
1004       if( prefix == null )
1005       {
1006          prefix = generatePrefixForNamespace( name.getNamespaceURI() );
1007          int cnt = 1;
1008          while( findNamespaceForPrefix( elm, prefix ) != null )
1009          {
1010             prefix = generatePrefixForNamespace( name.getNamespaceURI() );
1011          }
1012 
1013          elm.setAttribute( "xmlns:" + prefix, name.getNamespaceURI() );
1014       }
1015 
1016       elm.setAttributeNS( Constants.XSI_NS, "type", prefix + ":" + name.getLocalPart() );
1017    }
1018 
1019    private static String generatePrefixForNamespace( String namespaceURI )
1020    {
1021       return "ns" + (int) ( Math.random() * 1000 );
1022    }
1023 
1024    public static QName createQName( Node node )
1025    {
1026       return new QName( node.getNamespaceURI(), node.getLocalName() );
1027    }
1028 
1029    public static Node getNextElementSibling( Node node )
1030    {
1031       node = node.getNextSibling();
1032       while( node != null && node.getNodeType() != Node.ELEMENT_NODE )
1033       {
1034          node = node.getNextSibling();
1035       }
1036 
1037       return node;
1038    }
1039 
1040    public static Document createDocument( QName element )
1041    {
1042       ensureDocumentBuilder();
1043 
1044       Document document = documentBuilder.newDocument();
1045       document.appendChild( document.createElementNS( element.getNamespaceURI(), element.getLocalPart() ) );
1046       return document;
1047    }
1048 
1049    public static String getValueForMatch( XmlCursor cursor )
1050    {
1051       Node domNode = cursor.getDomNode();
1052       String stringValue;
1053 
1054       if( domNode.getNodeType() == Node.ATTRIBUTE_NODE || domNode.getNodeType() == Node.TEXT_NODE )
1055       {
1056          stringValue = domNode.getNodeValue();
1057       }
1058       else if( cursor.getObject() instanceof XmlAnySimpleType )
1059       {
1060          stringValue = ( (XmlAnySimpleType) cursor.getObject() ).getStringValue();
1061       }
1062       else
1063       {
1064          if( domNode.getNodeType() == Node.ELEMENT_NODE )
1065          {
1066             Element elm = (Element) domNode;
1067             if( elm.getChildNodes().getLength() == 1 && elm.getAttributes().getLength() == 0 )
1068             {
1069                stringValue = getElementText( elm );
1070             }
1071             else
1072             {
1073                stringValue = cursor.getObject().xmlText( new XmlOptions().setSavePrettyPrint().setSaveOuter().setSaveAggressiveNamespaces() );
1074             }
1075          }
1076          else
1077          {
1078             stringValue = domNode.getNodeValue();
1079          }
1080       }
1081       return stringValue;
1082    }
1083 
1084    public static String getValueForMatch( Node domNode, boolean prettyPrintXml )
1085    {
1086       String stringValue;
1087 
1088       if( domNode.getNodeType() == Node.ATTRIBUTE_NODE || domNode.getNodeType() == Node.TEXT_NODE )
1089       {
1090          stringValue = domNode.getNodeValue();
1091       }
1092       else
1093       {
1094          if( domNode.getNodeType() == Node.ELEMENT_NODE )
1095          {
1096             Element elm = (Element) domNode;
1097             if( elm.getChildNodes().getLength() == 1 && elm.getAttributes().getLength() == 0 )
1098             {
1099                stringValue = getElementText( elm );
1100             }
1101             else
1102             {
1103                stringValue = XmlUtils.serialize( domNode, prettyPrintXml );
1104             }
1105          }
1106          else
1107          {
1108             stringValue = domNode.getNodeValue();
1109          }
1110       }
1111 
1112       return stringValue;
1113    }
1114 
1115    public static String selectFirstNodeValue( XmlObject xmlObject, String xpath )
1116            throws XmlException
1117    {
1118       Node domNode = selectFirstDomNode( xmlObject, xpath );
1119       return domNode == null ? null : getNodeValue( domNode );
1120    }
1121 
1122    public static String[] selectNodeValues( XmlObject xmlObject, String xpath )
1123    {
1124       Node[] nodes = selectDomNodes( xmlObject, xpath );
1125 
1126       String[] result = new String[nodes.length];
1127       for( int c = 0; c < nodes.length; c++ )
1128       {
1129          result[c] = getNodeValue( nodes[c] );
1130       }
1131 
1132       return result;
1133    }
1134 
1135    public static Node selectFirstDomNode( XmlObject xmlObject, String xpath )
1136    {
1137       XmlCursor cursor = xmlObject.newCursor();
1138       try
1139       {
1140          cursor.selectPath( xpath );
1141 
1142          if( cursor.toNextSelection() )
1143          {
1144             return cursor.getDomNode();
1145          }
1146          else return null;
1147       }
1148       finally
1149       {
1150          cursor.dispose();
1151       }
1152    }
1153 
1154    public static Node[] selectDomNodes( XmlObject xmlObject, String xpath )
1155    {
1156       List<Node> result = new ArrayList<Node>();
1157 
1158       XmlCursor cursor = xmlObject.newCursor();
1159       try
1160       {
1161          cursor.selectPath( xpath );
1162 
1163          while( cursor.toNextSelection() )
1164          {
1165             result.add( cursor.getDomNode() );
1166          }
1167       }
1168       finally
1169       {
1170          cursor.dispose();
1171       }
1172 
1173       return result.toArray( new Node[result.size()] );
1174    }
1175 
1176    private final static class ElementNodeList implements NodeList
1177    {
1178       private final List<Element> list;
1179 
1180       public ElementNodeList( List<Element> list )
1181       {
1182          this.list = list;
1183       }
1184 
1185       public int getLength()
1186       {
1187          return list.size();
1188       }
1189 
1190       public Node item( int index )
1191       {
1192          return list.get( index );
1193       }
1194    }
1195 
1196    public static boolean seemsToBeXml( String str )
1197    {
1198       try
1199       {
1200          if( StringUtils.isNullOrEmpty( str ) )
1201             return false;
1202 
1203          return XmlObject.Factory.parse( str ) != null;
1204       }
1205       catch( Exception e )
1206       {
1207          return false;
1208       }
1209    }
1210 
1211    public static String extractNamespaces( String xpath )
1212    {
1213       String result = xpath;
1214       int ix = xpath.lastIndexOf( "declare namespace" );
1215       if( ix != -1 )
1216       {
1217          ix = xpath.indexOf( '\'', ix + 1 );
1218          if( ix != -1 )
1219          {
1220             ix = xpath.indexOf( '\'', ix + 1 );
1221             if( ix != -1 )
1222             {
1223                ix = xpath.indexOf( ';' );
1224                if( ix != -1 )
1225                {
1226                   result = xpath.substring( 0, ix + 1 );
1227                }
1228             }
1229          }
1230       }
1231       else
1232       {
1233          result = "";
1234       }
1235 
1236       return result;
1237    }
1238 
1239    public static String removeUnneccessaryNamespaces( String xml )
1240    {
1241       if( StringUtils.isNullOrEmpty( xml ) )
1242          return xml;
1243 
1244       XmlObject xmlObject = null;
1245       XmlCursor cursor = null;
1246       try
1247       {
1248          xmlObject = XmlObject.Factory.parse( xml );
1249 
1250          cursor = xmlObject.newCursor();
1251          while( cursor.currentTokenType() != TokenType.START && cursor.currentTokenType() != TokenType.ENDDOC )
1252          {
1253             cursor.toNextToken();
1254          }
1255 
1256          if( cursor.currentTokenType() == TokenType.START )
1257          {
1258             Map<?, ?> nsMap = new HashMap<Object, Object>();
1259 
1260             cursor.getAllNamespaces( nsMap );
1261             nsMap.remove( cursor.getDomNode().getPrefix() );
1262 
1263             NamedNodeMap attributes = cursor.getDomNode().getAttributes();
1264             for( int c = 0; attributes != null && c < attributes.getLength(); c++ )
1265             {
1266                nsMap.remove( attributes.item( c ).getPrefix() );
1267             }
1268 
1269             if( cursor.toFirstChild() )
1270             {
1271                while( cursor.getDomNode() != xmlObject.getDomNode() )
1272                {
1273                   attributes = cursor.getDomNode().getAttributes();
1274                   for( int c = 0; attributes != null && c < attributes.getLength(); c++ )
1275                   {
1276                      nsMap.remove( attributes.item( c ).getPrefix() );
1277                   }
1278 
1279                   nsMap.remove( cursor.getDomNode().getPrefix() );
1280                   cursor.toNextToken();
1281                }
1282             }
1283 
1284             xml = xmlObject.xmlText( new XmlOptions().setSaveOuter().setSavePrettyPrint()
1285                     .setSaveImplicitNamespaces( nsMap ) );
1286          }
1287       }
1288       catch( XmlException e )
1289       {
1290 
1291       }
1292       finally
1293       {
1294          if( cursor != null )
1295             cursor.dispose();
1296       }
1297 
1298       return xml;
1299    }
1300 
1301    public static String replaceNameInPathOrQuery( String pathOrQuery, String oldName, String newName ) throws Exception
1302    {
1303       Tokenizer t = new Tokenizer();
1304       t.tokenize( pathOrQuery, 0, -1, 1 );
1305       StringBuffer result = new StringBuffer();
1306       int lastIx = 0;
1307 
1308       while( t.currentToken != Token.EOF )
1309       {
1310          if( t.currentToken == Token.NAME && t.currentTokenValue.equals( oldName ) )
1311          {
1312             result.append( pathOrQuery.substring( lastIx, t.currentTokenStartOffset ) );
1313             result.append( newName );
1314             lastIx = t.currentTokenStartOffset + t.currentTokenValue.length();
1315          }
1316 
1317          t.next();
1318       }
1319 
1320       if( lastIx < pathOrQuery.length() )
1321          result.append( pathOrQuery.substring( lastIx ) );
1322       //
1323       System.out.println( "returning " + result.toString() );
1324       return result.toString();
1325    }
1326 }
1327