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