View Javadoc

1   /* XMLElement.java                                                 NanoXML/Lite
2    *
3    * This file is part of NanoXML 2 Lite.
4    * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
5    *
6    * This software is provided 'as-is', without any express or implied warranty.
7    * In no event will the authors be held liable for any damages arising from the
8    * use of this software.
9    *
10   * Permission is granted to anyone to use this software for any purpose,
11   * including commercial applications, and to alter it and redistribute it
12   * freely, subject to the following restrictions:
13   *
14   *  1. The origin of this software must not be misrepresented; you must not
15   *     claim that you wrote the original software. If you use this software in
16   *     a product, an acknowledgment in the product documentation would be
17   *     appreciated but is not required.
18   *
19   *  2. Altered source versions must be plainly marked as such, and must not be
20   *     misrepresented as being the original software.
21   *
22   *  3. This notice may not be removed or altered from any source distribution.
23   */
24  
25  package nanoxml;
26  
27  import java.io.ByteArrayOutputStream;
28  import java.io.CharArrayReader;
29  import java.io.IOException;
30  import java.io.OutputStreamWriter;
31  import java.io.Reader;
32  import java.io.StringReader;
33  import java.io.Writer;
34  import java.util.Enumeration;
35  import java.util.HashMap;
36  import java.util.Hashtable;
37  import java.util.Iterator;
38  import java.util.Map;
39  import java.util.Vector;
40  
41  /**
42   * XMLElement is a representation of an XML object. The object is able to parse
43   * XML code.
44   * <P>
45   * <DL>
46   * <DT><B>Parsing XML Data</B></DT>
47   * <DD> You can parse XML data using the following code:
48   * <UL>
49   * <CODE>
50   * XMLElement xml = new XMLElement();<BR>
51   * FileReader reader = new FileReader("filename.xml");<BR>
52   * xml.parseFromReader(reader);
53   * </CODE>
54   * </UL>
55   * </DD>
56   * </DL>
57   * <DL>
58   * <DT><B>Retrieving Attributes</B></DT>
59   * <DD> You can enumerate the attributes of an element using the method
60   * {@link #enumerateAttributeNames() enumerateAttributeNames}. The attribute
61   * values can be retrieved using the method
62   * {@link #getStringAttribute(java.lang.String) getStringAttribute}. The
63   * following example shows how to list the attributes of an element:
64   * <UL>
65   * <CODE>
66   * XMLElement element = ...;<BR>
67   * Enumeration enum = element.getAttributeNames();<BR>
68   * while (enum.hasMoreElements()) {<BR>
69   * &nbsp;&nbsp;&nbsp;&nbsp;String key = (String) enum.nextElement();<BR>
70   * &nbsp;&nbsp;&nbsp;&nbsp;String value = element.getStringAttribute(key);<BR>
71   * &nbsp;&nbsp;&nbsp;&nbsp;System.out.println(key + " = " + value);<BR>
72   * }
73   * </CODE>
74   * </UL>
75   * </DD>
76   * </DL>
77   * <DL>
78   * <DT><B>Retrieving Child Elements</B></DT>
79   * <DD> You can enumerate the children of an element using
80   * {@link #enumerateChildren() enumerateChildren}. The number of child elements
81   * can be retrieved using {@link #countChildren() countChildren}. </DD>
82   * </DL>
83   * <DL>
84   * <DT><B>Elements Containing Character Data</B></DT>
85   * <DD> If an elements contains character data, like in the following example:
86   * <UL>
87   * <CODE>
88   * &lt;title&gt;The Title&lt;/title&gt;
89   * </CODE>
90   * </UL>
91   * you can retrieve that data using the method {@link #getContent() getContent}.
92   * </DD>
93   * </DL>
94   * <DL>
95   * <DT><B>Subclassing XMLElement</B></DT>
96   * <DD> When subclassing XMLElement, you need to override the method
97   * {@link #createAnotherElement() createAnotherElement} which has to return a
98   * new copy of the receiver. </DD>
99   * </DL>
100  * <P>
101  * 
102  * @see nanoxml.XMLParseException
103  * 
104  * @author Marc De Scheemaecker &lt;<A
105  *         href="mailto:cyberelf@mac.com">cyberelf@mac.com</A>&gt;
106  */
107 public class XMLElement {
108 
109 	/**
110 	 * Serialization serial version ID.
111 	 */
112 	static final long serialVersionUID = 6685035139346394777L;
113 
114 	/**
115 	 * Major version of NanoXML. Classes with the same major and minor version
116 	 * are binary compatible. Classes with the same major version are source
117 	 * compatible. If the major version is different, you may need to modify the
118 	 * client source code.
119 	 * 
120 	 * @see nanoxml.XMLElement#NANOXML_MINOR_VERSION
121 	 */
122 	public static final int NANOXML_MAJOR_VERSION = 2;
123 
124 	/**
125 	 * Minor version of NanoXML. Classes with the same major and minor version
126 	 * are binary compatible. Classes with the same major version are source
127 	 * compatible. If the major version is different, you may need to modify the
128 	 * client source code.
129 	 * 
130 	 * @see nanoxml.XMLElement#NANOXML_MAJOR_VERSION
131 	 */
132 	public static final int NANOXML_MINOR_VERSION = 2;
133 
134 	/**
135 	 * The attributes given to the element.
136 	 * 
137 	 * <dl>
138 	 * <dt><b>Invariants:</b></dt>
139 	 * <dd>
140 	 * <ul>
141 	 * <li>The field can be empty.
142 	 * <li>The field is never <code>null</code>.
143 	 * <li>The keys and the values are strings.
144 	 * </ul>
145 	 * </dd>
146 	 * </dl>
147 	 */
148 	private Hashtable attributes;
149 
150 	/**
151 	 * Child elements of the element.
152 	 * 
153 	 * <dl>
154 	 * <dt><b>Invariants:</b></dt>
155 	 * <dd>
156 	 * <ul>
157 	 * <li>The field can be empty.
158 	 * <li>The field is never <code>null</code>.
159 	 * <li>The elements are instances of <code>XMLElement</code> or a
160 	 * subclass of <code>XMLElement</code>.
161 	 * </ul>
162 	 * </dd>
163 	 * </dl>
164 	 */
165 	private Vector children;
166 
167 	/**
168 	 * The name of the element.
169 	 * 
170 	 * <dl>
171 	 * <dt><b>Invariants:</b></dt>
172 	 * <dd>
173 	 * <ul>
174 	 * <li>The field is <code>null</code> iff the element is not initialized
175 	 * by either parse or setName.
176 	 * <li>If the field is not <code>null</code>, it's not empty.
177 	 * <li>If the field is not <code>null</code>, it contains a valid XML
178 	 * identifier.
179 	 * </ul>
180 	 * </dd>
181 	 * </dl>
182 	 */
183 	private String name;
184 
185 	/**
186 	 * The #PCDATA content of the object.
187 	 * 
188 	 * <dl>
189 	 * <dt><b>Invariants:</b></dt>
190 	 * <dd>
191 	 * <ul>
192 	 * <li>The field is <code>null</code> iff the element is not a #PCDATA
193 	 * element.
194 	 * <li>The field can be any string, including the empty string.
195 	 * </ul>
196 	 * </dd>
197 	 * </dl>
198 	 */
199 	private String contents;
200 
201 	/**
202 	 * Conversion table for &amp;...; entities. The keys are the entity names
203 	 * without the &amp; and ; delimiters.
204 	 * 
205 	 * <dl>
206 	 * <dt><b>Invariants:</b></dt>
207 	 * <dd>
208 	 * <ul>
209 	 * <li>The field is never <code>null</code>.
210 	 * <li>The field always contains the following associations:
211 	 * "lt"&nbsp;=&gt;&nbsp;"&lt;", "gt"&nbsp;=&gt;&nbsp;"&gt;",
212 	 * "quot"&nbsp;=&gt;&nbsp;"\"", "apos"&nbsp;=&gt;&nbsp;"'",
213 	 * "amp"&nbsp;=&gt;&nbsp;"&amp;"
214 	 * <li>The keys are strings
215 	 * <li>The values are char arrays
216 	 * </ul>
217 	 * </dd>
218 	 * </dl>
219 	 */
220 	private Hashtable entities;
221 
222 	/**
223 	 * The line number where the element starts.
224 	 * 
225 	 * <dl>
226 	 * <dt><b>Invariants:</b></dt>
227 	 * <dd>
228 	 * <ul>
229 	 * <li><code>lineNr &gt= 0</code>
230 	 * </ul>
231 	 * </dd>
232 	 * </dl>
233 	 */
234 	private int lineNr;
235 
236 	/**
237 	 * <code>true</code> if the case of the element and attribute names are
238 	 * case insensitive.
239 	 */
240 	private boolean ignoreCase;
241 
242 	/**
243 	 * <code>true</code> if the leading and trailing whitespace of #PCDATA
244 	 * sections have to be ignored.
245 	 */
246 	private boolean ignoreWhitespace;
247 
248 	/**
249 	 * Character read too much. This character provides push-back functionality
250 	 * to the input reader without having to use a PushbackReader. If there is
251 	 * no such character, this field is '\0'.
252 	 */
253 	private char charReadTooMuch;
254 
255 	/**
256 	 * The reader provided by the caller of the parse method.
257 	 * 
258 	 * <dl>
259 	 * <dt><b>Invariants:</b></dt>
260 	 * <dd>
261 	 * <ul>
262 	 * <li>The field is not <code>null</code> while the parse method is
263 	 * running.
264 	 * </ul>
265 	 * </dd>
266 	 * </dl>
267 	 */
268 	private Reader reader;
269 
270 	/**
271 	 * The current line number in the source content.
272 	 * 
273 	 * <dl>
274 	 * <dt><b>Invariants:</b></dt>
275 	 * <dd>
276 	 * <ul>
277 	 * <li>parserLineNr &gt; 0 while the parse method is running.
278 	 * </ul>
279 	 * </dd>
280 	 * </dl>
281 	 */
282 	private int parserLineNr;
283 
284 	/**
285 	 * Creates and initializes a new XML element. Calling the construction is
286 	 * equivalent to:
287 	 * <ul>
288 	 * <code>new XMLElement(new Hashtable(), false, true)
289 	 * </code>
290 	 * </ul>
291 	 * 
292 	 * <dl>
293 	 * <dt><b>Postconditions:</b></dt>
294 	 * <dd>
295 	 * <ul>
296 	 * <li>countChildren() => 0
297 	 * <li>enumerateChildren() => empty enumeration
298 	 * <li>enumeratePropertyNames() => empty enumeration
299 	 * <li>getChildren() => empty vector
300 	 * <li>getContent() => ""
301 	 * <li>getLineNr() => 0
302 	 * <li>getName() => null
303 	 * </ul>
304 	 * </dd>
305 	 * </dl>
306 	 * 
307 	 * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable)
308 	 *      XMLElement(Hashtable)
309 	 * @see nanoxml.XMLElement#XMLElement(boolean)
310 	 * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable,boolean)
311 	 *      XMLElement(Hashtable, boolean)
312 	 */
313 	public XMLElement() {
314 		this(new Hashtable(), false, true, true);
315 	}
316 
317 	public XMLElement(boolean skipLeadingWhitespace, boolean ignoreCase) {
318 		this(new Hashtable(), skipLeadingWhitespace, true, ignoreCase);
319 	}
320 
321 	/**
322 	 * Creates and initializes a new XML element. Calling the construction is
323 	 * equivalent to:
324 	 * <ul>
325 	 * <code>new XMLElement(entities, false, true)
326 	 * </code>
327 	 * </ul>
328 	 * 
329 	 * @param entities
330 	 *            The entity conversion table.
331 	 * 
332 	 * </dl>
333 	 * <dl>
334 	 * <dt><b>Preconditions:</b></dt>
335 	 * <dd>
336 	 * <ul>
337 	 * <li><code>entities != null</code>
338 	 * </ul>
339 	 * </dd>
340 	 * </dl>
341 	 * 
342 	 * <dl>
343 	 * <dt><b>Postconditions:</b></dt>
344 	 * <dd>
345 	 * <ul>
346 	 * <li>countChildren() => 0
347 	 * <li>enumerateChildren() => empty enumeration
348 	 * <li>enumeratePropertyNames() => empty enumeration
349 	 * <li>getChildren() => empty vector
350 	 * <li>getContent() => ""
351 	 * <li>getLineNr() => 0
352 	 * <li>getName() => null
353 	 * </ul>
354 	 * </dd>
355 	 * </dl>
356 	 * <dl>
357 	 * 
358 	 * @see nanoxml.XMLElement#XMLElement()
359 	 * @see nanoxml.XMLElement#XMLElement(boolean)
360 	 * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable,boolean)
361 	 *      XMLElement(Hashtable, boolean)
362 	 */
363 	public XMLElement(Hashtable entities) {
364 		this(entities, false, true, true);
365 	}
366 
367 	/**
368 	 * Creates and initializes a new XML element. Calling the construction is
369 	 * equivalent to:
370 	 * <ul>
371 	 * <code>new XMLElement(new Hashtable(), skipLeadingWhitespace, true)
372 	 * </code>
373 	 * </ul>
374 	 * 
375 	 * @param skipLeadingWhitespace
376 	 *            <code>true</code> if leading and trailing whitespace in
377 	 *            PCDATA content has to be removed.
378 	 * 
379 	 * </dl>
380 	 * <dl>
381 	 * <dt><b>Postconditions:</b></dt>
382 	 * <dd>
383 	 * <ul>
384 	 * <li>countChildren() => 0
385 	 * <li>enumerateChildren() => empty enumeration
386 	 * <li>enumeratePropertyNames() => empty enumeration
387 	 * <li>getChildren() => empty vector
388 	 * <li>getContent() => ""
389 	 * <li>getLineNr() => 0
390 	 * <li>getName() => null
391 	 * </ul>
392 	 * </dd>
393 	 * </dl>
394 	 * <dl>
395 	 * 
396 	 * @see nanoxml.XMLElement#XMLElement()
397 	 * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable)
398 	 *      XMLElement(Hashtable)
399 	 * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable,boolean)
400 	 *      XMLElement(Hashtable, boolean)
401 	 */
402 	public XMLElement(boolean skipLeadingWhitespace) {
403 		this(new Hashtable(), skipLeadingWhitespace, true, true);
404 	}
405 
406 	/**
407 	 * Creates and initializes a new XML element. Calling the construction is
408 	 * equivalent to:
409 	 * <ul>
410 	 * <code>new XMLElement(entities, skipLeadingWhitespace, true)
411 	 * </code>
412 	 * </ul>
413 	 * 
414 	 * @param entities
415 	 *            The entity conversion table.
416 	 * @param skipLeadingWhitespace
417 	 *            <code>true</code> if leading and trailing whitespace in
418 	 *            PCDATA content has to be removed.
419 	 * 
420 	 * </dl>
421 	 * <dl>
422 	 * <dt><b>Preconditions:</b></dt>
423 	 * <dd>
424 	 * <ul>
425 	 * <li><code>entities != null</code>
426 	 * </ul>
427 	 * </dd>
428 	 * </dl>
429 	 * 
430 	 * <dl>
431 	 * <dt><b>Postconditions:</b></dt>
432 	 * <dd>
433 	 * <ul>
434 	 * <li>countChildren() => 0
435 	 * <li>enumerateChildren() => empty enumeration
436 	 * <li>enumeratePropertyNames() => empty enumeration
437 	 * <li>getChildren() => empty vector
438 	 * <li>getContent() => ""
439 	 * <li>getLineNr() => 0
440 	 * <li>getName() => null
441 	 * </ul>
442 	 * </dd>
443 	 * </dl>
444 	 * <dl>
445 	 * 
446 	 * @see nanoxml.XMLElement#XMLElement()
447 	 * @see nanoxml.XMLElement#XMLElement(boolean)
448 	 * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable)
449 	 *      XMLElement(Hashtable)
450 	 */
451 	public XMLElement(Hashtable entities, boolean skipLeadingWhitespace) {
452 		this(entities, skipLeadingWhitespace, true, true);
453 	}
454 
455 	/**
456 	 * Creates and initializes a new XML element.
457 	 * 
458 	 * @param entities
459 	 *            The entity conversion table.
460 	 * @param skipLeadingWhitespace
461 	 *            <code>true</code> if leading and trailing whitespace in
462 	 *            PCDATA content has to be removed.
463 	 * @param ignoreCase
464 	 *            <code>true</code> if the case of element and attribute names
465 	 *            have to be ignored.
466 	 * 
467 	 * </dl>
468 	 * <dl>
469 	 * <dt><b>Preconditions:</b></dt>
470 	 * <dd>
471 	 * <ul>
472 	 * <li><code>entities != null</code>
473 	 * </ul>
474 	 * </dd>
475 	 * </dl>
476 	 * 
477 	 * <dl>
478 	 * <dt><b>Postconditions:</b></dt>
479 	 * <dd>
480 	 * <ul>
481 	 * <li>countChildren() => 0
482 	 * <li>enumerateChildren() => empty enumeration
483 	 * <li>enumeratePropertyNames() => empty enumeration
484 	 * <li>getChildren() => empty vector
485 	 * <li>getContent() => ""
486 	 * <li>getLineNr() => 0
487 	 * <li>getName() => null
488 	 * </ul>
489 	 * </dd>
490 	 * </dl>
491 	 * <dl>
492 	 * 
493 	 * @see nanoxml.XMLElement#XMLElement()
494 	 * @see nanoxml.XMLElement#XMLElement(boolean)
495 	 * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable)
496 	 *      XMLElement(Hashtable)
497 	 * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable,boolean)
498 	 *      XMLElement(Hashtable, boolean)
499 	 */
500 	public XMLElement(Hashtable entities, boolean skipLeadingWhitespace, boolean ignoreCase) {
501 		this(entities, skipLeadingWhitespace, true, ignoreCase);
502 	}
503 
504 	/**
505 	 * Creates and initializes a new XML element.
506 	 * <P>
507 	 * This constructor should <I>only</I> be called from
508 	 * {@link #createAnotherElement() createAnotherElement} to create child
509 	 * elements.
510 	 * 
511 	 * @param entities
512 	 *            The entity conversion table.
513 	 * @param skipLeadingWhitespace
514 	 *            <code>true</code> if leading and trailing whitespace in
515 	 *            PCDATA content has to be removed.
516 	 * @param fillBasicConversionTable
517 	 *            <code>true</code> if the basic entities need to be added to
518 	 *            the entity list.
519 	 * @param ignoreCase
520 	 *            <code>true</code> if the case of element and attribute names
521 	 *            have to be ignored.
522 	 * 
523 	 * </dl>
524 	 * <dl>
525 	 * <dt><b>Preconditions:</b></dt>
526 	 * <dd>
527 	 * <ul>
528 	 * <li><code>entities != null</code>
529 	 * <li>if <code>fillBasicConversionTable == false</code> then
530 	 * <code>entities</code> contains at least the following entries:
531 	 * <code>amp</code>, <code>lt</code>, <code>gt</code>,
532 	 * <code>apos</code> and <code>quot</code>
533 	 * </ul>
534 	 * </dd>
535 	 * </dl>
536 	 * 
537 	 * <dl>
538 	 * <dt><b>Postconditions:</b></dt>
539 	 * <dd>
540 	 * <ul>
541 	 * <li>countChildren() => 0
542 	 * <li>enumerateChildren() => empty enumeration
543 	 * <li>enumeratePropertyNames() => empty enumeration
544 	 * <li>getChildren() => empty vector
545 	 * <li>getContent() => ""
546 	 * <li>getLineNr() => 0
547 	 * <li>getName() => null
548 	 * </ul>
549 	 * </dd>
550 	 * </dl>
551 	 * <dl>
552 	 * 
553 	 * @see nanoxml.XMLElement#createAnotherElement()
554 	 */
555 	protected XMLElement(Hashtable entities, boolean skipLeadingWhitespace, boolean fillBasicConversionTable,
556 			boolean ignoreCase) {
557 		this.ignoreWhitespace = skipLeadingWhitespace;
558 		this.ignoreCase = ignoreCase;
559 		this.name = null;
560 		this.contents = "";
561 		this.attributes = new Hashtable();
562 		this.children = new Vector();
563 		this.entities = entities;
564 		this.lineNr = 0;
565 
566 		Enumeration en = this.entities.keys();
567 
568 		while (en.hasMoreElements()) {
569 			Object key = en.nextElement();
570 			Object value = this.entities.get(key);
571 
572 			if (value instanceof String) {
573 				value = ((String) value).toCharArray();
574 				this.entities.put(key, value);
575 			}
576 		}
577 
578 		if (fillBasicConversionTable) {
579 			this.entities.put("amp", new char[] { '&' });
580 			this.entities.put("quot", new char[] { '"' });
581 			this.entities.put("apos", new char[] { '\'' });
582 			this.entities.put("lt", new char[] { '<' });
583 			this.entities.put("gt", new char[] { '>' });
584 		}
585 	}
586 
587 	/**
588 	 * Adds a child element.
589 	 * 
590 	 * @param child
591 	 *            The child element to add.
592 	 * 
593 	 * </dl>
594 	 * <dl>
595 	 * <dt><b>Preconditions:</b></dt>
596 	 * <dd>
597 	 * <ul>
598 	 * <li><code>child != null</code>
599 	 * <li><code>child.getName() != null</code>
600 	 * <li><code>child</code> does not have a parent element
601 	 * </ul>
602 	 * </dd>
603 	 * </dl>
604 	 * 
605 	 * <dl>
606 	 * <dt><b>Postconditions:</b></dt>
607 	 * <dd>
608 	 * <ul>
609 	 * <li>countChildren() => old.countChildren() + 1
610 	 * <li>enumerateChildren() => old.enumerateChildren() + child
611 	 * <li>getChildren() => old.enumerateChildren() + child
612 	 * </ul>
613 	 * </dd>
614 	 * </dl>
615 	 * <dl>
616 	 * 
617 	 * @see nanoxml.XMLElement#countChildren()
618 	 * @see nanoxml.XMLElement#enumerateChildren()
619 	 * @see nanoxml.XMLElement#getChildren()
620 	 * @see nanoxml.XMLElement#removeChild(nanoxml.XMLElement)
621 	 *      removeChild(XMLElement)
622 	 */
623 	public void addChild(XMLElement child) {
624 		this.children.addElement(child);
625 	}
626 
627 	public XMLElement addChild(String name) {
628 		XMLElement xml = new XMLElement(true, false);
629 		xml.setName(name);
630 		this.addChild(xml);
631 		return xml;
632 	}
633 
634 	public XMLElement addChild(String name, String value) {
635 		XMLElement xml = addChild(name);
636 		xml.setContent(value);
637 		return xml;
638 	}
639 
640 	/**
641 	 * Adds or modifies an attribute.
642 	 * 
643 	 * @param name
644 	 *            The name of the attribute.
645 	 * @param value
646 	 *            The value of the attribute.
647 	 * 
648 	 * </dl>
649 	 * <dl>
650 	 * <dt><b>Preconditions:</b></dt>
651 	 * <dd>
652 	 * <ul>
653 	 * <li><code>name != null</code>
654 	 * <li><code>name</code> is a valid XML identifier
655 	 * <li><code>value != null</code>
656 	 * </ul>
657 	 * </dd>
658 	 * </dl>
659 	 * 
660 	 * <dl>
661 	 * <dt><b>Postconditions:</b></dt>
662 	 * <dd>
663 	 * <ul>
664 	 * <li>enumerateAttributeNames() => old.enumerateAttributeNames() + name
665 	 * <li>getAttribute(name) => value
666 	 * </ul>
667 	 * </dd>
668 	 * </dl>
669 	 * <dl>
670 	 * 
671 	 * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
672 	 *      setDoubleAttribute(String, double)
673 	 * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
674 	 *      setIntAttribute(String, int)
675 	 * @see nanoxml.XMLElement#enumerateAttributeNames()
676 	 * @see nanoxml.XMLElement#getAttribute(java.lang.String)
677 	 *      getAttribute(String)
678 	 * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
679 	 *      getAttribute(String, Object)
680 	 * @see nanoxml.XMLElement#getAttribute(java.lang.String,
681 	 *      java.util.Hashtable, java.lang.String, boolean) getAttribute(String,
682 	 *      Hashtable, String, boolean)
683 	 * @see nanoxml.XMLElement#getStringAttribute(java.lang.String)
684 	 *      getStringAttribute(String)
685 	 * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
686 	 *      java.lang.String) getStringAttribute(String, String)
687 	 * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
688 	 *      java.util.Hashtable, java.lang.String, boolean)
689 	 *      getStringAttribute(String, Hashtable, String, boolean)
690 	 */
691 	public void setAttribute(String name, Object value) {
692 		if (this.ignoreCase) {
693 			name = name.toUpperCase();
694 		}
695 
696 		this.attributes.put(name, value.toString());
697 	}
698 
699 	/**
700 	 * Adds or modifies an attribute.
701 	 * 
702 	 * @param name
703 	 *            The name of the attribute.
704 	 * @param value
705 	 *            The value of the attribute.
706 	 * 
707 	 * @deprecated Use {@link #setAttribute(java.lang.String, java.lang.Object)
708 	 *             setAttribute} instead.
709 	 */
710 	public void addProperty(String name, Object value) {
711 		this.setAttribute(name, value);
712 	}
713 
714 	/**
715 	 * Adds or modifies an attribute.
716 	 * 
717 	 * @param name
718 	 *            The name of the attribute.
719 	 * @param value
720 	 *            The value of the attribute.
721 	 * 
722 	 * </dl>
723 	 * <dl>
724 	 * <dt><b>Preconditions:</b></dt>
725 	 * <dd>
726 	 * <ul>
727 	 * <li><code>name != null</code>
728 	 * <li><code>name</code> is a valid XML identifier
729 	 * </ul>
730 	 * </dd>
731 	 * </dl>
732 	 * 
733 	 * <dl>
734 	 * <dt><b>Postconditions:</b></dt>
735 	 * <dd>
736 	 * <ul>
737 	 * <li>enumerateAttributeNames() => old.enumerateAttributeNames() + name
738 	 * <li>getIntAttribute(name) => value
739 	 * </ul>
740 	 * </dd>
741 	 * </dl>
742 	 * <dl>
743 	 * 
744 	 * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
745 	 *      setDoubleAttribute(String, double)
746 	 * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
747 	 *      setAttribute(String, Object)
748 	 * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
749 	 *      removeAttribute(String)
750 	 * @see nanoxml.XMLElement#enumerateAttributeNames()
751 	 * @see nanoxml.XMLElement#getIntAttribute(java.lang.String)
752 	 *      getIntAttribute(String)
753 	 * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
754 	 *      getIntAttribute(String, int)
755 	 * @see nanoxml.XMLElement#getIntAttribute(java.lang.String,
756 	 *      java.util.Hashtable, java.lang.String, boolean)
757 	 *      getIntAttribute(String, Hashtable, String, boolean)
758 	 */
759 	public void setIntAttribute(String name, int value) {
760 		if (this.ignoreCase) {
761 			name = name.toUpperCase();
762 		}
763 
764 		this.attributes.put(name, Integer.toString(value));
765 	}
766 
767 	/**
768 	 * Adds or modifies an attribute.
769 	 * 
770 	 * @param name
771 	 *            The name of the attribute.
772 	 * @param value
773 	 *            The value of the attribute.
774 	 * 
775 	 * @deprecated Use {@link #setIntAttribute(java.lang.String, int)
776 	 *             setIntAttribute} instead.
777 	 */
778 	public void addProperty(String key, int value) {
779 		this.setIntAttribute(key, value);
780 	}
781 
782 	/**
783 	 * Adds or modifies an attribute.
784 	 * 
785 	 * @param name
786 	 *            The name of the attribute.
787 	 * @param value
788 	 *            The value of the attribute.
789 	 * 
790 	 * </dl>
791 	 * <dl>
792 	 * <dt><b>Preconditions:</b></dt>
793 	 * <dd>
794 	 * <ul>
795 	 * <li><code>name != null</code>
796 	 * <li><code>name</code> is a valid XML identifier
797 	 * </ul>
798 	 * </dd>
799 	 * </dl>
800 	 * 
801 	 * <dl>
802 	 * <dt><b>Postconditions:</b></dt>
803 	 * <dd>
804 	 * <ul>
805 	 * <li>enumerateAttributeNames() => old.enumerateAttributeNames() + name
806 	 * <li>getDoubleAttribute(name) => value
807 	 * </ul>
808 	 * </dd>
809 	 * </dl>
810 	 * <dl>
811 	 * 
812 	 * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
813 	 *      setIntAttribute(String, int)
814 	 * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
815 	 *      setAttribute(String, Object)
816 	 * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
817 	 *      removeAttribute(String)
818 	 * @see nanoxml.XMLElement#enumerateAttributeNames()
819 	 * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
820 	 *      getDoubleAttribute(String)
821 	 * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
822 	 *      getDoubleAttribute(String, double)
823 	 * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
824 	 *      java.util.Hashtable, java.lang.String, boolean)
825 	 *      getDoubleAttribute(String, Hashtable, String, boolean)
826 	 */
827 	public void setDoubleAttribute(String name, double value) {
828 		if (this.ignoreCase) {
829 			name = name.toUpperCase();
830 		}
831 
832 		this.attributes.put(name, Double.toString(value));
833 	}
834 
835 	/**
836 	 * Adds or modifies an attribute.
837 	 * 
838 	 * @param name
839 	 *            The name of the attribute.
840 	 * @param value
841 	 *            The value of the attribute.
842 	 * 
843 	 * @deprecated Use {@link #setDoubleAttribute(java.lang.String, double)
844 	 *             setDoubleAttribute} instead.
845 	 */
846 	public void addProperty(String name, double value) {
847 		this.setDoubleAttribute(name, value);
848 	}
849 
850 	/**
851 	 * Returns the number of child elements of the element.
852 	 * 
853 	 * <dl>
854 	 * <dt><b>Postconditions:</b></dt>
855 	 * <dd>
856 	 * <ul>
857 	 * <li><code>result >= 0</code>
858 	 * </ul>
859 	 * </dd>
860 	 * </dl>
861 	 * 
862 	 * @see nanoxml.XMLElement#addChild(nanoxml.XMLElement) addChild(XMLElement)
863 	 * @see nanoxml.XMLElement#enumerateChildren()
864 	 * @see nanoxml.XMLElement#getChildren()
865 	 * @see nanoxml.XMLElement#removeChild(nanoxml.XMLElement)
866 	 *      removeChild(XMLElement)
867 	 */
868 	public int countChildren() {
869 		return this.children.size();
870 	}
871 
872 	/**
873 	 * Enumerates the attribute names.
874 	 * 
875 	 * <dl>
876 	 * <dt><b>Postconditions:</b></dt>
877 	 * <dd>
878 	 * <ul>
879 	 * <li><code>result != null</code>
880 	 * </ul>
881 	 * </dd>
882 	 * </dl>
883 	 * 
884 	 * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
885 	 *      setDoubleAttribute(String, double)
886 	 * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
887 	 *      setIntAttribute(String, int)
888 	 * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
889 	 *      setAttribute(String, Object)
890 	 * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
891 	 *      removeAttribute(String)
892 	 * @see nanoxml.XMLElement#getAttribute(java.lang.String)
893 	 *      getAttribute(String)
894 	 * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
895 	 *      getAttribute(String, String)
896 	 * @see nanoxml.XMLElement#getAttribute(java.lang.String,
897 	 *      java.util.Hashtable, java.lang.String, boolean) getAttribute(String,
898 	 *      Hashtable, String, boolean)
899 	 * @see nanoxml.XMLElement#getStringAttribute(java.lang.String)
900 	 *      getStringAttribute(String)
901 	 * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
902 	 *      java.lang.String) getStringAttribute(String, String)
903 	 * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
904 	 *      java.util.Hashtable, java.lang.String, boolean)
905 	 *      getStringAttribute(String, Hashtable, String, boolean)
906 	 * @see nanoxml.XMLElement#getIntAttribute(java.lang.String)
907 	 *      getIntAttribute(String)
908 	 * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
909 	 *      getIntAttribute(String, int)
910 	 * @see nanoxml.XMLElement#getIntAttribute(java.lang.String,
911 	 *      java.util.Hashtable, java.lang.String, boolean)
912 	 *      getIntAttribute(String, Hashtable, String, boolean)
913 	 * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
914 	 *      getDoubleAttribute(String)
915 	 * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
916 	 *      getDoubleAttribute(String, double)
917 	 * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
918 	 *      java.util.Hashtable, java.lang.String, boolean)
919 	 *      getDoubleAttribute(String, Hashtable, String, boolean)
920 	 * @see nanoxml.XMLElement#getBooleanAttribute(java.lang.String,
921 	 *      java.lang.String, java.lang.String, boolean)
922 	 *      getBooleanAttribute(String, String, String, boolean)
923 	 */
924 	public Enumeration enumerateAttributeNames() {
925 		return this.attributes.keys();
926 	}
927 
928 	/**
929 	 * Enumerates the attribute names.
930 	 * 
931 	 * @deprecated Use {@link #enumerateAttributeNames()
932 	 *             enumerateAttributeNames} instead.
933 	 */
934 	public Enumeration enumeratePropertyNames() {
935 		return this.enumerateAttributeNames();
936 	}
937 
938 	/**
939 	 * Enumerates the child elements.
940 	 * 
941 	 * <dl>
942 	 * <dt><b>Postconditions:</b></dt>
943 	 * <dd>
944 	 * <ul>
945 	 * <li><code>result != null</code>
946 	 * </ul>
947 	 * </dd>
948 	 * </dl>
949 	 * 
950 	 * @see nanoxml.XMLElement#addChild(nanoxml.XMLElement) addChild(XMLElement)
951 	 * @see nanoxml.XMLElement#countChildren()
952 	 * @see nanoxml.XMLElement#getChildren()
953 	 * @see nanoxml.XMLElement#removeChild(nanoxml.XMLElement)
954 	 *      removeChild(XMLElement)
955 	 */
956 	public Enumeration enumerateChildren() {
957 		return this.children.elements();
958 	}
959 
960 	/**
961 	 * Returns the child elements as a Vector. It is safe to modify this Vector.
962 	 * 
963 	 * <dl>
964 	 * <dt><b>Postconditions:</b></dt>
965 	 * <dd>
966 	 * <ul>
967 	 * <li><code>result != null</code>
968 	 * </ul>
969 	 * </dd>
970 	 * </dl>
971 	 * 
972 	 * @see nanoxml.XMLElement#addChild(nanoxml.XMLElement) addChild(XMLElement)
973 	 * @see nanoxml.XMLElement#countChildren()
974 	 * @see nanoxml.XMLElement#enumerateChildren()
975 	 * @see nanoxml.XMLElement#removeChild(nanoxml.XMLElement)
976 	 *      removeChild(XMLElement)
977 	 */
978 	public Vector getChildren() {
979 		try {
980 			return (Vector) this.children.clone();
981 		} catch (Exception e) {
982 			// this never happens, however, some Java compilers are so
983 			// braindead that they require this exception clause
984 			return null;
985 		}
986 	}
987 
988 	/**
989 	 * Returns the first child element by name .
990 	 */
991 	public XMLElement getChild(String name) {
992 		for (Enumeration en = this.children.elements(); en.hasMoreElements();) {
993 			XMLElement el = (XMLElement) en.nextElement();
994 			if (el.getName().equals(name)) {
995 				return el;
996 			}
997 		}
998 		return null;
999 	}
1000 
1001 	public XMLElement getChildOrNew(String name) {
1002 		XMLElement c = getChild(name);
1003 		if (c == null) {
1004 			c = addChild(name);
1005 		}
1006 		return c;
1007 	}
1008 
1009 	public int getChildCount(String name) {
1010 		int cnt = 0;
1011 		for (Enumeration en = this.children.elements(); en.hasMoreElements();) {
1012 			XMLElement el = (XMLElement) en.nextElement();
1013 			if (el.getName().equals(name)) {
1014 				cnt++;
1015 			}
1016 		}
1017 		return cnt;
1018 	}
1019 
1020 	public XMLElement getChild(String name, String attrValue) {
1021 		for (Enumeration en = this.children.elements(); en.hasMoreElements();) {
1022 			XMLElement el = (XMLElement) en.nextElement();
1023 			String elAttrValue = el.getStringAttribute("name");
1024 			if ((el.getName().equals(name)) && ((attrValue == elAttrValue) || attrValue.equals(elAttrValue))) {
1025 				return el;
1026 			}
1027 		}
1028 		return null;
1029 	}
1030 
1031 	public XMLElement getChild(String name, Map equalAttributesNameValue) {
1032 		nextChildren: for (Enumeration en = this.children.elements(); en.hasMoreElements();) {
1033 			XMLElement el = (XMLElement) en.nextElement();
1034 			if (el.getName().equals(name)) {
1035 				nextAttribute: for (Iterator i = equalAttributesNameValue.entrySet().iterator(); i.hasNext();) {
1036 					Map.Entry atrNameValue = (Map.Entry) i.next();
1037 					String attrValue = el.getStringAttribute((String) atrNameValue.getKey());
1038 					if (atrNameValue.getValue() == null) {
1039 						if (attrValue != null) {
1040 							continue nextChildren;
1041 						} else {
1042 							continue nextAttribute;
1043 						}
1044 					}
1045 					if (!atrNameValue.getValue().equals(attrValue)) {
1046 						continue nextChildren;
1047 					}
1048 				}
1049 				return el;
1050 			}
1051 		}
1052 		return null;
1053 	}
1054 
1055 	public int getChildInteger(String name, int defaultValue) {
1056 		XMLElement xml = this.getChild(name);
1057 		if (xml == null) {
1058 			return defaultValue;
1059 		}
1060 		try {
1061 			return Integer.parseInt(xml.getContent());
1062 		} catch (NumberFormatException e) {
1063 			return defaultValue;
1064 		}
1065 	}
1066 
1067 	public String getChildString(String name, String defaultValue) {
1068 		XMLElement xml = this.getChild(name);
1069 		if (xml == null) {
1070 			return defaultValue;
1071 		}
1072 		String v = xml.getContent();
1073 		if ((v == null) || (v.length() == 0)) {
1074 			return defaultValue;
1075 		}
1076 		return v;
1077 	}
1078 
1079 	/**
1080 	 * Returns the PCDATA content of the object. If there is no such content,
1081 	 * <CODE>null</CODE> is returned.
1082 	 * 
1083 	 * @deprecated Use {@link #getContent() getContent} instead.
1084 	 */
1085 	public String getContents() {
1086 		return this.getContent();
1087 	}
1088 
1089 	/**
1090 	 * Returns the PCDATA content of the object. If there is no such content,
1091 	 * <CODE>null</CODE> is returned.
1092 	 * 
1093 	 * @see nanoxml.XMLElement#setContent(java.lang.String) setContent(String)
1094 	 */
1095 	public String getContent() {
1096 		return this.contents;
1097 	}
1098 
1099 	/**
1100 	 * Returns the line nr in the source data on which the element is found.
1101 	 * This method returns <code>0</code> there is no associated source data.
1102 	 * 
1103 	 * <dl>
1104 	 * <dt><b>Postconditions:</b></dt>
1105 	 * <dd>
1106 	 * <ul>
1107 	 * <li><code>result >= 0</code>
1108 	 * </ul>
1109 	 * </dd>
1110 	 * </dl>
1111 	 */
1112 	public int getLineNr() {
1113 		return this.lineNr;
1114 	}
1115 
1116 	/**
1117 	 * Returns an attribute of the element. If the attribute doesn't exist,
1118 	 * <code>null</code> is returned.
1119 	 * 
1120 	 * @param name
1121 	 *            The name of the attribute.
1122 	 * 
1123 	 * </dl>
1124 	 * <dl>
1125 	 * <dt><b>Preconditions:</b></dt>
1126 	 * <dd>
1127 	 * <ul>
1128 	 * <li><code>name != null</code>
1129 	 * <li><code>name</code> is a valid XML identifier
1130 	 * </ul>
1131 	 * </dd>
1132 	 * </dl>
1133 	 * <dl>
1134 	 * 
1135 	 * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
1136 	 *      setAttribute(String, Object)
1137 	 * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
1138 	 *      removeAttribute(String)
1139 	 * @see nanoxml.XMLElement#enumerateAttributeNames()
1140 	 * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
1141 	 *      getAttribute(String, Object)
1142 	 * @see nanoxml.XMLElement#getAttribute(java.lang.String,
1143 	 *      java.util.Hashtable, java.lang.String, boolean) getAttribute(String,
1144 	 *      Hashtable, String, boolean)
1145 	 */
1146 	public Object getAttribute(String name) {
1147 		return this.getAttribute(name, null);
1148 	}
1149 
1150 	/**
1151 	 * Returns an attribute of the element. If the attribute doesn't exist,
1152 	 * <code>defaultValue</code> is returned.
1153 	 * 
1154 	 * @param name
1155 	 *            The name of the attribute.
1156 	 * @param defaultValue
1157 	 *            Key to use if the attribute is missing.
1158 	 * 
1159 	 * </dl>
1160 	 * <dl>
1161 	 * <dt><b>Preconditions:</b></dt>
1162 	 * <dd>
1163 	 * <ul>
1164 	 * <li><code>name != null</code>
1165 	 * <li><code>name</code> is a valid XML identifier
1166 	 * </ul>
1167 	 * </dd>
1168 	 * </dl>
1169 	 * <dl>
1170 	 * 
1171 	 * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
1172 	 *      setAttribute(String, Object)
1173 	 * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
1174 	 *      removeAttribute(String)
1175 	 * @see nanoxml.XMLElement#enumerateAttributeNames()
1176 	 * @see nanoxml.XMLElement#getAttribute(java.lang.String)
1177 	 *      getAttribute(String)
1178 	 * @see nanoxml.XMLElement#getAttribute(java.lang.String,
1179 	 *      java.util.Hashtable, java.lang.String, boolean) getAttribute(String,
1180 	 *      Hashtable, String, boolean)
1181 	 */
1182 	public Object getAttribute(String name, Object defaultValue) {
1183 		if (this.ignoreCase) {
1184 			name = name.toUpperCase();
1185 		}
1186 
1187 		Object value = this.attributes.get(name);
1188 
1189 		if (value == null) {
1190 			value = defaultValue;
1191 		}
1192 
1193 		return value;
1194 	}
1195 
1196 	/**
1197 	 * Returns an attribute by looking up a key in a hashtable. If the attribute
1198 	 * doesn't exist, the value corresponding to defaultKey is returned.
1199 	 * <P>
1200 	 * As an example, if valueSet contains the mapping <code>"one" =>
1201 	 * "1"</code>
1202 	 * and the element contains the attribute <code>attr="one"</code>, then
1203 	 * <code>getAttribute("attr", mapping, defaultKey, false)</code> returns
1204 	 * <code>"1"</code>.
1205 	 * 
1206 	 * @param name
1207 	 *            The name of the attribute.
1208 	 * @param valueSet
1209 	 *            Hashtable mapping keys to values.
1210 	 * @param defaultKey
1211 	 *            Key to use if the attribute is missing.
1212 	 * @param allowLiterals
1213 	 *            <code>true</code> if literals are valid.
1214 	 * 
1215 	 * </dl>
1216 	 * <dl>
1217 	 * <dt><b>Preconditions:</b></dt>
1218 	 * <dd>
1219 	 * <ul>
1220 	 * <li><code>name != null</code>
1221 	 * <li><code>name</code> is a valid XML identifier
1222 	 * <li><code>valueSet</code> != null
1223 	 * <li>the keys of <code>valueSet</code> are strings
1224 	 * </ul>
1225 	 * </dd>
1226 	 * </dl>
1227 	 * <dl>
1228 	 * 
1229 	 * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
1230 	 *      setAttribute(String, Object)
1231 	 * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
1232 	 *      removeAttribute(String)
1233 	 * @see nanoxml.XMLElement#enumerateAttributeNames()
1234 	 * @see nanoxml.XMLElement#getAttribute(java.lang.String)
1235 	 *      getAttribute(String)
1236 	 * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
1237 	 *      getAttribute(String, Object)
1238 	 */
1239 	public Object getAttribute(String name, Hashtable valueSet, String defaultKey, boolean allowLiterals) {
1240 		if (this.ignoreCase) {
1241 			name = name.toUpperCase();
1242 		}
1243 
1244 		Object key = this.attributes.get(name);
1245 		Object result;
1246 
1247 		if (key == null) {
1248 			key = defaultKey;
1249 		}
1250 
1251 		result = valueSet.get(key);
1252 
1253 		if (result == null) {
1254 			if (allowLiterals) {
1255 				result = key;
1256 			} else {
1257 				throw this.invalidValue(name, (String) key);
1258 			}
1259 		}
1260 
1261 		return result;
1262 	}
1263 
1264 	/**
1265 	 * Returns an attribute of the element. If the attribute doesn't exist,
1266 	 * <code>null</code> is returned.
1267 	 * 
1268 	 * @param name
1269 	 *            The name of the attribute.
1270 	 * 
1271 	 * </dl>
1272 	 * <dl>
1273 	 * <dt><b>Preconditions:</b></dt>
1274 	 * <dd>
1275 	 * <ul>
1276 	 * <li><code>name != null</code>
1277 	 * <li><code>name</code> is a valid XML identifier
1278 	 * </ul>
1279 	 * </dd>
1280 	 * </dl>
1281 	 * <dl>
1282 	 * 
1283 	 * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
1284 	 *      setAttribute(String, Object)
1285 	 * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
1286 	 *      removeAttribute(String)
1287 	 * @see nanoxml.XMLElement#enumerateAttributeNames()
1288 	 * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
1289