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
218 break;
219 default :
220 if (doc == null)
221 {
222
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
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 }