~ubuntu-branches/ubuntu/precise/xom/precise

« back to all changes in this revision

Viewing changes to src/nu/xom/tests/XMLWriter.java

  • Committer: Bazaar Package Importer
  • Author(s): Varun Hiremath
  • Date: 2007-11-25 15:50:40 UTC
  • Revision ID: james.westby@ubuntu.com-20071125155040-r75ikcqf1vu0cei7
Tags: upstream-1.1
ImportĀ upstreamĀ versionĀ 1.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// XMLWriter.java - serialize an XML document.
 
2
// Written by David Megginson, david@megginson.com
 
3
// NO WARRANTY!  This class is in the public domain.
 
4
 
 
5
 
 
6
package nu.xom.tests;
 
7
 
 
8
import java.io.IOException;
 
9
import java.io.OutputStreamWriter;
 
10
import java.io.Writer;
 
11
import java.util.Enumeration;
 
12
import java.util.Hashtable;
 
13
 
 
14
import org.xml.sax.Attributes;
 
15
import org.xml.sax.SAXException;
 
16
import org.xml.sax.ext.LexicalHandler;
 
17
import org.xml.sax.helpers.AttributesImpl;
 
18
import org.xml.sax.helpers.NamespaceSupport;
 
19
import org.xml.sax.helpers.XMLFilterImpl;
 
20
 
 
21
 
 
22
/**
 
23
 * <p>
 
24
 *   The original version of this class was written and placed in the
 
25
 *   public domain by David Megginson. Elliotte Rusty Harold added 
 
26
 *   <code>LexicalHandler</code> support. It is included here purely
 
27
 *   for help with testing the <code>SAXConverter</code> class. It is 
 
28
 *   not part of the XOM API; nor is it used internally by XOM anywhere
 
29
 *   except in the <code>SAXConverter</code> tests.
 
30
 * </p>
 
31
 * 
 
32
 * <p>
 
33
 *   This class does not properly preserve additional namespace
 
34
 *   declarations in non-root elements. If you encounter that, the 
 
35
 *   bug is here, not in <code>SAXConverter</code>.
 
36
 * </p>
 
37
 *
 
38
 * @author David Megginson, Elliotte Rusty Harold
 
39
 * @version 1.0
 
40
 */
 
41
class XMLWriter extends XMLFilterImpl implements LexicalHandler {
 
42
 
 
43
    ///////////////////////////////////////////////////////////////////
 
44
    // Constructors.
 
45
    ///////////////////////////////////////////////////////////////////
 
46
 
 
47
 
 
48
    /**
 
49
     * <p>
 
50
     * Create a new XML writer.
 
51
     * </p>
 
52
     * 
 
53
     * <p>Write to standard output.</p>
 
54
     */
 
55
    public XMLWriter() {
 
56
        init(null);
 
57
    }
 
58
    
 
59
 
 
60
    /**
 
61
     * <p>
 
62
     * Create a new XML writer.
 
63
     * </p>
 
64
     * 
 
65
     * <p>Write to the writer provided.</p>
 
66
     *
 
67
     * @param writer the output destination, or null to use standard
 
68
     *        output
 
69
     */
 
70
    public XMLWriter (Writer writer) {
 
71
           init(writer);
 
72
    }
 
73
 
 
74
    /**
 
75
     * <p>
 
76
     * Internal initialization method.
 
77
     * </p>
 
78
     * 
 
79
     * <p>All of the public constructors invoke this method.
 
80
     *
 
81
     * @param writer the output destination, or null to use
 
82
     *        standard output.
 
83
     */
 
84
    private void init (Writer writer) {
 
85
        setOutput(writer);
 
86
        nsSupport = new NamespaceSupport();
 
87
        prefixTable = new Hashtable();
 
88
        forcedDeclTable = new Hashtable();
 
89
        doneDeclTable = new Hashtable();
 
90
    }
 
91
 
 
92
 
 
93
    ///////////////////////////////////////////////////////////////////
 
94
    // Public methods.
 
95
    ///////////////////////////////////////////////////////////////////
 
96
 
 
97
 
 
98
    /**
 
99
     * <p>
 
100
     * Reset the writer.
 
101
     * </p>
 
102
     * 
 
103
     * <p>This method is especially useful if the writer throws an
 
104
     * exception before it is finished, and you want to reuse the
 
105
     * writer for a new document.  It is usually a good idea to
 
106
     * invoke {@link #flush flush} before resetting the writer,
 
107
     * to make sure that no output is lost.</p>
 
108
     *
 
109
     * <p>This method is invoked automatically by the
 
110
     * {@link #startDocument startDocument} method before writing
 
111
     * a new document.</p>
 
112
     *
 
113
     * <p><strong>Note:</strong> this method will <em>not</em>
 
114
     * clear the prefix or URI information in the writer or
 
115
     * the selected output writer.</p>
 
116
     *
 
117
     * @see #flush
 
118
     */
 
119
    public void reset() {
 
120
        elementLevel = 0;
 
121
        prefixCounter = 0;
 
122
        nsSupport.reset();
 
123
    }
 
124
    
 
125
 
 
126
    /**
 
127
     * <p>
 
128
     * Flush the output.
 
129
     * </p>
 
130
     * 
 
131
     * <p>This method flushes the output stream.  It is especially useful
 
132
     * when you need to make certain that the entire document has
 
133
     * been written to output but do not want to close the output
 
134
     * stream.</p>
 
135
     *
 
136
     * <p>This method is invoked automatically by the
 
137
     * {@link #endDocument endDocument} method after writing a
 
138
     * document.</p>
 
139
     *
 
140
     * @throws IOException
 
141
     *
 
142
     * @see #reset
 
143
     * 
 
144
     */
 
145
    public void flush() throws IOException {
 
146
           output.flush();
 
147
    }
 
148
    
 
149
 
 
150
    /**
 
151
     * <p>
 
152
     * Set a new output destination for the document.
 
153
     * </p>
 
154
     * 
 
155
     * @param writer the output destination, or null to use
 
156
     *        standard output
 
157
     * 
 
158
     * @return the current output writer
 
159
     * 
 
160
     * @see #flush
 
161
     */
 
162
    public void setOutput (Writer writer) {
 
163
        if (writer == null) {
 
164
            output = new OutputStreamWriter(System.out);
 
165
        } 
 
166
        else {
 
167
            output = writer;
 
168
        }
 
169
    }
 
170
 
 
171
 
 
172
    /**
 
173
     * <p>
 
174
     * Specify a preferred prefix for a namespace URI.
 
175
     * </p>
 
176
     * 
 
177
     * <p>Note that this method does not actually force the namespace
 
178
     * to be declared; to do that, use the {@link 
 
179
     * #forceNSDecl(java.lang.String) forceNSDecl} method as well.</p>
 
180
     *
 
181
     * @param uri the namespace URI
 
182
     * @param prefix the preferred prefix, or "" to select
 
183
     *        the default namespace
 
184
     * 
 
185
     * @see #getPrefix
 
186
     * @see #forceNSDecl(java.lang.String)
 
187
     * @see #forceNSDecl(java.lang.String,java.lang.String)
 
188
     */    
 
189
    public void setPrefix (String uri, String prefix) {
 
190
           prefixTable.put(uri, prefix);
 
191
    }
 
192
    
 
193
 
 
194
    /**
 
195
     * <p>
 
196
     * Get the current or preferred prefix for a namespace URI.
 
197
     * </p>
 
198
     * 
 
199
     * @param uri the namespace URI
 
200
     * 
 
201
     * @return the preferred prefix, or "" for the default namespace
 
202
     * 
 
203
     * @see #setPrefix
 
204
     */
 
205
    public String getPrefix (String uri) {
 
206
           return (String)prefixTable.get(uri);
 
207
    }
 
208
    
 
209
    public void startPrefixMapping(String prefix, String uri) {
 
210
        this.forceNSDecl(uri, prefix);
 
211
    }  
 
212
 
 
213
    /**
 
214
     * <p>
 
215
     * Force a namespace to be declared on the root element.
 
216
     * </p>
 
217
     * 
 
218
     * <p>By default, the XMLWriter will declare only the namespaces
 
219
     * needed for an element; as a result, a namespace may be
 
220
     * declared many places in a document if it is not used on the
 
221
     * root element.</p>
 
222
     *
 
223
     * <p>This method forces a namespace to be declared on the root
 
224
     * element even if it is not used there, and reduces the number
 
225
     * of xmlns attributes in the document.</p>
 
226
     *
 
227
     * @param uri the namespace URI to declare
 
228
     * 
 
229
     * @see #forceNSDecl(java.lang.String,java.lang.String)
 
230
     * @see #setPrefix
 
231
     */
 
232
    public void forceNSDecl (String uri) {
 
233
           forcedDeclTable.put(uri, Boolean.TRUE);
 
234
    }
 
235
    
 
236
 
 
237
    /**
 
238
     * <p>
 
239
     * Force a namespace declaration with a preferred prefix.
 
240
     * </p>
 
241
     * 
 
242
     * <p>This is a convenience method that invokes {@link
 
243
     * #setPrefix setPrefix} then {@link #forceNSDecl(java.lang.String)
 
244
     * forceNSDecl}.</p>
 
245
     *
 
246
     * @param uri the namespace URI to declare on the root element
 
247
     * @param prefix the preferred prefix for the namespace, or ""
 
248
     *        for the default namespace
 
249
     * 
 
250
     * @see #setPrefix
 
251
     * @see #forceNSDecl(java.lang.String)
 
252
     */
 
253
    public void forceNSDecl (String uri, String prefix) {
 
254
        setPrefix(uri, prefix);
 
255
        forceNSDecl(uri);
 
256
    }
 
257
    
 
258
 
 
259
    ///////////////////////////////////////////////////////////////////
 
260
    // Methods from org.xml.sax.ContentHandler.
 
261
    ///////////////////////////////////////////////////////////////////
 
262
 
 
263
 
 
264
    /**
 
265
     * <p>
 
266
     * Write the XML declaration at the beginning of the document.
 
267
     * </p>
 
268
     * 
 
269
     * <p>
 
270
     * Pass the event on down the filter chain for further processing.
 
271
     * </p>
 
272
     * 
 
273
     * @throws org.xml.sax.SAXException if there is an error
 
274
     *            writing the XML declaration, or if a handler further 
 
275
     *            down the filter chain raises an exception
 
276
     * 
 
277
     * @see org.xml.sax.ContentHandler#startDocument
 
278
     */
 
279
    public void startDocument() throws SAXException {
 
280
        reset();
 
281
        write("<?xml version=\"1.0\" standalone=\"yes\"?>\n\n");
 
282
        super.startDocument();
 
283
    }
 
284
 
 
285
 
 
286
    /**
 
287
     * <p>
 
288
     * Write a newline at the end of the document.
 
289
     * </p>
 
290
     * 
 
291
     * <p>
 
292
     * Pass the event on down the filter chain for further processing.
 
293
     * </p>
 
294
     * 
 
295
     * @throws org.xml.sax.SAXException if there is an error
 
296
     *            writing the newline, or if a handler further down
 
297
     *            the filter chain raises an exception
 
298
     * 
 
299
     * @see org.xml.sax.ContentHandler#endDocument
 
300
     */
 
301
    public void endDocument() throws SAXException {
 
302
        write('\n');
 
303
        super.endDocument();
 
304
        try {
 
305
            flush();
 
306
        } catch (IOException ex) {
 
307
            throw new SAXException(ex);
 
308
        }
 
309
    }
 
310
    
 
311
 
 
312
    /**
 
313
     * <p>
 
314
     * Write a start-tag.
 
315
     * </p>
 
316
     * 
 
317
     * <p>
 
318
     * Pass the event on down the filter chain for further processing.
 
319
     * </p>
 
320
     * 
 
321
     * @param uri the namespace URI, or the empty string if none
 
322
     *        is available
 
323
     * @param localName the element's local (unprefixed) name (required)
 
324
     * @param qualifiedName the element's qualified (prefixed) name,
 
325
     *        or the empty string is none is available.  This method 
 
326
     *        will use the qualified name as a template for generating
 
327
     *        a prefix if necessary, but it is not guaranteed to use 
 
328
     *        the same qualified name.
 
329
     * @param atts the element's attribute list (must not be null)
 
330
     * 
 
331
     * @throws org.xml.sax.SAXException if there is an error
 
332
     *            writing the start-tag, or if a handler further down
 
333
     *            the filter chain raises an exception
 
334
     * 
 
335
     * @see org.xml.sax.ContentHandler#startElement
 
336
     */
 
337
    public void startElement (String uri, String localName,
 
338
          String qualifiedName, Attributes atts) throws SAXException {
 
339
        elementLevel++;
 
340
        nsSupport.pushContext();
 
341
        write('<');
 
342
        writeName(uri, localName, qualifiedName, true);
 
343
        writeAttributes(atts);
 
344
        if (elementLevel == 1) {
 
345
            forceNSDecls();
 
346
        }
 
347
        writeNSDecls();
 
348
        write('>');
 
349
        super.startElement(uri, localName, qualifiedName, atts);
 
350
    }
 
351
 
 
352
 
 
353
    /**
 
354
     * <p>
 
355
     * Write an end-tag.
 
356
     * </p>
 
357
     * 
 
358
     * <p>
 
359
     * Pass the event on down the filter chain for further processing.
 
360
     * </p>
 
361
     * 
 
362
     * @param uri the namespace URI, or the empty string if none
 
363
     *        is available
 
364
     * @param localName the element's local (unprefixed) name (required)
 
365
     * @param qualifiedName the element's qualified (prefixed) name, or the
 
366
     *        empty string is none is available.  This method will
 
367
     *        use the qName as a template for generating a prefix
 
368
     *        if necessary, but it is not guaranteed to use the
 
369
     *        same qualified name.
 
370
     * 
 
371
     * @throws org.xml.sax.SAXException if there is an error
 
372
     *            writing the end-tag, or if a handler further down
 
373
     *            the filter chain raises an exception
 
374
     * 
 
375
     * @see org.xml.sax.ContentHandler#endElement
 
376
     */
 
377
    public void endElement (String uri, String localName, String qualifiedName)
 
378
          throws SAXException {
 
379
        write("</");
 
380
        writeName(uri, localName, qualifiedName, true);
 
381
        write('>');
 
382
        if (elementLevel == 1) {
 
383
            write('\n');
 
384
        }
 
385
        super.endElement(uri, localName, qualifiedName);
 
386
        nsSupport.popContext();
 
387
        elementLevel--;
 
388
    }
 
389
    
 
390
 
 
391
    /**
 
392
     * <p>
 
393
     * Write character data.
 
394
     * </p>
 
395
     * 
 
396
     * <p>
 
397
     * Pass the event on down the filter chain for further processing.
 
398
     * </p>
 
399
     * 
 
400
     * @param ch the array of characters to write
 
401
     * @param start the starting position in the array
 
402
     * @param length the number of characters to write
 
403
     * 
 
404
     * @throws org.xml.sax.SAXException if there is an error
 
405
     *            writing the characters, or if a handler further down
 
406
     *            the filter chain raises an exception
 
407
     * 
 
408
     * @see org.xml.sax.ContentHandler#characters
 
409
     */
 
410
    public void characters (char[] ch, int start, int length)
 
411
      throws SAXException {
 
412
        writeEsc(ch, start, length, false);
 
413
        super.characters(ch, start, length);
 
414
    }
 
415
    
 
416
 
 
417
    /**
 
418
     * <p>
 
419
     * Write ignorable whitespace.
 
420
     * </p>
 
421
     * 
 
422
     * <p>
 
423
     * Pass the event on down the filter chain for further processing.
 
424
     * </p>
 
425
     * 
 
426
     * @param ch the array of characters to write
 
427
     * @param start the starting position in the array
 
428
     * @param length the number of characters to write
 
429
     * 
 
430
     * @throws org.xml.sax.SAXException if there is an error
 
431
     *            writing the whitespace, or if a handler further down
 
432
     *            the filter chain raises an exception
 
433
     * 
 
434
     * @see org.xml.sax.ContentHandler#ignorableWhitespace
 
435
     */
 
436
    public void ignorableWhitespace (char[] ch, int start, int length)
 
437
          throws SAXException {
 
438
        writeEsc(ch, start, length, false);
 
439
        super.ignorableWhitespace(ch, start, length);
 
440
    }
 
441
    
 
442
 
 
443
 
 
444
    /**
 
445
     * <p>
 
446
     * Write a processing instruction.
 
447
     * </p>
 
448
     * 
 
449
     * <p>
 
450
     * Pass the event on down the filter chain for further processing.
 
451
     * </p>
 
452
     * 
 
453
     * @param target the processing instruction target
 
454
     * @param data the processing instruction data
 
455
     * 
 
456
     * @throws org.xml.sax.SAXException if there is an error
 
457
     *            writing the PI, or if a handler further down
 
458
     *            the filter chain raises an exception
 
459
     * 
 
460
     * @see org.xml.sax.ContentHandler#processingInstruction
 
461
     */
 
462
    public void processingInstruction (String target, String data)
 
463
          throws SAXException {
 
464
        write("<?");
 
465
        write(target);
 
466
        write(' ');
 
467
        write(data);
 
468
        write("?>");
 
469
        if (elementLevel < 1) {
 
470
            write('\n');
 
471
        }
 
472
        super.processingInstruction(target, data);
 
473
    }
 
474
    
 
475
 
 
476
 
 
477
    ///////////////////////////////////////////////////////////////////
 
478
    // Additional markup.
 
479
    ///////////////////////////////////////////////////////////////////
 
480
 
 
481
    /**
 
482
     * <p>
 
483
     * Write an empty element.
 
484
     * </p>
 
485
     * 
 
486
     * <p>
 
487
     * This method writes an empty-element tag rather than a start-tag
 
488
     * followed by an end-tag.  Both a {@link #startElement
 
489
     * startElement} and an {@link #endElement endElement} event will
 
490
     * be passed on down the filter chain.
 
491
     * </p>
 
492
     *
 
493
     * @param uri the element's namespace URI, or the empty string
 
494
     *        if the element has no namespace or if namespace
 
495
     *        processing is not being performed
 
496
     * @param localName the element's local name (without prefix).  This
 
497
     *        parameter must be provided.
 
498
     * @param qualifiedName the element's qualified name (with prefix), or
 
499
     *        the empty string if none is available.  This parameter
 
500
     *        is strictly advisory: the writer may or may not use
 
501
     *        the prefix attached.
 
502
     * @param atts the element's attribute list
 
503
     * 
 
504
     * @throws org.xml.sax.SAXException if there is an error
 
505
     *            writing the empty tag, or if a handler further down
 
506
     *            the filter chain raises an exception
 
507
     * 
 
508
     * @see #startElement
 
509
     * @see #endElement 
 
510
     */
 
511
    public void emptyElement (String uri, String localName,
 
512
          String qualifiedName, Attributes atts) throws SAXException {
 
513
        nsSupport.pushContext();
 
514
        write('<');
 
515
        writeName(uri, localName, qualifiedName, true);
 
516
        writeAttributes(atts);
 
517
        if (elementLevel == 1) {
 
518
            forceNSDecls();
 
519
        }
 
520
        writeNSDecls();
 
521
        write("/>");
 
522
        super.startElement(uri, localName, qualifiedName, atts);
 
523
        super.endElement(uri, localName, qualifiedName);
 
524
    }
 
525
 
 
526
 
 
527
 
 
528
    ///////////////////////////////////////////////////////////////////
 
529
    // Convenience methods.
 
530
    ///////////////////////////////////////////////////////////////////
 
531
    
 
532
 
 
533
 
 
534
    /**
 
535
     * <p>
 
536
     * Start a new element without a qualified name or attributes.
 
537
     * </p>
 
538
     * 
 
539
     * <p>This method will provide a default empty attribute
 
540
     * list and an empty string for the qualified name.  
 
541
     * It invokes {@link 
 
542
     * #startElement(String, String, String, Attributes)}
 
543
     * directly.</p>
 
544
     *
 
545
     * @param uri the element's namespace URI
 
546
     * @param localName the element's local name
 
547
     * 
 
548
     * @throws org.xml.sax.SAXException if there is an error
 
549
     *            writing the start-tag, or if a handler further down
 
550
     *            the filter chain raises an exception
 
551
     * 
 
552
     * @see #startElement(String, String, String, Attributes)
 
553
     */
 
554
    public void startElement (String uri, String localName) 
 
555
      throws SAXException {
 
556
        startElement(uri, localName, "", EMPTY_ATTS);
 
557
    }
 
558
 
 
559
 
 
560
    /**
 
561
     * <p>
 
562
     * Start a new element without a qualified name, 
 
563
     * attributes or a namespace URI.</p>
 
564
     *
 
565
     * <p>This method will provide an empty string for the
 
566
     * namespace URI, and empty string for the qualified name,
 
567
     * and a default empty attribute list. It invokes
 
568
     * #startElement(String, String, String, Attributes)}
 
569
     * directly.</p>
 
570
     *
 
571
     * @param localName the element's local name
 
572
     * 
 
573
     * @throws org.xml.sax.SAXException if there is an error
 
574
     *            writing the start-tag, or if a handler further down
 
575
     *            the filter chain raises an exception
 
576
     * 
 
577
     * @see #startElement(String, String, String, Attributes)
 
578
     */
 
579
    public void startElement (String localName) throws SAXException {
 
580
           startElement("", localName, "", EMPTY_ATTS);
 
581
    }
 
582
 
 
583
 
 
584
    /**
 
585
     * <p>
 
586
     * End an element without a qualfied name.
 
587
     * </p>
 
588
     * 
 
589
     * <p>This method will supply an empty string for the qName.
 
590
     * It invokes {@link #endElement(String, String, String)}
 
591
     * directly.</p>
 
592
     *
 
593
     * @param uri the element's namespace URI
 
594
     * @param localName the element's local name
 
595
     * 
 
596
     * @throws org.xml.sax.SAXException if there is an error
 
597
     *            writing the end-tag, or if a handler further down
 
598
     *            the filter chain raises an exception
 
599
     * 
 
600
     * @see #endElement(String, String, String)
 
601
     */
 
602
    public void endElement(String uri, String localName)
 
603
          throws SAXException {
 
604
            endElement(uri, localName, "");
 
605
    }
 
606
 
 
607
 
 
608
    /**
 
609
     * <p>
 
610
     * End an element without a namespace URI or qualfiied name.
 
611
     * </p>
 
612
     * 
 
613
     * <p>This method will supply an empty string for the qName
 
614
     * and an empty string for the namespace URI.
 
615
     * It invokes {@link #endElement(String, String, String)}
 
616
     * directly.</p>
 
617
     *
 
618
     * @param localName the element's local name`
 
619
     * 
 
620
     * @throws org.xml.sax.SAXException if there is an error
 
621
     *            writing the end-tag, or if a handler further down
 
622
     *            the filter chain raises an exception
 
623
     * 
 
624
     * @see #endElement(String, String, String)
 
625
     */
 
626
    public void endElement(String localName) throws SAXException {
 
627
            endElement("", localName, "");
 
628
    }
 
629
 
 
630
 
 
631
    /**
 
632
     * <p>
 
633
     * Add an empty element without a qualified name or attributes.
 
634
     * </p>
 
635
     * 
 
636
     * <p>This method will supply an empty string for the qualified name
 
637
     * and an empty attribute list.  It invokes
 
638
     * {@link #emptyElement(String, String, String, Attributes)} 
 
639
     * directly.</p>
 
640
     *
 
641
     * @param uri the element's namespace URI
 
642
     * @param localName the element's local name
 
643
     * 
 
644
     * @throws org.xml.sax.SAXException if there is an error
 
645
     *            writing the empty tag, or if a handler further down
 
646
     *            the filter chain raises an exception
 
647
     * 
 
648
     * @see #emptyElement(String, String, String, Attributes)
 
649
     */
 
650
    public void emptyElement (String uri, String localName)
 
651
          throws SAXException {
 
652
            emptyElement(uri, localName, "", EMPTY_ATTS);
 
653
    }
 
654
 
 
655
 
 
656
    /**
 
657
     * <p>
 
658
     * Add an empty element without a namespace URI, qualified
 
659
     * name or attributes.
 
660
     * </p>
 
661
     * 
 
662
     * <p>This method will supply an empty string for the qualified 
 
663
     * name, and empty string for the namespace URI, and an empty
 
664
     * attribute list.  It invokes
 
665
     * {@link #emptyElement(String, String, String, Attributes)} 
 
666
     * directly.</p>
 
667
     *
 
668
     * @param localName the element's local name
 
669
     * 
 
670
     * @throws org.xml.sax.SAXException if there is an error
 
671
     *            writing the empty tag, or if a handler further down
 
672
     *            the filter chain raises an exception
 
673
     * 
 
674
     * @see #emptyElement(String, String, String, Attributes)
 
675
     */
 
676
    public void emptyElement (String localName) throws SAXException {
 
677
           emptyElement("", localName, "", EMPTY_ATTS);
 
678
    }
 
679
 
 
680
 
 
681
    /**
 
682
     * <p>
 
683
     * Write an element with character data content.
 
684
     * </p>
 
685
     * 
 
686
     * <p>This is a convenience method to write a complete element
 
687
     * with character data content, including the start-tag
 
688
     * and end-tag.</p>
 
689
     *
 
690
     * <p>This method invokes
 
691
     * {@link #startElement(String, String, String, Attributes)},
 
692
     * followed by
 
693
     * {@link #characters(String)}, followed by
 
694
     * {@link #endElement(String, String, String)}.</p>
 
695
     *
 
696
     * @param uri the element's namespace URI
 
697
     * @param localName the element's local name
 
698
     * @param qualifiedName the element's default qualified name
 
699
     * @param atts the element's attributes
 
700
     * @param content the character data content
 
701
     * 
 
702
     * @throws org.xml.sax.SAXException if there is an error
 
703
     *            writing the empty tag, or if a handler further down
 
704
     *            the filter chain raises an exception
 
705
     * 
 
706
     * @see #startElement(String, String, String, Attributes)
 
707
     * @see #characters(String)
 
708
     * @see #endElement(String, String, String)
 
709
     */
 
710
    public void dataElement (String uri, String localName,
 
711
          String qualifiedName, Attributes atts, String content) 
 
712
      throws SAXException {
 
713
        startElement(uri, localName, qualifiedName, atts);
 
714
        characters(content);
 
715
        endElement(uri, localName, qualifiedName);
 
716
    }
 
717
 
 
718
 
 
719
    /**
 
720
     * <p>
 
721
     * Write an element with character data content but no attributes.
 
722
     * </p>
 
723
     * 
 
724
     * <p>This is a convenience method to write a complete element
 
725
     * with character data content, including the start-tag
 
726
     * and end-tag.  This method provides an empty string
 
727
     * for the qualified name and an empty attribute list.</p>
 
728
     *
 
729
     * <p>This method invokes
 
730
     * {@link #startElement(String, String, String, Attributes)},
 
731
     * followed by
 
732
     * {@link #characters(String)}, followed by
 
733
     * {@link #endElement(String, String, String)}.</p>
 
734
     *
 
735
     * @param uri the element's namespace URI
 
736
     * @param localName the element's local name
 
737
     * @param content the character data content
 
738
     * 
 
739
     * @throws org.xml.sax.SAXException if there is an error
 
740
     *            writing the empty tag, or if a handler further down
 
741
     *            the filter chain raises an exception
 
742
     * 
 
743
     * @see #startElement(String, String, String, Attributes)
 
744
     * @see #characters(String)
 
745
     * @see #endElement(String, String, String)
 
746
     */
 
747
    public void dataElement(String uri, String localName, String content)
 
748
          throws SAXException {
 
749
           dataElement(uri, localName, "", EMPTY_ATTS, content);
 
750
    }
 
751
 
 
752
 
 
753
    /**
 
754
     * <p>
 
755
     * Write an element with character data content but no attributes 
 
756
     * or namespace URI.
 
757
     * </p>
 
758
     * 
 
759
     * <p>This is a convenience method to write a complete element
 
760
     * with character data content, including the start-tag
 
761
     * and end-tag.  The method provides an empty string for the
 
762
     * namespace URI, and empty string for the qualified name,
 
763
     * and an empty attribute list.</p>
 
764
     *
 
765
     * <p>This method invokes
 
766
     * {@link #startElement(String, String, String, Attributes)},
 
767
     * followed by
 
768
     * {@link #characters(String)}, followed by
 
769
     * {@link #endElement(String, String, String)}.</p>
 
770
     *
 
771
     * @param localName the element's local name
 
772
     * @param content the character data content
 
773
     * 
 
774
     * @throws org.xml.sax.SAXException if there is an error
 
775
     *            writing the empty tag, or if a handler further down
 
776
     *            the filter chain raises an exception
 
777
     * 
 
778
     * @see #startElement(String, String, String, Attributes)
 
779
     * @see #characters(String)
 
780
     * @see #endElement(String, String, String)
 
781
     */
 
782
    public void dataElement (String localName, String content) 
 
783
      throws SAXException {
 
784
            dataElement("", localName, "", EMPTY_ATTS, content);
 
785
    }
 
786
 
 
787
    /**
 
788
     * <p>
 
789
     * Write a string of character data, with XML escaping.
 
790
     * </p>
 
791
     * 
 
792
     * <p>This is a convenience method that takes an XML
 
793
     * String, converts it to a character array, then invokes
 
794
     * {@link #characters(char[], int, int)}.</p>
 
795
     *
 
796
     * @param data the character data
 
797
     * @throws org.xml.sax.SAXException if there is an error
 
798
     *            writing the string, or if a handler further down
 
799
     *            the filter chain raises an exception
 
800
     * @see #characters(char[], int, int)
 
801
     */
 
802
    public void characters(String data) throws SAXException {
 
803
        char[] ch = data.toCharArray();
 
804
        characters(ch, 0, ch.length);
 
805
    }
 
806
 
 
807
 
 
808
 
 
809
    ///////////////////////////////////////////////////////////////////
 
810
    // Internal methods.
 
811
    ///////////////////////////////////////////////////////////////////
 
812
    
 
813
 
 
814
    /**
 
815
     * <p>
 
816
     * Force all namespaces to be declared.
 
817
     * </p>
 
818
     * 
 
819
     * <p>
 
820
     * This method is used on the root element to ensure that
 
821
     * the predeclared namespaces all appear.
 
822
     * </p>
 
823
     */
 
824
    private void forceNSDecls() {
 
825
        Enumeration prefixes = forcedDeclTable.keys();
 
826
        while (prefixes.hasMoreElements()) {
 
827
            String prefix = (String)prefixes.nextElement();
 
828
            doPrefix(prefix, null, true);
 
829
        }
 
830
    }
 
831
 
 
832
 
 
833
    /**
 
834
     * <p>
 
835
     * Determine the prefix for an element or attribute name.
 
836
     * </p>
 
837
     * 
 
838
     * TODO: this method probably needs some cleanup.
 
839
     *
 
840
     * @param uri the namespace URI
 
841
     * @param qName the qualified name (optional); this will be used
 
842
     *        to indicate the preferred prefix if none is currently
 
843
     *        bound.
 
844
     * @param isElement true if this is an element name, false
 
845
     *        if it is an attribute name (which cannot use the
 
846
     *        default namespace).
 
847
     */
 
848
    private String doPrefix (String uri, String qName, boolean isElement) {
 
849
        String defaultNS = nsSupport.getURI("");
 
850
        if ("".equals(uri)) {
 
851
            if (isElement && defaultNS != null)
 
852
                nsSupport.declarePrefix("", "");
 
853
            return null;
 
854
        }
 
855
        String prefix;
 
856
        if (isElement && defaultNS != null && uri.equals(defaultNS)) {
 
857
            prefix = "";
 
858
        } else {
 
859
            prefix = nsSupport.getPrefix(uri);
 
860
        }
 
861
        if (prefix != null) {
 
862
            return prefix;
 
863
        }
 
864
        prefix = (String) doneDeclTable.get(uri);
 
865
        if (prefix != null &&
 
866
            ((!isElement || defaultNS != null) &&
 
867
             "".equals(prefix) || nsSupport.getURI(prefix) != null)) {
 
868
            prefix = null;
 
869
        }
 
870
        if (prefix == null) {
 
871
            prefix = (String) prefixTable.get(uri);
 
872
            if (prefix != null &&
 
873
                ((!isElement || defaultNS != null) &&
 
874
                 "".equals(prefix) || nsSupport.getURI(prefix) != null)) {
 
875
                prefix = null;
 
876
            }
 
877
        }
 
878
        if (prefix == null && qName != null && !"".equals(qName)) {
 
879
            int i = qName.indexOf(':');
 
880
            if (i == -1) {
 
881
                if (isElement && defaultNS == null) {
 
882
                    prefix = "";
 
883
                }
 
884
            } else {
 
885
                prefix = qName.substring(0, i);
 
886
            }
 
887
        }
 
888
        for (;
 
889
             prefix == null || nsSupport.getURI(prefix) != null;
 
890
             prefix = "__NS" + ++prefixCounter)
 
891
            ;
 
892
        nsSupport.declarePrefix(prefix, uri);
 
893
        doneDeclTable.put(uri, prefix);
 
894
        return prefix;
 
895
    }
 
896
    
 
897
 
 
898
    /**
 
899
     * <p>
 
900
     * Write a raw character.
 
901
     * </p>
 
902
     * 
 
903
     * @param c the character to write
 
904
     * 
 
905
     * @throws org.xml.sax.SAXException if there is an error writing
 
906
     *            the character, this method will throw an IOException
 
907
     *            wrapped in a SAXException
 
908
     */
 
909
    private void write(char c) throws SAXException {
 
910
        try {
 
911
            output.write(c);
 
912
        } 
 
913
        catch (IOException ex) {
 
914
            throw new SAXException(ex);
 
915
        }
 
916
    }
 
917
    
 
918
 
 
919
    /**
 
920
     * <p>
 
921
     * Write a raw string.
 
922
     * </p>
 
923
     * 
 
924
     * @param s
 
925
     * 
 
926
     * @throws org.xml.sax.SAXException if there is an error writing
 
927
     *            the string, this method will throw an IOException
 
928
     *            wrapped in a SAXException
 
929
     */
 
930
    private void write (String s) throws SAXException {
 
931
        try {
 
932
            output.write(s);
 
933
        } 
 
934
        catch (IOException e) {
 
935
            throw new SAXException(e);
 
936
        }
 
937
    }
 
938
 
 
939
 
 
940
    /**
 
941
     * <p>
 
942
     * Write out an attribute list, escaping values.
 
943
     *</p>
 
944
     *
 
945
     * <p>
 
946
     * The names will have prefixes added to them.
 
947
     * </p>
 
948
     * 
 
949
     * @param atts the attribute list to write
 
950
     * 
 
951
     * @throws org.xml.SAXException if there is an error writing
 
952
     *            the attribute list, this method will throw an
 
953
     *            IOException wrapped in a SAXException
 
954
     */
 
955
    private void writeAttributes (Attributes atts) 
 
956
      throws SAXException {
 
957
        int len = atts.getLength();
 
958
        for (int i = 0; i < len; i++) {
 
959
            char[] ch = atts.getValue(i).toCharArray();
 
960
            write(' ');
 
961
            writeName(atts.getURI(i), atts.getLocalName(i),
 
962
                      atts.getQName(i), false);
 
963
            write("=\"");
 
964
            writeEsc(ch, 0, ch.length, true);
 
965
            write('"');
 
966
        }
 
967
    }
 
968
 
 
969
 
 
970
    /**
 
971
     * <p>
 
972
     * Write an array of data characters with escaping.
 
973
     * </p>
 
974
     * 
 
975
     * @param ch the array of characters
 
976
     * @param start the starting position
 
977
     * @param length the number of characters to use
 
978
     * @param isAttVal true if this is an attribute value literal
 
979
     * 
 
980
     * @throws org.xml.SAXException if there is an error writing
 
981
     *            the characters, this method will throw an
 
982
     *            IOException wrapped in a SAXException
 
983
     */    
 
984
    private void writeEsc (char[] ch, int start, int length, boolean isAttVal)
 
985
          throws SAXException {
 
986
        for (int i = start; i < start + length; i++) {
 
987
            switch (ch[i]) {
 
988
            case '&':
 
989
                        write("&amp;");
 
990
                        break;
 
991
            case '<':
 
992
                        write("&lt;");
 
993
                        break;
 
994
            case '>':
 
995
                        write("&gt;");
 
996
                        break;
 
997
            case '\"':
 
998
                        if (isAttVal) {
 
999
                            write("&quot;");
 
1000
                        } 
 
1001
                else {
 
1002
                            write('\"');
 
1003
                        }
 
1004
                    break;
 
1005
            default:
 
1006
                        if (ch[i] > '\u007f') {
 
1007
                            write("&#");
 
1008
                            write(Integer.toString(ch[i]));
 
1009
                            write(';');
 
1010
                        } 
 
1011
                else {
 
1012
                            write(ch[i]);
 
1013
                        }
 
1014
            }
 
1015
           }
 
1016
    }
 
1017
 
 
1018
    /**
 
1019
     * <p>
 
1020
     * Write an array of data characters without escaping.
 
1021
     * </p>
 
1022
     * 
 
1023
     * @param ch the array of characters
 
1024
     * @param start the starting position
 
1025
     * @param length the number of characters to use
 
1026
     * 
 
1027
     * @throws org.xml.SAXException if there is an error writing
 
1028
     *            the characters, this method will throw an
 
1029
     *            IOException wrapped in a SAXException.
 
1030
     */    
 
1031
    private void write(char[] ch, int start, int length)
 
1032
      throws SAXException {   
 
1033
          
 
1034
        try {
 
1035
            output.write(ch, start, length);
 
1036
        } 
 
1037
        catch (IOException e) {
 
1038
            throw new SAXException(e);
 
1039
        }  
 
1040
        
 
1041
    }
 
1042
 
 
1043
 
 
1044
    /**
 
1045
     * <p>
 
1046
     * Write out the list of namespace declarations.
 
1047
     * </p>
 
1048
     * 
 
1049
     * @throws org.xml.sax.SAXException This method will throw
 
1050
     *            an IOException wrapped in a SAXException if
 
1051
     *            there is an error writing the namespace
 
1052
     *            declarations
 
1053
     */    
 
1054
    private void writeNSDecls() throws SAXException {
 
1055
        Enumeration prefixes = nsSupport.getDeclaredPrefixes();
 
1056
        while (prefixes.hasMoreElements()) {
 
1057
            String prefix = (String) prefixes.nextElement();
 
1058
            String uri = nsSupport.getURI(prefix);
 
1059
            if (uri == null) {
 
1060
                  uri = "";
 
1061
            }
 
1062
            char[] ch = uri.toCharArray();
 
1063
            write(' ');
 
1064
            if ("".equals(prefix)) {
 
1065
                  write("xmlns=\"");
 
1066
            } 
 
1067
            else {
 
1068
                        write("xmlns:");
 
1069
                        write(prefix);
 
1070
                        write("=\"");
 
1071
            }
 
1072
            writeEsc(ch, 0, ch.length, true);
 
1073
            write('\"');
 
1074
        }
 
1075
    }
 
1076
    
 
1077
 
 
1078
    /**
 
1079
     * <p>
 
1080
     * Write an element or attribute name.
 
1081
     * </p>
 
1082
     *
 
1083
     * @param uri the namespace URI
 
1084
     * @param localName the local name
 
1085
     * @param qualifiedName the prefixed name, if available, 
 
1086
     *        or the empty string.
 
1087
     * @param isElement true if this is an element name, false if it
 
1088
     *        is an attribute name
 
1089
     * 
 
1090
     * @throws org.xml.sax.SAXException this method will throw an
 
1091
     *            IOException wrapped in a SAXException if there is
 
1092
     *            an error writing the name
 
1093
     */
 
1094
    private void writeName (String uri, String localName,
 
1095
          String qualifiedName, boolean isElement)
 
1096
          throws SAXException {
 
1097
        String prefix = doPrefix(uri, qualifiedName, isElement);
 
1098
        if (prefix != null && !"".equals(prefix)) {
 
1099
            write(prefix);
 
1100
            write(':');
 
1101
        }
 
1102
        write(localName);
 
1103
    }
 
1104
 
 
1105
 
 
1106
 
 
1107
    ////////////////////////////////////////////////////////////////////
 
1108
    // Constants.
 
1109
    ////////////////////////////////////////////////////////////////////
 
1110
 
 
1111
    private final Attributes EMPTY_ATTS = new AttributesImpl();
 
1112
 
 
1113
 
 
1114
    ////////////////////////////////////////////////////////////////////
 
1115
    // Internal state.
 
1116
    ////////////////////////////////////////////////////////////////////
 
1117
 
 
1118
    private Hashtable        prefixTable;
 
1119
    private Hashtable        forcedDeclTable;
 
1120
    private Hashtable        doneDeclTable;
 
1121
    private int              elementLevel = 0;
 
1122
    private Writer           output;
 
1123
    private NamespaceSupport nsSupport;
 
1124
    private int              prefixCounter = 0;
 
1125
 
 
1126
 
 
1127
    ///////////////////////////////////////////////////////////////////
 
1128
    // LexicalHandler methods.
 
1129
    ///////////////////////////////////////////////////////////////////
 
1130
    
 
1131
 
 
1132
    public void endCDATA() {}
 
1133
    
 
1134
    public void endDTD() throws SAXException {
 
1135
        write(">");
 
1136
    }
 
1137
    
 
1138
    public void startCDATA() {}
 
1139
    
 
1140
    public void comment(char[] ch, int start, int length) 
 
1141
      throws SAXException {
 
1142
        write("<!--");
 
1143
        write(ch, start, length);
 
1144
        write("-->");
 
1145
        if (elementLevel < 1) {
 
1146
            write('\n');
 
1147
        }
 
1148
    }
 
1149
 
 
1150
    public void endEntity(String name) {}
 
1151
    public void startEntity(String name) {}
 
1152
    
 
1153
    public void startDTD(String name, String publicID, String systemID)
 
1154
      throws SAXException {
 
1155
        write("<!DOCTYPE ");
 
1156
        write(name);
 
1157
        if (systemID != null) {
 
1158
            if (publicID != null) {
 
1159
                write(" PUBLIC \"");
 
1160
                write(publicID);
 
1161
                write("\" \"");
 
1162
                write(systemID);
 
1163
                write("\"");
 
1164
            }
 
1165
            else {
 
1166
                write(" SYSTEM \"");
 
1167
                write(systemID);
 
1168
                write("\"");
 
1169
            }
 
1170
        }
 
1171
 
 
1172
    }
 
1173
    
 
1174
}
 
1175
 
 
1176
// end of XMLWriter.java
 
1177