View Javadoc

1   /*
2    *  soapui, copyright (C) 2005 Ole Matzura / eviware.com 
3    *
4    *  SoapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of the GNU Lesser General Public License as published by the Free Software Foundation; 
6    *  either version 2.1 of the License, or (at your option) any later version.
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;
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.HashMap;
23  import java.util.HashSet;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  import java.util.StringTokenizer;
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  
35  import org.apache.log4j.Logger;
36  import org.apache.xmlbeans.XmlCursor;
37  import org.apache.xmlbeans.XmlException;
38  import org.apache.xmlbeans.XmlObject;
39  import org.apache.xmlbeans.XmlOptions;
40  import org.w3c.dom.Attr;
41  import org.w3c.dom.Document;
42  import org.w3c.dom.Element;
43  import org.w3c.dom.NamedNodeMap;
44  import org.w3c.dom.Node;
45  import org.w3c.dom.NodeList;
46  import org.w3c.dom.Text;
47  import org.w3c.dom.traversal.DocumentTraversal;
48  import org.w3c.dom.traversal.NodeFilter;
49  import org.w3c.dom.traversal.TreeWalker;
50  import org.xml.sax.InputSource;
51  import org.xml.sax.SAXException;
52  
53  import com.sun.org.apache.xpath.internal.XPathAPI;
54  
55  /***
56   * General XML-related utilities
57   */
58  
59  public final class XmlUtils
60  {
61     private static DocumentBuilder documentBuilder;
62     private final static Logger log = Logger.getLogger( XmlUtils.class );
63  
64     static public Document parse( InputStream in )
65     {
66        try
67        {
68           return ensureDocumentBuilder().parse( in );
69        }
70        catch( Exception e )
71        {
72        	log.error( "Error parsing InputStream; " + e.getMessage(), e );
73        }
74  
75        return null;
76     }
77  
78     static public Document parse( String fileName ) throws IOException
79     {
80        try
81        {
82           return  ensureDocumentBuilder().parse( fileName );
83        }
84        catch( SAXException e )
85        {
86        	log.error( "Error parsing fileName [" + fileName + "]; " + e.getMessage(), e );
87        }
88  
89        return null;
90     }
91  
92     static public Document parse( InputSource inputSource ) throws IOException
93     {
94        try
95        {
96           return ensureDocumentBuilder().parse( inputSource );
97        }
98        catch( SAXException e )
99        {
100       	log.error( "Error parsing InputSource; " + e.getMessage(), e );
101       }
102 
103       return null;
104    }
105 
106 
107    private static DocumentBuilder ensureDocumentBuilder()
108    {
109       if( documentBuilder == null )
110       {
111          try
112          {
113             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
114             dbf.setNamespaceAware( true );
115 				documentBuilder = dbf.newDocumentBuilder();
116          }
117          catch( ParserConfigurationException e )
118          {
119          	log.error( "Error creating DocumentBuilder; " + e.getMessage(), e );
120          }
121       }
122 
123       return documentBuilder;
124    }
125 
126    public static void serializePretty( Document document )
127    {
128       try
129       {
130          serializePretty( document, new OutputStreamWriter( System.out ) );
131       }
132       catch( IOException e )
133       {
134          e.printStackTrace();
135       }
136    }
137 
138    public static void serializePretty( Document dom, Writer writer )
139       throws IOException
140    {
141    	try
142 		{
143 			XmlObject xmlObject = XmlObject.Factory
144 					.parse(dom.getDocumentElement());
145 			serializePretty(xmlObject, writer);
146 		}
147 		catch (Exception e)
148 		{
149 			throw new IOException( e.getMessage() );
150 		}
151    }
152    
153    public static void serializePretty( XmlObject xmlObject, Writer writer )
154    throws IOException
155 	{
156 		try
157 		{
158 			XmlOptions options = new XmlOptions();
159 			options.setSavePrettyPrint();
160 			options.setSavePrettyPrintIndent( 3 );
161 			options.setSaveNoXmlDecl();
162 			options.setSaveAggressiveNamespaces();
163 			xmlObject.save(writer, options);
164 		}
165 		catch (Exception e)
166 		{
167 			throw new IOException( e.getMessage() );
168 		}
169 	}
170 
171    public static void serialize( Document dom, Writer writer )
172    throws IOException
173 	{
174    	try
175 		{
176 			XmlObject xmlObject = XmlObject.Factory
177 					.parse(dom.getDocumentElement());
178 			xmlObject.save( writer );
179 		}
180 		catch (Exception e)
181 		{
182 			throw new IOException( e.getMessage() );
183 		}
184 	}
185    
186    static public void setElementText( Element elm, String text )
187    {
188       Node node = elm.getFirstChild();
189       if( node == null  )
190       {
191       	if( text != null)
192       		elm.appendChild( elm.getOwnerDocument().createTextNode( text ) );
193       }
194       else if( node.getNodeType() == Node.TEXT_NODE )
195       {
196          if( text == null )
197             node.getParentNode().removeChild( node );
198          else
199             node.setNodeValue( text );
200       }
201       else if( text != null )
202       {
203          Text textNode = node.getOwnerDocument().createTextNode( text );
204          elm.insertBefore( textNode, elm.getFirstChild() );
205       }
206    }
207 
208    public static String getChildElementText( Element elm, String name )
209    {
210       Element child = getFirstChildElement( elm, name );
211       return child == null ? null : getElementText( child );
212    }
213 
214    public static Element getFirstChildElement( Element elm, String name )
215    {
216       NodeList nl = elm.getChildNodes();
217       for( int c = 0; c < nl.getLength(); c++ )
218       {
219          Node node = nl.item( c );
220          if( node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals( name ) )
221             return (Element) node;
222       }
223 
224       return null;
225    }
226 
227    static public String getElementText( Element elm )
228    {
229       Node node = elm.getFirstChild();
230       if( node != null && node.getNodeType() == Node.TEXT_NODE )
231          return node.getNodeValue();
232 
233       return null;
234    }
235 
236    public static String getChildElementText( Element elm, String name, String defaultValue )
237    {
238       String result = getChildElementText( elm, name );
239       return result == null ? defaultValue : result;
240    }
241 
242    static public String getNodeValue( Node node )
243    {
244       return node.getNodeType() == Node.ELEMENT_NODE ? getElementText( (Element) node ) : node.getNodeValue();
245    }
246 
247    public static Node createNodeFromPath( Element modelElement, String path )
248    {
249       try
250       {
251          Document document = modelElement.getOwnerDocument();
252          StringTokenizer st = new StringTokenizer( path, "/" );
253          while( st.hasMoreTokens() )
254          {
255             String t = st.nextToken();
256 
257             if( st.hasMoreTokens() )
258             {
259                if( t.equals( ".." ) )
260                {
261                   modelElement = (Element) modelElement.getParentNode();
262                }
263                else
264                {
265                   Element elm = getFirstChildElement( modelElement, t );
266                   if( elm == null )
267                      modelElement = (Element) modelElement.insertBefore(
268                         document.createElement( t ),
269                         getFirstChildElement( modelElement, t ) );
270                   else
271                      modelElement = elm;
272                }
273             }
274             else
275             {
276                modelElement = (Element) modelElement.insertBefore(
277                   document.createElement( t ),
278                   getFirstChildElement( modelElement, t ) );
279             }
280          }
281 
282          return modelElement;
283       }
284       catch( Exception e )
285       {
286          //  System.err.println( "Failed to create path [" + path + "]" );
287       }
288 
289       return null;
290    }
291 
292    public static Element addChildElement( Element element, String name, String text )
293    {
294       Document document = element.getOwnerDocument();
295       Element result = (Element) element.appendChild( document.createElement( name ) );
296       if( text != null )
297          result.appendChild( document.createTextNode( text ) );
298 
299       return result;
300    }
301 
302    public static void setChildElementText( Element element, String name, String text )
303    {
304       Element elm = getFirstChildElement( element, name );
305       if( elm == null )
306       {
307          elm = element.getOwnerDocument().createElement( name );
308          element.appendChild( elm );
309       }
310 
311       setElementText( elm, text );
312    }
313 
314    public static Document parseXml( String xmlString ) throws IOException
315    {
316       return parse( new InputSource( new StringReader( xmlString )));
317    }
318    
319    public static void dumpParserErrors(XmlObject xmlObject)
320    {
321       List errors = new ArrayList();
322       xmlObject.validate(new XmlOptions().setErrorListener(errors));
323       for (Iterator i = errors.iterator(); i.hasNext();)
324       {
325          System.out.println(i.next());
326       }
327    }
328 
329 	public static String transferValues(String source, String dest)
330 	{
331 		try
332 		{
333 			Document sourceDom = parseXml(source);
334 			Document destDom = parseXml(dest);
335 			
336 			TreeWalker walker = ((DocumentTraversal)sourceDom).createTreeWalker( sourceDom.getDocumentElement(),
337 					NodeFilter.SHOW_ELEMENT, null, true );
338 			
339 			Element elm = (Element) walker.nextNode();
340 			while( elm != null )
341 			{
342 				String path = getElementPath( elm );
343 				Node nsNode = getNSNode( elm );
344 				Element elm2 = (Element) XPathAPI.selectSingleNode( destDom.getDocumentElement(), path, nsNode );
345 				if( elm2 != null )
346 				{
347 					// transfer attributes
348 					NamedNodeMap attributes = elm.getAttributes();
349 					for( int c = 0; c < attributes.getLength(); c++ )
350 					{
351 						Attr attr = (Attr) attributes.item( c );
352 						elm2.setAttribute( attr.getNodeName(), attr.getNodeValue() );
353 					}
354 					
355 					// transfer text
356 					setElementText( elm2, getElementText( elm ));
357 				}
358 				
359 				elm = (Element) walker.nextNode();
360 			}
361 
362 			StringWriter writer = new StringWriter();
363 			serialize( destDom, writer );
364 			return writer.toString();
365 		}
366 		catch (Exception e)
367 		{
368 			e.printStackTrace();
369 		}
370 		
371 		return dest;
372 	}
373 
374 	private static Node getNSNode(Node elm)
375 	{
376 		Element result = elm.getOwnerDocument().createElement("nsnode");
377 		
378 		while( elm != null )
379 		{
380 			NamedNodeMap attributes = elm.getAttributes();
381 			if( attributes != null )
382 			{
383 				for( int c = 0; c < attributes.getLength(); c++ )
384 				{
385 					Node attr = attributes.item( c );
386 					if( attr.getNodeName().startsWith( "xmlns:" ) || attr.getNodeName().equals( "xmlns") )
387 					{
388 						result.setAttribute( attr.getNodeName(), attr.getNodeValue() );
389 					}
390 				}
391 			}
392 			
393 			elm = elm.getParentNode();
394 		}
395 		
396 		return result;
397 	}
398 
399 	/***
400 	 * Returns absolute xpath for specified element, ignores namespaces
401 	 *  
402 	 * @param elm the element to create for
403 	 * @return the elements path in its containing document
404 	 */
405 	
406 	public static String getElementPath(Element element)
407 	{
408 		Node elm = element;
409 		
410 		String result = elm.getNodeName() + "[" + getElementIndex( elm ) + "]";
411 		while( elm.getParentNode() != null && elm.getParentNode().getNodeType() != Node.DOCUMENT_NODE )
412 		{
413 			elm = elm.getParentNode();
414 			result = elm.getNodeName() + "[" + getElementIndex( elm ) + "]/" + result;
415 		}
416 
417 		return "/" + result;
418 	}
419 
420 	/***
421 	 * Gets the index of the specified element amongst elements with the same name
422 	 * 
423 	 * @param element the element to get for
424 	 * @return the index of the element, will be >= 1
425 	 */
426 	
427 	public static int getElementIndex(Node element)
428 	{
429 		int result = 1;
430 		
431 		Node elm = element.getPreviousSibling();
432 		while( elm != null )
433 		{
434 			if( elm.getNodeType() == Node.ELEMENT_NODE && elm.getNodeName().equals( element.getNodeName() ))
435 				result++;
436 			elm = elm.getPreviousSibling();
437 		}
438 		
439 		return result;	
440 	}
441 	
442 	public static String declareXPathNamespaces( String xmlString ) throws XmlException
443 	{
444 		XmlObject xml = XmlObject.Factory.parse(xmlString);
445 		Map<QName,String> map = new HashMap<QName,String>();
446 		XmlCursor cursor = xml.newCursor();
447 		
448 		while( cursor.hasNextToken() )
449 		{
450 			if( cursor.toNextToken().isNamespace() )
451 				map.put( cursor.getName(), cursor.getTextValue() );
452 		}
453 		
454 		Iterator<QName> i = map.keySet().iterator();
455 		int nsCnt = 0;
456 		
457 		StringBuffer buf = new StringBuffer();
458 		Set<String> prefixes = new HashSet<String>();
459 		Set<String> usedPrefixes = new HashSet<String>();
460 		
461 		while( i.hasNext() )
462 		{
463 			QName name = i.next();
464 			String prefix = name.getLocalPart();
465 			if( prefix.length() == 0 ) prefix = "ns" + (++nsCnt);
466 			else if( prefix.equals( "xsd") || prefix.equals( "xsi")) continue;
467 			
468 			if( prefixes.contains( prefix ))
469 			{
470 				int c = 1;
471 				while( usedPrefixes.contains( prefix + c )) c++;
472 				
473 				prefix = prefix + c;
474 			}
475 			else prefixes.add( prefix );
476 			
477 			buf.append( "declare namespace " );
478 			buf.append( prefix );
479 			buf.append( "='" );
480 			buf.append( map.get( name ));
481 			buf.append( "';\n");
482 			
483 			usedPrefixes.add( prefix );
484 		}
485 		
486 		return buf.toString();
487 		
488 	}
489 }
490