View Javadoc

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