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