~ubuntu-branches/debian/stretch/insubstantial/stretch

« back to all changes in this revision

Viewing changes to laf-plugin/src/main/java/org/pushingpixels/lafplugin/XMLElement.java

  • Committer: Package Import Robot
  • Author(s): Felix Natter
  • Date: 2016-01-18 20:58:45 UTC
  • Revision ID: package-import@ubuntu.com-20160118205845-crbmrkda61qsi5qa
Tags: upstream-7.3+dfsg2
ImportĀ upstreamĀ versionĀ 7.3+dfsg2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* XMLElement.java
 
2
 *
 
3
 * $Revision: 39 $
 
4
 * $Date: 2009-10-14 19:49:29 -0700 (Wed, 14 Oct 2009) $
 
5
 * $Name$
 
6
 *
 
7
 * This file is part of NanoXML 2 Lite.
 
8
 * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
 
9
 *
 
10
 * This software is provided 'as-is', without any express or implied warranty.
 
11
 * In no event will the authors be held liable for any damages arising from the
 
12
 * use of this software.
 
13
 *
 
14
 * Permission is granted to anyone to use this software for any purpose,
 
15
 * including commercial applications, and to alter it and redistribute it
 
16
 * freely, subject to the following restrictions:
 
17
 *
 
18
 *  1. The origin of this software must not be misrepresented; you must not
 
19
 *     claim that you wrote the original software. If you use this software in
 
20
 *     a product, an acknowledgment in the product documentation would be
 
21
 *     appreciated but is not required.
 
22
 *
 
23
 *  2. Altered source versions must be plainly marked as such, and must not be
 
24
 *     misrepresented as being the original software.
 
25
 *
 
26
 *  3. This notice may not be removed or altered from any source distribution.
 
27
 *****************************************************************************/
 
28
 
 
29
 
 
30
package org.pushingpixels.lafplugin;
 
31
 
 
32
 
 
33
import java.io.ByteArrayOutputStream;
 
34
import java.io.CharArrayReader;
 
35
import java.io.IOException;
 
36
import java.io.OutputStreamWriter;
 
37
import java.io.Reader;
 
38
import java.io.StringReader;
 
39
import java.io.Writer;
 
40
import java.util.Enumeration;
 
41
import java.util.Hashtable;
 
42
import java.util.Vector;
 
43
 
 
44
 
 
45
/**
 
46
 * XMLElement is a representation of an XML object. The object is able to parse
 
47
 * XML code.
 
48
 * <P><DL>
 
49
 * <DT><B>Parsing XML Data</B></DT>
 
50
 * <DD>
 
51
 * You can parse XML data using the following code:
 
52
 * <UL><CODE>
 
53
 * XMLElement xml = new XMLElement();<BR>
 
54
 * FileReader reader = new FileReader("filename.xml");<BR>
 
55
 * xml.parseFromReader(reader);
 
56
 * </CODE></UL></DD></DL>
 
57
 * <DL><DT><B>Retrieving Attributes</B></DT>
 
58
 * <DD>
 
59
 * You can enumerate the attributes of an element using the method
 
60
 * {@link #enumerateAttributeNames() enumerateAttributeNames}.
 
61
 * The attribute values can be retrieved using the method
 
62
 * {@link #getStringAttribute(java.lang.String) getStringAttribute}.
 
63
 * The following example shows how to list the attributes of an element:
 
64
 * <UL><CODE>
 
65
 * XMLElement element = ...;<BR>
 
66
 * Enumeration enum = element.getAttributeNames();<BR>
 
67
 * while (enum.hasMoreElements()) {<BR>
 
68
 * &nbsp;&nbsp;&nbsp;&nbsp;String key = (String) enum.nextElement();<BR>
 
69
 * &nbsp;&nbsp;&nbsp;&nbsp;String value = element.getStringAttribute(key);<BR>
 
70
 * &nbsp;&nbsp;&nbsp;&nbsp;System.out.println(key + " = " + value);<BR>
 
71
 * }
 
72
 * </CODE></UL></DD></DL>
 
73
 * <DL><DT><B>Retrieving Child Elements</B></DT>
 
74
 * <DD>
 
75
 * You can enumerate the children of an element using
 
76
 * {@link #enumerateChildren() enumerateChildren}.
 
77
 * The number of child elements can be retrieved using
 
78
 * {@link #countChildren() countChildren}.
 
79
 * </DD></DL>
 
80
 * <DL><DT><B>Elements Containing Character Data</B></DT>
 
81
 * <DD>
 
82
 * If an elements contains character data, like in the following example:
 
83
 * <UL><CODE>
 
84
 * &lt;title&gt;The Title&lt;/title&gt;
 
85
 * </CODE></UL>
 
86
 * you can retrieve that data using the method
 
87
 * {@link #getContent() getContent}.
 
88
 * </DD></DL>
 
89
 * <DL><DT><B>Subclassing XMLElement</B></DT>
 
90
 * <DD>
 
91
 * When subclassing XMLElement, you need to override the method
 
92
 * {@link #createAnotherElement() createAnotherElement}
 
93
 * which has to return a new copy of the receiver.
 
94
 * </DD></DL>
 
95
 * <P>
 
96
 *
 
97
 * see nanoxml.XMLParseException
 
98
 *
 
99
 * @author Marc De Scheemaecker
 
100
 *         &lt;<A href="mailto:cyberelf@mac.com">cyberelf@mac.com</A>&gt;
 
101
 * @version $Name$, $Revision: 39 $
 
102
 */
 
103
public class XMLElement
 
104
{
 
105
 
 
106
    /**
 
107
     * Serialization serial version ID.
 
108
     */
 
109
    static final long serialVersionUID = 6685035139346394777L;
 
110
 
 
111
 
 
112
    /**
 
113
     * Major version of NanoXML. Classes with the same major and minor
 
114
     * version are binary compatible. Classes with the same major version
 
115
     * are source compatible. If the major version is different, you may
 
116
     * need to modify the client source code.
 
117
     *
 
118
     * see nanoxml.XMLElement#NANOXML_MINOR_VERSION
 
119
     */
 
120
    public static final int NANOXML_MAJOR_VERSION = 2;
 
121
    
 
122
 
 
123
    /**
 
124
     * Minor version of NanoXML. Classes with the same major and minor
 
125
     * version are binary compatible. Classes with the same major version
 
126
     * are source compatible. If the major version is different, you may
 
127
     * need to modify the client source code.
 
128
     *
 
129
     * see nanoxml.XMLElement#NANOXML_MAJOR_VERSION
 
130
     */
 
131
    public static final int NANOXML_MINOR_VERSION = 2;
 
132
 
 
133
 
 
134
    /**
 
135
     * The attributes given to the element.
 
136
     *
 
137
     * <dl><dt><b>Invariants:</b></dt><dd>
 
138
     * <ul><li>The field can be empty.
 
139
     *     <li>The field is never <code>null</code>.
 
140
     *     <li>The keys and the values are strings.
 
141
     * </ul></dd></dl>
 
142
     */
 
143
    private Hashtable attributes;
 
144
 
 
145
 
 
146
    /**
 
147
     * Child elements of the element.
 
148
     *
 
149
     * <dl><dt><b>Invariants:</b></dt><dd>
 
150
     * <ul><li>The field can be empty.
 
151
     *     <li>The field is never <code>null</code>.
 
152
     *     <li>The elements are instances of <code>XMLElement</code>
 
153
     *         or a subclass of <code>XMLElement</code>.
 
154
     * </ul></dd></dl>
 
155
     */
 
156
    private Vector children;
 
157
 
 
158
 
 
159
    /**
 
160
     * The name of the element.
 
161
     *
 
162
     * <dl><dt><b>Invariants:</b></dt><dd>
 
163
     * <ul><li>The field is <code>null</code> iff the element is not
 
164
     *         initialized by either parse or setName.
 
165
     *     <li>If the field is not <code>null</code>, it's not empty.
 
166
     *     <li>If the field is not <code>null</code>, it contains a valid
 
167
     *         XML identifier.
 
168
     * </ul></dd></dl>
 
169
     */
 
170
    private String name;
 
171
 
 
172
 
 
173
    /**
 
174
     * The #PCDATA content of the object.
 
175
     *
 
176
     * <dl><dt><b>Invariants:</b></dt><dd>
 
177
     * <ul><li>The field is <code>null</code> iff the element is not a
 
178
     *         #PCDATA element.
 
179
     *     <li>The field can be any string, including the empty string.
 
180
     * </ul></dd></dl>
 
181
     */
 
182
    private String contents;
 
183
 
 
184
 
 
185
    /**
 
186
     * Conversion table for &amp;...; entities. The keys are the entity names
 
187
     * without the &amp; and ; delimiters.
 
188
     *
 
189
     * <dl><dt><b>Invariants:</b></dt><dd>
 
190
     * <ul><li>The field is never <code>null</code>.
 
191
     *     <li>The field always contains the following associations:
 
192
     *         "lt"&nbsp;=&gt;&nbsp;"&lt;", "gt"&nbsp;=&gt;&nbsp;"&gt;",
 
193
     *         "quot"&nbsp;=&gt;&nbsp;"\"", "apos"&nbsp;=&gt;&nbsp;"'",
 
194
     *         "amp"&nbsp;=&gt;&nbsp;"&amp;"
 
195
     *     <li>The keys are strings
 
196
     *     <li>The values are char arrays
 
197
     * </ul></dd></dl>
 
198
     */
 
199
    private Hashtable entities;
 
200
 
 
201
 
 
202
    /**
 
203
     * The line number where the element starts.
 
204
     *
 
205
     * <dl><dt><b>Invariants:</b></dt><dd>
 
206
     * <ul><li><code>lineNr &gt= 0</code>
 
207
     * </ul></dd></dl>
 
208
     */
 
209
    private int lineNr;
 
210
 
 
211
 
 
212
    /**
 
213
     * <code>true</code> if the case of the element and attribute names
 
214
     * are case insensitive.
 
215
     */
 
216
    private boolean ignoreCase;
 
217
 
 
218
 
 
219
    /**
 
220
     * <code>true</code> if the leading and trailing whitespace of #PCDATA
 
221
     * sections have to be ignored.
 
222
     */
 
223
    private boolean ignoreWhitespace;
 
224
 
 
225
 
 
226
    /**
 
227
     * Character read too much.
 
228
     * This character provides push-back functionality to the input reader
 
229
     * without having to use a PushbackReader.
 
230
     * If there is no such character, this field is '\0'.
 
231
     */
 
232
    private char charReadTooMuch;
 
233
 
 
234
 
 
235
    /**
 
236
     * The reader provided by the caller of the parse method.
 
237
     *
 
238
     * <dl><dt><b>Invariants:</b></dt><dd>
 
239
     * <ul><li>The field is not <code>null</code> while the parse method
 
240
     *         is running.
 
241
     * </ul></dd></dl>
 
242
     */
 
243
    private Reader reader;
 
244
 
 
245
 
 
246
    /**
 
247
     * The current line number in the source content.
 
248
     *
 
249
     * <dl><dt><b>Invariants:</b></dt><dd>
 
250
     * <ul><li>parserLineNr &gt; 0 while the parse method is running.
 
251
     * </ul></dd></dl>
 
252
     */
 
253
    private int parserLineNr;
 
254
 
 
255
 
 
256
    /**
 
257
     * Creates and initializes a new XML element.
 
258
     * Calling the construction is equivalent to:
 
259
     * <ul><code>new XMLElement(new Hashtable(), false, true)
 
260
     * </code></ul>
 
261
     *
 
262
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
263
     * <ul><li>countChildren() => 0
 
264
     *     <li>enumerateChildren() => empty enumeration
 
265
     *     <li>enumeratePropertyNames() => empty enumeration
 
266
     *     <li>getChildren() => empty vector
 
267
     *     <li>getContent() => ""
 
268
     *     <li>getLineNr() => 0
 
269
     *     <li>getName() => null
 
270
     * </ul></dd></dl>
 
271
     *
 
272
     * see nanoxml.XMLElement#XMLElement(java.util.Hashtable)
 
273
     *         XMLElement(Hashtable)
 
274
     * see nanoxml.XMLElement#XMLElement(boolean)
 
275
     * see nanoxml.XMLElement#XMLElement(java.util.Hashtable,boolean)
 
276
     *         XMLElement(Hashtable, boolean)
 
277
     */
 
278
    public XMLElement()
 
279
    {
 
280
        this(new Hashtable(), false, true, true);
 
281
    }
 
282
    
 
283
 
 
284
    /**
 
285
     * Creates and initializes a new XML element.
 
286
     * Calling the construction is equivalent to:
 
287
     * <ul><code>new XMLElement(entities, false, true)
 
288
     * </code></ul>
 
289
     *
 
290
     * @param entities
 
291
     *     The entity conversion table.
 
292
     *
 
293
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
294
     * <ul><li><code>entities != null</code>
 
295
     * </ul></dd></dl>
 
296
     *
 
297
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
298
     * <ul><li>countChildren() => 0
 
299
     *     <li>enumerateChildren() => empty enumeration
 
300
     *     <li>enumeratePropertyNames() => empty enumeration
 
301
     *     <li>getChildren() => empty vector
 
302
     *     <li>getContent() => ""
 
303
     *     <li>getLineNr() => 0
 
304
     *     <li>getName() => null
 
305
     * </ul></dd></dl><dl>
 
306
     *
 
307
     * see nanoxml.XMLElement#XMLElement()
 
308
     * see nanoxml.XMLElement#XMLElement(boolean)
 
309
     * see nanoxml.XMLElement#XMLElement(java.util.Hashtable,boolean)
 
310
     *         XMLElement(Hashtable, boolean)
 
311
     */
 
312
    public XMLElement(Hashtable entities)
 
313
    {
 
314
        this(entities, false, true, true);
 
315
    }
 
316
 
 
317
 
 
318
    /**
 
319
     * Creates and initializes a new XML element.
 
320
     * Calling the construction is equivalent to:
 
321
     * <ul><code>new XMLElement(new Hashtable(), skipLeadingWhitespace, true)
 
322
     * </code></ul>
 
323
     *
 
324
     * @param skipLeadingWhitespace
 
325
     *     <code>true</code> if leading and trailing whitespace in PCDATA
 
326
     *     content has to be removed.
 
327
     *
 
328
     * </dl><dl><dt><b>Postconditions:</b></dt><dd>
 
329
     * <ul><li>countChildren() => 0
 
330
     *     <li>enumerateChildren() => empty enumeration
 
331
     *     <li>enumeratePropertyNames() => empty enumeration
 
332
     *     <li>getChildren() => empty vector
 
333
     *     <li>getContent() => ""
 
334
     *     <li>getLineNr() => 0
 
335
     *     <li>getName() => null
 
336
     * </ul></dd></dl><dl>
 
337
     *
 
338
     * see nanoxml.XMLElement#XMLElement()
 
339
     * see nanoxml.XMLElement#XMLElement(java.util.Hashtable)
 
340
     *         XMLElement(Hashtable)
 
341
     * see nanoxml.XMLElement#XMLElement(java.util.Hashtable,boolean)
 
342
     *         XMLElement(Hashtable, boolean)
 
343
     */
 
344
    public XMLElement(boolean skipLeadingWhitespace)
 
345
    {
 
346
        this(new Hashtable(), skipLeadingWhitespace, true, true);
 
347
    }
 
348
 
 
349
 
 
350
    /**
 
351
     * Creates and initializes a new XML element.
 
352
     * Calling the construction is equivalent to:
 
353
     * <ul><code>new XMLElement(entities, skipLeadingWhitespace, true)
 
354
     * </code></ul>
 
355
     *
 
356
     * @param entities
 
357
     *     The entity conversion table.
 
358
     * @param skipLeadingWhitespace
 
359
     *     <code>true</code> if leading and trailing whitespace in PCDATA
 
360
     *     content has to be removed.
 
361
     *
 
362
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
363
     * <ul><li><code>entities != null</code>
 
364
     * </ul></dd></dl>
 
365
     *
 
366
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
367
     * <ul><li>countChildren() => 0
 
368
     *     <li>enumerateChildren() => empty enumeration
 
369
     *     <li>enumeratePropertyNames() => empty enumeration
 
370
     *     <li>getChildren() => empty vector
 
371
     *     <li>getContent() => ""
 
372
     *     <li>getLineNr() => 0
 
373
     *     <li>getName() => null
 
374
     * </ul></dd></dl><dl>
 
375
     *
 
376
     * see nanoxml.XMLElement#XMLElement()
 
377
     * see nanoxml.XMLElement#XMLElement(boolean)
 
378
     * see nanoxml.XMLElement#XMLElement(java.util.Hashtable)
 
379
     *         XMLElement(Hashtable)
 
380
     */
 
381
    public XMLElement(Hashtable entities,
 
382
                      boolean   skipLeadingWhitespace)
 
383
    {
 
384
        this(entities, skipLeadingWhitespace, true, true);
 
385
    }
 
386
 
 
387
 
 
388
    /**
 
389
     * Creates and initializes a new XML element.
 
390
     *
 
391
     * @param entities
 
392
     *     The entity conversion table.
 
393
     * @param skipLeadingWhitespace
 
394
     *     <code>true</code> if leading and trailing whitespace in PCDATA
 
395
     *     content has to be removed.
 
396
     * @param ignoreCase
 
397
     *     <code>true</code> if the case of element and attribute names have
 
398
     *     to be ignored.
 
399
     *
 
400
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
401
     * <ul><li><code>entities != null</code>
 
402
     * </ul></dd></dl>
 
403
     *
 
404
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
405
     * <ul><li>countChildren() => 0
 
406
     *     <li>enumerateChildren() => empty enumeration
 
407
     *     <li>enumeratePropertyNames() => empty enumeration
 
408
     *     <li>getChildren() => empty vector
 
409
     *     <li>getContent() => ""
 
410
     *     <li>getLineNr() => 0
 
411
     *     <li>getName() => null
 
412
     * </ul></dd></dl><dl>
 
413
     *
 
414
     * see nanoxml.XMLElement#XMLElement()
 
415
     * see nanoxml.XMLElement#XMLElement(boolean)
 
416
     * see nanoxml.XMLElement#XMLElement(java.util.Hashtable)
 
417
     *         XMLElement(Hashtable)
 
418
     * see nanoxml.XMLElement#XMLElement(java.util.Hashtable,boolean)
 
419
     *         XMLElement(Hashtable, boolean)
 
420
     */
 
421
    public XMLElement(Hashtable entities,
 
422
                      boolean   skipLeadingWhitespace,
 
423
                      boolean   ignoreCase)
 
424
    {
 
425
        this(entities, skipLeadingWhitespace, true, ignoreCase);
 
426
    }
 
427
 
 
428
 
 
429
    /**
 
430
     * Creates and initializes a new XML element.
 
431
     * <P>
 
432
     * This constructor should <I>only</I> be called from
 
433
     * {@link #createAnotherElement() createAnotherElement}
 
434
     * to create child elements.
 
435
     *
 
436
     * @param entities
 
437
     *     The entity conversion table.
 
438
     * @param skipLeadingWhitespace
 
439
     *     <code>true</code> if leading and trailing whitespace in PCDATA
 
440
     *     content has to be removed.
 
441
     * @param fillBasicConversionTable
 
442
     *     <code>true</code> if the basic entities need to be added to
 
443
     *     the entity list.
 
444
     * @param ignoreCase
 
445
     *     <code>true</code> if the case of element and attribute names have
 
446
     *     to be ignored.
 
447
     *
 
448
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
449
     * <ul><li><code>entities != null</code>
 
450
     *     <li>if <code>fillBasicConversionTable == false</code>
 
451
     *         then <code>entities</code> contains at least the following
 
452
     *         entries: <code>amp</code>, <code>lt</code>, <code>gt</code>,
 
453
     *         <code>apos</code> and <code>quot</code>
 
454
     * </ul></dd></dl>
 
455
     *
 
456
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
457
     * <ul><li>countChildren() => 0
 
458
     *     <li>enumerateChildren() => empty enumeration
 
459
     *     <li>enumeratePropertyNames() => empty enumeration
 
460
     *     <li>getChildren() => empty vector
 
461
     *     <li>getContent() => ""
 
462
     *     <li>getLineNr() => 0
 
463
     *     <li>getName() => null
 
464
     * </ul></dd></dl><dl>
 
465
     *
 
466
     * see nanoxml.XMLElement#createAnotherElement()
 
467
     */
 
468
    protected XMLElement(Hashtable entities,
 
469
                         boolean   skipLeadingWhitespace,
 
470
                         boolean   fillBasicConversionTable,
 
471
                         boolean   ignoreCase)
 
472
    {
 
473
        this.ignoreWhitespace = skipLeadingWhitespace;
 
474
        this.ignoreCase = ignoreCase;
 
475
        this.name = null;
 
476
        this.contents = "";
 
477
        this.attributes = new Hashtable();
 
478
        this.children = new Vector();
 
479
        this.entities = entities;
 
480
        this.lineNr = 0;
 
481
        Enumeration enumeration = this.entities.keys();
 
482
        while (enumeration.hasMoreElements()) {
 
483
            Object key = enumeration.nextElement();
 
484
            Object value = this.entities.get(key);
 
485
            if (value instanceof String) {
 
486
                value = ((String) value).toCharArray();
 
487
                this.entities.put(key, value);
 
488
            }
 
489
        }
 
490
        if (fillBasicConversionTable) {
 
491
            this.entities.put("amp", new char[] { '&' });
 
492
            this.entities.put("quot", new char[] { '"' });
 
493
            this.entities.put("apos", new char[] { '\'' });
 
494
            this.entities.put("lt", new char[] { '<' });
 
495
            this.entities.put("gt", new char[] { '>' });
 
496
        }
 
497
    }
 
498
 
 
499
 
 
500
    /**
 
501
     * Adds a child element.
 
502
     *
 
503
     * @param child
 
504
     *     The child element to add.
 
505
     *
 
506
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
507
     * <ul><li><code>child != null</code>
 
508
     *     <li><code>child.getName() != null</code>
 
509
     *     <li><code>child</code> does not have a parent element
 
510
     * </ul></dd></dl>
 
511
     *
 
512
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
513
     * <ul><li>countChildren() => old.countChildren() + 1
 
514
     *     <li>enumerateChildren() => old.enumerateChildren() + child
 
515
     *     <li>getChildren() => old.enumerateChildren() + child
 
516
     * </ul></dd></dl><dl>
 
517
     *
 
518
     * see nanoxml.XMLElement#countChildren()
 
519
     * see nanoxml.XMLElement#enumerateChildren()
 
520
     * see nanoxml.XMLElement#getChildren()
 
521
     * see nanoxml.XMLElement#removeChild(nanoxml.XMLElement)
 
522
     *         removeChild(XMLElement)
 
523
     */
 
524
    public void addChild(XMLElement child)
 
525
    {
 
526
        this.children.addElement(child);
 
527
    }
 
528
 
 
529
 
 
530
    /**
 
531
     * Adds or modifies an attribute.
 
532
     *
 
533
     * @param name
 
534
     *     The name of the attribute.
 
535
     * @param value
 
536
     *     The value of the attribute.
 
537
     *
 
538
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
539
     * <ul><li><code>name != null</code>
 
540
     *     <li><code>name</code> is a valid XML identifier
 
541
     *     <li><code>value != null</code>
 
542
     * </ul></dd></dl>
 
543
     *
 
544
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
545
     * <ul><li>enumerateAttributeNames()
 
546
     *         => old.enumerateAttributeNames() + name
 
547
     *     <li>getAttribute(name) => value
 
548
     * </ul></dd></dl><dl>
 
549
     *
 
550
     * see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
 
551
     *         setDoubleAttribute(String, double)
 
552
     * see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
 
553
     *         setIntAttribute(String, int)
 
554
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
555
     * see nanoxml.XMLElement#getAttribute(java.lang.String)
 
556
     *         getAttribute(String)
 
557
     * see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
 
558
     *         getAttribute(String, Object)
 
559
     * see nanoxml.XMLElement#getAttribute(java.lang.String,
 
560
     *                                      java.util.Hashtable,
 
561
     *                                      java.lang.String, boolean)
 
562
     *         getAttribute(String, Hashtable, String, boolean)
 
563
     * see nanoxml.XMLElement#getStringAttribute(java.lang.String)
 
564
     *         getStringAttribute(String)
 
565
     * see nanoxml.XMLElement#getStringAttribute(java.lang.String,
 
566
     *                                            java.lang.String)
 
567
     *         getStringAttribute(String, String)
 
568
     * see nanoxml.XMLElement#getStringAttribute(java.lang.String,
 
569
     *                                            java.util.Hashtable,
 
570
     *                                            java.lang.String, boolean)
 
571
     *         getStringAttribute(String, Hashtable, String, boolean)
 
572
     */
 
573
    public void setAttribute(String name,
 
574
                             Object value)
 
575
    {
 
576
        if (this.ignoreCase) {
 
577
            name = name.toUpperCase();
 
578
        }
 
579
        this.attributes.put(name, value.toString());
 
580
    }
 
581
 
 
582
 
 
583
    /**
 
584
     * Adds or modifies an attribute.
 
585
     *
 
586
     * @param name
 
587
     *     The name of the attribute.
 
588
     * @param value
 
589
     *     The value of the attribute.
 
590
     *
 
591
     * @deprecated Use {@link #setAttribute(java.lang.String, java.lang.Object)
 
592
     *             setAttribute} instead.
 
593
     */
 
594
    public void addProperty(String name,
 
595
                            Object value)
 
596
    {
 
597
        this.setAttribute(name, value);
 
598
    }
 
599
 
 
600
 
 
601
    /**
 
602
     * Adds or modifies an attribute.
 
603
     *
 
604
     * @param name
 
605
     *     The name of the attribute.
 
606
     * @param value
 
607
     *     The value of the attribute.
 
608
     *
 
609
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
610
     * <ul><li><code>name != null</code>
 
611
     *     <li><code>name</code> is a valid XML identifier
 
612
     * </ul></dd></dl>
 
613
     *
 
614
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
615
     * <ul><li>enumerateAttributeNames()
 
616
     *         => old.enumerateAttributeNames() + name
 
617
     *     <li>getIntAttribute(name) => value
 
618
     * </ul></dd></dl><dl>
 
619
     *
 
620
     * see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
 
621
     *         setDoubleAttribute(String, double)
 
622
     * see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
 
623
     *         setAttribute(String, Object)
 
624
     * see nanoxml.XMLElement#removeAttribute(java.lang.String)
 
625
     *         removeAttribute(String)
 
626
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
627
     * see nanoxml.XMLElement#getIntAttribute(java.lang.String)
 
628
     *         getIntAttribute(String)
 
629
     * see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
 
630
     *         getIntAttribute(String, int)
 
631
     * see nanoxml.XMLElement#getIntAttribute(java.lang.String,
 
632
     *                                         java.util.Hashtable,
 
633
     *                                         java.lang.String, boolean)
 
634
     *         getIntAttribute(String, Hashtable, String, boolean)
 
635
     */
 
636
    public void setIntAttribute(String name,
 
637
                                int    value)
 
638
    {
 
639
        if (this.ignoreCase) {
 
640
            name = name.toUpperCase();
 
641
        }
 
642
        this.attributes.put(name, Integer.toString(value));
 
643
    }
 
644
 
 
645
 
 
646
    /**
 
647
     * Adds or modifies an attribute.
 
648
     *
 
649
     * @param key
 
650
     *     The name of the attribute.
 
651
     * @param value
 
652
     *     The value of the attribute.
 
653
     *
 
654
     * @deprecated Use {@link #setIntAttribute(java.lang.String, int)
 
655
     *             setIntAttribute} instead.
 
656
     */
 
657
    public void addProperty(String key,
 
658
                            int    value)
 
659
    {
 
660
        this.setIntAttribute(key, value);
 
661
    }
 
662
 
 
663
 
 
664
    /**
 
665
     * Adds or modifies an attribute.
 
666
     *
 
667
     * @param name
 
668
     *     The name of the attribute.
 
669
     * @param value
 
670
     *     The value of the attribute.
 
671
     *
 
672
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
673
     * <ul><li><code>name != null</code>
 
674
     *     <li><code>name</code> is a valid XML identifier
 
675
     * </ul></dd></dl>
 
676
     *
 
677
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
678
     * <ul><li>enumerateAttributeNames()
 
679
     *         => old.enumerateAttributeNames() + name
 
680
     *     <li>getDoubleAttribute(name) => value
 
681
     * </ul></dd></dl><dl>
 
682
     *
 
683
     * see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
 
684
     *         setIntAttribute(String, int)
 
685
     * see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
 
686
     *         setAttribute(String, Object)
 
687
     * see nanoxml.XMLElement#removeAttribute(java.lang.String)
 
688
     *         removeAttribute(String)
 
689
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
690
     * see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
 
691
     *         getDoubleAttribute(String)
 
692
     * see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
 
693
     *         getDoubleAttribute(String, double)
 
694
     * see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
 
695
     *                                            java.util.Hashtable,
 
696
     *                                            java.lang.String, boolean)
 
697
     *         getDoubleAttribute(String, Hashtable, String, boolean)
 
698
     */
 
699
    public void setDoubleAttribute(String name,
 
700
                                   double value)
 
701
    {
 
702
        if (this.ignoreCase) {
 
703
            name = name.toUpperCase();
 
704
        }
 
705
        this.attributes.put(name, Double.toString(value));
 
706
    }
 
707
 
 
708
 
 
709
    /**
 
710
     * Adds or modifies an attribute.
 
711
     *
 
712
     * @param name
 
713
     *     The name of the attribute.
 
714
     * @param value
 
715
     *     The value of the attribute.
 
716
     *
 
717
     * @deprecated Use {@link #setDoubleAttribute(java.lang.String, double)
 
718
     *             setDoubleAttribute} instead.
 
719
     */
 
720
    public void addProperty(String name,
 
721
                            double value)
 
722
    {
 
723
        this.setDoubleAttribute(name, value);
 
724
    }
 
725
 
 
726
 
 
727
    /**
 
728
     * Returns the number of child elements of the element.
 
729
     *
 
730
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
731
     * <ul><li><code>result >= 0</code>
 
732
     * </ul></dd></dl>
 
733
     *
 
734
     * see nanoxml.XMLElement#addChild(nanoxml.XMLElement)
 
735
     *         addChild(XMLElement)
 
736
     * see nanoxml.XMLElement#enumerateChildren()
 
737
     * see nanoxml.XMLElement#getChildren()
 
738
     * see nanoxml.XMLElement#removeChild(nanoxml.XMLElement)
 
739
     *         removeChild(XMLElement)
 
740
     */
 
741
    public int countChildren()
 
742
    {
 
743
        return this.children.size();
 
744
    }
 
745
 
 
746
 
 
747
    /**
 
748
     * Enumerates the attribute names.
 
749
     *
 
750
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
751
     * <ul><li><code>result != null</code>
 
752
     * </ul></dd></dl>
 
753
     *
 
754
     * see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
 
755
     *         setDoubleAttribute(String, double)
 
756
     * see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
 
757
     *         setIntAttribute(String, int)
 
758
     * see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
 
759
     *         setAttribute(String, Object)
 
760
     * see nanoxml.XMLElement#removeAttribute(java.lang.String)
 
761
     *         removeAttribute(String)
 
762
     * see nanoxml.XMLElement#getAttribute(java.lang.String)
 
763
     *         getAttribute(String)
 
764
     * see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
 
765
     *         getAttribute(String, String)
 
766
     * see nanoxml.XMLElement#getAttribute(java.lang.String,
 
767
     *                                      java.util.Hashtable,
 
768
     *                                      java.lang.String, boolean)
 
769
     *         getAttribute(String, Hashtable, String, boolean)
 
770
     * see nanoxml.XMLElement#getStringAttribute(java.lang.String)
 
771
     *         getStringAttribute(String)
 
772
     * see nanoxml.XMLElement#getStringAttribute(java.lang.String,
 
773
     *                                            java.lang.String)
 
774
     *         getStringAttribute(String, String)
 
775
     * see nanoxml.XMLElement#getStringAttribute(java.lang.String,
 
776
     *                                            java.util.Hashtable,
 
777
     *                                            java.lang.String, boolean)
 
778
     *         getStringAttribute(String, Hashtable, String, boolean)
 
779
     * see nanoxml.XMLElement#getIntAttribute(java.lang.String)
 
780
     *         getIntAttribute(String)
 
781
     * see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
 
782
     *         getIntAttribute(String, int)
 
783
     * see nanoxml.XMLElement#getIntAttribute(java.lang.String,
 
784
     *                                         java.util.Hashtable,
 
785
     *                                         java.lang.String, boolean)
 
786
     *         getIntAttribute(String, Hashtable, String, boolean)
 
787
     * see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
 
788
     *         getDoubleAttribute(String)
 
789
     * see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
 
790
     *         getDoubleAttribute(String, double)
 
791
     * see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
 
792
     *                                            java.util.Hashtable,
 
793
     *                                            java.lang.String, boolean)
 
794
     *         getDoubleAttribute(String, Hashtable, String, boolean)
 
795
     * see nanoxml.XMLElement#getBooleanAttribute(java.lang.String,
 
796
     *                                             java.lang.String,
 
797
     *                                             java.lang.String, boolean)
 
798
     *         getBooleanAttribute(String, String, String, boolean)
 
799
     */
 
800
    public Enumeration enumerateAttributeNames()
 
801
    {
 
802
        return this.attributes.keys();
 
803
    }
 
804
 
 
805
 
 
806
    /**
 
807
     * Enumerates the attribute names.
 
808
     *
 
809
     * @deprecated Use {@link #enumerateAttributeNames()
 
810
     *             enumerateAttributeNames} instead.
 
811
     */
 
812
    public Enumeration enumeratePropertyNames()
 
813
    {
 
814
        return this.enumerateAttributeNames();
 
815
    }
 
816
 
 
817
 
 
818
    /**
 
819
     * Enumerates the child elements.
 
820
     *
 
821
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
822
     * <ul><li><code>result != null</code>
 
823
     * </ul></dd></dl>
 
824
     *
 
825
     * see nanoxml.XMLElement#addChild(nanoxml.XMLElement)
 
826
     *         addChild(XMLElement)
 
827
     * see nanoxml.XMLElement#countChildren()
 
828
     * see nanoxml.XMLElement#getChildren()
 
829
     * see nanoxml.XMLElement#removeChild(nanoxml.XMLElement)
 
830
     *         removeChild(XMLElement)
 
831
     */
 
832
    public Enumeration enumerateChildren()
 
833
    {
 
834
        return this.children.elements();
 
835
    }
 
836
 
 
837
 
 
838
    /**
 
839
     * Returns the child elements as a Vector. It is safe to modify this
 
840
     * Vector.
 
841
     *
 
842
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
843
     * <ul><li><code>result != null</code>
 
844
     * </ul></dd></dl>
 
845
     *
 
846
     * see nanoxml.XMLElement#addChild(nanoxml.XMLElement)
 
847
     *         addChild(XMLElement)
 
848
     * see nanoxml.XMLElement#countChildren()
 
849
     * see nanoxml.XMLElement#enumerateChildren()
 
850
     * see nanoxml.XMLElement#removeChild(nanoxml.XMLElement)
 
851
     *         removeChild(XMLElement)
 
852
     */
 
853
    public Vector getChildren()
 
854
    {
 
855
        try {
 
856
            return (Vector) this.children.clone();
 
857
        } catch (Exception e) {
 
858
            // this never happens, however, some Java compilers are so
 
859
            // braindead that they require this exception clause
 
860
            return null;
 
861
        }
 
862
    }
 
863
 
 
864
 
 
865
    /**
 
866
     * Returns the PCDATA content of the object. If there is no such content,
 
867
     * <CODE>null</CODE> is returned.
 
868
     *
 
869
     * @deprecated Use {@link #getContent() getContent} instead.
 
870
     */
 
871
    public String getContents()
 
872
    {
 
873
        return this.getContent();
 
874
    }
 
875
 
 
876
 
 
877
    /**
 
878
     * Returns the PCDATA content of the object. If there is no such content,
 
879
     * <CODE>null</CODE> is returned.
 
880
     *
 
881
     * see nanoxml.XMLElement#setContent(java.lang.String)
 
882
     *         setContent(String)
 
883
     */
 
884
    public String getContent()
 
885
    {
 
886
        return this.contents;
 
887
    }
 
888
 
 
889
 
 
890
    /**
 
891
     * Returns the line nr in the source data on which the element is found.
 
892
     * This method returns <code>0</code> there is no associated source data.
 
893
     *
 
894
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
895
     * <ul><li><code>result >= 0</code>
 
896
     * </ul></dd></dl>
 
897
     */
 
898
    public int getLineNr()
 
899
    {
 
900
        return this.lineNr;
 
901
    }
 
902
 
 
903
 
 
904
    /**
 
905
     * Returns an attribute of the element.
 
906
     * If the attribute doesn't exist, <code>null</code> is returned.
 
907
     *
 
908
     * @param name The name of the attribute.
 
909
     *
 
910
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
911
     * <ul><li><code>name != null</code>
 
912
     *     <li><code>name</code> is a valid XML identifier
 
913
     * </ul></dd></dl><dl>
 
914
     *
 
915
     * see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
 
916
     *         setAttribute(String, Object)
 
917
     * see nanoxml.XMLElement#removeAttribute(java.lang.String)
 
918
     *         removeAttribute(String)
 
919
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
920
     * see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
 
921
     *         getAttribute(String, Object)
 
922
     * see nanoxml.XMLElement#getAttribute(java.lang.String,
 
923
     *                                      java.util.Hashtable,
 
924
     *                                      java.lang.String, boolean)
 
925
     *         getAttribute(String, Hashtable, String, boolean)
 
926
     */
 
927
    public Object getAttribute(String name)
 
928
    {
 
929
        return this.getAttribute(name, null);
 
930
    }
 
931
 
 
932
 
 
933
    /**
 
934
     * Returns an attribute of the element.
 
935
     * If the attribute doesn't exist, <code>defaultValue</code> is returned.
 
936
     *
 
937
     * @param name         The name of the attribute.
 
938
     * @param defaultValue Key to use if the attribute is missing.
 
939
     *
 
940
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
941
     * <ul><li><code>name != null</code>
 
942
     *     <li><code>name</code> is a valid XML identifier
 
943
     * </ul></dd></dl><dl>
 
944
     *
 
945
     * see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
 
946
     *         setAttribute(String, Object)
 
947
     * see nanoxml.XMLElement#removeAttribute(java.lang.String)
 
948
     *         removeAttribute(String)
 
949
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
950
     * see nanoxml.XMLElement#getAttribute(java.lang.String)
 
951
     *         getAttribute(String)
 
952
     * see nanoxml.XMLElement#getAttribute(java.lang.String,
 
953
     *                                      java.util.Hashtable,
 
954
     *                                      java.lang.String, boolean)
 
955
     *         getAttribute(String, Hashtable, String, boolean)
 
956
     */
 
957
    public Object getAttribute(String name,
 
958
                               Object defaultValue)
 
959
    {
 
960
        if (this.ignoreCase) {
 
961
            name = name.toUpperCase();
 
962
        }
 
963
        Object value = this.attributes.get(name);
 
964
        if (value == null) {
 
965
            value = defaultValue;
 
966
        }
 
967
        return value;
 
968
    }
 
969
 
 
970
 
 
971
    /**
 
972
     * Returns an attribute by looking up a key in a hashtable.
 
973
     * If the attribute doesn't exist, the value corresponding to defaultKey
 
974
     * is returned.
 
975
     * <P>
 
976
     * As an example, if valueSet contains the mapping <code>"one" =>
 
977
     * "1"</code>
 
978
     * and the element contains the attribute <code>attr="one"</code>, then
 
979
     * <code>getAttribute("attr", mapping, defaultKey, false)</code> returns
 
980
     * <code>"1"</code>.
 
981
     *
 
982
     * @param name
 
983
     *     The name of the attribute.
 
984
     * @param valueSet
 
985
     *     Hashtable mapping keys to values.
 
986
     * @param defaultKey
 
987
     *     Key to use if the attribute is missing.
 
988
     * @param allowLiterals
 
989
     *     <code>true</code> if literals are valid.
 
990
     *
 
991
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
992
     * <ul><li><code>name != null</code>
 
993
     *     <li><code>name</code> is a valid XML identifier
 
994
     *     <li><code>valueSet</code> != null
 
995
     *     <li>the keys of <code>valueSet</code> are strings
 
996
     * </ul></dd></dl><dl>
 
997
     *
 
998
     * see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
 
999
     *         setAttribute(String, Object)
 
1000
     * see nanoxml.XMLElement#removeAttribute(java.lang.String)
 
1001
     *         removeAttribute(String)
 
1002
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
1003
     * see nanoxml.XMLElement#getAttribute(java.lang.String)
 
1004
     *         getAttribute(String)
 
1005
     * see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
 
1006
     *         getAttribute(String, Object)
 
1007
     */
 
1008
    public Object getAttribute(String    name,
 
1009
                               Hashtable valueSet,
 
1010
                               String    defaultKey,
 
1011
                               boolean   allowLiterals)
 
1012
    {
 
1013
        if (this.ignoreCase) {
 
1014
            name = name.toUpperCase();
 
1015
        }
 
1016
        Object key = this.attributes.get(name);
 
1017
        Object result;
 
1018
        if (key == null) {
 
1019
            key = defaultKey;
 
1020
        }
 
1021
        result = valueSet.get(key);
 
1022
        if (result == null) {
 
1023
            if (allowLiterals) {
 
1024
                result = key;
 
1025
            } else {
 
1026
                throw this.invalidValue(name, (String) key);
 
1027
            }
 
1028
        }
 
1029
        return result;
 
1030
    }
 
1031
 
 
1032
 
 
1033
    /**
 
1034
     * Returns an attribute of the element.
 
1035
     * If the attribute doesn't exist, <code>null</code> is returned.
 
1036
     *
 
1037
     * @param name The name of the attribute.
 
1038
     *
 
1039
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1040
     * <ul><li><code>name != null</code>
 
1041
     *     <li><code>name</code> is a valid XML identifier
 
1042
     * </ul></dd></dl><dl>
 
1043
     *
 
1044
     * see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
 
1045
     *         setAttribute(String, Object)
 
1046
     * see nanoxml.XMLElement#removeAttribute(java.lang.String)
 
1047
     *         removeAttribute(String)
 
1048
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
1049
     * see nanoxml.XMLElement#getStringAttribute(java.lang.String,
 
1050
     *                                            java.lang.String)
 
1051
     *         getStringAttribute(String, String)
 
1052
     * see nanoxml.XMLElement#getStringAttribute(java.lang.String,
 
1053
     *                                            java.util.Hashtable,
 
1054
     *                                            java.lang.String, boolean)
 
1055
     *         getStringAttribute(String, Hashtable, String, boolean)
 
1056
     */
 
1057
    public String getStringAttribute(String name)
 
1058
    {
 
1059
        return this.getStringAttribute(name, null);
 
1060
    }
 
1061
 
 
1062
 
 
1063
    /**
 
1064
     * Returns an attribute of the element.
 
1065
     * If the attribute doesn't exist, <code>defaultValue</code> is returned.
 
1066
     *
 
1067
     * @param name         The name of the attribute.
 
1068
     * @param defaultValue Key to use if the attribute is missing.
 
1069
     *
 
1070
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1071
     * <ul><li><code>name != null</code>
 
1072
     *     <li><code>name</code> is a valid XML identifier
 
1073
     * </ul></dd></dl><dl>
 
1074
     *
 
1075
     * see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
 
1076
     *         setAttribute(String, Object)
 
1077
     * see nanoxml.XMLElement#removeAttribute(java.lang.String)
 
1078
     *         removeAttribute(String)
 
1079
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
1080
     * see nanoxml.XMLElement#getStringAttribute(java.lang.String)
 
1081
     *         getStringAttribute(String)
 
1082
     * see nanoxml.XMLElement#getStringAttribute(java.lang.String,
 
1083
     *                                            java.util.Hashtable,
 
1084
     *                                            java.lang.String, boolean)
 
1085
     *         getStringAttribute(String, Hashtable, String, boolean)
 
1086
     */
 
1087
    public String getStringAttribute(String name,
 
1088
                                     String defaultValue)
 
1089
    {
 
1090
        return (String) this.getAttribute(name, defaultValue);
 
1091
    }
 
1092
 
 
1093
 
 
1094
    /**
 
1095
     * Returns an attribute by looking up a key in a hashtable.
 
1096
     * If the attribute doesn't exist, the value corresponding to defaultKey
 
1097
     * is returned.
 
1098
     * <P>
 
1099
     * As an example, if valueSet contains the mapping <code>"one" =>
 
1100
     * "1"</code>
 
1101
     * and the element contains the attribute <code>attr="one"</code>, then
 
1102
     * <code>getAttribute("attr", mapping, defaultKey, false)</code> returns
 
1103
     * <code>"1"</code>.
 
1104
     *
 
1105
     * @param name
 
1106
     *     The name of the attribute.
 
1107
     * @param valueSet
 
1108
     *     Hashtable mapping keys to values.
 
1109
     * @param defaultKey
 
1110
     *     Key to use if the attribute is missing.
 
1111
     * @param allowLiterals
 
1112
     *     <code>true</code> if literals are valid.
 
1113
     *
 
1114
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1115
     * <ul><li><code>name != null</code>
 
1116
     *     <li><code>name</code> is a valid XML identifier
 
1117
     *     <li><code>valueSet</code> != null
 
1118
     *     <li>the keys of <code>valueSet</code> are strings
 
1119
     *     <li>the values of <code>valueSet</code> are strings
 
1120
     * </ul></dd></dl><dl>
 
1121
     *
 
1122
     * see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
 
1123
     *         setAttribute(String, Object)
 
1124
     * see nanoxml.XMLElement#removeAttribute(java.lang.String)
 
1125
     *         removeAttribute(String)
 
1126
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
1127
     * see nanoxml.XMLElement#getStringAttribute(java.lang.String)
 
1128
     *         getStringAttribute(String)
 
1129
     * see nanoxml.XMLElement#getStringAttribute(java.lang.String,
 
1130
     *                                            java.lang.String)
 
1131
     *         getStringAttribute(String, String)
 
1132
     */
 
1133
    public String getStringAttribute(String    name,
 
1134
                                     Hashtable valueSet,
 
1135
                                     String    defaultKey,
 
1136
                                     boolean   allowLiterals)
 
1137
    {
 
1138
        return (String) this.getAttribute(name, valueSet, defaultKey,
 
1139
                                          allowLiterals);
 
1140
    }
 
1141
 
 
1142
 
 
1143
    /**
 
1144
     * Returns an attribute of the element.
 
1145
     * If the attribute doesn't exist, <code>0</code> is returned.
 
1146
     *
 
1147
     * @param name The name of the attribute.
 
1148
     *
 
1149
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1150
     * <ul><li><code>name != null</code>
 
1151
     *     <li><code>name</code> is a valid XML identifier
 
1152
     * </ul></dd></dl><dl>
 
1153
     *
 
1154
     * see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
 
1155
     *         setIntAttribute(String, int)
 
1156
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
1157
     * see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
 
1158
     *         getIntAttribute(String, int)
 
1159
     * see nanoxml.XMLElement#getIntAttribute(java.lang.String,
 
1160
     *                                         java.util.Hashtable,
 
1161
     *                                         java.lang.String, boolean)
 
1162
     *         getIntAttribute(String, Hashtable, String, boolean)
 
1163
     */
 
1164
    public int getIntAttribute(String name)
 
1165
    {
 
1166
        return this.getIntAttribute(name, 0);
 
1167
    }
 
1168
 
 
1169
 
 
1170
    /**
 
1171
     * Returns an attribute of the element.
 
1172
     * If the attribute doesn't exist, <code>defaultValue</code> is returned.
 
1173
     *
 
1174
     * @param name         The name of the attribute.
 
1175
     * @param defaultValue Key to use if the attribute is missing.
 
1176
     *
 
1177
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1178
     * <ul><li><code>name != null</code>
 
1179
     *     <li><code>name</code> is a valid XML identifier
 
1180
     * </ul></dd></dl><dl>
 
1181
     *
 
1182
     * see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
 
1183
     *         setIntAttribute(String, int)
 
1184
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
1185
     * see nanoxml.XMLElement#getIntAttribute(java.lang.String)
 
1186
     *         getIntAttribute(String)
 
1187
     * see nanoxml.XMLElement#getIntAttribute(java.lang.String,
 
1188
     *                                         java.util.Hashtable,
 
1189
     *                                         java.lang.String, boolean)
 
1190
     *         getIntAttribute(String, Hashtable, String, boolean)
 
1191
     */
 
1192
    public int getIntAttribute(String name,
 
1193
                               int    defaultValue)
 
1194
    {
 
1195
        if (this.ignoreCase) {
 
1196
            name = name.toUpperCase();
 
1197
        }
 
1198
        String value = (String) this.attributes.get(name);
 
1199
        if (value == null) {
 
1200
            return defaultValue;
 
1201
        } else {
 
1202
            try {
 
1203
                return Integer.parseInt(value);
 
1204
            } catch (NumberFormatException e) {
 
1205
                throw this.invalidValue(name, value);
 
1206
            }
 
1207
        }
 
1208
    }
 
1209
 
 
1210
 
 
1211
    /**
 
1212
     * Returns an attribute by looking up a key in a hashtable.
 
1213
     * If the attribute doesn't exist, the value corresponding to defaultKey
 
1214
     * is returned.
 
1215
     * <P>
 
1216
     * As an example, if valueSet contains the mapping <code>"one" => 1</code>
 
1217
     * and the element contains the attribute <code>attr="one"</code>, then
 
1218
     * <code>getIntAttribute("attr", mapping, defaultKey, false)</code> returns
 
1219
     * <code>1</code>.
 
1220
     *
 
1221
     * @param name
 
1222
     *     The name of the attribute.
 
1223
     * @param valueSet
 
1224
     *     Hashtable mapping keys to values.
 
1225
     * @param defaultKey
 
1226
     *     Key to use if the attribute is missing.
 
1227
     * @param allowLiteralNumbers
 
1228
     *     <code>true</code> if literal numbers are valid.
 
1229
     *
 
1230
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1231
     * <ul><li><code>name != null</code>
 
1232
     *     <li><code>name</code> is a valid XML identifier
 
1233
     *     <li><code>valueSet</code> != null
 
1234
     *     <li>the keys of <code>valueSet</code> are strings
 
1235
     *     <li>the values of <code>valueSet</code> are Integer objects
 
1236
     *     <li><code>defaultKey</code> is either <code>null</code>, a
 
1237
     *         key in <code>valueSet</code> or an integer.
 
1238
     * </ul></dd></dl><dl>
 
1239
     *
 
1240
     * see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
 
1241
     *         setIntAttribute(String, int)
 
1242
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
1243
     * see nanoxml.XMLElement#getIntAttribute(java.lang.String)
 
1244
     *         getIntAttribute(String)
 
1245
     * see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
 
1246
     *         getIntAttribute(String, int)
 
1247
     */
 
1248
    public int getIntAttribute(String    name,
 
1249
                               Hashtable valueSet,
 
1250
                               String    defaultKey,
 
1251
                               boolean   allowLiteralNumbers)
 
1252
    {
 
1253
        if (this.ignoreCase) {
 
1254
            name = name.toUpperCase();
 
1255
        }
 
1256
        Object key = this.attributes.get(name);
 
1257
        Integer result;
 
1258
        if (key == null) {
 
1259
            key = defaultKey;
 
1260
        }
 
1261
        try {
 
1262
            result = (Integer) valueSet.get(key);
 
1263
        } catch (ClassCastException e) {
 
1264
            throw this.invalidValueSet(name);
 
1265
        }
 
1266
        if (result == null) {
 
1267
            if (! allowLiteralNumbers) {
 
1268
                throw this.invalidValue(name, (String) key);
 
1269
            }
 
1270
            try {
 
1271
                result = Integer.valueOf((String) key);
 
1272
            } catch (NumberFormatException e) {
 
1273
                throw this.invalidValue(name, (String) key);
 
1274
            }
 
1275
        }
 
1276
        return result;
 
1277
    }
 
1278
 
 
1279
 
 
1280
    /**
 
1281
     * Returns an attribute of the element.
 
1282
     * If the attribute doesn't exist, <code>0.0</code> is returned.
 
1283
     *
 
1284
     * @param name The name of the attribute.
 
1285
     *
 
1286
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1287
     * <ul><li><code>name != null</code>
 
1288
     *     <li><code>name</code> is a valid XML identifier
 
1289
     * </ul></dd></dl><dl>
 
1290
     *
 
1291
     * see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
 
1292
     *         setDoubleAttribute(String, double)
 
1293
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
1294
     * see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
 
1295
     *         getDoubleAttribute(String, double)
 
1296
     * see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
 
1297
     *                                            java.util.Hashtable,
 
1298
     *                                            java.lang.String, boolean)
 
1299
     *         getDoubleAttribute(String, Hashtable, String, boolean)
 
1300
     */
 
1301
    public double getDoubleAttribute(String name)
 
1302
    {
 
1303
        return this.getDoubleAttribute(name, 0.);
 
1304
    }
 
1305
 
 
1306
 
 
1307
    /**
 
1308
     * Returns an attribute of the element.
 
1309
     * If the attribute doesn't exist, <code>defaultValue</code> is returned.
 
1310
     *
 
1311
     * @param name         The name of the attribute.
 
1312
     * @param defaultValue Key to use if the attribute is missing.
 
1313
     *
 
1314
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1315
     * <ul><li><code>name != null</code>
 
1316
     *     <li><code>name</code> is a valid XML identifier
 
1317
     * </ul></dd></dl><dl>
 
1318
     *
 
1319
     * see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
 
1320
     *         setDoubleAttribute(String, double)
 
1321
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
1322
     * see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
 
1323
     *         getDoubleAttribute(String)
 
1324
     * see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
 
1325
     *                                            java.util.Hashtable,
 
1326
     *                                            java.lang.String, boolean)
 
1327
     *         getDoubleAttribute(String, Hashtable, String, boolean)
 
1328
     */
 
1329
    public double getDoubleAttribute(String name,
 
1330
                                     double defaultValue)
 
1331
    {
 
1332
        if (this.ignoreCase) {
 
1333
            name = name.toUpperCase();
 
1334
        }
 
1335
        String value = (String) this.attributes.get(name);
 
1336
        if (value == null) {
 
1337
            return defaultValue;
 
1338
        } else {
 
1339
            try {
 
1340
                return Double.valueOf(value);
 
1341
            } catch (NumberFormatException e) {
 
1342
                throw this.invalidValue(name, value);
 
1343
            }
 
1344
        }
 
1345
    }
 
1346
 
 
1347
 
 
1348
    /**
 
1349
     * Returns an attribute by looking up a key in a hashtable.
 
1350
     * If the attribute doesn't exist, the value corresponding to defaultKey
 
1351
     * is returned.
 
1352
     * <P>
 
1353
     * As an example, if valueSet contains the mapping <code>"one" =&gt;
 
1354
     * 1.0</code>
 
1355
     * and the element contains the attribute <code>attr="one"</code>, then
 
1356
     * <code>getDoubleAttribute("attr", mapping, defaultKey, false)</code>
 
1357
     * returns <code>1.0</code>.
 
1358
     *
 
1359
     * @param name
 
1360
     *     The name of the attribute.
 
1361
     * @param valueSet
 
1362
     *     Hashtable mapping keys to values.
 
1363
     * @param defaultKey
 
1364
     *     Key to use if the attribute is missing.
 
1365
     * @param allowLiteralNumbers
 
1366
     *     <code>true</code> if literal numbers are valid.
 
1367
     *
 
1368
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1369
     * <ul><li><code>name != null</code>
 
1370
     *     <li><code>name</code> is a valid XML identifier
 
1371
     *     <li><code>valueSet != null</code>
 
1372
     *     <li>the keys of <code>valueSet</code> are strings
 
1373
     *     <li>the values of <code>valueSet</code> are Double objects
 
1374
     *     <li><code>defaultKey</code> is either <code>null</code>, a
 
1375
     *         key in <code>valueSet</code> or a double.
 
1376
     * </ul></dd></dl><dl>
 
1377
     *
 
1378
     * see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
 
1379
     *         setDoubleAttribute(String, double)
 
1380
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
1381
     * see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
 
1382
     *         getDoubleAttribute(String)
 
1383
     * see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
 
1384
     *         getDoubleAttribute(String, double)
 
1385
     */
 
1386
    public double getDoubleAttribute(String    name,
 
1387
                                     Hashtable valueSet,
 
1388
                                     String    defaultKey,
 
1389
                                     boolean   allowLiteralNumbers)
 
1390
    {
 
1391
        if (this.ignoreCase) {
 
1392
            name = name.toUpperCase();
 
1393
        }
 
1394
        Object key = this.attributes.get(name);
 
1395
        Double result;
 
1396
        if (key == null) {
 
1397
            key = defaultKey;
 
1398
        }
 
1399
        try {
 
1400
            result = (Double) valueSet.get(key);
 
1401
        } catch (ClassCastException e) {
 
1402
            throw this.invalidValueSet(name);
 
1403
        }
 
1404
        if (result == null) {
 
1405
            if (! allowLiteralNumbers) {
 
1406
                throw this.invalidValue(name, (String) key);
 
1407
            }
 
1408
            try {
 
1409
                result = Double.valueOf((String) key);
 
1410
            } catch (NumberFormatException e) {
 
1411
                throw this.invalidValue(name, (String) key);
 
1412
            }
 
1413
        }
 
1414
        return result;
 
1415
    }
 
1416
 
 
1417
 
 
1418
    /**
 
1419
     * Returns an attribute of the element.
 
1420
     * If the attribute doesn't exist, <code>defaultValue</code> is returned.
 
1421
     * If the value of the attribute is equal to <code>trueValue</code>,
 
1422
     * <code>true</code> is returned.
 
1423
     * If the value of the attribute is equal to <code>falseValue</code>,
 
1424
     * <code>false</code> is returned.
 
1425
     * If the value doesn't match <code>trueValue</code> or
 
1426
     * <code>falseValue</code>, an exception is thrown.
 
1427
     *
 
1428
     * @param name         The name of the attribute.
 
1429
     * @param trueValue    The value associated with <code>true</code>.
 
1430
     * @param falseValue   The value associated with <code>true</code>.
 
1431
     * @param defaultValue Value to use if the attribute is missing.
 
1432
     *
 
1433
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1434
     * <ul><li><code>name != null</code>
 
1435
     *     <li><code>name</code> is a valid XML identifier
 
1436
     *     <li><code>trueValue</code> and <code>falseValue</code>
 
1437
     *         are different strings.
 
1438
     * </ul></dd></dl><dl>
 
1439
     *
 
1440
     * see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
 
1441
     *         setAttribute(String, Object)
 
1442
     * see nanoxml.XMLElement#removeAttribute(java.lang.String)
 
1443
     *         removeAttribute(String)
 
1444
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
1445
     */
 
1446
    public boolean getBooleanAttribute(String  name,
 
1447
                                       String  trueValue,
 
1448
                                       String  falseValue,
 
1449
                                       boolean defaultValue)
 
1450
    {
 
1451
        if (this.ignoreCase) {
 
1452
            name = name.toUpperCase();
 
1453
        }
 
1454
        Object value = this.attributes.get(name);
 
1455
        if (value == null) {
 
1456
            return defaultValue;
 
1457
        } else if (value.equals(trueValue)) {
 
1458
            return true;
 
1459
        } else if (value.equals(falseValue)) {
 
1460
            return false;
 
1461
        } else {
 
1462
            throw this.invalidValue(name, (String) value);
 
1463
        }
 
1464
    }
 
1465
 
 
1466
 
 
1467
    /**
 
1468
     * Returns an attribute by looking up a key in a hashtable.
 
1469
     *
 
1470
     * @deprecated Use {@link #getIntAttribute(java.lang.String,
 
1471
     *             java.util.Hashtable, java.lang.String, boolean)
 
1472
     *             getIntAttribute} instead.
 
1473
     */
 
1474
    public int getIntProperty(String    name,
 
1475
                              Hashtable valueSet,
 
1476
                              String    defaultKey)
 
1477
    {
 
1478
        return this.getIntAttribute(name, valueSet, defaultKey, false);
 
1479
    }
 
1480
 
 
1481
 
 
1482
    /**
 
1483
     * Returns an attribute.
 
1484
     *
 
1485
     * @deprecated Use {@link #getStringAttribute(java.lang.String)
 
1486
     *             getStringAttribute} instead.
 
1487
     */
 
1488
    public String getProperty(String name)
 
1489
    {
 
1490
        return this.getStringAttribute(name);
 
1491
    }
 
1492
 
 
1493
 
 
1494
    /**
 
1495
     * Returns an attribute.
 
1496
     *
 
1497
     * @deprecated Use {@link #getStringAttribute(java.lang.String,
 
1498
     *             java.lang.String) getStringAttribute} instead.
 
1499
     */
 
1500
    public String getProperty(String name,
 
1501
                              String defaultValue)
 
1502
    {
 
1503
        return this.getStringAttribute(name, defaultValue);
 
1504
    }
 
1505
 
 
1506
 
 
1507
    /**
 
1508
     * Returns an attribute.
 
1509
     *
 
1510
     * @deprecated Use {@link #getIntAttribute(java.lang.String, int)
 
1511
     *             getIntAttribute} instead.
 
1512
     */
 
1513
    public int getProperty(String name,
 
1514
                           int    defaultValue)
 
1515
    {
 
1516
        return this.getIntAttribute(name, defaultValue);
 
1517
    }
 
1518
 
 
1519
 
 
1520
    /**
 
1521
     * Returns an attribute.
 
1522
     *
 
1523
     * @deprecated Use {@link #getDoubleAttribute(java.lang.String, double)
 
1524
     *             getDoubleAttribute} instead.
 
1525
     */
 
1526
    public double getProperty(String name,
 
1527
                              double defaultValue)
 
1528
    {
 
1529
        return this.getDoubleAttribute(name, defaultValue);
 
1530
    }
 
1531
 
 
1532
 
 
1533
    /**
 
1534
     * Returns an attribute.
 
1535
     *
 
1536
     * @deprecated Use {@link #getBooleanAttribute(java.lang.String,
 
1537
     *             java.lang.String, java.lang.String, boolean)
 
1538
     *             getBooleanAttribute} instead.
 
1539
     */
 
1540
    public boolean getProperty(String  key,
 
1541
                               String  trueValue,
 
1542
                               String  falseValue,
 
1543
                               boolean defaultValue)
 
1544
    {
 
1545
        return this.getBooleanAttribute(key, trueValue, falseValue,
 
1546
                                        defaultValue);
 
1547
    }
 
1548
 
 
1549
 
 
1550
    /**
 
1551
     * Returns an attribute by looking up a key in a hashtable.
 
1552
     *
 
1553
     * @deprecated Use {@link #getAttribute(java.lang.String,
 
1554
     *             java.util.Hashtable, java.lang.String, boolean)
 
1555
     *             getAttribute} instead.
 
1556
     */
 
1557
    public Object getProperty(String    name,
 
1558
                              Hashtable valueSet,
 
1559
                              String    defaultKey)
 
1560
    {
 
1561
        return this.getAttribute(name, valueSet, defaultKey, false);
 
1562
    }
 
1563
 
 
1564
 
 
1565
    /**
 
1566
     * Returns an attribute by looking up a key in a hashtable.
 
1567
     *
 
1568
     * @deprecated Use {@link #getStringAttribute(java.lang.String,
 
1569
     *             java.util.Hashtable, java.lang.String, boolean)
 
1570
     *             getStringAttribute} instead.
 
1571
     */
 
1572
    public String getStringProperty(String    name,
 
1573
                                    Hashtable valueSet,
 
1574
                                    String    defaultKey)
 
1575
    {
 
1576
        return this.getStringAttribute(name, valueSet, defaultKey, false);
 
1577
    }
 
1578
 
 
1579
 
 
1580
    /**
 
1581
     * Returns an attribute by looking up a key in a hashtable.
 
1582
     *
 
1583
     * @deprecated Use {@link #getIntAttribute(java.lang.String,
 
1584
     *             java.util.Hashtable, java.lang.String, boolean)
 
1585
     *             getIntAttribute} instead.
 
1586
     */
 
1587
    public int getSpecialIntProperty(String    name,
 
1588
                                     Hashtable valueSet,
 
1589
                                     String    defaultKey)
 
1590
    {
 
1591
        return this.getIntAttribute(name, valueSet, defaultKey, true);
 
1592
    }
 
1593
 
 
1594
 
 
1595
    /**
 
1596
     * Returns an attribute by looking up a key in a hashtable.
 
1597
     *
 
1598
     * @deprecated Use {@link #getDoubleAttribute(java.lang.String,
 
1599
     *             java.util.Hashtable, java.lang.String, boolean)
 
1600
     *             getDoubleAttribute} instead.
 
1601
     */
 
1602
    public double getSpecialDoubleProperty(String    name,
 
1603
                                           Hashtable valueSet,
 
1604
                                           String    defaultKey)
 
1605
    {
 
1606
        return this.getDoubleAttribute(name, valueSet, defaultKey, true);
 
1607
    }
 
1608
 
 
1609
 
 
1610
    /**
 
1611
     * Returns the name of the element.
 
1612
     *
 
1613
     * see nanoxml.XMLElement#setName(java.lang.String) setName(String)
 
1614
     */
 
1615
    public String getName()
 
1616
    {
 
1617
        return this.name;
 
1618
    }
 
1619
 
 
1620
 
 
1621
    /**
 
1622
     * Returns the name of the element.
 
1623
     *
 
1624
     * @deprecated Use {@link #getName() getName} instead.
 
1625
     */
 
1626
    public String getTagName()
 
1627
    {
 
1628
        return this.getName();
 
1629
    }
 
1630
 
 
1631
 
 
1632
    /**
 
1633
     * Reads one XML element from a java.io.Reader and parses it.
 
1634
     *
 
1635
     * @param reader
 
1636
     *     The reader from which to retrieve the XML data.
 
1637
     *
 
1638
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1639
     * <ul><li><code>reader != null</code>
 
1640
     *     <li><code>reader</code> is not closed
 
1641
     * </ul></dd></dl>
 
1642
     *
 
1643
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
1644
     * <ul><li>the state of the receiver is updated to reflect the XML element
 
1645
     *         parsed from the reader
 
1646
     *     <li>the reader points to the first character following the last
 
1647
     *         '&gt;' character of the XML element
 
1648
     * </ul></dd></dl><dl>
 
1649
     *
 
1650
     * @throws java.io.IOException
 
1651
     *     If an error occured while reading the input.
 
1652
     * throws nanoxml.XMLParseException
 
1653
     *     If an error occured while parsing the read data.
 
1654
     */
 
1655
    public void parseFromReader(Reader reader)
 
1656
    throws IOException, XMLParseException
 
1657
    {
 
1658
        this.parseFromReader(reader, /*startingLineNr*/ 1);
 
1659
    }
 
1660
 
 
1661
 
 
1662
    /**
 
1663
     * Reads one XML element from a java.io.Reader and parses it.
 
1664
     *
 
1665
     * @param reader
 
1666
     *     The reader from which to retrieve the XML data.
 
1667
     * @param startingLineNr
 
1668
     *     The line number of the first line in the data.
 
1669
     *
 
1670
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1671
     * <ul><li><code>reader != null</code>
 
1672
     *     <li><code>reader</code> is not closed
 
1673
     * </ul></dd></dl>
 
1674
     *
 
1675
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
1676
     * <ul><li>the state of the receiver is updated to reflect the XML element
 
1677
     *         parsed from the reader
 
1678
     *     <li>the reader points to the first character following the last
 
1679
     *         '&gt;' character of the XML element
 
1680
     * </ul></dd></dl><dl>
 
1681
     *
 
1682
     * @throws java.io.IOException
 
1683
     *     If an error occured while reading the input.
 
1684
     * throws nanoxml.XMLParseException
 
1685
     *     If an error occured while parsing the read data.
 
1686
     */
 
1687
    public void parseFromReader(Reader reader,
 
1688
                                int    startingLineNr)
 
1689
        throws IOException, XMLParseException
 
1690
    {
 
1691
        this.name = null;
 
1692
        this.contents = "";
 
1693
        this.attributes = new Hashtable();
 
1694
        this.children = new Vector();
 
1695
        this.charReadTooMuch = '\0';
 
1696
        this.reader = reader;
 
1697
        this.parserLineNr = startingLineNr;
 
1698
 
 
1699
        for (;;) {
 
1700
            char ch = this.scanWhitespace();
 
1701
 
 
1702
            if (ch != '<') {
 
1703
                throw this.expectedInput("<");
 
1704
            }
 
1705
 
 
1706
            ch = this.readChar();
 
1707
 
 
1708
            if ((ch == '!') || (ch == '?')) {
 
1709
                this.skipSpecialTag(0);
 
1710
            } else {
 
1711
                this.unreadChar(ch);
 
1712
                this.scanElement(this);
 
1713
                return;
 
1714
            }
 
1715
        }
 
1716
    }
 
1717
 
 
1718
 
 
1719
    /**
 
1720
     * Reads one XML element from a String and parses it.
 
1721
     *
 
1722
     * @param string
 
1723
     *     The of the XML data.
 
1724
     *
 
1725
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1726
     * <ul><li><code>string != null</code>
 
1727
     *     <li><code>string.length() &gt; 0</code>
 
1728
     * </ul></dd></dl>
 
1729
     *
 
1730
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
1731
     * <ul><li>the state of the receiver is updated to reflect the XML element
 
1732
     *         parsed from the reader
 
1733
     * </ul></dd></dl><dl>
 
1734
     *
 
1735
     * throws nanoxml.XMLParseException
 
1736
     *     If an error occured while parsing the string.
 
1737
     */
 
1738
    public void parseString(String string)
 
1739
        throws XMLParseException
 
1740
    {
 
1741
        try {
 
1742
            this.parseFromReader(new StringReader(string),
 
1743
                                 /*startingLineNr*/ 1);
 
1744
        } catch (IOException e) {
 
1745
            // Java exception handling suxx
 
1746
        }
 
1747
    }
 
1748
 
 
1749
 
 
1750
    /**
 
1751
     * Reads one XML element from a String and parses it.
 
1752
     *
 
1753
     * @param string
 
1754
     *     The string ofthe XML data.
 
1755
     * @param offset
 
1756
     *     The first character in <code>string</code> to scan.
 
1757
     *
 
1758
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1759
     * <ul><li><code>string != null</code>
 
1760
     *     <li><code>offset &lt; string.length()</code>
 
1761
     *     <li><code>offset &gt;= 0</code>
 
1762
     * </ul></dd></dl>
 
1763
     *
 
1764
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
1765
     * <ul><li>the state of the receiver is updated to reflect the XML element
 
1766
     *         parsed from the reader
 
1767
     * </ul></dd></dl><dl>
 
1768
     *
 
1769
     * throws nanoxml.XMLParseException
 
1770
     *     If an error occured while parsing the string.
 
1771
     */
 
1772
    public void parseString(String string,
 
1773
                            int    offset)
 
1774
        throws XMLParseException
 
1775
    {
 
1776
        this.parseString(string.substring(offset));
 
1777
    }
 
1778
 
 
1779
 
 
1780
    /**
 
1781
     * Reads one XML element from a String and parses it.
 
1782
     *
 
1783
     * @param string
 
1784
     *     The string of the the XML data.
 
1785
     * @param offset
 
1786
     *     The first character in <code>string</code> to scan.
 
1787
     * @param end
 
1788
     *     The character where to stop scanning.
 
1789
     *     This character is not scanned.
 
1790
     *
 
1791
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1792
     * <ul><li><code>string != null</code>
 
1793
     *     <li><code>end &lt;= string.length()</code>
 
1794
     *     <li><code>offset &lt; end</code>
 
1795
     *     <li><code>offset &gt;= 0</code>
 
1796
     * </ul></dd></dl>
 
1797
     *
 
1798
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
1799
     * <ul><li>the state of the receiver is updated to reflect the XML element
 
1800
     *         parsed from the reader
 
1801
     * </ul></dd></dl><dl>
 
1802
     *
 
1803
     * throws nanoxml.XMLParseException
 
1804
     *     If an error occured while parsing the string.
 
1805
     */
 
1806
    public void parseString(String string,
 
1807
                            int    offset,
 
1808
                            int    end)
 
1809
        throws XMLParseException
 
1810
    {
 
1811
        this.parseString(string.substring(offset, end));
 
1812
    }
 
1813
 
 
1814
 
 
1815
    /**
 
1816
     * Reads one XML element from a String and parses it.
 
1817
     *
 
1818
     * @param string
 
1819
     *     The string of the the XML data.
 
1820
     * @param offset
 
1821
     *     The first character in <code>string</code> to scan.
 
1822
     * @param end
 
1823
     *     The character where to stop scanning.
 
1824
     *     This character is not scanned.
 
1825
     * @param startingLineNr
 
1826
     *     The line number of the first line in the data.
 
1827
     *
 
1828
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1829
     * <ul><li><code>string != null</code>
 
1830
     *     <li><code>end &lt;= string.length()</code>
 
1831
     *     <li><code>offset &lt; end</code>
 
1832
     *     <li><code>offset &gt;= 0</code>
 
1833
     * </ul></dd></dl>
 
1834
     *
 
1835
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
1836
     * <ul><li>the state of the receiver is updated to reflect the XML element
 
1837
     *         parsed from the reader
 
1838
     * </ul></dd></dl><dl>
 
1839
     *
 
1840
     * throws nanoxml.XMLParseException
 
1841
     *     If an error occured while parsing the string.
 
1842
     */
 
1843
    public void parseString(String string,
 
1844
                            int    offset,
 
1845
                            int    end,
 
1846
                            int    startingLineNr)
 
1847
        throws XMLParseException
 
1848
    {
 
1849
        string = string.substring(offset, end);
 
1850
        try {
 
1851
            this.parseFromReader(new StringReader(string), startingLineNr);
 
1852
        } catch (IOException e) {
 
1853
            // Java exception handling suxx
 
1854
        }
 
1855
    }
 
1856
 
 
1857
 
 
1858
    /**
 
1859
     * Reads one XML element from a char array and parses it.
 
1860
     *
 
1861
     * @param input
 
1862
     *     The string of the the XML data.
 
1863
     * @param offset
 
1864
     *     The first character in <code>string</code> to scan.
 
1865
     * @param end
 
1866
     *     The character where to stop scanning.
 
1867
     *     This character is not scanned.
 
1868
     *
 
1869
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1870
     * <ul><li><code>input != null</code>
 
1871
     *     <li><code>end &lt;= input.length</code>
 
1872
     *     <li><code>offset &lt; end</code>
 
1873
     *     <li><code>offset &gt;= 0</code>
 
1874
     * </ul></dd></dl>
 
1875
     *
 
1876
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
1877
     * <ul><li>the state of the receiver is updated to reflect the XML element
 
1878
     *         parsed from the reader
 
1879
     * </ul></dd></dl><dl>
 
1880
     *
 
1881
     * throws nanoxml.XMLParseException
 
1882
     *     If an error occured while parsing the string.
 
1883
     */
 
1884
    public void parseCharArray(char[] input,
 
1885
                               int    offset,
 
1886
                               int    end)
 
1887
        throws XMLParseException
 
1888
    {
 
1889
        this.parseCharArray(input, offset, end, /*startingLineNr*/ 1);
 
1890
    }
 
1891
 
 
1892
 
 
1893
    /**
 
1894
     * Reads one XML element from a char array and parses it.
 
1895
     *
 
1896
     * @param input
 
1897
     *     The string of the the XML data.
 
1898
     * @param offset
 
1899
     *     The first character in <code>string</code> to scan.
 
1900
     * @param end
 
1901
     *     The character where to stop scanning.
 
1902
     *     This character is not scanned.
 
1903
     * @param startingLineNr
 
1904
     *     The line number of the first line in the data.
 
1905
     *
 
1906
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1907
     * <ul><li><code>input != null</code>
 
1908
     *     <li><code>end &lt;= input.length</code>
 
1909
     *     <li><code>offset &lt; end</code>
 
1910
     *     <li><code>offset &gt;= 0</code>
 
1911
     * </ul></dd></dl>
 
1912
     *
 
1913
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
1914
     * <ul><li>the state of the receiver is updated to reflect the XML element
 
1915
     *         parsed from the reader
 
1916
     * </ul></dd></dl><dl>
 
1917
     *
 
1918
     * throws nanoxml.XMLParseException
 
1919
     *     If an error occured while parsing the string.
 
1920
     */
 
1921
    public void parseCharArray(char[] input,
 
1922
                               int    offset,
 
1923
                               int    end,
 
1924
                               int    startingLineNr)
 
1925
        throws XMLParseException
 
1926
    {
 
1927
        try {
 
1928
            Reader reader = new CharArrayReader(input, offset, end);
 
1929
            this.parseFromReader(reader, startingLineNr);
 
1930
        } catch (IOException e) {
 
1931
            // This exception will never happen.
 
1932
        }
 
1933
    }
 
1934
 
 
1935
 
 
1936
    /**
 
1937
     * Removes a child element.
 
1938
     *
 
1939
     * @param child
 
1940
     *     The child element to remove.
 
1941
     *
 
1942
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1943
     * <ul><li><code>child != null</code>
 
1944
     *     <li><code>child</code> is a child element of the receiver
 
1945
     * </ul></dd></dl>
 
1946
     *
 
1947
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
1948
     * <ul><li>countChildren() => old.countChildren() - 1
 
1949
     *     <li>enumerateChildren() => old.enumerateChildren() - child
 
1950
     *     <li>getChildren() => old.enumerateChildren() - child
 
1951
     * </ul></dd></dl><dl>
 
1952
     *
 
1953
     * see nanoxml.XMLElement#addChild(nanoxml.XMLElement)
 
1954
     *         addChild(XMLElement)
 
1955
     * see nanoxml.XMLElement#countChildren()
 
1956
     * see nanoxml.XMLElement#enumerateChildren()
 
1957
     * see nanoxml.XMLElement#getChildren()
 
1958
     */
 
1959
    public void removeChild(XMLElement child)
 
1960
    {
 
1961
        this.children.removeElement(child);
 
1962
    }
 
1963
 
 
1964
 
 
1965
    /**
 
1966
     * Removes an attribute.
 
1967
     *
 
1968
     * @param name
 
1969
     *     The name of the attribute.
 
1970
     *
 
1971
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
1972
     * <ul><li><code>name != null</code>
 
1973
     *     <li><code>name</code> is a valid XML identifier
 
1974
     * </ul></dd></dl>
 
1975
     *
 
1976
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
1977
     * <ul><li>enumerateAttributeNames()
 
1978
     *         => old.enumerateAttributeNames() - name
 
1979
     *     <li>getAttribute(name) => <code>null</code>
 
1980
     * </ul></dd></dl><dl>
 
1981
     *
 
1982
     * see nanoxml.XMLElement#enumerateAttributeNames()
 
1983
     * see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
 
1984
     *         setDoubleAttribute(String, double)
 
1985
     * see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
 
1986
     *         setIntAttribute(String, int)
 
1987
     * see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
 
1988
     *         setAttribute(String, Object)
 
1989
     * see nanoxml.XMLElement#getAttribute(java.lang.String)
 
1990
     *         getAttribute(String)
 
1991
     * see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
 
1992
     *         getAttribute(String, Object)
 
1993
     * see nanoxml.XMLElement#getAttribute(java.lang.String,
 
1994
     *                                      java.util.Hashtable,
 
1995
     *                                      java.lang.String, boolean)
 
1996
     *         getAttribute(String, Hashtable, String, boolean)
 
1997
     * see nanoxml.XMLElement#getStringAttribute(java.lang.String)
 
1998
     *         getStringAttribute(String)
 
1999
     * see nanoxml.XMLElement#getStringAttribute(java.lang.String,
 
2000
     *                                            java.lang.String)
 
2001
     *         getStringAttribute(String, String)
 
2002
     * see nanoxml.XMLElement#getStringAttribute(java.lang.String,
 
2003
     *                                            java.util.Hashtable,
 
2004
     *                                            java.lang.String, boolean)
 
2005
     *         getStringAttribute(String, Hashtable, String, boolean)
 
2006
     * see nanoxml.XMLElement#getIntAttribute(java.lang.String)
 
2007
     *         getIntAttribute(String)
 
2008
     * see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
 
2009
     *         getIntAttribute(String, int)
 
2010
     * see nanoxml.XMLElement#getIntAttribute(java.lang.String,
 
2011
     *                                         java.util.Hashtable,
 
2012
     *                                         java.lang.String, boolean)
 
2013
     *         getIntAttribute(String, Hashtable, String, boolean)
 
2014
     * see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
 
2015
     *         getDoubleAttribute(String)
 
2016
     * see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
 
2017
     *         getDoubleAttribute(String, double)
 
2018
     * see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
 
2019
     *                                            java.util.Hashtable,
 
2020
     *                                            java.lang.String, boolean)
 
2021
     *         getDoubleAttribute(String, Hashtable, String, boolean)
 
2022
     * see nanoxml.XMLElement#getBooleanAttribute(java.lang.String,
 
2023
     *                                             java.lang.String,
 
2024
     *                                             java.lang.String, boolean)
 
2025
     *         getBooleanAttribute(String, String, String, boolean)
 
2026
     */
 
2027
    public void removeAttribute(String name)
 
2028
    {
 
2029
        if (this.ignoreCase) {
 
2030
            name = name.toUpperCase();
 
2031
        }
 
2032
        this.attributes.remove(name);
 
2033
    }
 
2034
 
 
2035
 
 
2036
    /**
 
2037
     * Removes an attribute.
 
2038
     *
 
2039
     * @param name
 
2040
     *     The name of the attribute.
 
2041
     *
 
2042
     * @deprecated Use {@link #removeAttribute(java.lang.String)
 
2043
     *             removeAttribute} instead.
 
2044
     */
 
2045
    public void removeProperty(String name)
 
2046
    {
 
2047
        this.removeAttribute(name);
 
2048
    }
 
2049
 
 
2050
 
 
2051
    /**
 
2052
     * Removes an attribute.
 
2053
     *
 
2054
     * @param name
 
2055
     *     The name of the attribute.
 
2056
     *
 
2057
     * @deprecated Use {@link #removeAttribute(java.lang.String)
 
2058
     *             removeAttribute} instead.
 
2059
     */
 
2060
    public void removeChild(String name)
 
2061
    {
 
2062
        this.removeAttribute(name);
 
2063
    }
 
2064
 
 
2065
 
 
2066
    /**
 
2067
     * Creates a new similar XML element.
 
2068
     * <P>
 
2069
     * You should override this method when subclassing XMLElement.
 
2070
     */
 
2071
    protected XMLElement createAnotherElement()
 
2072
    {
 
2073
        return new XMLElement(this.entities,
 
2074
                              this.ignoreWhitespace,
 
2075
                              false,
 
2076
                              this.ignoreCase);
 
2077
    }
 
2078
 
 
2079
 
 
2080
    /**
 
2081
     * Changes the content string.
 
2082
     *
 
2083
     * @param content
 
2084
     *     The new content string.
 
2085
     */
 
2086
    public void setContent(String content)
 
2087
    {
 
2088
        this.contents = content;
 
2089
    }
 
2090
 
 
2091
 
 
2092
    /**
 
2093
     * Changes the name of the element.
 
2094
     *
 
2095
     * @param name
 
2096
     *     The new name.
 
2097
     *
 
2098
     * @deprecated Use {@link #setName(java.lang.String) setName} instead.
 
2099
     */
 
2100
    public void setTagName(String name)
 
2101
    {
 
2102
        this.setName(name);
 
2103
    }
 
2104
 
 
2105
 
 
2106
    /**
 
2107
     * Changes the name of the element.
 
2108
     *
 
2109
     * @param name
 
2110
     *     The new name.
 
2111
     *
 
2112
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2113
     * <ul><li><code>name != null</code>
 
2114
     *     <li><code>name</code> is a valid XML identifier
 
2115
     * </ul></dd></dl>
 
2116
     *
 
2117
     * see nanoxml.XMLElement#getName()
 
2118
     */
 
2119
    public void setName(String name)
 
2120
    {
 
2121
        this.name = name;
 
2122
    }
 
2123
 
 
2124
 
 
2125
    /**
 
2126
     * Writes the XML element to a string.
 
2127
     *
 
2128
     * see nanoxml.XMLElement#write(java.io.Writer) write(Writer)
 
2129
     */
 
2130
    public String toString()
 
2131
    {
 
2132
        try {
 
2133
            ByteArrayOutputStream out = new ByteArrayOutputStream();
 
2134
            OutputStreamWriter writer = new OutputStreamWriter(out);
 
2135
            this.write(writer);
 
2136
            writer.flush();
 
2137
            return new String(out.toByteArray());
 
2138
        } catch (IOException e) {
 
2139
            // Java exception handling suxx
 
2140
            return super.toString();
 
2141
        }
 
2142
    }
 
2143
 
 
2144
 
 
2145
    /**
 
2146
     * Writes the XML element to a writer.
 
2147
     *
 
2148
     * @param writer
 
2149
     *     The writer to write the XML data to.
 
2150
     *
 
2151
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2152
     * <ul><li><code>writer != null</code>
 
2153
     *     <li><code>writer</code> is not closed
 
2154
     * </ul></dd></dl>
 
2155
     *
 
2156
     * @throws java.io.IOException
 
2157
     *      If the data could not be written to the writer.
 
2158
     *
 
2159
     * see nanoxml.XMLElement#toString()
 
2160
     */
 
2161
    public void write(Writer writer)
 
2162
        throws IOException
 
2163
    {
 
2164
        if (this.name == null) {
 
2165
            this.writeEncoded(writer, this.contents);
 
2166
            return;
 
2167
        }
 
2168
        writer.write('<');
 
2169
        writer.write(this.name);
 
2170
        if (! this.attributes.isEmpty()) {
 
2171
            Enumeration enumeration = this.attributes.keys();
 
2172
            while (enumeration.hasMoreElements()) {
 
2173
                writer.write(' ');
 
2174
                String key = (String) enumeration.nextElement();
 
2175
                String value = (String) this.attributes.get(key);
 
2176
                writer.write(key);
 
2177
                writer.write('='); writer.write('"');
 
2178
                this.writeEncoded(writer, value);
 
2179
                writer.write('"');
 
2180
            }
 
2181
        }
 
2182
        if ((this.contents != null) && (this.contents.length() > 0)) {
 
2183
            writer.write('>');
 
2184
            this.writeEncoded(writer, this.contents);
 
2185
            writer.write('<'); writer.write('/');
 
2186
            writer.write(this.name);
 
2187
            writer.write('>');
 
2188
        } else if (this.children.isEmpty()) {
 
2189
            writer.write('/'); writer.write('>');
 
2190
        } else {
 
2191
            writer.write('>');
 
2192
            Enumeration enumeration = this.enumerateChildren();
 
2193
            while (enumeration.hasMoreElements()) {
 
2194
                XMLElement child = (XMLElement) enumeration.nextElement();
 
2195
                child.write(writer);
 
2196
            }
 
2197
            writer.write('<'); writer.write('/');
 
2198
            writer.write(this.name);
 
2199
            writer.write('>');
 
2200
        }
 
2201
    }
 
2202
 
 
2203
 
 
2204
    /**
 
2205
     * Writes a string encoded to a writer.
 
2206
     *
 
2207
     * @param writer
 
2208
     *     The writer to write the XML data to.
 
2209
     * @param str
 
2210
     *     The string to write encoded.
 
2211
     *
 
2212
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2213
     * <ul><li><code>writer != null</code>
 
2214
     *     <li><code>writer</code> is not closed
 
2215
     *     <li><code>str != null</code>
 
2216
     * </ul></dd></dl>
 
2217
     */
 
2218
    protected void writeEncoded(Writer writer,
 
2219
                                String str)
 
2220
        throws IOException
 
2221
    {
 
2222
        for (int i = 0; i < str.length(); i += 1) {
 
2223
            char ch = str.charAt(i);
 
2224
            switch (ch) {
 
2225
                case '<':
 
2226
                    writer.write('&'); writer.write('l'); writer.write('t');
 
2227
                    writer.write(';');
 
2228
                    break;
 
2229
                case '>':
 
2230
                    writer.write('&'); writer.write('g'); writer.write('t');
 
2231
                    writer.write(';');
 
2232
                    break;
 
2233
                case '&':
 
2234
                    writer.write('&'); writer.write('a'); writer.write('m');
 
2235
                    writer.write('p'); writer.write(';');
 
2236
                    break;
 
2237
                case '"':
 
2238
                    writer.write('&'); writer.write('q'); writer.write('u');
 
2239
                    writer.write('o'); writer.write('t'); writer.write(';');
 
2240
                    break;
 
2241
                case '\'':
 
2242
                    writer.write('&'); writer.write('a'); writer.write('p');
 
2243
                    writer.write('o'); writer.write('s'); writer.write(';');
 
2244
                    break;
 
2245
                default:
 
2246
                    int unicode = (int) ch;
 
2247
                    if ((unicode < 32) || (unicode > 126)) {
 
2248
                        writer.write('&'); writer.write('#');
 
2249
                        writer.write('x');
 
2250
                        writer.write(Integer.toString(unicode, 16));
 
2251
                        writer.write(';');
 
2252
                    } else {
 
2253
                        writer.write(ch);
 
2254
                    }
 
2255
            }
 
2256
        }
 
2257
    }
 
2258
 
 
2259
 
 
2260
    /**
 
2261
     * Scans an identifier from the current reader.
 
2262
     * The scanned identifier is appended to <code>result</code>.
 
2263
     *
 
2264
     * @param result
 
2265
     *     The buffer in which the scanned identifier will be put.
 
2266
     *
 
2267
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2268
     * <ul><li><code>result != null</code>
 
2269
     *     <li>The next character read from the reader is a valid first
 
2270
     *         character of an XML identifier.
 
2271
     * </ul></dd></dl>
 
2272
     *
 
2273
     * <dl><dt><b>Postconditions:</b></dt><dd>
 
2274
     * <ul><li>The next character read from the reader won't be an identifier
 
2275
     *         character.
 
2276
     * </ul></dd></dl><dl>
 
2277
     */
 
2278
    protected void scanIdentifier(StringBuffer result)
 
2279
        throws IOException
 
2280
    {
 
2281
        for (;;) {
 
2282
            char ch = this.readChar();
 
2283
            if (((ch < 'A') || (ch > 'Z')) && ((ch < 'a') || (ch > 'z'))
 
2284
                && ((ch < '0') || (ch > '9')) && (ch != '_') && (ch != '.')
 
2285
                && (ch != ':') && (ch != '-') && (ch <= '\u007E')) {
 
2286
                this.unreadChar(ch);
 
2287
                return;
 
2288
            }
 
2289
            result.append(ch);
 
2290
        }
 
2291
    }
 
2292
 
 
2293
 
 
2294
    /**
 
2295
     * This method scans an identifier from the current reader.
 
2296
     *
 
2297
     * @return the next character following the whitespace.
 
2298
     */
 
2299
    protected char scanWhitespace()
 
2300
        throws IOException
 
2301
    {
 
2302
        for (;;) {
 
2303
            char ch = this.readChar();
 
2304
            switch (ch) {
 
2305
                case ' ':
 
2306
                case '\t':
 
2307
                case '\n':
 
2308
                case '\r':
 
2309
                    break;
 
2310
                default:
 
2311
                    return ch;
 
2312
            }
 
2313
        }
 
2314
    }
 
2315
 
 
2316
 
 
2317
    /**
 
2318
     * This method scans an identifier from the current reader.
 
2319
     * The scanned whitespace is appended to <code>result</code>.
 
2320
     *
 
2321
     * @return the next character following the whitespace.
 
2322
     *
 
2323
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2324
     * <ul><li><code>result != null</code>
 
2325
     * </ul></dd></dl>
 
2326
     */
 
2327
    protected char scanWhitespace(StringBuffer result)
 
2328
        throws IOException
 
2329
    {
 
2330
        for (;;) {
 
2331
            char ch = this.readChar();
 
2332
            switch (ch) {
 
2333
                case ' ':
 
2334
                case '\t':
 
2335
                case '\n':
 
2336
                    result.append(ch);
 
2337
                case '\r':
 
2338
                    break;
 
2339
                default:
 
2340
                    return ch;
 
2341
            }
 
2342
        }
 
2343
    }
 
2344
 
 
2345
 
 
2346
    /**
 
2347
     * This method scans a delimited string from the current reader.
 
2348
     * The scanned string without delimiters is appended to
 
2349
     * <code>string</code>.
 
2350
     *
 
2351
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2352
     * <ul><li><code>string != null</code>
 
2353
     *     <li>the next char read is the string delimiter
 
2354
     * </ul></dd></dl>
 
2355
     */
 
2356
    protected void scanString(StringBuffer string)
 
2357
        throws IOException
 
2358
    {
 
2359
        char delimiter = this.readChar();
 
2360
        if ((delimiter != '\'') && (delimiter != '"')) {
 
2361
            throw this.expectedInput("' or \"");
 
2362
        }
 
2363
        for (;;) {
 
2364
            char ch = this.readChar();
 
2365
            if (ch == delimiter) {
 
2366
                return;
 
2367
            } else if (ch == '&') {
 
2368
                this.resolveEntity(string);
 
2369
            } else {
 
2370
                string.append(ch);
 
2371
            }
 
2372
        }
 
2373
    }
 
2374
 
 
2375
 
 
2376
    /**
 
2377
     * Scans a #PCDATA element. CDATA sections and entities are resolved.
 
2378
     * The next &lt; char is skipped.
 
2379
     * The scanned data is appended to <code>data</code>.
 
2380
     *
 
2381
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2382
     * <ul><li><code>data != null</code>
 
2383
     * </ul></dd></dl>
 
2384
     */
 
2385
    protected void scanPCData(StringBuffer data)
 
2386
        throws IOException
 
2387
    {
 
2388
        for (;;) {
 
2389
            char ch = this.readChar();
 
2390
            if (ch == '<') {
 
2391
                ch = this.readChar();
 
2392
                if (ch == '!') {
 
2393
                    this.checkCDATA(data);
 
2394
                } else {
 
2395
                    this.unreadChar(ch);
 
2396
                    return;
 
2397
                }
 
2398
            } else if (ch == '&') {
 
2399
                this.resolveEntity(data);
 
2400
            } else {
 
2401
                data.append(ch);
 
2402
            }
 
2403
        }
 
2404
    }
 
2405
 
 
2406
 
 
2407
    /**
 
2408
     * Scans a special tag and if the tag is a CDATA section, append its
 
2409
     * content to <code>buf</code>.
 
2410
     *
 
2411
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2412
     * <ul><li><code>buf != null</code>
 
2413
     *     <li>The first &lt; has already been read.
 
2414
     * </ul></dd></dl>
 
2415
     */
 
2416
    protected boolean checkCDATA(StringBuffer buf)
 
2417
        throws IOException
 
2418
    {
 
2419
        char ch = this.readChar();
 
2420
        if (ch != '[') {
 
2421
            this.unreadChar(ch);
 
2422
            this.skipSpecialTag(0);
 
2423
            return false;
 
2424
        } else if (! this.checkLiteral("CDATA[")) {
 
2425
            this.skipSpecialTag(1); // one [ has already been read
 
2426
            return false;
 
2427
        } else {
 
2428
            int delimiterCharsSkipped = 0;
 
2429
            while (delimiterCharsSkipped < 3) {
 
2430
                ch = this.readChar();
 
2431
                switch (ch) {
 
2432
                    case ']':
 
2433
                        if (delimiterCharsSkipped < 2) {
 
2434
                            delimiterCharsSkipped += 1;
 
2435
                        } else {
 
2436
                            buf.append(']');
 
2437
                            buf.append(']');
 
2438
                            delimiterCharsSkipped = 0;
 
2439
                        }
 
2440
                        break;
 
2441
                    case '>':
 
2442
                        if (delimiterCharsSkipped < 2) {
 
2443
                            for (int i = 0; i < delimiterCharsSkipped; i++) {
 
2444
                                buf.append(']');
 
2445
                            }
 
2446
                            delimiterCharsSkipped = 0;
 
2447
                            buf.append('>');
 
2448
                        } else {
 
2449
                            delimiterCharsSkipped = 3;
 
2450
                        }
 
2451
                        break;
 
2452
                    default:
 
2453
                        for (int i = 0; i < delimiterCharsSkipped; i += 1) {
 
2454
                            buf.append(']');
 
2455
                        }
 
2456
                        buf.append(ch);
 
2457
                        delimiterCharsSkipped = 0;
 
2458
                }
 
2459
            }
 
2460
            return true;
 
2461
        }
 
2462
    }
 
2463
 
 
2464
 
 
2465
    /**
 
2466
     * Skips a comment.
 
2467
     *
 
2468
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2469
     * <ul><li>The first &lt;!-- has already been read.
 
2470
     * </ul></dd></dl>
 
2471
     */
 
2472
    protected void skipComment()
 
2473
        throws IOException
 
2474
    {
 
2475
        int dashesToRead = 2;
 
2476
        while (dashesToRead > 0) {
 
2477
            char ch = this.readChar();
 
2478
            if (ch == '-') {
 
2479
                dashesToRead -= 1;
 
2480
            } else {
 
2481
                dashesToRead = 2;
 
2482
            }
 
2483
        }
 
2484
        if (this.readChar() != '>') {
 
2485
            throw this.expectedInput(">");
 
2486
        }
 
2487
    }
 
2488
 
 
2489
 
 
2490
    /**
 
2491
     * Skips a special tag or comment.
 
2492
     *
 
2493
     * @param bracketLevel The number of open square brackets ([) that have
 
2494
     *                     already been read.
 
2495
     *
 
2496
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2497
     * <ul><li>The first &lt;! has already been read.
 
2498
     *     <li><code>bracketLevel >= 0</code>
 
2499
     * </ul></dd></dl>
 
2500
     */
 
2501
    protected void skipSpecialTag(int bracketLevel)
 
2502
        throws IOException
 
2503
    {
 
2504
        int tagLevel = 1; // <
 
2505
        char stringDelimiter = '\0';
 
2506
        if (bracketLevel == 0) {
 
2507
            char ch = this.readChar();
 
2508
            if (ch == '[') {
 
2509
                bracketLevel += 1;
 
2510
            } else if (ch == '-') {
 
2511
                ch = this.readChar();
 
2512
                if (ch == '[') {
 
2513
                    bracketLevel += 1;
 
2514
                } else if (ch == ']') {
 
2515
                    bracketLevel -= 1;
 
2516
                } else if (ch == '-') {
 
2517
                    this.skipComment();
 
2518
                    return;
 
2519
                }
 
2520
            }
 
2521
        }
 
2522
        while (tagLevel > 0) {
 
2523
            char ch = this.readChar();
 
2524
            if (stringDelimiter == '\0') {
 
2525
                if ((ch == '"') || (ch == '\'')) {
 
2526
                    stringDelimiter = ch;
 
2527
                } else if (bracketLevel <= 0) {
 
2528
                    if (ch == '<') {
 
2529
                        tagLevel += 1;
 
2530
                    } else if (ch == '>') {
 
2531
                        tagLevel -= 1;
 
2532
                    }
 
2533
                }
 
2534
                if (ch == '[') {
 
2535
                    bracketLevel += 1;
 
2536
                } else if (ch == ']') {
 
2537
                    bracketLevel -= 1;
 
2538
                }
 
2539
            } else {
 
2540
                if (ch == stringDelimiter) {
 
2541
                    stringDelimiter = '\0';
 
2542
                }
 
2543
            }
 
2544
        }
 
2545
    }
 
2546
 
 
2547
 
 
2548
    /**
 
2549
     * Scans the data for literal text.
 
2550
     * Scanning stops when a character does not match or after the complete
 
2551
     * text has been checked, whichever comes first.
 
2552
     *
 
2553
     * @param literal the literal to check.
 
2554
     *
 
2555
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2556
     * <ul><li><code>literal != null</code>
 
2557
     * </ul></dd></dl>
 
2558
     */
 
2559
    protected boolean checkLiteral(String literal)
 
2560
        throws IOException
 
2561
    {
 
2562
        int length = literal.length();
 
2563
        for (int i = 0; i < length; i += 1) {
 
2564
            if (this.readChar() != literal.charAt(i)) {
 
2565
                return false;
 
2566
            }
 
2567
        }
 
2568
        return true;
 
2569
    }
 
2570
 
 
2571
 
 
2572
    /**
 
2573
     * Reads a character from a reader.
 
2574
     */
 
2575
    protected char readChar()
 
2576
        throws IOException
 
2577
    {
 
2578
        if (this.charReadTooMuch != '\0') {
 
2579
            char ch = this.charReadTooMuch;
 
2580
            this.charReadTooMuch = '\0';
 
2581
            return ch;
 
2582
        } else {
 
2583
            int i = this.reader.read();
 
2584
            if (i < 0) {
 
2585
                throw this.unexpectedEndOfData();
 
2586
            } else if (i == 10) {
 
2587
                this.parserLineNr += 1;
 
2588
                return '\n';
 
2589
            } else {
 
2590
                return (char) i;
 
2591
            }
 
2592
        }
 
2593
    }
 
2594
 
 
2595
 
 
2596
    /**
 
2597
     * Scans an XML element.
 
2598
     *
 
2599
     * @param elt The element that will contain the result.
 
2600
     *
 
2601
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2602
     * <ul><li>The first &lt; has already been read.
 
2603
     *     <li><code>elt != null</code>
 
2604
     * </ul></dd></dl>
 
2605
     */
 
2606
    protected void scanElement(XMLElement elt)
 
2607
        throws IOException
 
2608
    {
 
2609
        StringBuffer buf = new StringBuffer();
 
2610
        this.scanIdentifier(buf);
 
2611
        String name = buf.toString();
 
2612
        elt.setName(name);
 
2613
        char ch = this.scanWhitespace();
 
2614
        while ((ch != '>') && (ch != '/')) {
 
2615
            buf.setLength(0);
 
2616
            this.unreadChar(ch);
 
2617
            this.scanIdentifier(buf);
 
2618
            String key = buf.toString();
 
2619
            ch = this.scanWhitespace();
 
2620
            if (ch != '=') {
 
2621
                throw this.expectedInput("=");
 
2622
            }
 
2623
            this.unreadChar(this.scanWhitespace());
 
2624
            buf.setLength(0);
 
2625
            this.scanString(buf);
 
2626
            elt.setAttribute(key, buf);
 
2627
            ch = this.scanWhitespace();
 
2628
        }
 
2629
        if (ch == '/') {
 
2630
            ch = this.readChar();
 
2631
            if (ch != '>') {
 
2632
                throw this.expectedInput(">");
 
2633
            }
 
2634
            return;
 
2635
        }
 
2636
        buf.setLength(0);
 
2637
        ch = this.scanWhitespace(buf);
 
2638
        if (ch != '<') {
 
2639
            this.unreadChar(ch);
 
2640
            this.scanPCData(buf);
 
2641
        } else {
 
2642
            for (;;) {
 
2643
                ch = this.readChar();
 
2644
                if (ch == '!') {
 
2645
                    if (this.checkCDATA(buf)) {
 
2646
                        this.scanPCData(buf);
 
2647
                        break;
 
2648
                    } else {
 
2649
                        ch = this.scanWhitespace(buf);
 
2650
                        if (ch != '<') {
 
2651
                            this.unreadChar(ch);
 
2652
                            this.scanPCData(buf);
 
2653
                            break;
 
2654
                        }
 
2655
                    }
 
2656
                } else {
 
2657
                    if ((ch != '/') || this.ignoreWhitespace) {
 
2658
                        buf.setLength(0);
 
2659
                    }
 
2660
                    if (ch == '/') {
 
2661
                        this.unreadChar(ch);
 
2662
                    }
 
2663
                    break;
 
2664
                }
 
2665
            }
 
2666
        }
 
2667
        if (buf.length() == 0) {
 
2668
            while (ch != '/') {
 
2669
                if (ch == '!') {
 
2670
                    ch = this.readChar();
 
2671
                    if (ch != '-') {
 
2672
                        throw this.expectedInput("Comment or Element");
 
2673
                    }
 
2674
                    ch = this.readChar();
 
2675
                    if (ch != '-') {
 
2676
                        throw this.expectedInput("Comment or Element");
 
2677
                    }
 
2678
                    this.skipComment();
 
2679
                } else {
 
2680
                    this.unreadChar(ch);
 
2681
                    XMLElement child = this.createAnotherElement();
 
2682
                    this.scanElement(child);
 
2683
                    elt.addChild(child);
 
2684
                }
 
2685
                ch = this.scanWhitespace();
 
2686
                if (ch != '<') {
 
2687
                    throw this.expectedInput("<");
 
2688
                }
 
2689
                ch = this.readChar();
 
2690
            }
 
2691
            this.unreadChar(ch);
 
2692
        } else {
 
2693
            if (this.ignoreWhitespace) {
 
2694
                elt.setContent(buf.toString().trim());
 
2695
            } else {
 
2696
                elt.setContent(buf.toString());
 
2697
            }
 
2698
        }
 
2699
        ch = this.readChar();
 
2700
        if (ch != '/') {
 
2701
            throw this.expectedInput("/");
 
2702
        }
 
2703
        this.unreadChar(this.scanWhitespace());
 
2704
        if (! this.checkLiteral(name)) {
 
2705
            throw this.expectedInput(name);
 
2706
        }
 
2707
        if (this.scanWhitespace() != '>') {
 
2708
            throw this.expectedInput(">");
 
2709
        }
 
2710
    }
 
2711
 
 
2712
 
 
2713
    /**
 
2714
     * Resolves an entity. The name of the entity is read from the reader.
 
2715
     * The value of the entity is appended to <code>buf</code>.
 
2716
     *
 
2717
     * @param buf Where to put the entity value.
 
2718
     *
 
2719
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2720
     * <ul><li>The first &amp; has already been read.
 
2721
     *     <li><code>buf != null</code>
 
2722
     * </ul></dd></dl>
 
2723
     */
 
2724
    protected void resolveEntity(StringBuffer buf)
 
2725
        throws IOException
 
2726
    {
 
2727
        char ch = '\0';
 
2728
        StringBuffer keyBuf = new StringBuffer();
 
2729
        for (;;) {
 
2730
            ch = this.readChar();
 
2731
            if (ch == ';') {
 
2732
                break;
 
2733
            }
 
2734
            keyBuf.append(ch);
 
2735
        }
 
2736
        String key = keyBuf.toString();
 
2737
        if (key.charAt(0) == '#') {
 
2738
            try {
 
2739
                if (key.charAt(1) == 'x') {
 
2740
                    ch = (char) Integer.parseInt(key.substring(2), 16);
 
2741
                } else {
 
2742
                    ch = (char) Integer.parseInt(key.substring(1), 10);
 
2743
                }
 
2744
            } catch (NumberFormatException e) {
 
2745
                throw this.unknownEntity(key);
 
2746
            }
 
2747
            buf.append(ch);
 
2748
        } else {
 
2749
            char[] value = (char[]) this.entities.get(key);
 
2750
            if (value == null) {
 
2751
                throw this.unknownEntity(key);
 
2752
            }
 
2753
            buf.append(value);
 
2754
        }
 
2755
    }
 
2756
 
 
2757
 
 
2758
    /**
 
2759
     * Pushes a character back to the read-back buffer.
 
2760
     *
 
2761
     * @param ch The character to push back.
 
2762
     *
 
2763
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2764
     * <ul><li>The read-back buffer is empty.
 
2765
     *     <li><code>ch != '\0'</code>
 
2766
     * </ul></dd></dl>
 
2767
     */
 
2768
    protected void unreadChar(char ch)
 
2769
    {
 
2770
        this.charReadTooMuch = ch;
 
2771
    }
 
2772
 
 
2773
 
 
2774
    /**
 
2775
     * Creates a parse exception for when an invalid valueset is given to
 
2776
     * a method.
 
2777
     *
 
2778
     * @param name The name of the entity.
 
2779
     *
 
2780
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2781
     * <ul><li><code>name != null</code>
 
2782
     * </ul></dd></dl>
 
2783
     */
 
2784
    protected XMLParseException invalidValueSet(String name)
 
2785
    {
 
2786
        String msg = "Invalid value set (entity name = \"" + name + "\")";
 
2787
        return new XMLParseException(this.getName(), this.parserLineNr, msg);
 
2788
    }
 
2789
 
 
2790
 
 
2791
    /**
 
2792
     * Creates a parse exception for when an invalid value is given to a
 
2793
     * method.
 
2794
     *
 
2795
     * @param name  The name of the entity.
 
2796
     * @param value The value of the entity.
 
2797
     *
 
2798
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2799
     * <ul><li><code>name != null</code>
 
2800
     *     <li><code>value != null</code>
 
2801
     * </ul></dd></dl>
 
2802
     */
 
2803
    protected XMLParseException invalidValue(String name,
 
2804
                                             String value)
 
2805
    {
 
2806
        String msg = "Attribute \"" + name + "\" does not contain a valid "
 
2807
                   + "value (\"" + value + "\")";
 
2808
        return new XMLParseException(this.getName(), this.parserLineNr, msg);
 
2809
    }
 
2810
 
 
2811
 
 
2812
    /**
 
2813
     * Creates a parse exception for when the end of the data input has been
 
2814
     * reached.
 
2815
     */
 
2816
    protected XMLParseException unexpectedEndOfData()
 
2817
    {
 
2818
        String msg = "Unexpected end of data reached";
 
2819
        return new XMLParseException(this.getName(), this.parserLineNr, msg);
 
2820
    }
 
2821
 
 
2822
 
 
2823
    /**
 
2824
     * Creates a parse exception for when a syntax error occured.
 
2825
     *
 
2826
     * @param context The context in which the error occured.
 
2827
     *
 
2828
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2829
     * <ul><li><code>context != null</code>
 
2830
     *     <li><code>context.length() &gt; 0</code>
 
2831
     * </ul></dd></dl>
 
2832
     */
 
2833
    protected XMLParseException syntaxError(String context)
 
2834
    {
 
2835
        String msg = "Syntax error while parsing " + context;
 
2836
        return new XMLParseException(this.getName(), this.parserLineNr, msg);
 
2837
    }
 
2838
 
 
2839
 
 
2840
    /**
 
2841
     * Creates a parse exception for when the next character read is not
 
2842
     * the character that was expected.
 
2843
     *
 
2844
     * @param charSet The set of characters (in human readable form) that was
 
2845
     *                expected.
 
2846
     *
 
2847
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2848
     * <ul><li><code>charSet != null</code>
 
2849
     *     <li><code>charSet.length() &gt; 0</code>
 
2850
     * </ul></dd></dl>
 
2851
     */
 
2852
    protected XMLParseException expectedInput(String charSet)
 
2853
    {
 
2854
        String msg = "Expected: " + charSet;
 
2855
        return new XMLParseException(this.getName(), this.parserLineNr, msg);
 
2856
    }
 
2857
 
 
2858
 
 
2859
    /**
 
2860
     * Creates a parse exception for when an entity could not be resolved.
 
2861
     *
 
2862
     * @param name The name of the entity.
 
2863
     *
 
2864
     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
 
2865
     * <ul><li><code>name != null</code>
 
2866
     *     <li><code>name.length() &gt; 0</code>
 
2867
     * </ul></dd></dl>
 
2868
     */
 
2869
    protected XMLParseException unknownEntity(String name)
 
2870
    {
 
2871
        String msg = "Unknown or invalid entity: &" + name + ";";
 
2872
        return new XMLParseException(this.getName(), this.parserLineNr, msg);
 
2873
    }
 
2874
    
 
2875
}