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 * String key = (String) enum.nextElement();<BR>
70 * String value = element.getStringAttribute(key);<BR>
71 * 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 * <title>The Title</title>
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 <<A
105 * href="mailto:cyberelf@mac.com">cyberelf@mac.com</A>>
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 &...; entities. The keys are the entity names
203 * without the & 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" => "<", "gt" => ">",
212 * "quot" => "\"", "apos" => "'",
213 * "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 >= 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 > 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