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