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