|
1 /* |
|
2 * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 |
|
26 // @@3RD PARTY CODE@@ |
|
27 |
|
28 // XMLWriter.java - serialize an XML document. |
|
29 // Written by David Megginson, david@megginson.com |
|
30 // NO WARRANTY! This class is in the public domain. |
|
31 |
|
32 // Id: XMLWriter.java,v 1.5 2000/09/17 01:08:16 david Exp |
|
33 |
|
34 package com.sun.xml.internal.bind.marshaller; |
|
35 |
|
36 import java.io.IOException; |
|
37 import java.io.OutputStreamWriter; |
|
38 import java.io.Writer; |
|
39 import java.util.HashMap; |
|
40 import java.util.Map; |
|
41 |
|
42 import org.xml.sax.Attributes; |
|
43 import org.xml.sax.SAXException; |
|
44 import org.xml.sax.helpers.AttributesImpl; |
|
45 import org.xml.sax.helpers.XMLFilterImpl; |
|
46 |
|
47 |
|
48 /** |
|
49 * Filter to write an XML document from a SAX event stream. |
|
50 * |
|
51 * <p>This class can be used by itself or as part of a SAX event |
|
52 * stream: it takes as input a series of SAX2 ContentHandler |
|
53 * events and uses the information in those events to write |
|
54 * an XML document. Since this class is a filter, it can also |
|
55 * pass the events on down a filter chain for further processing |
|
56 * (you can use the XMLWriter to take a snapshot of the current |
|
57 * state at any point in a filter chain), and it can be |
|
58 * used directly as a ContentHandler for a SAX2 XMLReader.</p> |
|
59 * |
|
60 * <p>The client creates a document by invoking the methods for |
|
61 * standard SAX2 events, always beginning with the |
|
62 * {@link #startDocument startDocument} method and ending with |
|
63 * the {@link #endDocument endDocument} method. There are convenience |
|
64 * methods provided so that clients to not have to create empty |
|
65 * attribute lists or provide empty strings as parameters; for |
|
66 * example, the method invocation</p> |
|
67 * |
|
68 * <pre> |
|
69 * w.startElement("foo"); |
|
70 * </pre> |
|
71 * |
|
72 * <p>is equivalent to the regular SAX2 ContentHandler method</p> |
|
73 * |
|
74 * <pre> |
|
75 * w.startElement("", "foo", "", new AttributesImpl()); |
|
76 * </pre> |
|
77 * |
|
78 * <p>Except that it is more efficient because it does not allocate |
|
79 * a new empty attribute list each time. The following code will send |
|
80 * a simple XML document to standard output:</p> |
|
81 * |
|
82 * <pre> |
|
83 * XMLWriter w = new XMLWriter(); |
|
84 * |
|
85 * w.startDocument(); |
|
86 * w.startElement("greeting"); |
|
87 * w.characters("Hello, world!"); |
|
88 * w.endElement("greeting"); |
|
89 * w.endDocument(); |
|
90 * </pre> |
|
91 * |
|
92 * <p>The resulting document will look like this:</p> |
|
93 * |
|
94 * <pre> |
|
95 * <?xml version="1.0" standalone="yes"?> |
|
96 * |
|
97 * <greeting>Hello, world!</greeting> |
|
98 * </pre> |
|
99 * |
|
100 * <p>In fact, there is an even simpler convenience method, |
|
101 * <var>dataElement</var>, designed for writing elements that |
|
102 * contain only character data, so the code to generate the |
|
103 * document could be shortened to</p> |
|
104 * |
|
105 * <pre> |
|
106 * XMLWriter w = new XMLWriter(); |
|
107 * |
|
108 * w.startDocument(); |
|
109 * w.dataElement("greeting", "Hello, world!"); |
|
110 * w.endDocument(); |
|
111 * </pre> |
|
112 * |
|
113 * <h2>Whitespace</h2> |
|
114 * |
|
115 * <p>According to the XML Recommendation, <em>all</em> whitespace |
|
116 * in an XML document is potentially significant to an application, |
|
117 * so this class never adds newlines or indentation. If you |
|
118 * insert three elements in a row, as in</p> |
|
119 * |
|
120 * <pre> |
|
121 * w.dataElement("item", "1"); |
|
122 * w.dataElement("item", "2"); |
|
123 * w.dataElement("item", "3"); |
|
124 * </pre> |
|
125 * |
|
126 * <p>you will end up with</p> |
|
127 * |
|
128 * <pre> |
|
129 * <item>1</item><item>3</item><item>3</item> |
|
130 * </pre> |
|
131 * |
|
132 * <p>You need to invoke one of the <var>characters</var> methods |
|
133 * explicitly to add newlines or indentation. Alternatively, you |
|
134 * can use {@link DataWriter}, which |
|
135 * is derived from this class -- it is optimized for writing |
|
136 * purely data-oriented (or field-oriented) XML, and does automatic |
|
137 * linebreaks and indentation (but does not support mixed content |
|
138 * properly).</p> |
|
139 * |
|
140 * |
|
141 * <h2>Namespace Support</h2> |
|
142 * |
|
143 * <p>The writer contains extensive support for XML Namespaces, so that |
|
144 * a client application does not have to keep track of prefixes and |
|
145 * supply <var>xmlns</var> attributes. By default, the XML writer will |
|
146 * generate Namespace declarations in the form _NS1, _NS2, etc., wherever |
|
147 * they are needed, as in the following example:</p> |
|
148 * |
|
149 * <pre> |
|
150 * w.startDocument(); |
|
151 * w.emptyElement("http://www.foo.com/ns/", "foo"); |
|
152 * w.endDocument(); |
|
153 * </pre> |
|
154 * |
|
155 * <p>The resulting document will look like this:</p> |
|
156 * |
|
157 * <pre> |
|
158 * <?xml version="1.0" standalone="yes"?> |
|
159 * |
|
160 * <_NS1:foo xmlns:_NS1="http://www.foo.com/ns/"/> |
|
161 * </pre> |
|
162 * |
|
163 * <p>In many cases, document authors will prefer to choose their |
|
164 * own prefixes rather than using the (ugly) default names. The |
|
165 * XML writer allows two methods for selecting prefixes:</p> |
|
166 * |
|
167 * <ol> |
|
168 * <li>the qualified name</li> |
|
169 * </ol> |
|
170 * |
|
171 * <p>Whenever the XML writer finds a new Namespace URI, it checks |
|
172 * to see if a qualified (prefixed) name is also available; if so |
|
173 * it attempts to use the name's prefix (as long as the prefix is |
|
174 * not already in use for another Namespace URI).</p> |
|
175 * |
|
176 * <p>The resulting document will look like this:</p> |
|
177 * |
|
178 * <pre> |
|
179 * <?xml version="1.0" standalone="yes"?> |
|
180 * |
|
181 * <foo:foo xmlns:foo="http://www.foo.com/ns/"/> |
|
182 * </pre> |
|
183 * |
|
184 * <p>The default Namespace simply uses an empty string as the prefix:</p> |
|
185 * |
|
186 * <pre> |
|
187 * w.setPrefix("http://www.foo.com/ns/", ""); |
|
188 * w.startDocument(); |
|
189 * w.emptyElement("http://www.foo.com/ns/", "foo"); |
|
190 * w.endDocument(); |
|
191 * </pre> |
|
192 * |
|
193 * <p>The resulting document will look like this:</p> |
|
194 * |
|
195 * <pre> |
|
196 * <?xml version="1.0" standalone="yes"?> |
|
197 * |
|
198 * <foo xmlns="http://www.foo.com/ns/"/> |
|
199 * </pre> |
|
200 * |
|
201 * <p>By default, the XML writer will not declare a Namespace until |
|
202 * it is actually used. Sometimes, this approach will create |
|
203 * a large number of Namespace declarations, as in the following |
|
204 * example:</p> |
|
205 * |
|
206 * <pre> |
|
207 * <xml version="1.0" standalone="yes"?> |
|
208 * |
|
209 * <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> |
|
210 * <rdf:Description about="http://www.foo.com/ids/books/12345"> |
|
211 * <dc:title xmlns:dc="http://www.purl.org/dc/">A Dark Night</dc:title> |
|
212 * <dc:creator xmlns:dc="http://www.purl.org/dc/">Jane Smith</dc:title> |
|
213 * <dc:date xmlns:dc="http://www.purl.org/dc/">2000-09-09</dc:title> |
|
214 * </rdf:Description> |
|
215 * </rdf:RDF> |
|
216 * </pre> |
|
217 * |
|
218 * <p>The "rdf" prefix is declared only once, because the RDF Namespace |
|
219 * is used by the root element and can be inherited by all of its |
|
220 * descendants; the "dc" prefix, on the other hand, is declared three |
|
221 * times, because no higher element uses the Namespace. To solve this |
|
222 * problem, you can instruct the XML writer to predeclare Namespaces |
|
223 * on the root element even if they are not used there:</p> |
|
224 * |
|
225 * <pre> |
|
226 * w.forceNSDecl("http://www.purl.org/dc/"); |
|
227 * </pre> |
|
228 * |
|
229 * <p>Now, the "dc" prefix will be declared on the root element even |
|
230 * though it's not needed there, and can be inherited by its |
|
231 * descendants:</p> |
|
232 * |
|
233 * <pre> |
|
234 * <xml version="1.0" standalone="yes"?> |
|
235 * |
|
236 * <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" |
|
237 * xmlns:dc="http://www.purl.org/dc/"> |
|
238 * <rdf:Description about="http://www.foo.com/ids/books/12345"> |
|
239 * <dc:title>A Dark Night</dc:title> |
|
240 * <dc:creator>Jane Smith</dc:title> |
|
241 * <dc:date>2000-09-09</dc:title> |
|
242 * </rdf:Description> |
|
243 * </rdf:RDF> |
|
244 * </pre> |
|
245 * |
|
246 * <p>This approach is also useful for declaring Namespace prefixes |
|
247 * that be used by qualified names appearing in attribute values or |
|
248 * character data.</p> |
|
249 * |
|
250 * @author David Megginson, david@megginson.com |
|
251 * @version 0.2 |
|
252 * @since JAXB1.0 |
|
253 * @see org.xml.sax.XMLFilter |
|
254 * @see org.xml.sax.ContentHandler |
|
255 */ |
|
256 public class XMLWriter extends XMLFilterImpl |
|
257 { |
|
258 |
|
259 //////////////////////////////////////////////////////////////////// |
|
260 // Constructors. |
|
261 //////////////////////////////////////////////////////////////////// |
|
262 |
|
263 |
|
264 |
|
265 |
|
266 /** |
|
267 * Create a new XML writer. |
|
268 * |
|
269 * <p>Write to the writer provided.</p> |
|
270 * |
|
271 * @param writer |
|
272 * The output destination, or null to use standard output. |
|
273 * @param encoding |
|
274 * If non-null string is specified, it is written as a part |
|
275 * of the XML declaration. |
|
276 */ |
|
277 public XMLWriter (Writer writer, String encoding, CharacterEscapeHandler _escapeHandler ) |
|
278 { |
|
279 init(writer,encoding); |
|
280 this.escapeHandler = _escapeHandler; |
|
281 } |
|
282 |
|
283 public XMLWriter (Writer writer, String encoding ) { |
|
284 this( writer, encoding, DumbEscapeHandler.theInstance ); |
|
285 } |
|
286 |
|
287 |
|
288 |
|
289 /** |
|
290 * Internal initialization method. |
|
291 * |
|
292 * <p>All of the public constructors invoke this method. |
|
293 * |
|
294 * @param writer The output destination, or null to use |
|
295 * standard output. |
|
296 */ |
|
297 private void init (Writer writer,String encoding) |
|
298 { |
|
299 setOutput(writer,encoding); |
|
300 } |
|
301 |
|
302 |
|
303 |
|
304 //////////////////////////////////////////////////////////////////// |
|
305 // Public methods. |
|
306 //////////////////////////////////////////////////////////////////// |
|
307 |
|
308 |
|
309 /** |
|
310 * Reset the writer. |
|
311 * |
|
312 * <p>This method is especially useful if the writer throws an |
|
313 * exception before it is finished, and you want to reuse the |
|
314 * writer for a new document. It is usually a good idea to |
|
315 * invoke {@link #flush flush} before resetting the writer, |
|
316 * to make sure that no output is lost.</p> |
|
317 * |
|
318 * <p>This method is invoked automatically by the |
|
319 * {@link #startDocument startDocument} method before writing |
|
320 * a new document.</p> |
|
321 * |
|
322 * <p><strong>Note:</strong> this method will <em>not</em> |
|
323 * clear the prefix or URI information in the writer or |
|
324 * the selected output writer.</p> |
|
325 * |
|
326 * @see #flush() |
|
327 */ |
|
328 public void reset () |
|
329 { |
|
330 elementLevel = 0; |
|
331 startTagIsClosed = true; |
|
332 } |
|
333 |
|
334 |
|
335 /** |
|
336 * Flush the output. |
|
337 * |
|
338 * <p>This method flushes the output stream. It is especially useful |
|
339 * when you need to make certain that the entire document has |
|
340 * been written to output but do not want to close the output |
|
341 * stream.</p> |
|
342 * |
|
343 * <p>This method is invoked automatically by the |
|
344 * {@link #endDocument endDocument} method after writing a |
|
345 * document.</p> |
|
346 * |
|
347 * @see #reset() |
|
348 */ |
|
349 public void flush () |
|
350 throws IOException |
|
351 { |
|
352 output.flush(); |
|
353 } |
|
354 |
|
355 |
|
356 /** |
|
357 * Set a new output destination for the document. |
|
358 * |
|
359 * @param writer The output destination, or null to use |
|
360 * standard output. |
|
361 * @see #flush() |
|
362 */ |
|
363 public void setOutput (Writer writer,String _encoding) |
|
364 { |
|
365 if (writer == null) { |
|
366 output = new OutputStreamWriter(System.out); |
|
367 } else { |
|
368 output = writer; |
|
369 } |
|
370 encoding = _encoding; |
|
371 } |
|
372 |
|
373 /** |
|
374 * Set whether the writer should print out the XML declaration |
|
375 * (<?xml version='1.0' ... ?>). |
|
376 * <p> |
|
377 * This option is set to true by default. |
|
378 */ |
|
379 public void setXmlDecl( boolean _writeXmlDecl ) { |
|
380 this.writeXmlDecl = _writeXmlDecl; |
|
381 } |
|
382 |
|
383 /** |
|
384 * Sets the header string. |
|
385 * |
|
386 * This string will be written right after the xml declaration |
|
387 * without any escaping. Useful for generating a boiler-plate |
|
388 * DOCTYPE decl, PIs, and comments. |
|
389 * |
|
390 * @param _header |
|
391 * passing null will work as if the empty string is passed. |
|
392 */ |
|
393 public void setHeader( String _header ) { |
|
394 this.header = _header; |
|
395 } |
|
396 |
|
397 |
|
398 private final HashMap<String,String> locallyDeclaredPrefix = new HashMap<String,String>(); |
|
399 public void startPrefixMapping( String prefix, String uri ) throws SAXException { |
|
400 locallyDeclaredPrefix.put(prefix,uri); |
|
401 } |
|
402 |
|
403 |
|
404 //////////////////////////////////////////////////////////////////// |
|
405 // Methods from org.xml.sax.ContentHandler. |
|
406 //////////////////////////////////////////////////////////////////// |
|
407 |
|
408 /** |
|
409 * Write the XML declaration at the beginning of the document. |
|
410 * |
|
411 * Pass the event on down the filter chain for further processing. |
|
412 * |
|
413 * @exception org.xml.sax.SAXException If there is an error |
|
414 * writing the XML declaration, or if a handler further down |
|
415 * the filter chain raises an exception. |
|
416 * @see org.xml.sax.ContentHandler#startDocument() |
|
417 */ |
|
418 public void startDocument () |
|
419 throws SAXException |
|
420 { |
|
421 try { |
|
422 reset(); |
|
423 |
|
424 if(writeXmlDecl) { |
|
425 String e=""; |
|
426 if(encoding!=null) |
|
427 e = " encoding=\""+encoding+'\"'; |
|
428 |
|
429 writeXmlDecl("<?xml version=\"1.0\""+e +" standalone=\"yes\"?>"); |
|
430 } |
|
431 |
|
432 if(header!=null) |
|
433 write(header); |
|
434 |
|
435 super.startDocument(); |
|
436 } catch( IOException e ) { |
|
437 throw new SAXException(e); |
|
438 } |
|
439 } |
|
440 |
|
441 protected void writeXmlDecl(String decl) throws IOException { |
|
442 write(decl); |
|
443 } |
|
444 |
|
445 |
|
446 /** |
|
447 * Write a newline at the end of the document. |
|
448 * |
|
449 * Pass the event on down the filter chain for further processing. |
|
450 * |
|
451 * @exception org.xml.sax.SAXException If there is an error |
|
452 * writing the newline, or if a handler further down |
|
453 * the filter chain raises an exception. |
|
454 * @see org.xml.sax.ContentHandler#endDocument() |
|
455 */ |
|
456 public void endDocument () |
|
457 throws SAXException |
|
458 { |
|
459 try { |
|
460 super.endDocument(); |
|
461 flush(); |
|
462 } catch( IOException e ) { |
|
463 throw new SAXException(e); |
|
464 } |
|
465 } |
|
466 |
|
467 |
|
468 /** |
|
469 * Write a start tag. |
|
470 * |
|
471 * Pass the event on down the filter chain for further processing. |
|
472 * |
|
473 * @param uri The Namespace URI, or the empty string if none |
|
474 * is available. |
|
475 * @param localName The element's local (unprefixed) name (required). |
|
476 * @param qName The element's qualified (prefixed) name, or the |
|
477 * empty string is none is available. This method will |
|
478 * use the qName as a template for generating a prefix |
|
479 * if necessary, but it is not guaranteed to use the |
|
480 * same qName. |
|
481 * @param atts The element's attribute list (must not be null). |
|
482 * @exception org.xml.sax.SAXException If there is an error |
|
483 * writing the start tag, or if a handler further down |
|
484 * the filter chain raises an exception. |
|
485 * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) |
|
486 */ |
|
487 public void startElement (String uri, String localName, |
|
488 String qName, Attributes atts) |
|
489 throws SAXException |
|
490 { |
|
491 try { |
|
492 if (!startTagIsClosed) { |
|
493 write(">"); |
|
494 } |
|
495 elementLevel++; |
|
496 // nsSupport.pushContext(); |
|
497 |
|
498 write('<'); |
|
499 write(qName); |
|
500 writeAttributes(atts); |
|
501 |
|
502 // declare namespaces specified by the startPrefixMapping methods |
|
503 if(!locallyDeclaredPrefix.isEmpty()) { |
|
504 for (Map.Entry<String,String> e : locallyDeclaredPrefix.entrySet()) { |
|
505 String p = e.getKey(); |
|
506 String u = e.getValue(); |
|
507 if (u == null) { |
|
508 u = ""; |
|
509 } |
|
510 write(' '); |
|
511 if ("".equals(p)) { |
|
512 write("xmlns=\""); |
|
513 } else { |
|
514 write("xmlns:"); |
|
515 write(p); |
|
516 write("=\""); |
|
517 } |
|
518 char ch[] = u.toCharArray(); |
|
519 writeEsc(ch, 0, ch.length, true); |
|
520 write('\"'); |
|
521 } |
|
522 locallyDeclaredPrefix.clear(); // clear the contents |
|
523 } |
|
524 |
|
525 // if (elementLevel == 1) { |
|
526 // forceNSDecls(); |
|
527 // } |
|
528 // writeNSDecls(); |
|
529 super.startElement(uri, localName, qName, atts); |
|
530 startTagIsClosed = false; |
|
531 } catch( IOException e ) { |
|
532 throw new SAXException(e); |
|
533 } |
|
534 } |
|
535 |
|
536 |
|
537 /** |
|
538 * Write an end tag. |
|
539 * |
|
540 * Pass the event on down the filter chain for further processing. |
|
541 * |
|
542 * @param uri The Namespace URI, or the empty string if none |
|
543 * is available. |
|
544 * @param localName The element's local (unprefixed) name (required). |
|
545 * @param qName The element's qualified (prefixed) name, or the |
|
546 * empty string is none is available. This method will |
|
547 * use the qName as a template for generating a prefix |
|
548 * if necessary, but it is not guaranteed to use the |
|
549 * same qName. |
|
550 * @exception org.xml.sax.SAXException If there is an error |
|
551 * writing the end tag, or if a handler further down |
|
552 * the filter chain raises an exception. |
|
553 * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String) |
|
554 */ |
|
555 public void endElement (String uri, String localName, String qName) |
|
556 throws SAXException |
|
557 { |
|
558 try { |
|
559 if (startTagIsClosed) { |
|
560 write("</"); |
|
561 write(qName); |
|
562 write('>'); |
|
563 } else { |
|
564 write("/>"); |
|
565 startTagIsClosed = true; |
|
566 } |
|
567 super.endElement(uri, localName, qName); |
|
568 // nsSupport.popContext(); |
|
569 elementLevel--; |
|
570 } catch( IOException e ) { |
|
571 throw new SAXException(e); |
|
572 } |
|
573 } |
|
574 |
|
575 |
|
576 /** |
|
577 * Write character data. |
|
578 * |
|
579 * Pass the event on down the filter chain for further processing. |
|
580 * |
|
581 * @param ch The array of characters to write. |
|
582 * @param start The starting position in the array. |
|
583 * @param len The number of characters to write. |
|
584 * @exception org.xml.sax.SAXException If there is an error |
|
585 * writing the characters, or if a handler further down |
|
586 * the filter chain raises an exception. |
|
587 * @see org.xml.sax.ContentHandler#characters(char[], int, int) |
|
588 */ |
|
589 public void characters (char ch[], int start, int len) |
|
590 throws SAXException |
|
591 { |
|
592 try { |
|
593 if (!startTagIsClosed) { |
|
594 write('>'); |
|
595 startTagIsClosed = true; |
|
596 } |
|
597 writeEsc(ch, start, len, false); |
|
598 super.characters(ch, start, len); |
|
599 } catch( IOException e ) { |
|
600 throw new SAXException(e); |
|
601 } |
|
602 } |
|
603 |
|
604 |
|
605 /** |
|
606 * Write ignorable whitespace. |
|
607 * |
|
608 * Pass the event on down the filter chain for further processing. |
|
609 * |
|
610 * @param ch The array of characters to write. |
|
611 * @param start The starting position in the array. |
|
612 * @param length The number of characters to write. |
|
613 * @exception org.xml.sax.SAXException If there is an error |
|
614 * writing the whitespace, or if a handler further down |
|
615 * the filter chain raises an exception. |
|
616 * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int) |
|
617 */ |
|
618 public void ignorableWhitespace (char ch[], int start, int length) |
|
619 throws SAXException |
|
620 { |
|
621 try { |
|
622 writeEsc(ch, start, length, false); |
|
623 super.ignorableWhitespace(ch, start, length); |
|
624 } catch( IOException e ) { |
|
625 throw new SAXException(e); |
|
626 } |
|
627 } |
|
628 |
|
629 |
|
630 |
|
631 /** |
|
632 * Write a processing instruction. |
|
633 * |
|
634 * Pass the event on down the filter chain for further processing. |
|
635 * |
|
636 * @param target The PI target. |
|
637 * @param data The PI data. |
|
638 * @exception org.xml.sax.SAXException If there is an error |
|
639 * writing the PI, or if a handler further down |
|
640 * the filter chain raises an exception. |
|
641 * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String) |
|
642 */ |
|
643 public void processingInstruction (String target, String data) |
|
644 throws SAXException |
|
645 { |
|
646 try { |
|
647 if (!startTagIsClosed) { |
|
648 write('>'); |
|
649 startTagIsClosed = true; |
|
650 } |
|
651 write("<?"); |
|
652 write(target); |
|
653 write(' '); |
|
654 write(data); |
|
655 write("?>"); |
|
656 if (elementLevel < 1) { |
|
657 write('\n'); |
|
658 } |
|
659 super.processingInstruction(target, data); |
|
660 } catch( IOException e ) { |
|
661 throw new SAXException(e); |
|
662 } |
|
663 } |
|
664 |
|
665 |
|
666 |
|
667 //////////////////////////////////////////////////////////////////// |
|
668 // Convenience methods. |
|
669 //////////////////////////////////////////////////////////////////// |
|
670 |
|
671 |
|
672 |
|
673 /** |
|
674 * Start a new element without a qname or attributes. |
|
675 * |
|
676 * <p>This method will provide a default empty attribute |
|
677 * list and an empty string for the qualified name. |
|
678 * It invokes {@link |
|
679 * #startElement(String, String, String, Attributes)} |
|
680 * directly.</p> |
|
681 * |
|
682 * @param uri The element's Namespace URI. |
|
683 * @param localName The element's local name. |
|
684 * @exception org.xml.sax.SAXException If there is an error |
|
685 * writing the start tag, or if a handler further down |
|
686 * the filter chain raises an exception. |
|
687 * @see #startElement(String, String, String, Attributes) |
|
688 */ |
|
689 public void startElement (String uri, String localName) |
|
690 throws SAXException |
|
691 { |
|
692 startElement(uri, localName, "", EMPTY_ATTS); |
|
693 } |
|
694 |
|
695 |
|
696 /** |
|
697 * Start a new element without a qname, attributes or a Namespace URI. |
|
698 * |
|
699 * <p>This method will provide an empty string for the |
|
700 * Namespace URI, and empty string for the qualified name, |
|
701 * and a default empty attribute list. It invokes |
|
702 * #startElement(String, String, String, Attributes)} |
|
703 * directly.</p> |
|
704 * |
|
705 * @param localName The element's local name. |
|
706 * @exception org.xml.sax.SAXException If there is an error |
|
707 * writing the start tag, or if a handler further down |
|
708 * the filter chain raises an exception. |
|
709 * @see #startElement(String, String, String, Attributes) |
|
710 */ |
|
711 public void startElement (String localName) |
|
712 throws SAXException |
|
713 { |
|
714 startElement("", localName, "", EMPTY_ATTS); |
|
715 } |
|
716 |
|
717 |
|
718 /** |
|
719 * End an element without a qname. |
|
720 * |
|
721 * <p>This method will supply an empty string for the qName. |
|
722 * It invokes {@link #endElement(String, String, String)} |
|
723 * directly.</p> |
|
724 * |
|
725 * @param uri The element's Namespace URI. |
|
726 * @param localName The element's local name. |
|
727 * @exception org.xml.sax.SAXException If there is an error |
|
728 * writing the end tag, or if a handler further down |
|
729 * the filter chain raises an exception. |
|
730 * @see #endElement(String, String, String) |
|
731 */ |
|
732 public void endElement (String uri, String localName) |
|
733 throws SAXException |
|
734 { |
|
735 endElement(uri, localName, ""); |
|
736 } |
|
737 |
|
738 |
|
739 /** |
|
740 * End an element without a Namespace URI or qname. |
|
741 * |
|
742 * <p>This method will supply an empty string for the qName |
|
743 * and an empty string for the Namespace URI. |
|
744 * It invokes {@link #endElement(String, String, String)} |
|
745 * directly.</p> |
|
746 * |
|
747 * @param localName The element's local name. |
|
748 * @exception org.xml.sax.SAXException If there is an error |
|
749 * writing the end tag, or if a handler further down |
|
750 * the filter chain raises an exception. |
|
751 * @see #endElement(String, String, String) |
|
752 */ |
|
753 public void endElement (String localName) |
|
754 throws SAXException |
|
755 { |
|
756 endElement("", localName, ""); |
|
757 } |
|
758 |
|
759 |
|
760 /** |
|
761 * Write an element with character data content. |
|
762 * |
|
763 * <p>This is a convenience method to write a complete element |
|
764 * with character data content, including the start tag |
|
765 * and end tag.</p> |
|
766 * |
|
767 * <p>This method invokes |
|
768 * {@link #startElement(String, String, String, Attributes)}, |
|
769 * followed by |
|
770 * {@link #characters(String)}, followed by |
|
771 * {@link #endElement(String, String, String)}.</p> |
|
772 * |
|
773 * @param uri The element's Namespace URI. |
|
774 * @param localName The element's local name. |
|
775 * @param qName The element's default qualified name. |
|
776 * @param atts The element's attributes. |
|
777 * @param content The character data content. |
|
778 * @exception org.xml.sax.SAXException If there is an error |
|
779 * writing the empty tag, or if a handler further down |
|
780 * the filter chain raises an exception. |
|
781 * @see #startElement(String, String, String, Attributes) |
|
782 * @see #characters(String) |
|
783 * @see #endElement(String, String, String) |
|
784 */ |
|
785 public void dataElement (String uri, String localName, |
|
786 String qName, Attributes atts, |
|
787 String content) |
|
788 throws SAXException |
|
789 { |
|
790 startElement(uri, localName, qName, atts); |
|
791 characters(content); |
|
792 endElement(uri, localName, qName); |
|
793 } |
|
794 |
|
795 |
|
796 /** |
|
797 * Write an element with character data content but no attributes. |
|
798 * |
|
799 * <p>This is a convenience method to write a complete element |
|
800 * with character data content, including the start tag |
|
801 * and end tag. This method provides an empty string |
|
802 * for the qname and an empty attribute list.</p> |
|
803 * |
|
804 * <p>This method invokes |
|
805 * {@link #startElement(String, String, String, Attributes)}, |
|
806 * followed by |
|
807 * {@link #characters(String)}, followed by |
|
808 * {@link #endElement(String, String, String)}.</p> |
|
809 * |
|
810 * @param uri The element's Namespace URI. |
|
811 * @param localName The element's local name. |
|
812 * @param content The character data content. |
|
813 * @exception org.xml.sax.SAXException If there is an error |
|
814 * writing the empty tag, or if a handler further down |
|
815 * the filter chain raises an exception. |
|
816 * @see #startElement(String, String, String, Attributes) |
|
817 * @see #characters(String) |
|
818 * @see #endElement(String, String, String) |
|
819 */ |
|
820 public void dataElement (String uri, String localName, String content) |
|
821 throws SAXException |
|
822 { |
|
823 dataElement(uri, localName, "", EMPTY_ATTS, content); |
|
824 } |
|
825 |
|
826 |
|
827 /** |
|
828 * Write an element with character data content but no attributes or Namespace URI. |
|
829 * |
|
830 * <p>This is a convenience method to write a complete element |
|
831 * with character data content, including the start tag |
|
832 * and end tag. The method provides an empty string for the |
|
833 * Namespace URI, and empty string for the qualified name, |
|
834 * and an empty attribute list.</p> |
|
835 * |
|
836 * <p>This method invokes |
|
837 * {@link #startElement(String, String, String, Attributes)}, |
|
838 * followed by |
|
839 * {@link #characters(String)}, followed by |
|
840 * {@link #endElement(String, String, String)}.</p> |
|
841 * |
|
842 * @param localName The element's local name. |
|
843 * @param content The character data content. |
|
844 * @exception org.xml.sax.SAXException If there is an error |
|
845 * writing the empty tag, or if a handler further down |
|
846 * the filter chain raises an exception. |
|
847 * @see #startElement(String, String, String, Attributes) |
|
848 * @see #characters(String) |
|
849 * @see #endElement(String, String, String) |
|
850 */ |
|
851 public void dataElement (String localName, String content) |
|
852 throws SAXException |
|
853 { |
|
854 dataElement("", localName, "", EMPTY_ATTS, content); |
|
855 } |
|
856 |
|
857 |
|
858 /** |
|
859 * Write a string of character data, with XML escaping. |
|
860 * |
|
861 * <p>This is a convenience method that takes an XML |
|
862 * String, converts it to a character array, then invokes |
|
863 * {@link #characters(char[], int, int)}.</p> |
|
864 * |
|
865 * @param data The character data. |
|
866 * @exception org.xml.sax.SAXException If there is an error |
|
867 * writing the string, or if a handler further down |
|
868 * the filter chain raises an exception. |
|
869 * @see #characters(char[], int, int) |
|
870 */ |
|
871 public void characters (String data) throws SAXException { |
|
872 try { |
|
873 if (!startTagIsClosed) { |
|
874 write('>'); |
|
875 startTagIsClosed = true; |
|
876 } |
|
877 char ch[] = data.toCharArray(); |
|
878 characters(ch, 0, ch.length); |
|
879 } catch( IOException e ) { |
|
880 throw new SAXException(e); |
|
881 } |
|
882 } |
|
883 |
|
884 |
|
885 |
|
886 //////////////////////////////////////////////////////////////////// |
|
887 // Internal methods. |
|
888 //////////////////////////////////////////////////////////////////// |
|
889 |
|
890 |
|
891 |
|
892 |
|
893 /** |
|
894 * Write a raw character. |
|
895 * |
|
896 * @param c The character to write. |
|
897 */ |
|
898 protected final void write (char c) throws IOException { |
|
899 output.write(c); |
|
900 } |
|
901 |
|
902 |
|
903 /** |
|
904 * Write a raw string. |
|
905 */ |
|
906 protected final void write(String s) throws IOException { |
|
907 output.write(s); |
|
908 } |
|
909 |
|
910 |
|
911 /** |
|
912 * Write out an attribute list, escaping values. |
|
913 * |
|
914 * The names will have prefixes added to them. |
|
915 * |
|
916 * @param atts The attribute list to write. |
|
917 */ |
|
918 private void writeAttributes (Attributes atts) throws IOException { |
|
919 int len = atts.getLength(); |
|
920 for (int i = 0; i < len; i++) { |
|
921 char ch[] = atts.getValue(i).toCharArray(); |
|
922 write(' '); |
|
923 write(atts.getQName(i)); |
|
924 write("=\""); |
|
925 writeEsc(ch, 0, ch.length, true); |
|
926 write('"'); |
|
927 } |
|
928 } |
|
929 |
|
930 |
|
931 /** |
|
932 * Write an array of data characters with escaping. |
|
933 * |
|
934 * @param ch The array of characters. |
|
935 * @param start The starting position. |
|
936 * @param length The number of characters to use. |
|
937 * @param isAttVal true if this is an attribute value literal. |
|
938 */ |
|
939 private void writeEsc (char ch[], int start, |
|
940 int length, boolean isAttVal) |
|
941 throws IOException |
|
942 { |
|
943 escapeHandler.escape(ch, start, length, isAttVal, output); |
|
944 } |
|
945 |
|
946 |
|
947 |
|
948 //////////////////////////////////////////////////////////////////// |
|
949 // Constants. |
|
950 //////////////////////////////////////////////////////////////////// |
|
951 |
|
952 private final Attributes EMPTY_ATTS = new AttributesImpl(); |
|
953 |
|
954 |
|
955 |
|
956 //////////////////////////////////////////////////////////////////// |
|
957 // Internal state. |
|
958 //////////////////////////////////////////////////////////////////// |
|
959 |
|
960 private int elementLevel = 0; |
|
961 private Writer output; |
|
962 private String encoding; |
|
963 private boolean writeXmlDecl = true; |
|
964 /** |
|
965 * This string will be written right after the xml declaration |
|
966 * without any escaping. Useful for generating a boiler-plate DOCTYPE decl |
|
967 * , PIs, and comments. |
|
968 */ |
|
969 private String header=null; |
|
970 |
|
971 private final CharacterEscapeHandler escapeHandler; |
|
972 |
|
973 private boolean startTagIsClosed = true; |
|
974 } |
|
975 |
|
976 // end of XMLWriter.java |