View Javadoc

1   /*
2    * $Id: XMLUtils.java 12370 2008-07-17 13:11:17Z tcarlson $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.module.xml.util;
12  
13  import org.mule.RequestContext;
14  import org.mule.api.transport.OutputHandler;
15  import org.mule.module.xml.stax.DelegateXMLStreamReader;
16  import org.mule.module.xml.stax.StaxSource;
17  import org.mule.module.xml.transformer.DelayedResult;
18  import org.mule.module.xml.transformer.XmlToDomDocument;
19  import org.mule.util.IOUtils;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.StringReader;
26  
27  import javax.xml.stream.XMLStreamConstants;
28  import javax.xml.stream.XMLStreamException;
29  import javax.xml.stream.XMLStreamReader;
30  import javax.xml.stream.XMLStreamWriter;
31  import javax.xml.transform.Source;
32  import javax.xml.transform.Transformer;
33  import javax.xml.transform.TransformerConfigurationException;
34  import javax.xml.transform.TransformerFactory;
35  import javax.xml.transform.TransformerFactoryConfigurationError;
36  import javax.xml.transform.dom.DOMResult;
37  import javax.xml.transform.dom.DOMSource;
38  import javax.xml.transform.sax.SAXSource;
39  import javax.xml.transform.stream.StreamSource;
40  
41  import org.apache.commons.io.output.ByteArrayOutputStream;
42  import org.apache.commons.lang.StringUtils;
43  import org.dom4j.DocumentException;
44  import org.dom4j.io.DOMReader;
45  import org.dom4j.io.DocumentSource;
46  import org.w3c.dom.Document;
47  import org.xml.sax.InputSource;
48  
49  /**
50   * General utility methods for working with XML.
51   */
52  public class XMLUtils extends org.mule.util.XMLUtils
53  {
54      public static final String TRANSFORMER_FACTORY_JDK5 = "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl";
55  
56      // xml parser feature names for optional XSD validation
57      public static final String APACHE_XML_FEATURES_VALIDATION_SCHEMA = "http://apache.org/xml/features/validation/schema";
58      public static final String APACHE_XML_FEATURES_VALIDATION_SCHEMA_FULL_CHECKING = "http://apache.org/xml/features/validation/schema-full-checking";
59  
60      // JAXP property for specifying external XSD location
61      public static final String JAXP_PROPERTIES_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
62  
63      // JAXP properties for specifying external XSD language (as required by newer
64      // JAXP implementation)
65      public static final String JAXP_PROPERTIES_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
66      public static final String JAXP_PROPERTIES_SCHEMA_LANGUAGE_VALUE = "http://www.w3.org/2001/XMLSchema";
67  
68      /**
69       * Converts a DOM to an XML string.
70       */
71      public static String toXml(Document dom)
72      {
73          return new DOMReader().read(dom).asXML();
74      }
75  
76      /**
77       * @return a new XSLT transformer
78       * @throws TransformerConfigurationException if no TransformerFactory can be located in the
79       * runtime environment.
80       */
81      public static Transformer getTransformer() throws TransformerConfigurationException
82      {
83          TransformerFactory tf;
84          try
85          {
86              tf = TransformerFactory.newInstance();
87          }
88          catch (TransformerFactoryConfigurationError e)
89          {
90              System.setProperty("javax.xml.transform.TransformerFactory", TRANSFORMER_FACTORY_JDK5);
91              tf = TransformerFactory.newInstance();
92          }
93          if (tf != null)
94          {
95              return tf.newTransformer();
96          }
97          else
98          {
99              throw new TransformerConfigurationException("Unable to instantiate a TransformerFactory");
100         }
101     }
102 
103     public static org.dom4j.Document toDocument(Object obj) throws Exception
104     {
105         return toDocument(obj, null);
106     }
107     
108     /**
109      * Converts an object of unknown type to an org.dom4j.Document if possible.
110      * @return null if object cannot be converted
111      * @throws DocumentException if an error occurs while parsing
112      */
113     public static org.dom4j.Document toDocument(Object obj, String externalSchemaLocation) throws Exception
114     {
115         org.dom4j.io.SAXReader reader = new org.dom4j.io.SAXReader();
116         if (externalSchemaLocation != null)
117         {
118             reader.setValidation(true);
119             reader.setFeature(APACHE_XML_FEATURES_VALIDATION_SCHEMA, true);
120             reader.setFeature(APACHE_XML_FEATURES_VALIDATION_SCHEMA_FULL_CHECKING, true);
121             
122             InputStream xsdAsStream = IOUtils.getResourceAsStream(externalSchemaLocation, XMLUtils.class);
123             if (xsdAsStream == null)
124             {
125                 throw new IllegalArgumentException("Couldn't find schema at " + externalSchemaLocation);
126             }
127     
128             // Set schema language property (must be done before the schemaSource
129             // is set)
130             reader.setProperty(JAXP_PROPERTIES_SCHEMA_LANGUAGE, JAXP_PROPERTIES_SCHEMA_LANGUAGE_VALUE);
131     
132             // Need this one to map schemaLocation to a physical location
133             reader.setProperty(JAXP_PROPERTIES_SCHEMA_SOURCE, xsdAsStream);
134         }
135         
136         if (obj instanceof org.dom4j.Document)
137         {
138             return (org.dom4j.Document) obj;
139         }
140         else if (obj instanceof org.w3c.dom.Document)
141         {
142             org.dom4j.io.DOMReader domReader = new org.dom4j.io.DOMReader();
143             return domReader.read((org.w3c.dom.Document) obj);
144         }
145         else if (obj instanceof org.xml.sax.InputSource)
146         {                
147             return reader.read((org.xml.sax.InputSource) obj);
148         }
149         else if (obj instanceof javax.xml.transform.Source || obj instanceof javax.xml.stream.XMLStreamReader)
150         {                
151             // TODO Find a more direct way to do this
152             XmlToDomDocument tr = new XmlToDomDocument();
153             tr.setReturnClass(org.dom4j.Document.class);
154             return (org.dom4j.Document) tr.transform(obj);
155         }
156         else if (obj instanceof java.io.InputStream)
157         {                
158             return reader.read((java.io.InputStream) obj);
159         }
160         else if (obj instanceof String)
161         {
162             return reader.read(new StringReader((String) obj));
163         }
164         else if (obj instanceof byte[])
165         {
166             // TODO Handle encoding/charset somehow
167             return reader.read(new StringReader(new String((byte[]) obj)));
168         }
169         else if (obj instanceof File)
170         {                
171             return reader.read((File) obj);
172         }
173         else
174         {
175             return null;
176         }
177     }
178     /**
179      * Returns an XMLStreamReader for an object of unknown type if possible.
180      * @return null if no XMLStreamReader can be created for the object type
181      * @throws XMLStreamException
182      */
183     public static javax.xml.stream.XMLStreamReader toXMLStreamReader(javax.xml.stream.XMLInputFactory factory, Object obj) throws XMLStreamException
184     {
185         if (obj instanceof javax.xml.stream.XMLStreamReader)
186         {
187             return (javax.xml.stream.XMLStreamReader) obj;
188         }
189         else if (obj instanceof org.mule.module.xml.stax.StaxSource)
190         {
191             return ((org.mule.module.xml.stax.StaxSource) obj).getXMLStreamReader();
192         }
193         else if (obj instanceof javax.xml.transform.Source)
194         {
195             return factory.createXMLStreamReader((javax.xml.transform.Source) obj);
196         }
197         else if (obj instanceof org.xml.sax.InputSource)
198         {
199             return factory.createXMLStreamReader(((org.xml.sax.InputSource) obj).getByteStream());
200         }
201         else if (obj instanceof org.w3c.dom.Document)
202         {
203             return factory.createXMLStreamReader(new javax.xml.transform.dom.DOMSource((org.w3c.dom.Document) obj));
204         }
205         else if (obj instanceof org.dom4j.Document)
206         {
207             return factory.createXMLStreamReader(new org.dom4j.io.DocumentSource((org.dom4j.Document) obj));
208         }
209         else if (obj instanceof java.io.InputStream)
210         {
211             final InputStream is = (java.io.InputStream) obj;
212             
213             XMLStreamReader xsr = factory.createXMLStreamReader(is);
214             return new DelegateXMLStreamReader(xsr) {
215 
216                 public void close() throws XMLStreamException
217                 {
218                     super.close();
219                     
220                     try
221                     {
222                         is.close();
223                     }
224                     catch (IOException e)
225                     {
226                         throw new XMLStreamException(e);
227                     }
228                 }
229                 
230             };
231         }
232         else if (obj instanceof String)
233         {
234             return factory.createXMLStreamReader(new StringReader((String) obj));
235         }
236         else if (obj instanceof byte[])
237         {
238             // TODO Handle encoding/charset?
239             return factory.createXMLStreamReader(new ByteArrayInputStream((byte[]) obj));
240         }
241         else
242         {
243             return null;
244         }
245     }
246     
247     /**
248      * Convert our object to a Source type efficiently.
249      */ 
250     public static javax.xml.transform.Source toXmlSource(javax.xml.stream.XMLInputFactory xmlInputFactory, boolean useStaxSource, Object src) throws Exception
251     {
252         if (src instanceof javax.xml.transform.Source)
253         {
254             return (Source) src;
255         }
256         else if (src instanceof byte[])
257         {
258             ByteArrayInputStream stream = new ByteArrayInputStream((byte[]) src);
259             return toStreamSource(xmlInputFactory, useStaxSource, stream);
260         }
261         else if (src instanceof InputStream)
262         {
263             return toStreamSource(xmlInputFactory, useStaxSource, (InputStream) src);
264         }
265         else if (src instanceof String)
266         {
267             if (useStaxSource)
268             {
269                 return new StaxSource(xmlInputFactory.createXMLStreamReader(new StringReader((String) src)));
270             }
271             else
272             {
273                 return new StreamSource(new StringReader((String) src));
274             }
275         }
276         else if (src instanceof org.dom4j.Document)
277         {
278             return new DocumentSource((org.dom4j.Document) src);
279         }
280         else if (src instanceof org.xml.sax.InputSource)
281         {
282             return new SAXSource((InputSource) src);
283         }
284         // TODO MULE-3555
285         else if (src instanceof XMLStreamReader)
286         {
287             XMLStreamReader xsr = (XMLStreamReader) src;
288             
289             // StaxSource requires that we advance to a start element/document event
290             if (!xsr.isStartElement() && 
291                             xsr.getEventType() != XMLStreamConstants.START_DOCUMENT) 
292             {
293                 xsr.nextTag();
294             }
295             
296             return new StaxSource((XMLStreamReader) src);
297         }
298         else if (src instanceof org.w3c.dom.Document || src instanceof org.w3c.dom.Element)
299         {
300             return new DOMSource((org.w3c.dom.Node) src);
301         }
302         else if (src instanceof DelayedResult) 
303         {
304             DelayedResult result = ((DelayedResult) src);
305             DOMResult domResult = new DOMResult();
306             result.write(domResult);
307             return new DOMSource(domResult.getNode());
308         }
309         else if (src instanceof OutputHandler) 
310         {
311             OutputHandler handler = ((OutputHandler) src);
312             ByteArrayOutputStream output = new ByteArrayOutputStream();
313             
314             handler.write(RequestContext.getEvent(), output);
315             
316             return toStreamSource(xmlInputFactory, useStaxSource, new ByteArrayInputStream(output.toByteArray()));
317         }
318         else
319         {
320             return null;
321         }
322     }
323 
324     public static javax.xml.transform.Source toStreamSource(javax.xml.stream.XMLInputFactory xmlInputFactory, boolean useStaxSource, InputStream stream) throws XMLStreamException
325     {
326         if (useStaxSource)
327         {
328             return new org.mule.module.xml.stax.StaxSource(xmlInputFactory.createXMLStreamReader(stream));
329         }
330         else 
331         {
332             return new javax.xml.transform.stream.StreamSource(stream);
333         }
334     }
335     
336     /**
337      * Copies the reader to the writer. The start and end document methods must
338      * be handled on the writer manually. TODO: if the namespace on the reader
339      * has been declared previously to where we are in the stream, this probably
340      * won't work.
341      * 
342      * @param reader
343      * @param writer
344      * @throws XMLStreamException
345      */
346     public static void copy(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException {
347         copy(reader, writer, false);
348     }
349     public static void copy(XMLStreamReader reader, XMLStreamWriter writer,
350                             boolean fragment) throws XMLStreamException {
351         // number of elements read in
352         int read = 0;
353         int event = reader.getEventType();
354 
355         while (reader.hasNext()) {
356             switch (event) {
357             case XMLStreamConstants.START_ELEMENT:
358                 read++;
359                 writeStartElement(reader, writer);
360                 break;
361             case XMLStreamConstants.END_ELEMENT:
362                 writer.writeEndElement();
363                 read--;
364                 if (read <= 0 && !fragment) {
365                     return;
366                 }
367                 break;
368             case XMLStreamConstants.CHARACTERS:
369                 writer.writeCharacters(reader.getText());
370                 break;
371             case XMLStreamConstants.START_DOCUMENT:
372             case XMLStreamConstants.END_DOCUMENT:
373             case XMLStreamConstants.ATTRIBUTE:
374             case XMLStreamConstants.NAMESPACE:
375                 break;
376             default:
377                 break;
378             }
379             event = reader.next();
380         }
381     }
382 
383     private static void writeStartElement(XMLStreamReader reader, XMLStreamWriter writer)
384         throws XMLStreamException {
385         String local = reader.getLocalName();
386         String uri = reader.getNamespaceURI();
387         String prefix = reader.getPrefix();
388         if (prefix == null) {
389             prefix = "";
390         }
391 
392         
393 //        System.out.println("STAXUTILS:writeStartElement : node name : " + local +  " namespace URI" + uri);
394         boolean writeElementNS = false;
395         if (uri != null) {
396             String boundPrefix = writer.getPrefix(uri);
397             if (boundPrefix == null || !prefix.equals(boundPrefix)) {
398                 writeElementNS = true;
399             }
400         }
401 
402         // Write out the element name
403         if (uri != null) {
404             if (prefix.length() == 0 && StringUtils.isEmpty(uri)) {
405                 writer.writeStartElement(local);
406                 writer.setDefaultNamespace(uri);
407 
408             } else {
409                 writer.writeStartElement(prefix, local, uri);
410                 writer.setPrefix(prefix, uri);
411             }
412         } else {
413             writer.writeStartElement(local);
414         }
415 
416         // Write out the namespaces
417         for (int i = 0; i < reader.getNamespaceCount(); i++) {
418             String nsURI = reader.getNamespaceURI(i);
419             String nsPrefix = reader.getNamespacePrefix(i);
420             if (nsPrefix == null) {
421                 nsPrefix = "";
422             }
423 
424             if (nsPrefix.length() == 0) {
425                 writer.writeDefaultNamespace(nsURI);
426             } else {
427                 writer.writeNamespace(nsPrefix, nsURI);
428             }
429 
430             if (nsURI.equals(uri) && nsPrefix.equals(prefix)) {
431                 writeElementNS = false;
432             }
433         }
434 
435         // Check if the namespace still needs to be written.
436         // We need this check because namespace writing works
437         // different on Woodstox and the RI.
438         if (writeElementNS) {
439             if (prefix == null || prefix.length() == 0) {
440                 writer.writeDefaultNamespace(uri);
441             } else {
442                 writer.writeNamespace(prefix, uri);
443             }
444         }        
445         
446         // Write out attributes
447         for (int i = 0; i < reader.getAttributeCount(); i++) {
448             String ns = reader.getAttributeNamespace(i);
449             String nsPrefix = reader.getAttributePrefix(i);
450             if (ns == null || ns.length() == 0) {
451                 writer.writeAttribute(reader.getAttributeLocalName(i), reader.getAttributeValue(i));
452             } else if (nsPrefix == null || nsPrefix.length() == 0) {
453                 writer.writeAttribute(reader.getAttributeNamespace(i), reader.getAttributeLocalName(i),
454                                       reader.getAttributeValue(i));
455             } else {
456                 writer.writeAttribute(reader.getAttributePrefix(i), reader.getAttributeNamespace(i), reader
457                     .getAttributeLocalName(i), reader.getAttributeValue(i));
458             }
459 
460         }
461     }
462 }