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;
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
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
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
200
201
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
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 }