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