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