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