View Javadoc

1   package org.codehaus.xfire.util;
2   
3   import java.io.InputStream;
4   import java.io.Reader;
5   import javax.xml.stream.XMLInputFactory;
6   import javax.xml.stream.XMLStreamConstants;
7   import javax.xml.stream.XMLStreamException;
8   import javax.xml.stream.XMLStreamReader;
9   import javax.xml.stream.events.StartElement;
10  import org.dom4j.Branch;
11  import org.dom4j.Document;
12  import org.dom4j.DocumentFactory;
13  import org.dom4j.Element;
14  import org.dom4j.Entity;
15  import org.dom4j.Namespace;
16  import org.dom4j.Node;
17  import org.dom4j.ProcessingInstruction;
18  
19  /***
20   * Reads a DOM4J {@link Document}, as well as other {@link Node}s, from a StAX
21   * {@link XMLStreamReader}.
22   * 
23   * @author Dan Diephouse
24   * @author Christian Niles
25   */
26  public class STAXStreamReader
27  {
28      /*** Reference to the DocumentFactory used to build DOM4J nodes. */
29      private DocumentFactory factory;
30      
31      /*** A StAX input factory, used to construct streams from IO streams. */
32      private XMLInputFactory inputFactory = XMLInputFactory.newInstance();
33      
34      /***
35       * Constructs a default <code>STAXEventReader</code> instance with a
36       * default {@link DocumentFactory}.
37       */
38      public STAXStreamReader()
39      {
40          this.factory = DocumentFactory.getInstance();
41      }
42      
43      /***
44       * Constructs a <code>STAXStreamReader</code> instance that uses the
45       * specified {@link DocumentFactory}to construct DOM4J {@link Node}s.
46       * 
47       * @param factory
48       *            The DocumentFactory to use when constructing DOM4J nodes, or
49       *            <code>null</code> if a default should be used.
50       */
51      public STAXStreamReader( DocumentFactory factory )
52      {
53          if (factory != null)
54          {
55              this.factory = factory;
56          }
57          else
58          {
59              this.factory = DocumentFactory.getInstance();
60          }
61      }
62      /***
63       * Sets the DocumentFactory to be used when constructing DOM4J nodes.
64       * 
65       * @param factory
66       *            The DocumentFactory to use when constructing DOM4J nodes, or
67       *            <code>null</code> if a default should be used.
68       */
69      public void setDocumentFactory( DocumentFactory factory )
70      {
71          if (factory != null)
72          {
73              this.factory = factory;
74          }
75          else
76          {
77              this.factory = DocumentFactory.getInstance();
78          }
79      }
80      
81      /***
82       * Constructs a StAX event stream from the provided I/O stream and reads a
83       * DOM4J document from it.
84       * 
85       * @param is
86       *            The I/O stream from which the Document will be read.
87       * @return The Document that was read from the stream.
88       * @throws XMLStreamException
89       *             If an error occurs reading content from the stream.
90       */
91      public Document readDocument( InputStream is ) throws XMLStreamException
92      {
93          return readDocument( is, null );
94      }
95      
96      /***
97       * Constructs a StAX event stream from the provided I/O character stream and
98       * reads a DOM4J document from it.
99       * 
100      * @param reader
101      *            The character stream from which the Document will be read.
102      * @return The Document that was read from the stream.
103      * @throws XMLStreamException
104      *             If an error occurs reading content from the stream.
105      */
106     public Document readDocument( Reader reader ) throws XMLStreamException
107     {
108         return readDocument( reader, null );
109     }
110 
111     public Document readDocument( InputStream is, String systemId )
112             throws XMLStreamException
113     {
114         XMLStreamReader eventReader = inputFactory.createXMLStreamReader(
115                 systemId, is );
116         try
117         {
118             return readDocument( eventReader );
119         }
120         finally
121         {
122             eventReader.close();
123         }
124     }
125     
126     /***
127      * Constructs a StAX event stream from the provided I/O character stream and
128      * reads a DOM4J document from it.
129      * 
130      * @param reader
131      *            The character stream from which the Document will be read.
132      * @param systemId
133      *            A system id used to resolve entities.
134      * @return The Document that was read from the stream.
135      * @throws XMLStreamException
136      *             If an error occurs reading content from the stream.
137      */
138     public Document readDocument( Reader reader, String systemId )
139             throws XMLStreamException
140     {
141         XMLStreamReader eventReader = 
142             inputFactory.createXMLStreamReader( systemId, reader );
143         try
144         {
145             return readDocument( eventReader );
146         }
147         finally
148         {
149             eventReader.close();
150         }
151     }
152     
153     public void readNode( XMLStreamReader reader, Branch node ) throws XMLStreamException
154     {
155         int event = reader.getEventType();
156         
157         switch ( event )
158         {
159             case XMLStreamReader.START_ELEMENT:
160                 Element e = readElement( reader );
161                 node.add(e);
162                 break;
163             case XMLStreamReader.CHARACTERS:
164                 node.setText( reader.getText() );
165                 break;
166             case XMLStreamReader.PROCESSING_INSTRUCTION:
167                 ProcessingInstruction pi = readProcessingInstruction( reader );
168                 break;
169             case XMLStreamReader.ENTITY_REFERENCE:
170                 Entity er = readEntityReference( reader );
171                 node.add(er);
172                 break;
173             case XMLStreamReader.ATTRIBUTE:
174             case XMLStreamReader.NAMESPACE:
175             case XMLStreamReader.START_DOCUMENT:
176                 default:
177                 break;
178         }
179         
180         
181     }
182     
183     /***
184      * Reads a DOM4J {@link Document}from the provided stream. The stream
185      * should be positioned at the start of a document, or before a
186      * {@link StartElement}event.
187      * 
188      * @param reader
189      *            The event stream from which to read the {@link Document}.
190      * @return The {@link Document}that was read from the stream.
191      * @throws XMLStreamException
192      *             If an error occurs reading events from the stream.
193      */
194     public Document readDocument( XMLStreamReader reader )
195             throws XMLStreamException
196     {
197         Document doc = null;
198         for ( ; reader.hasNext(); reader.next() )
199         {
200             int type = reader.getEventType();
201             switch (type)
202             {
203                 case XMLStreamConstants.START_DOCUMENT:
204                     String encodingScheme = reader.getEncoding();
205                     if ( encodingScheme != null )
206                     {
207                         doc = factory.createDocument( encodingScheme );
208                     }
209                     else
210                     {
211                         doc = factory.createDocument();
212                     }
213                     break;
214                 case XMLStreamConstants.END_DOCUMENT :
215                 case XMLStreamConstants.SPACE :
216                 case XMLStreamConstants.CHARACTERS :
217                     // skip end document and space outside the root element
218                     break;
219                 default :
220                     if (doc == null)
221                     {
222                         // create document
223                         doc = factory.createDocument();
224                     }
225                     
226                     readNode( reader, (Branch) doc );
227             }
228         }
229 
230         return doc;
231     }
232 
233     public Element readElement( XMLStreamReader reader )
234             throws XMLStreamException
235     {
236         Element elem = createElement( reader );
237         
238         // read element content
239         while (reader.hasNext())
240         {
241             reader.next();
242             int event = reader.getEventType();
243             switch ( event )
244             {
245                 case XMLStreamConstants.END_ELEMENT:
246                     return elem;
247                 default:
248                     readNode( reader, elem );
249                     break;
250             }
251             
252         }
253         
254         return elem;
255     }
256 
257     public Entity readEntityReference( XMLStreamReader reader )
258             throws XMLStreamException
259     {
260         throw new UnsupportedOperationException("no entity ref");
261     }
262     
263     /***
264      * Constructs a DOM4J ProcessingInstruction from the provided event stream.
265      * The stream must be positioned before a {@link ProcessingInstruction}
266      * event.
267      * 
268      * @param reader
269      *            The event stream from which to read the ProcessingInstruction.
270      * @return The ProcessingInstruction that was read from the stream.
271      * @throws XMLStreamException
272      *             If an error occured reading events from the stream, or the
273      *             stream was not positioned before a
274      *             {@link ProcessingInstruction}event.
275      */
276     public org.dom4j.ProcessingInstruction readProcessingInstruction(
277             XMLStreamReader reader ) throws XMLStreamException
278     {
279         return factory.createProcessingInstruction( 
280                 reader.getPIData(),
281                 reader.getPITarget());
282     }
283 
284     public Element createElement( XMLStreamReader reader )
285     {
286         String prefix = reader.getPrefix();
287         if ( prefix == null )
288             prefix = "";
289         
290         org.dom4j.QName elemName =
291             factory.createQName( reader.getLocalName(), 
292                                  prefix,
293                                  reader.getNamespaceURI() );
294         
295         Element elem = factory.createElement( elemName );
296         
297         int count = reader.getNamespaceCount();
298         for ( int i = 0; i < count; i++ )
299         {  
300             prefix = reader.getNamespacePrefix(i);
301             if ( prefix == null )
302                 prefix = "";
303             
304             Namespace ns = factory.createNamespace( 
305                     prefix,
306                     reader.getNamespaceURI(i) );
307             
308             elem.add( ns );
309         }
310 
311         count = reader.getAttributeCount();
312         for ( int i = 0; i < count; i++ )
313         {
314             prefix = reader.getAttributePrefix(i);
315             if ( prefix == null )
316                 prefix = "";
317             
318             org.dom4j.QName attrName =
319                 factory.createQName( reader.getAttributeLocalName(i), 
320                                      prefix,
321                                      reader.getAttributeNamespace(i) );
322             
323             elem.addAttribute( attrName, reader.getAttributeValue(i) );
324         }
325 
326         return elem;
327     }
328 }