View Javadoc

1   package org.codehaus.xfire.util;
2   
3   import java.io.InputStream;
4   import java.io.OutputStream;
5   import java.io.Reader;
6   
7   import javax.xml.parsers.DocumentBuilder;
8   import javax.xml.stream.XMLInputFactory;
9   import javax.xml.stream.XMLOutputFactory;
10  import javax.xml.stream.XMLStreamConstants;
11  import javax.xml.stream.XMLStreamException;
12  import javax.xml.stream.XMLStreamReader;
13  import javax.xml.stream.XMLStreamWriter;
14  
15  import org.codehaus.xfire.XFireRuntimeException;
16  import org.codehaus.xfire.util.stax.DepthXMLStreamReader;
17  import org.w3c.dom.Attr;
18  import org.w3c.dom.Document;
19  import org.w3c.dom.Element;
20  import org.w3c.dom.NamedNodeMap;
21  import org.w3c.dom.Node;
22  import org.w3c.dom.NodeList;
23  
24  /***
25   * Common StAX utilities.
26   * 
27   * @author <a href="mailto:dan@envoisolutions.com">Dan Diephouse</a>
28   * @since Oct 26, 2004
29   */
30  public class STAXUtils
31  {
32      private static final String XML_NS = "http://www.w3.org/2000/xmlns/";
33  
34      /***
35       * Returns true if currently at the start of an element, otherwise move forwards to the next
36       * element start and return true, otherwise false is returned if the end of the stream is reached.
37       */
38      public static boolean skipToStartOfElement(XMLStreamReader in)
39          throws XMLStreamException
40      {
41          for (int code = in.getEventType(); code != XMLStreamReader.END_DOCUMENT; code = in.next())
42          {
43              if (code == XMLStreamReader.START_ELEMENT)
44              {
45                  return true;
46              }
47          }
48          return false;
49      }
50  
51      public static boolean toNextElement(DepthXMLStreamReader dr)
52      {
53          if (dr.getEventType() == XMLStreamReader.START_ELEMENT)
54              return true;
55          
56          if (dr.getEventType() == XMLStreamReader.END_ELEMENT)
57              return false;
58          
59          try
60          {
61              int depth = dr.getDepth();
62              
63              for (int event = dr.getEventType(); dr.getDepth() >= depth; event = dr.next())
64              {
65                  if (event == XMLStreamReader.START_ELEMENT && dr.getDepth() == depth + 1)
66                  {
67                      return true;
68                  }
69                  else if (event == XMLStreamReader.END_ELEMENT)
70                  {
71                      depth--;
72                  }
73              }
74              
75              return false;
76          }
77          catch (XMLStreamException e)
78          {
79              throw new XFireRuntimeException("Couldn't parse stream.", e);
80          }
81      }
82     
83      public static boolean skipToStartOfElement(DepthXMLStreamReader in)
84          throws XMLStreamException
85      {
86          for (int code = in.getEventType(); code != XMLStreamReader.END_DOCUMENT; code = in.next())
87          {
88              if (code == XMLStreamReader.START_ELEMENT)
89              {
90                  return true;
91              }
92          }
93          return false;
94      }
95      
96      /***
97       * Copies the reader to the writer.  The start and end document
98       * methods must be handled on the writer manually.
99       * 
100      * TODO: if the namespace on the reader has been declared previously
101      * to where we are in the stream, this probably won't work.
102      * 
103      * @param reader
104      * @param writer
105      * @throws XMLStreamException
106      */
107     public static void copy( XMLStreamReader reader, XMLStreamWriter writer ) 
108         throws XMLStreamException
109     {
110         int read = 0; // number of elements read in
111         int event = reader.getEventType();
112         
113         while ( reader.hasNext() )
114         {
115             switch( event )
116             {
117                 case XMLStreamConstants.START_ELEMENT:
118                     read++;
119                     writeStartElement( reader, writer );
120                     break;
121                 case XMLStreamConstants.END_ELEMENT:
122                     writer.writeEndElement();
123                     read--;
124                     if ( read <= 0 )
125                         return;
126                     break;
127                 case XMLStreamConstants.CHARACTERS:
128                     writer.writeCharacters( reader.getText() );  
129                     break;
130                 case XMLStreamConstants.START_DOCUMENT:
131                 case XMLStreamConstants.END_DOCUMENT:
132                 case XMLStreamConstants.ATTRIBUTE:
133                 case XMLStreamConstants.NAMESPACE:
134                     break;
135                 default:
136                     break;
137             }
138             event = reader.next();
139         }
140     }
141 
142     private static void writeStartElement(XMLStreamReader reader, XMLStreamWriter writer) 
143         throws XMLStreamException
144     {
145         String local = reader.getLocalName();
146         String uri = reader.getNamespaceURI();
147         String prefix = reader.getPrefix();
148         if (prefix == null)
149         {
150             prefix = "";
151         }
152         
153         String boundPrefix = writer.getPrefix(uri);
154         boolean writeElementNS = false;
155         if ( boundPrefix == null || !prefix.equals(boundPrefix) )
156         {   
157             writeElementNS = true;
158         }
159         
160         // Write out the element name
161         if (uri != null)
162         {
163             if (prefix.length() == 0) 
164             { 
165                 writer.writeStartElement(local); 
166                 writer.setDefaultNamespace(uri); 
167             } 
168             else 
169             { 
170                 writer.writeStartElement(prefix, local, uri); 
171                 writer.setPrefix(prefix, uri); 
172             } 
173         }
174         else
175         {
176             writer.writeStartElement( reader.getLocalName() );
177         }
178 
179         // Write out the namespaces
180         for ( int i = 0; i < reader.getNamespaceCount(); i++ )
181         {
182             String nsURI = reader.getNamespaceURI(i);
183             String nsPrefix = reader.getNamespacePrefix(i);
184             if (nsPrefix == null) nsPrefix = "";
185             
186             if ( nsPrefix.length() ==  0 )
187             {
188                 writer.writeDefaultNamespace(nsURI);
189             }
190             else
191             {
192                 writer.writeNamespace(nsPrefix, nsURI);
193             }
194 
195             if (nsURI.equals(uri) && nsPrefix.equals(prefix))
196             {
197                 writeElementNS = false;
198             }
199         }
200         
201         // Check if the namespace still needs to be written.
202         // We need this check because namespace writing works 
203         // different on Woodstox and the RI.
204         if (writeElementNS)
205         {
206             if ( prefix == null || prefix.length() ==  0 )
207             {
208                 writer.writeDefaultNamespace(uri);
209             }
210             else
211             {
212                 writer.writeNamespace(prefix, uri);
213             }
214         }
215 
216         // Write out attributes
217         for ( int i = 0; i < reader.getAttributeCount(); i++ )
218         {
219             String ns = reader.getAttributeNamespace(i);
220             String nsPrefix = reader.getAttributePrefix(i);
221             if ( ns == null || ns.length() == 0 )
222             {
223                 writer.writeAttribute(
224                         reader.getAttributeLocalName(i),
225                         reader.getAttributeValue(i));
226             }
227             else if (nsPrefix == null || nsPrefix.length() == 0)
228             {
229                 writer.writeAttribute(
230                     reader.getAttributeNamespace(i),
231                     reader.getAttributeLocalName(i),
232                     reader.getAttributeValue(i));
233             }
234             else
235             {
236                 writer.writeAttribute(reader.getAttributePrefix(i),
237                                       reader.getAttributeNamespace(i),
238                                       reader.getAttributeLocalName(i),
239                                       reader.getAttributeValue(i));
240             }
241             
242             
243         }
244     }
245 
246     /***
247      * Writes an Element to an XMLStreamWriter.  The writer must already
248      * have started the doucment (via writeStartDocument()). Also, this probably
249      * won't work with just a fragment of a document. The Element should be
250      * the root element of the document.
251      * 
252      * @param e
253      * @param writer
254      * @throws XMLStreamException
255      */
256     public static void writeElement( Element e, XMLStreamWriter writer ) 
257         throws XMLStreamException
258     {
259         String prefix = e.getPrefix();
260         String ns = e.getNamespaceURI();
261         String localName = e.getLocalName();
262         
263         if ( prefix == null )
264         {
265             if ( ns == null )
266             {
267                 writer.writeStartElement( localName );
268             }
269             else
270             {
271                 prefix = "";
272                 writer.setDefaultNamespace(ns);
273                 writer.writeStartElement( ns, localName );
274                 
275                 String curUri = writer.getNamespaceContext().getNamespaceURI(prefix);
276                 if ( curUri == null || curUri.length() != ns.length() )
277                 {
278                     writer.writeDefaultNamespace(ns);
279                 }
280             }
281         }
282         else
283         {
284             writer.writeStartElement(prefix, localName, ns);
285             
286             String curUri = writer.getNamespaceContext().getNamespaceURI(prefix);
287             if ( curUri == null || curUri.length() != ns.length() || !curUri.equals(ns) )
288             {
289                 writer.writeNamespace(prefix, ns);
290             }
291         }
292 
293         NamedNodeMap attrs = e.getAttributes();
294         for ( int i = 0; i < attrs.getLength(); i++ )
295         {
296             Node attr = attrs.item(i);
297             
298             String attrPrefix = writer.getNamespaceContext().getPrefix(attr.getNamespaceURI());
299             if ( attrPrefix == null )
300             {
301                 writer.writeAttribute(attr.getNamespaceURI(), attr.getNodeName(), attr.getNodeValue());
302             }
303             else
304             {
305                 writer.writeAttribute(attrPrefix, attr.getNamespaceURI(), attr.getNodeName(), attr.getNodeValue());
306             }
307         }
308     
309         String value = DOMUtils.getContent(e);
310         
311         if ( value != null && value.length() > 0)
312             writer.writeCharacters( value );
313         
314         NodeList nodes = e.getChildNodes();
315         for ( int i = 0; i < nodes.getLength(); i++ )
316         {
317             Node n = nodes.item(i);
318             if ( n instanceof Element )
319             {
320                 writeElement((Element)n, writer);
321             }
322         }
323 
324         writer.writeEndElement();
325     }
326     
327     /***
328      * @param e
329      * @return
330      */
331     private static Element getNamespaceDeclarer(Element e)
332     {
333         while( true )
334         {
335             Node n = e.getParentNode();
336             if ( n.equals(e) )
337                 return null;
338             if ( n.getNamespaceURI() != null )
339                 return (Element) n;
340         }
341     }
342 
343     public static Document read(DocumentBuilder builder, XMLStreamReader reader)
344         throws XMLStreamException
345     {
346         Document doc = builder.newDocument();
347 
348         readDocElements(doc, reader);
349         
350         return doc;
351     }
352 
353     /***
354      * @param parent
355      * @return
356      */
357     private static Document getDocument(Node parent)
358     {
359         return (parent instanceof Document) ? (Document) parent : parent.getOwnerDocument();
360     }
361 
362     /***
363      * @param parent
364      * @param reader
365      * @return
366      * @throws XMLStreamException
367      */
368     private static Element startElement(Node parent, XMLStreamReader reader)
369         throws XMLStreamException
370     {
371         Document doc = getDocument(parent);
372         
373         Element e = doc.createElementNS(reader.getNamespaceURI(), reader.getLocalName());
374         e.setPrefix(reader.getPrefix());
375         
376         declareNamespaces(reader, e);
377         
378         for (int i = 0; i < reader.getAttributeCount(); i++)
379         {
380             Attr attr = doc.createAttributeNS(reader.getAttributeNamespace(i), reader
381                     .getAttributeLocalName(i));
382             attr.setValue(reader.getAttributeValue(i));
383             e.setAttributeNode(attr);
384         }
385 
386         parent.appendChild(e);
387         
388         reader.next();
389         
390         readDocElements(e, reader);
391         
392         return e;
393     }
394 
395     /***
396      * @param parent
397      * @param reader
398      * @throws XMLStreamException
399      */
400     public static void readDocElements(Node parent, XMLStreamReader reader)
401         throws XMLStreamException
402     {
403         Document doc = getDocument(parent);
404 
405         int event = reader.getEventType();
406         while (reader.hasNext())
407         {
408             switch (event)
409             {
410             case XMLStreamConstants.START_ELEMENT:
411                 startElement(parent, reader);
412 
413                 if (parent instanceof Document) 
414                 {
415                     if (reader.hasNext()) reader.next();
416                     return;
417                 }
418                 
419                 break;
420             case XMLStreamConstants.END_ELEMENT:
421                 return;
422             case XMLStreamConstants.CHARACTERS:
423                 if (parent != null)
424                 {
425                     parent.appendChild(doc.createTextNode(reader.getText()));
426                 }
427 
428                 break;
429             default:
430                 break;
431             }
432             
433             if (reader.hasNext())
434             {
435                 event = reader.next();
436             }
437         }
438     }
439 
440     private static void declareNamespaces(XMLStreamReader reader, Element node)
441     {
442         for (int i = 0; i < reader.getNamespaceCount(); i++)
443         {
444             String uri = reader.getNamespaceURI(i);
445             String prefix = reader.getNamespacePrefix(i);
446             // TODO : i'm not sure about skiping parent namespaces, so i comment it for a while
447             if (prefix != null && prefix.length()>0 /* && !uri.equals(node.getNamespaceURI()) */)
448             {
449                 node.setAttributeNS(XML_NS,"xmlns:"+prefix, uri);
450             }else{
451                 if( uri != null && uri.length()>0){
452                     node.setAttribute("xmlns",uri);
453                 }
454             }
455         }
456     }
457 
458     public static XMLStreamWriter createXMLStreamWriter(OutputStream out, String encoding)
459     {
460         XMLOutputFactory factory = XMLOutputFactory.newInstance();
461 
462         if (encoding == null) encoding = "UTF-8";
463         
464         try
465         {
466             XMLStreamWriter writer = factory.createXMLStreamWriter(out, encoding);
467 
468             return writer;
469         }
470         catch (XMLStreamException e)
471         {
472             throw new XFireRuntimeException("Couldn't parse stream.", e);
473         }
474     }
475 
476     public static XMLStreamReader createXMLStreamReader(InputStream in, String encoding)
477     {
478         XMLInputFactory factory = XMLInputFactory.newInstance();
479 
480         if (encoding == null) encoding = "UTF-8";
481         
482         try
483         {
484             return factory.createXMLStreamReader(in, encoding);
485         }
486         catch (XMLStreamException e)
487         {
488             throw new XFireRuntimeException("Couldn't parse stream.", e);
489         }
490     }
491 
492     public static XMLStreamReader createXMLStreamReader(Reader reader)
493     {
494         XMLInputFactory factory = XMLInputFactory.newInstance();
495  
496         try
497         {
498             return factory.createXMLStreamReader(reader);
499         }
500         catch (XMLStreamException e)
501         {
502             throw new XFireRuntimeException("Couldn't parse stream.", e);
503         }
504     }
505 }