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          for (int code = in.getEventType(); code != XMLStreamReader.END_DOCUMENT; code = in.next())
84          {
85              if (code == XMLStreamReader.START_ELEMENT)
86              {
87                  return true;
88              }
89          }
90          return false;
91      }
92      
93      /***
94       * Copies the reader to the writer.  The start and end document
95       * methods must be handled on the writer manually.
96       * 
97       * TODO: if the namespace on the reader has been declared previously
98       * to where we are in the stream, this probably won't work.
99       * 
100      * @param reader
101      * @param writer
102      * @throws XMLStreamException
103      */
104     public static void copy( XMLStreamReader reader, XMLStreamWriter writer ) 
105         throws XMLStreamException
106     {
107         int read = 0; // number of elements read in
108         int event = reader.getEventType();
109         
110         while ( reader.hasNext() )
111         {
112             switch( event )
113             {
114                 case XMLStreamConstants.START_ELEMENT:
115                     read++;
116                     writeStartElement( reader, writer );
117                     break;
118                 case XMLStreamConstants.END_ELEMENT:
119                     writer.writeEndElement();
120                     read--;
121                     if ( read <= 0 )
122                         return;
123                     break;
124                 case XMLStreamConstants.CHARACTERS:
125                     writer.writeCharacters( reader.getText() );  
126                     break;
127                 case XMLStreamConstants.START_DOCUMENT:
128                 case XMLStreamConstants.END_DOCUMENT:
129                 case XMLStreamConstants.ATTRIBUTE:
130                 case XMLStreamConstants.NAMESPACE:
131                     break;
132                 default:
133                     break;
134             }
135             event = reader.next();
136         }
137     }
138 
139     private static void writeStartElement(XMLStreamReader reader, XMLStreamWriter writer) 
140         throws XMLStreamException
141     {
142         String local = reader.getLocalName();
143         String uri = reader.getNamespaceURI();
144         String prefix = reader.getPrefix();
145         if (prefix == null)
146         {
147             prefix = "";
148         }
149         
150         String boundPrefix = writer.getPrefix(uri);
151         boolean writeElementNS = false;
152         if ( boundPrefix == null || !prefix.equals(boundPrefix) )
153         {   
154             writeElementNS = true;
155         }
156         
157         // Write out the element name
158         if (uri != null && prefix.length() > 0)
159         {
160             writer.writeStartElement(prefix, local, uri);
161         }
162         else
163         {
164             writer.writeStartElement( reader.getLocalName() );
165         }
166 
167         // Write out the namespaces
168         for ( int i = 0; i < reader.getNamespaceCount(); i++ )
169         {
170             String nsURI = reader.getNamespaceURI(i);
171             String nsPrefix = reader.getNamespacePrefix(i);
172             if (nsPrefix == null) nsPrefix = "";
173             
174             if ( nsPrefix.length() ==  0 )
175             {
176                 writer.writeDefaultNamespace(nsURI);
177             }
178             else
179             {
180                 writer.writeNamespace(nsPrefix, nsURI);
181             }
182 
183             if (nsURI.equals(uri) && nsPrefix.equals(prefix))
184             {
185                 writeElementNS = false;
186             }
187         }
188         
189         // Check if the namespace still needs to be written.
190         // We need this check because namespace writing works 
191         // different on Woodstox and the RI.
192         if (writeElementNS)
193         {
194             if ( prefix == null || prefix.length() ==  0 )
195             {
196                 writer.writeDefaultNamespace(uri);
197             }
198             else
199             {
200                 writer.writeNamespace(prefix, uri);
201             }
202         }
203 
204         // Write out attributes
205         for ( int i = 0; i < reader.getAttributeCount(); i++ )
206         {
207             String ns = reader.getAttributeNamespace(i);
208             String nsPrefix = reader.getAttributePrefix(i);
209             if ( ns == null || ns.length() == 0 )
210             {
211                 writer.writeAttribute(
212                         reader.getAttributeLocalName(i),
213                         reader.getAttributeValue(i));
214             }
215             else if (nsPrefix == null || nsPrefix.length() == 0)
216             {
217                 writer.writeAttribute(
218                     reader.getAttributeNamespace(i),
219                     reader.getAttributeLocalName(i),
220                     reader.getAttributeValue(i));
221             }
222             else
223             {
224                 writer.writeAttribute(reader.getAttributePrefix(i),
225                                       reader.getAttributeNamespace(i),
226                                       reader.getAttributeLocalName(i),
227                                       reader.getAttributeValue(i));
228             }
229             
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 }