Author: saces
Date: 2008-03-30 15:23:56 +0000 (Sun, 30 Mar 2008)
New Revision: 18860

Added:
   trunk/plugins/ThawIndexBrowser/nanoxml/
   trunk/plugins/ThawIndexBrowser/nanoxml/XMLElement.java
   trunk/plugins/ThawIndexBrowser/nanoxml/XMLParseException.java
Modified:
   trunk/plugins/ThawIndexBrowser/ThawIndexBrowser.java
Log:
switch parser to nanoxml

Modified: trunk/plugins/ThawIndexBrowser/ThawIndexBrowser.java
===================================================================
--- trunk/plugins/ThawIndexBrowser/ThawIndexBrowser.java        2008-03-30 
14:12:01 UTC (rev 18859)
+++ trunk/plugins/ThawIndexBrowser/ThawIndexBrowser.java        2008-03-30 
15:23:56 UTC (rev 18860)
@@ -1,14 +1,12 @@
 package plugins.ThawIndexBrowser;

 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.net.MalformedURLException;
+import java.util.Enumeration;

-import nu.xom.Builder;
-import nu.xom.Document;
-import nu.xom.Element;
-import nu.xom.Elements;
-import nu.xom.ParsingException;
-import nu.xom.ValidityException;
+import plugins.ThawIndexBrowser.nanoxml.XMLElement;
+import plugins.ThawIndexBrowser.nanoxml.XMLParseException;
 import freenet.client.FetchException;
 import freenet.client.FetchResult;
 import freenet.client.HighLevelSimpleClient;
@@ -29,7 +27,7 @@

 public class ThawIndexBrowser implements FredPlugin, FredPluginThreadless, 
FredPluginHTTP {

-       public static String SELF_URI = 
"/plugins/plugins.ThawIndexBrowser.ThawIndexBrowser/";
+       public static String SELF_URI = 
"/plugins/plugins.IndexBrowser.IndexBrowser/";

        private PluginRespirator pr;

@@ -145,19 +143,19 @@
                        FetchResult content = client.fetch(uri, 90000);
                        String mime = content.getMimeType();
                        if (!"application/x-freenet-index".equals(mime)) {
-                               return makeErrorPage("Wrong mime type: " + 
mime, "Expectedmime type \"application/x-freenet-index\", but found \""
+                               return makeErrorPage("Wrong mime type: " + 
mime, "Expected mime type \"application/x-freenet-index\", but found \""
                                                + mime + "\".");
                        }

                        // data here, parse xml

-                       Builder builder = new Builder();
+                       XMLElement xmldoc = new XMLElement();

-                       Document doc = 
builder.build(content.asBucket().getInputStream());
+                       xmldoc.parseFromReader(new 
InputStreamReader(content.asBucket().getInputStream()));

                        // now print the result...

-                       return printIndexPage(uri, doc, add);
+                       return printIndexPage(uri, xmldoc, add);

                } catch (MalformedURLException e) {
                        Logger.error(this, "Invalid URI: " + index, e);
@@ -180,12 +178,9 @@
                } catch (IOException e) {
                        Logger.error(this, "IOError", e);
                        return makeErrorPage("IOError", "IOError while 
processing " + index + ": " + e.getLocalizedMessage());
-               } catch (ValidityException e) {
+               } catch (XMLParseException e) {
                        Logger.error(this, "DEBUG", e);
                        return makeErrorPage("Parser error", "Error while 
processing " + index + ": " + e.getLocalizedMessage());
-               } catch (ParsingException e) {
-                       Logger.error(this, "DEBUG", e);
-                       return makeErrorPage("Parser error", "Error while 
processing " + index + ": " + e.getLocalizedMessage());
                } catch (Exception e) {
                        Logger.error(this, "DEBUG", e);
                        return makeErrorPage("Error while processing " + index 
+ ": " + e.getLocalizedMessage());
@@ -206,101 +201,134 @@
                return browseBox;
        }

-       private String printIndexPage(FreenetURI uri, Document doc, boolean 
add) {
+       private String printIndexPage(FreenetURI uri, XMLElement doc, boolean 
add) {
                HTMLNode pageNode = pm.getPageNode("Index Browser", null);
                HTMLNode contentNode = pm.getContentNode(pageNode);

-               Element root = doc.getRootElement();
-               Element header = root.getFirstChildElement("header");
+               XMLElement root = doc;

                HTMLNode titleBox = pm.getInfobox("Index: " + uri);
-               Element titelelement = header.getFirstChildElement("title");
-               if (titelelement != null) {
-                       titleBox.addChild("#", "Titel: \u00a0 " + 
titelelement.getValue());
-                       titleBox.addChild("BR");
-               }
+               HTMLNode indexBox = pm.getInfobox("Links:");
+               HTMLNode fileBox = pm.getInfobox("Files:");

-               Element clientelement = header.getFirstChildElement("client");
-               if (clientelement != null) {
-                       titleBox.addChild("#", "Client: \u00a0 " + 
clientelement.getValue());
-                       titleBox.addChild("BR");
-               }
+               HTMLNode table = new HTMLNode("table", "class", "requests");
+               HTMLNode headerRow = table.addChild("tr", "class", 
"table-header");
+               headerRow.addChild("th");
+               headerRow.addChild("th", "Key/Name");
+               headerRow.addChild("th", "Mimetype");
+               headerRow.addChild("th", "Size");

-               Element dateelement = header.getFirstChildElement("date");
-               if (dateelement != null) {
-                       titleBox.addChild("#", "Date: \u00a0 " + 
dateelement.getValue());
-                       titleBox.addChild("BR");
-               }
+               boolean hasfiles = false;

-               contentNode.addChild(titleBox);
-               
-               Element indizies = root.getFirstChildElement("indexes");
+               Enumeration rootchilds = root.enumerateChildren();

-               if (indizies.getChildCount() > 0) {
-                       HTMLNode indexBox = pm.getInfobox("Links:");
-                       Elements es = indizies.getChildElements();
+               while (rootchilds.hasMoreElements()) {
+                       XMLElement rc = (XMLElement) rootchilds.nextElement();
+                       String name = rc.getName();
+                       if ("header".equals(name)) {
+                               Enumeration headerchilds = 
rc.enumerateChildren();
+                               String titel = null;
+                               String clientname = null;
+                               String date = null;
+                               String category = null;
+                               while (headerchilds.hasMoreElements()) {
+                                       XMLElement he = (XMLElement) 
headerchilds.nextElement();
+                                       String hename = he.getName();
+                                       if ("title".equals(hename)) {
+                                               titel = he.getContent();
+                                       } else if ("client".equals(hename)) {
+                                               clientname = he.getContent();
+                                       } else if ("date".equals(hename)) {
+                                               date = he.getContent();
+                                       } else if ("category".equals(hename)) {
+                                               category = he.getContent();
+                                       } else {
+                                               Logger
+                                                               .error(this, 
"Unexpected xml element '" + hename + "' at line: " + rc.getLineNr(), new Error(
+                                                                               
"DEBUG"));
+                                       }
+                               }

-                       for (int i = 0; i < es.size(); i++) {
-                               Element e = es.get(i);
-                               indexBox.addChild(new HTMLNode("a", "href", 
SELF_URI + "?key=" + e.getAttribute("key").getValue(), e.getAttribute(
-                                               "key").getValue()));
-                               indexBox.addChild("BR");
-                       }
-                       contentNode.addChild(indexBox);
-               }
+                               if (titel != null) {
+                                       titleBox.addChild("#", "Titel: \u00a0 " 
+ titel);
+                                       titleBox.addChild("BR");
+                               }
+                               if (clientname != null) {
+                                       titleBox.addChild("#", "Client: \u00a0 
" + clientname);
+                                       titleBox.addChild("BR");
+                               }
+                               if (date != null) {
+                                       titleBox.addChild("#", "Date: \u00a0 " 
+ date);
+                                       titleBox.addChild("BR");
+                               }
+                               if (category != null) {
+                                       titleBox.addChild("#", "Category: 
\u00a0 " + category);
+                                       titleBox.addChild("BR");
+                               }

-               Element files = root.getFirstChildElement("files");
+                       } else if ("indexes".equals(name)) {
+                               Enumeration indexchilds = 
rc.enumerateChildren();
+                               while (indexchilds.hasMoreElements()) {
+                                       XMLElement ie = (XMLElement) 
indexchilds.nextElement();
+                                       indexBox.addChild(new HTMLNode("a", 
"href", SELF_URI + "?key=" + ie.getStringAttribute("key"), ie
+                                                       
.getStringAttribute("key")));
+                                       indexBox.addChild("BR");
+                               }

-               if (files.getChildCount() > 0) {
-                       HTMLNode fileBox = pm.getInfobox("Files:");
-                       
-                       HTMLNode table = new HTMLNode("table", "class", 
"requests");
-                       HTMLNode headerRow = table.addChild("tr", "class", 
"table-header");
-                       headerRow.addChild("th");
-                       headerRow.addChild("th", "Key/Name");
-                       headerRow.addChild("th", "Mimetype");
-                       headerRow.addChild("th", "Size");               
-                       
-                       Elements es = files.getChildElements();
+                       } else if ("files".equals(name)) {
+                               Enumeration filechilds = rc.enumerateChildren();
+                               hasfiles = rc.countChildren() > 0;
+                               while (filechilds.hasMoreElements()) {
+                                       XMLElement fe = (XMLElement) 
filechilds.nextElement();
+                                       HTMLNode fileRow = table.addChild("tr");
+                                       String s = fe.getStringAttribute("key");
+                                       String s1;
+                                       try {
+                                               FreenetURI u = new 
FreenetURI(s);
+                                               if (add) {
+                                                       try {
+                                                               
fcp.makePersistentGlobalRequest(u, null, "forever", "disk");
+                                                       } catch 
(NotAllowedException e1) {
+                                                               
Logger.error(this, "DEBUG", e1);
+                                                       }
+                                               }

-                       for (int i = 0; i < es.size(); i++) {
-                               Element e = es.get(i);
-                               HTMLNode fileRow = table.addChild("tr");
-                               String s = e.getAttribute("key").getValue();
-                               String s1;
-                               try {
-                                       FreenetURI u = new FreenetURI(s);
-                                       if (add) {
-                                               try {
-                                                       
fcp.makePersistentGlobalRequest(u, null, "forever", "disk");
-                                               } catch (NotAllowedException 
e1) {
-                                                       Logger.error(this, 
"DEBUG", e1);
+                                               if (s.length() > 100) {
+                                                       s1 = s.substring(0, 12);
+                                                       s1 += "...";
+                                                       s1 += s.substring(85);
+                                                       // //s = s1;
+                                               } else {
+                                                       s1 = s;
                                                }
-                                               
+                                               
fileRow.addChild(createAddCell(s, uri.toString()));
+                                               fileRow.addChild(createCell(new 
HTMLNode("a", "href", "/?key=" + s, s1)));
+                                       } catch (MalformedURLException e1) {
+                                               fileRow.addChild(new 
HTMLNode("td"));
+                                               fileRow.addChild(createCell(new 
HTMLNode("#", s)));
                                        }
-                                       if (s.length() > 100) {
-                                               s1 = s.substring(0, 12);
-                                               s1 += "...";
-                                               s1 += s.substring(85);
-                                               //s = s1;
-                                       } else {
-                                               s1 = s;
-                                       }
-                                       fileRow.addChild(createAddCell(s, 
uri.toString()));
-                                       fileRow.addChild(createCell(new 
HTMLNode("a", "href", "/?key=" + s, s1)));
-                               } catch (MalformedURLException e1) {
-                                       fileRow.addChild(new HTMLNode("td"));
-                                       fileRow.addChild(createCell(new 
HTMLNode("#", s)));
+
+                                       fileRow.addChild(createCell(new 
HTMLNode("#", fe.getStringAttribute("mime"))));
+                                       fileRow.addChild(createCell(new 
HTMLNode("#", fe.getStringAttribute("size"))));
+
                                }
+                               HTMLNode allRow = table.addChild("tr");
+                               
allRow.addChild(createAddAllCell(uri.toString()));
+                               fileBox.addChild(table);

-                               fileRow.addChild(createCell(new HTMLNode("#", 
e.getAttribute("mime").getValue())));
-                               fileRow.addChild(createCell(new HTMLNode("#", 
e.getAttribute("size").getValue())));
+                       } else if ("comments".equals(name)) {
+
+                       } else {
+                               Logger.error(this, "Unexpected xml element '" + 
name + "' at line: " + rc.getLineNr(), new Error("DEBUG"));
                        }
-                       HTMLNode fileRow = table.addChild("tr");
-                       fileRow.addChild(createAddAllCell(uri.toString()));
-                       fileBox.addChild(table);
+               }
+
+               contentNode.addChild(titleBox);
+               contentNode.addChild(indexBox);
+
+               if (hasfiles)
                        contentNode.addChild(fileBox);
-               }
+
                contentNode.addChild(createUriBox());
                return pageNode.generate();
        }

Added: trunk/plugins/ThawIndexBrowser/nanoxml/XMLElement.java
===================================================================
--- trunk/plugins/ThawIndexBrowser/nanoxml/XMLElement.java                      
        (rev 0)
+++ trunk/plugins/ThawIndexBrowser/nanoxml/XMLElement.java      2008-03-30 
15:23:56 UTC (rev 18860)
@@ -0,0 +1,2604 @@
+/* XMLElement.java
+ *
+ * $Revision: 1.4 $
+ * $Date: 2002/03/24 10:27:59 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 Lite.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ *****************************************************************************/
+/*
+ * Changes:
+ *   - removed deprecaded functions
+ *   - renames enum. its a reserved word.
+ *   - renames to avoid "hiding" warnings.
+ */
+
+package plugins.ThawIndexBrowser.nanoxml;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.CharArrayReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+
+/**
+ * XMLElement is a representation of an XML object. The object is able to parse
+ * XML code.
+ * <P><DL>
+ * <DT><B>Parsing XML Data</B></DT>
+ * <DD>
+ * You can parse XML data using the following code:
+ * <UL><CODE>
+ * XMLElement xml = new XMLElement();<BR>
+ * FileReader reader = new FileReader("filename.xml");<BR>
+ * xml.parseFromReader(reader);
+ * </CODE></UL></DD></DL>
+ * <DL><DT><B>Retrieving Attributes</B></DT>
+ * <DD>
+ * You can enumerate the attributes of an element using the method
+ * {@link #enumerateAttributeNames() enumerateAttributeNames}.
+ * The attribute values can be retrieved using the method
+ * {@link #getStringAttribute(java.lang.String) getStringAttribute}.
+ * The following example shows how to list the attributes of an element:
+ * <UL><CODE>
+ * XMLElement element = ...;<BR>
+ * Enumeration enum = element.getAttributeNames();<BR>
+ * while (enum.hasMoreElements()) {<BR>
+ * &nbsp;&nbsp;&nbsp;&nbsp;String key = (String) enum.nextElement();<BR>
+ * &nbsp;&nbsp;&nbsp;&nbsp;String value = element.getStringAttribute(key);<BR>
+ * &nbsp;&nbsp;&nbsp;&nbsp;System.out.println(key + " = " + value);<BR>
+ * }
+ * </CODE></UL></DD></DL>
+ * <DL><DT><B>Retrieving Child Elements</B></DT>
+ * <DD>
+ * You can enumerate the children of an element using
+ * {@link #enumerateChildren() enumerateChildren}.
+ * The number of child elements can be retrieved using
+ * {@link #countChildren() countChildren}.
+ * </DD></DL>
+ * <DL><DT><B>Elements Containing Character Data</B></DT>
+ * <DD>
+ * If an elements contains character data, like in the following example:
+ * <UL><CODE>
+ * &lt;title&gt;The Title&lt;/title&gt;
+ * </CODE></UL>
+ * you can retrieve that data using the method
+ * {@link #getContent() getContent}.
+ * </DD></DL>
+ * <DL><DT><B>Subclassing XMLElement</B></DT>
+ * <DD>
+ * When subclassing XMLElement, you need to override the method
+ * {@link #createAnotherElement() createAnotherElement}
+ * which has to return a new copy of the receiver.
+ * </DD></DL>
+ * <P>
+ *
+ * @see nanoxml.XMLParseException
+ *
+ * @author Marc De Scheemaecker
+ *         &lt;<A href="mailto:cyberelf at mac.com">cyberelf at mac.com</A>&gt;
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
+ */
+public class XMLElement
+{
+
+    /**
+     * Serialization serial version ID.
+     */
+    static final long serialVersionUID = 6685035139346394777L;
+
+
+    /**
+     * Major version of NanoXML. Classes with the same major and minor
+     * version are binary compatible. Classes with the same major version
+     * are source compatible. If the major version is different, you may
+     * need to modify the client source code.
+     *
+     * @see nanoxml.XMLElement#NANOXML_MINOR_VERSION
+     */
+    public static final int NANOXML_MAJOR_VERSION = 2;
+    
+
+    /**
+     * Minor version of NanoXML. Classes with the same major and minor
+     * version are binary compatible. Classes with the same major version
+     * are source compatible. If the major version is different, you may
+     * need to modify the client source code.
+     *
+     * @see nanoxml.XMLElement#NANOXML_MAJOR_VERSION
+     */
+    public static final int NANOXML_MINOR_VERSION = 2;
+
+
+    /**
+     * The attributes given to the element.
+     *
+     * <dl><dt><b>Invariants:</b></dt><dd>
+     * <ul><li>The field can be empty.
+     *     <li>The field is never <code>null</code>.
+     *     <li>The keys and the values are strings.
+     * </ul></dd></dl>
+     */
+    private Hashtable attributes;
+
+
+    /**
+     * Child elements of the element.
+     *
+     * <dl><dt><b>Invariants:</b></dt><dd>
+     * <ul><li>The field can be empty.
+     *     <li>The field is never <code>null</code>.
+     *     <li>The elements are instances of <code>XMLElement</code>
+     *         or a subclass of <code>XMLElement</code>.
+     * </ul></dd></dl>
+     */
+    private Vector children;
+
+
+    /**
+     * The name of the element.
+     *
+     * <dl><dt><b>Invariants:</b></dt><dd>
+     * <ul><li>The field is <code>null</code> iff the element is not
+     *         initialized by either parse or setName.
+     *     <li>If the field is not <code>null</code>, it's not empty.
+     *     <li>If the field is not <code>null</code>, it contains a valid
+     *         XML identifier.
+     * </ul></dd></dl>
+     */
+    private String name;
+
+
+    /**
+     * The #PCDATA content of the object.
+     *
+     * <dl><dt><b>Invariants:</b></dt><dd>
+     * <ul><li>The field is <code>null</code> iff the element is not a
+     *         #PCDATA element.
+     *     <li>The field can be any string, including the empty string.
+     * </ul></dd></dl>
+     */
+    private String contents;
+
+
+    /**
+     * Conversion table for &amp;...; entities. The keys are the entity names
+     * without the &amp; and ; delimiters.
+     *
+     * <dl><dt><b>Invariants:</b></dt><dd>
+     * <ul><li>The field is never <code>null</code>.
+     *     <li>The field always contains the following associations:
+     *         "lt"&nbsp;=&gt;&nbsp;"&lt;", "gt"&nbsp;=&gt;&nbsp;"&gt;",
+     *         "quot"&nbsp;=&gt;&nbsp;"\"", "apos"&nbsp;=&gt;&nbsp;"'",
+     *         "amp"&nbsp;=&gt;&nbsp;"&amp;"
+     *     <li>The keys are strings
+     *     <li>The values are char arrays
+     * </ul></dd></dl>
+     */
+    private Hashtable entities;
+
+
+    /**
+     * The line number where the element starts.
+     *
+     * <dl><dt><b>Invariants:</b></dt><dd>
+     * <ul><li><code>lineNr &gt= 0</code>
+     * </ul></dd></dl>
+     */
+    private int lineNr;
+
+
+    /**
+     * <code>true</code> if the case of the element and attribute names
+     * are case insensitive.
+     */
+    private boolean ignoreCase;
+
+
+    /**
+     * <code>true</code> if the leading and trailing whitespace of #PCDATA
+     * sections have to be ignored.
+     */
+    private boolean ignoreWhitespace;
+
+
+    /**
+     * Character read too much.
+     * This character provides push-back functionality to the input reader
+     * without having to use a PushbackReader.
+     * If there is no such character, this field is '\0'.
+     */
+    private char charReadTooMuch;
+
+
+    /**
+     * The reader provided by the caller of the parse method.
+     *
+     * <dl><dt><b>Invariants:</b></dt><dd>
+     * <ul><li>The field is not <code>null</code> while the parse method
+     *         is running.
+     * </ul></dd></dl>
+     */
+    private Reader reader;
+
+
+    /**
+     * The current line number in the source content.
+     *
+     * <dl><dt><b>Invariants:</b></dt><dd>
+     * <ul><li>parserLineNr &gt; 0 while the parse method is running.
+     * </ul></dd></dl>
+     */
+    private int parserLineNr;
+
+
+    /**
+     * Creates and initializes a new XML element.
+     * Calling the construction is equivalent to:
+     * <ul><code>new XMLElement(new Hashtable(), false, true)
+     * </code></ul>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>countChildren() => 0
+     *     <li>enumerateChildren() => empty enumeration
+     *     <li>enumeratePropertyNames() => empty enumeration
+     *     <li>getChildren() => empty vector
+     *     <li>getContent() => ""
+     *     <li>getLineNr() => 0
+     *     <li>getName() => null
+     * </ul></dd></dl>
+     *
+     * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable)
+     *         XMLElement(Hashtable)
+     * @see nanoxml.XMLElement#XMLElement(boolean)
+     * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable,boolean)
+     *         XMLElement(Hashtable, boolean)
+     */
+    public XMLElement()
+    {
+        this(new Hashtable(), false, true, true);
+    }
+    
+
+    /**
+     * Creates and initializes a new XML element.
+     * Calling the construction is equivalent to:
+     * <ul><code>new XMLElement(entities, false, true)
+     * </code></ul>
+     *
+     * @param entities
+     *     The entity conversion table.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>entities != null</code>
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>countChildren() => 0
+     *     <li>enumerateChildren() => empty enumeration
+     *     <li>enumeratePropertyNames() => empty enumeration
+     *     <li>getChildren() => empty vector
+     *     <li>getContent() => ""
+     *     <li>getLineNr() => 0
+     *     <li>getName() => null
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#XMLElement()
+     * @see nanoxml.XMLElement#XMLElement(boolean)
+     * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable,boolean)
+     *         XMLElement(Hashtable, boolean)
+     */
+    public XMLElement(Hashtable entities2)
+    {
+        this(entities2, false, true, true);
+    }
+
+
+    /**
+     * Creates and initializes a new XML element.
+     * Calling the construction is equivalent to:
+     * <ul><code>new XMLElement(new Hashtable(), skipLeadingWhitespace, true)
+     * </code></ul>
+     *
+     * @param skipLeadingWhitespace
+     *     <code>true</code> if leading and trailing whitespace in PCDATA
+     *     content has to be removed.
+     *
+     * </dl><dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>countChildren() => 0
+     *     <li>enumerateChildren() => empty enumeration
+     *     <li>enumeratePropertyNames() => empty enumeration
+     *     <li>getChildren() => empty vector
+     *     <li>getContent() => ""
+     *     <li>getLineNr() => 0
+     *     <li>getName() => null
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#XMLElement()
+     * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable)
+     *         XMLElement(Hashtable)
+     * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable,boolean)
+     *         XMLElement(Hashtable, boolean)
+     */
+    public XMLElement(boolean skipLeadingWhitespace)
+    {
+        this(new Hashtable(), skipLeadingWhitespace, true, true);
+    }
+
+
+    /**
+     * Creates and initializes a new XML element.
+     * Calling the construction is equivalent to:
+     * <ul><code>new XMLElement(entities, skipLeadingWhitespace, true)
+     * </code></ul>
+     *
+     * @param entities
+     *     The entity conversion table.
+     * @param skipLeadingWhitespace
+     *     <code>true</code> if leading and trailing whitespace in PCDATA
+     *     content has to be removed.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>entities != null</code>
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>countChildren() => 0
+     *     <li>enumerateChildren() => empty enumeration
+     *     <li>enumeratePropertyNames() => empty enumeration
+     *     <li>getChildren() => empty vector
+     *     <li>getContent() => ""
+     *     <li>getLineNr() => 0
+     *     <li>getName() => null
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#XMLElement()
+     * @see nanoxml.XMLElement#XMLElement(boolean)
+     * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable)
+     *         XMLElement(Hashtable)
+     */
+    public XMLElement(Hashtable entities2,
+                      boolean   skipLeadingWhitespace)
+    {
+        this(entities2, skipLeadingWhitespace, true, true);
+    }
+
+
+    /**
+     * Creates and initializes a new XML element.
+     *
+     * @param entities
+     *     The entity conversion table.
+     * @param skipLeadingWhitespace
+     *     <code>true</code> if leading and trailing whitespace in PCDATA
+     *     content has to be removed.
+     * @param ignoreCase
+     *     <code>true</code> if the case of element and attribute names have
+     *     to be ignored.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>entities != null</code>
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>countChildren() => 0
+     *     <li>enumerateChildren() => empty enumeration
+     *     <li>enumeratePropertyNames() => empty enumeration
+     *     <li>getChildren() => empty vector
+     *     <li>getContent() => ""
+     *     <li>getLineNr() => 0
+     *     <li>getName() => null
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#XMLElement()
+     * @see nanoxml.XMLElement#XMLElement(boolean)
+     * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable)
+     *         XMLElement(Hashtable)
+     * @see nanoxml.XMLElement#XMLElement(java.util.Hashtable,boolean)
+     *         XMLElement(Hashtable, boolean)
+     */
+    public XMLElement(Hashtable entities2,
+                      boolean   skipLeadingWhitespace,
+                      boolean   ignoreCase2)
+    {
+        this(entities2, skipLeadingWhitespace, true, ignoreCase2);
+    }
+
+
+    /**
+     * Creates and initializes a new XML element.
+     * <P>
+     * This constructor should <I>only</I> be called from
+     * {@link #createAnotherElement() createAnotherElement}
+     * to create child elements.
+     *
+     * @param entities
+     *     The entity conversion table.
+     * @param skipLeadingWhitespace
+     *     <code>true</code> if leading and trailing whitespace in PCDATA
+     *     content has to be removed.
+     * @param fillBasicConversionTable
+     *     <code>true</code> if the basic entities need to be added to
+     *     the entity list.
+     * @param ignoreCase
+     *     <code>true</code> if the case of element and attribute names have
+     *     to be ignored.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>entities != null</code>
+     *     <li>if <code>fillBasicConversionTable == false</code>
+     *         then <code>entities</code> contains at least the following
+     *         entries: <code>amp</code>, <code>lt</code>, <code>gt</code>,
+     *         <code>apos</code> and <code>quot</code>
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>countChildren() => 0
+     *     <li>enumerateChildren() => empty enumeration
+     *     <li>enumeratePropertyNames() => empty enumeration
+     *     <li>getChildren() => empty vector
+     *     <li>getContent() => ""
+     *     <li>getLineNr() => 0
+     *     <li>getName() => null
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#createAnotherElement()
+     */
+    protected XMLElement(Hashtable entities2,
+                         boolean   skipLeadingWhitespace,
+                         boolean   fillBasicConversionTable,
+                         boolean   ignoreCase2)
+    {
+        this.ignoreWhitespace = skipLeadingWhitespace;
+        this.ignoreCase = ignoreCase2;
+        this.name = null;
+        this.contents = "";
+        this.attributes = new Hashtable();
+        this.children = new Vector();
+        this.entities = entities2;
+        this.lineNr = 0;
+        Enumeration e = this.entities.keys();
+        while (e.hasMoreElements()) {
+            Object key = e.nextElement();
+            Object value = this.entities.get(key);
+            if (value instanceof String) {
+                value = ((String) value).toCharArray();
+                this.entities.put(key, value);
+            }
+        }
+        if (fillBasicConversionTable) {
+            this.entities.put("amp", new char[] { '&' });
+            this.entities.put("quot", new char[] { '"' });
+            this.entities.put("apos", new char[] { '\'' });
+            this.entities.put("lt", new char[] { '<' });
+            this.entities.put("gt", new char[] { '>' });
+        }
+    }
+
+
+    /**
+     * Adds a child element.
+     *
+     * @param child
+     *     The child element to add.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>child != null</code>
+     *     <li><code>child.getName() != null</code>
+     *     <li><code>child</code> does not have a parent element
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>countChildren() => old.countChildren() + 1
+     *     <li>enumerateChildren() => old.enumerateChildren() + child
+     *     <li>getChildren() => old.enumerateChildren() + child
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#countChildren()
+     * @see nanoxml.XMLElement#enumerateChildren()
+     * @see nanoxml.XMLElement#getChildren()
+     * @see nanoxml.XMLElement#removeChild(nanoxml.XMLElement)
+     *         removeChild(XMLElement)
+     */
+    public void addChild(XMLElement child)
+    {
+        this.children.addElement(child);
+    }
+
+
+    /**
+     * Adds or modifies an attribute.
+     *
+     * @param name
+     *     The name of the attribute.
+     * @param value
+     *     The value of the attribute.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     *     <li><code>value != null</code>
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>enumerateAttributeNames()
+     *         => old.enumerateAttributeNames() + name
+     *     <li>getAttribute(name) => value
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
+     *         setDoubleAttribute(String, double)
+     * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
+     *         setIntAttribute(String, int)
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     * @see nanoxml.XMLElement#getAttribute(java.lang.String)
+     *         getAttribute(String)
+     * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
+     *         getAttribute(String, Object)
+     * @see nanoxml.XMLElement#getAttribute(java.lang.String,
+     *                                      java.util.Hashtable,
+     *                                      java.lang.String, boolean)
+     *         getAttribute(String, Hashtable, String, boolean)
+     * @see nanoxml.XMLElement#getStringAttribute(java.lang.String)
+     *         getStringAttribute(String)
+     * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
+     *                                            java.lang.String)
+     *         getStringAttribute(String, String)
+     * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
+     *                                            java.util.Hashtable,
+     *                                            java.lang.String, boolean)
+     *         getStringAttribute(String, Hashtable, String, boolean)
+     */
+    public void setAttribute(String name,
+                             Object value)
+    {
+        if (this.ignoreCase) {
+            name = name.toUpperCase();
+        }
+        this.attributes.put(name, value.toString());
+    }
+
+
+    /**
+     * Adds or modifies an attribute.
+     *
+     * @param name
+     *     The name of the attribute.
+     * @param value
+     *     The value of the attribute.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>enumerateAttributeNames()
+     *         => old.enumerateAttributeNames() + name
+     *     <li>getIntAttribute(name) => value
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
+     *         setDoubleAttribute(String, double)
+     * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
+     *         setAttribute(String, Object)
+     * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
+     *         removeAttribute(String)
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     * @see nanoxml.XMLElement#getIntAttribute(java.lang.String)
+     *         getIntAttribute(String)
+     * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
+     *         getIntAttribute(String, int)
+     * @see nanoxml.XMLElement#getIntAttribute(java.lang.String,
+     *                                         java.util.Hashtable,
+     *                                         java.lang.String, boolean)
+     *         getIntAttribute(String, Hashtable, String, boolean)
+     */
+    public void setIntAttribute(String name,
+                                int    value)
+    {
+        if (this.ignoreCase) {
+            name = name.toUpperCase();
+        }
+        this.attributes.put(name, Integer.toString(value));
+    }
+
+
+    /**
+     * Adds or modifies an attribute.
+     *
+     * @param name
+     *     The name of the attribute.
+     * @param value
+     *     The value of the attribute.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>enumerateAttributeNames()
+     *         => old.enumerateAttributeNames() + name
+     *     <li>getDoubleAttribute(name) => value
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
+     *         setIntAttribute(String, int)
+     * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
+     *         setAttribute(String, Object)
+     * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
+     *         removeAttribute(String)
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
+     *         getDoubleAttribute(String)
+     * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
+     *         getDoubleAttribute(String, double)
+     * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
+     *                                            java.util.Hashtable,
+     *                                            java.lang.String, boolean)
+     *         getDoubleAttribute(String, Hashtable, String, boolean)
+     */
+    public void setDoubleAttribute(String name,
+                                   double value)
+    {
+        if (this.ignoreCase) {
+            name = name.toUpperCase();
+        }
+        this.attributes.put(name, Double.toString(value));
+    }
+
+
+    /**
+     * Returns the number of child elements of the element.
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li><code>result >= 0</code>
+     * </ul></dd></dl>
+     *
+     * @see nanoxml.XMLElement#addChild(nanoxml.XMLElement)
+     *         addChild(XMLElement)
+     * @see nanoxml.XMLElement#enumerateChildren()
+     * @see nanoxml.XMLElement#getChildren()
+     * @see nanoxml.XMLElement#removeChild(nanoxml.XMLElement)
+     *         removeChild(XMLElement)
+     */
+    public int countChildren()
+    {
+        return this.children.size();
+    }
+
+
+    /**
+     * Enumerates the attribute names.
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li><code>result != null</code>
+     * </ul></dd></dl>
+     *
+     * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
+     *         setDoubleAttribute(String, double)
+     * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
+     *         setIntAttribute(String, int)
+     * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
+     *         setAttribute(String, Object)
+     * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
+     *         removeAttribute(String)
+     * @see nanoxml.XMLElement#getAttribute(java.lang.String)
+     *         getAttribute(String)
+     * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
+     *         getAttribute(String, String)
+     * @see nanoxml.XMLElement#getAttribute(java.lang.String,
+     *                                      java.util.Hashtable,
+     *                                      java.lang.String, boolean)
+     *         getAttribute(String, Hashtable, String, boolean)
+     * @see nanoxml.XMLElement#getStringAttribute(java.lang.String)
+     *         getStringAttribute(String)
+     * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
+     *                                            java.lang.String)
+     *         getStringAttribute(String, String)
+     * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
+     *                                            java.util.Hashtable,
+     *                                            java.lang.String, boolean)
+     *         getStringAttribute(String, Hashtable, String, boolean)
+     * @see nanoxml.XMLElement#getIntAttribute(java.lang.String)
+     *         getIntAttribute(String)
+     * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
+     *         getIntAttribute(String, int)
+     * @see nanoxml.XMLElement#getIntAttribute(java.lang.String,
+     *                                         java.util.Hashtable,
+     *                                         java.lang.String, boolean)
+     *         getIntAttribute(String, Hashtable, String, boolean)
+     * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
+     *         getDoubleAttribute(String)
+     * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
+     *         getDoubleAttribute(String, double)
+     * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
+     *                                            java.util.Hashtable,
+     *                                            java.lang.String, boolean)
+     *         getDoubleAttribute(String, Hashtable, String, boolean)
+     * @see nanoxml.XMLElement#getBooleanAttribute(java.lang.String,
+     *                                             java.lang.String,
+     *                                             java.lang.String, boolean)
+     *         getBooleanAttribute(String, String, String, boolean)
+     */
+    public Enumeration enumerateAttributeNames()
+    {
+        return this.attributes.keys();
+    }
+
+
+    /**
+     * Enumerates the child elements.
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li><code>result != null</code>
+     * </ul></dd></dl>
+     *
+     * @see nanoxml.XMLElement#addChild(nanoxml.XMLElement)
+     *         addChild(XMLElement)
+     * @see nanoxml.XMLElement#countChildren()
+     * @see nanoxml.XMLElement#getChildren()
+     * @see nanoxml.XMLElement#removeChild(nanoxml.XMLElement)
+     *         removeChild(XMLElement)
+     */
+    public Enumeration enumerateChildren()
+    {
+        return this.children.elements();
+    }
+
+
+    /**
+     * Returns the child elements as a Vector. It is safe to modify this
+     * Vector.
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li><code>result != null</code>
+     * </ul></dd></dl>
+     *
+     * @see nanoxml.XMLElement#addChild(nanoxml.XMLElement)
+     *         addChild(XMLElement)
+     * @see nanoxml.XMLElement#countChildren()
+     * @see nanoxml.XMLElement#enumerateChildren()
+     * @see nanoxml.XMLElement#removeChild(nanoxml.XMLElement)
+     *         removeChild(XMLElement)
+     */
+    public Vector getChildren()
+    {
+        try {
+            return (Vector) this.children.clone();
+        } catch (Exception e) {
+            // this never happens, however, some Java compilers are so
+            // braindead that they require this exception clause
+            return null;
+        }
+    }
+
+
+    /**
+     * Returns the PCDATA content of the object. If there is no such content,
+     * <CODE>null</CODE> is returned.
+     *
+     * @see nanoxml.XMLElement#setContent(java.lang.String)
+     *         setContent(String)
+     */
+    public String getContent()
+    {
+        return this.contents;
+    }
+
+
+    /**
+     * Returns the line nr in the source data on which the element is found.
+     * This method returns <code>0</code> there is no associated source data.
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li><code>result >= 0</code>
+     * </ul></dd></dl>
+     */
+    public int getLineNr()
+    {
+        return this.lineNr;
+    }
+
+
+    /**
+     * Returns an attribute of the element.
+     * If the attribute doesn't exist, <code>null</code> is returned.
+     *
+     * @param name The name of the attribute.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
+     *         setAttribute(String, Object)
+     * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
+     *         removeAttribute(String)
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
+     *         getAttribute(String, Object)
+     * @see nanoxml.XMLElement#getAttribute(java.lang.String,
+     *                                      java.util.Hashtable,
+     *                                      java.lang.String, boolean)
+     *         getAttribute(String, Hashtable, String, boolean)
+     */
+    public Object getAttribute(String name)
+    {
+        return this.getAttribute(name, null);
+    }
+
+
+    /**
+     * Returns an attribute of the element.
+     * If the attribute doesn't exist, <code>defaultValue</code> is returned.
+     *
+     * @param name         The name of the attribute.
+     * @param defaultValue Key to use if the attribute is missing.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
+     *         setAttribute(String, Object)
+     * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
+     *         removeAttribute(String)
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     * @see nanoxml.XMLElement#getAttribute(java.lang.String)
+     *         getAttribute(String)
+     * @see nanoxml.XMLElement#getAttribute(java.lang.String,
+     *                                      java.util.Hashtable,
+     *                                      java.lang.String, boolean)
+     *         getAttribute(String, Hashtable, String, boolean)
+     */
+    public Object getAttribute(String name,
+                               Object defaultValue)
+    {
+        if (this.ignoreCase) {
+            name = name.toUpperCase();
+        }
+        Object value = this.attributes.get(name);
+        if (value == null) {
+            value = defaultValue;
+        }
+        return value;
+    }
+
+
+    /**
+     * Returns an attribute by looking up a key in a hashtable.
+     * If the attribute doesn't exist, the value corresponding to defaultKey
+     * is returned.
+     * <P>
+     * As an example, if valueSet contains the mapping <code>"one" =>
+     * "1"</code>
+     * and the element contains the attribute <code>attr="one"</code>, then
+     * <code>getAttribute("attr", mapping, defaultKey, false)</code> returns
+     * <code>"1"</code>.
+     *
+     * @param name
+     *     The name of the attribute.
+     * @param valueSet
+     *     Hashtable mapping keys to values.
+     * @param defaultKey
+     *     Key to use if the attribute is missing.
+     * @param allowLiterals
+     *     <code>true</code> if literals are valid.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     *     <li><code>valueSet</code> != null
+     *     <li>the keys of <code>valueSet</code> are strings
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
+     *         setAttribute(String, Object)
+     * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
+     *         removeAttribute(String)
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     * @see nanoxml.XMLElement#getAttribute(java.lang.String)
+     *         getAttribute(String)
+     * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
+     *         getAttribute(String, Object)
+     */
+    public Object getAttribute(String    name,
+                               Hashtable valueSet,
+                               String    defaultKey,
+                               boolean   allowLiterals)
+    {
+        if (this.ignoreCase) {
+            name = name.toUpperCase();
+        }
+        Object key = this.attributes.get(name);
+        Object result;
+        if (key == null) {
+            key = defaultKey;
+        }
+        result = valueSet.get(key);
+        if (result == null) {
+            if (allowLiterals) {
+                result = key;
+            } else {
+                throw this.invalidValue(name, (String) key);
+            }
+        }
+        return result;
+    }
+
+
+    /**
+     * Returns an attribute of the element.
+     * If the attribute doesn't exist, <code>null</code> is returned.
+     *
+     * @param name The name of the attribute.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
+     *         setAttribute(String, Object)
+     * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
+     *         removeAttribute(String)
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
+     *                                            java.lang.String)
+     *         getStringAttribute(String, String)
+     * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
+     *                                            java.util.Hashtable,
+     *                                            java.lang.String, boolean)
+     *         getStringAttribute(String, Hashtable, String, boolean)
+     */
+    public String getStringAttribute(String name)
+    {
+        return this.getStringAttribute(name, null);
+    }
+
+
+    /**
+     * Returns an attribute of the element.
+     * If the attribute doesn't exist, <code>defaultValue</code> is returned.
+     *
+     * @param name         The name of the attribute.
+     * @param defaultValue Key to use if the attribute is missing.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
+     *         setAttribute(String, Object)
+     * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
+     *         removeAttribute(String)
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     * @see nanoxml.XMLElement#getStringAttribute(java.lang.String)
+     *         getStringAttribute(String)
+     * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
+     *                                            java.util.Hashtable,
+     *                                            java.lang.String, boolean)
+     *         getStringAttribute(String, Hashtable, String, boolean)
+     */
+    public String getStringAttribute(String name,
+                                     String defaultValue)
+    {
+        return (String) this.getAttribute(name, defaultValue);
+    }
+
+
+    /**
+     * Returns an attribute by looking up a key in a hashtable.
+     * If the attribute doesn't exist, the value corresponding to defaultKey
+     * is returned.
+     * <P>
+     * As an example, if valueSet contains the mapping <code>"one" =>
+     * "1"</code>
+     * and the element contains the attribute <code>attr="one"</code>, then
+     * <code>getAttribute("attr", mapping, defaultKey, false)</code> returns
+     * <code>"1"</code>.
+     *
+     * @param name
+     *     The name of the attribute.
+     * @param valueSet
+     *     Hashtable mapping keys to values.
+     * @param defaultKey
+     *     Key to use if the attribute is missing.
+     * @param allowLiterals
+     *     <code>true</code> if literals are valid.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     *     <li><code>valueSet</code> != null
+     *     <li>the keys of <code>valueSet</code> are strings
+     *     <li>the values of <code>valueSet</code> are strings
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
+     *         setAttribute(String, Object)
+     * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
+     *         removeAttribute(String)
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     * @see nanoxml.XMLElement#getStringAttribute(java.lang.String)
+     *         getStringAttribute(String)
+     * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
+     *                                            java.lang.String)
+     *         getStringAttribute(String, String)
+     */
+    public String getStringAttribute(String    name,
+                                     Hashtable valueSet,
+                                     String    defaultKey,
+                                     boolean   allowLiterals)
+    {
+        return (String) this.getAttribute(name, valueSet, defaultKey,
+                                          allowLiterals);
+    }
+
+
+    /**
+     * Returns an attribute of the element.
+     * If the attribute doesn't exist, <code>0</code> is returned.
+     *
+     * @param name The name of the attribute.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
+     *         setIntAttribute(String, int)
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
+     *         getIntAttribute(String, int)
+     * @see nanoxml.XMLElement#getIntAttribute(java.lang.String,
+     *                                         java.util.Hashtable,
+     *                                         java.lang.String, boolean)
+     *         getIntAttribute(String, Hashtable, String, boolean)
+     */
+    public int getIntAttribute(String name)
+    {
+        return this.getIntAttribute(name, 0);
+    }
+
+
+    /**
+     * Returns an attribute of the element.
+     * If the attribute doesn't exist, <code>defaultValue</code> is returned.
+     *
+     * @param name         The name of the attribute.
+     * @param defaultValue Key to use if the attribute is missing.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
+     *         setIntAttribute(String, int)
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     * @see nanoxml.XMLElement#getIntAttribute(java.lang.String)
+     *         getIntAttribute(String)
+     * @see nanoxml.XMLElement#getIntAttribute(java.lang.String,
+     *                                         java.util.Hashtable,
+     *                                         java.lang.String, boolean)
+     *         getIntAttribute(String, Hashtable, String, boolean)
+     */
+    public int getIntAttribute(String name,
+                               int    defaultValue)
+    {
+        if (this.ignoreCase) {
+            name = name.toUpperCase();
+        }
+        String value = (String) this.attributes.get(name);
+        if (value == null) {
+            return defaultValue;
+        } else {
+            try {
+                return Integer.parseInt(value);
+            } catch (NumberFormatException e) {
+                throw this.invalidValue(name, value);
+            }
+        }
+    }
+
+
+    /**
+     * Returns an attribute by looking up a key in a hashtable.
+     * If the attribute doesn't exist, the value corresponding to defaultKey
+     * is returned.
+     * <P>
+     * As an example, if valueSet contains the mapping <code>"one" => 1</code>
+     * and the element contains the attribute <code>attr="one"</code>, then
+     * <code>getIntAttribute("attr", mapping, defaultKey, false)</code> returns
+     * <code>1</code>.
+     *
+     * @param name
+     *     The name of the attribute.
+     * @param valueSet
+     *     Hashtable mapping keys to values.
+     * @param defaultKey
+     *     Key to use if the attribute is missing.
+     * @param allowLiteralNumbers
+     *     <code>true</code> if literal numbers are valid.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     *     <li><code>valueSet</code> != null
+     *     <li>the keys of <code>valueSet</code> are strings
+     *     <li>the values of <code>valueSet</code> are Integer objects
+     *     <li><code>defaultKey</code> is either <code>null</code>, a
+     *         key in <code>valueSet</code> or an integer.
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
+     *         setIntAttribute(String, int)
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     * @see nanoxml.XMLElement#getIntAttribute(java.lang.String)
+     *         getIntAttribute(String)
+     * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
+     *         getIntAttribute(String, int)
+     */
+    public int getIntAttribute(String    name,
+                               Hashtable valueSet,
+                               String    defaultKey,
+                               boolean   allowLiteralNumbers)
+    {
+        if (this.ignoreCase) {
+            name = name.toUpperCase();
+        }
+        Object key = this.attributes.get(name);
+        Integer result;
+        if (key == null) {
+            key = defaultKey;
+        }
+        try {
+            result = (Integer) valueSet.get(key);
+        } catch (ClassCastException e) {
+            throw this.invalidValueSet(name);
+        }
+        if (result == null) {
+            if (! allowLiteralNumbers) {
+                throw this.invalidValue(name, (String) key);
+            }
+            try {
+                result = Integer.valueOf((String) key);
+            } catch (NumberFormatException e) {
+                throw this.invalidValue(name, (String) key);
+            }
+        }
+        return result.intValue();
+    }
+
+
+    /**
+     * Returns an attribute of the element.
+     * If the attribute doesn't exist, <code>0.0</code> is returned.
+     *
+     * @param name The name of the attribute.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
+     *         setDoubleAttribute(String, double)
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
+     *         getDoubleAttribute(String, double)
+     * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
+     *                                            java.util.Hashtable,
+     *                                            java.lang.String, boolean)
+     *         getDoubleAttribute(String, Hashtable, String, boolean)
+     */
+    public double getDoubleAttribute(String name)
+    {
+        return this.getDoubleAttribute(name, 0.);
+    }
+
+
+    /**
+     * Returns an attribute of the element.
+     * If the attribute doesn't exist, <code>defaultValue</code> is returned.
+     *
+     * @param name         The name of the attribute.
+     * @param defaultValue Key to use if the attribute is missing.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
+     *         setDoubleAttribute(String, double)
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
+     *         getDoubleAttribute(String)
+     * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
+     *                                            java.util.Hashtable,
+     *                                            java.lang.String, boolean)
+     *         getDoubleAttribute(String, Hashtable, String, boolean)
+     */
+    public double getDoubleAttribute(String name,
+                                     double defaultValue)
+    {
+        if (this.ignoreCase) {
+            name = name.toUpperCase();
+        }
+        String value = (String) this.attributes.get(name);
+        if (value == null) {
+            return defaultValue;
+        } else {
+            try {
+                return Double.valueOf(value).doubleValue();
+            } catch (NumberFormatException e) {
+                throw this.invalidValue(name, value);
+            }
+        }
+    }
+
+
+    /**
+     * Returns an attribute by looking up a key in a hashtable.
+     * If the attribute doesn't exist, the value corresponding to defaultKey
+     * is returned.
+     * <P>
+     * As an example, if valueSet contains the mapping <code>"one" =&gt;
+     * 1.0</code>
+     * and the element contains the attribute <code>attr="one"</code>, then
+     * <code>getDoubleAttribute("attr", mapping, defaultKey, false)</code>
+     * returns <code>1.0</code>.
+     *
+     * @param name
+     *     The name of the attribute.
+     * @param valueSet
+     *     Hashtable mapping keys to values.
+     * @param defaultKey
+     *     Key to use if the attribute is missing.
+     * @param allowLiteralNumbers
+     *     <code>true</code> if literal numbers are valid.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     *     <li><code>valueSet != null</code>
+     *     <li>the keys of <code>valueSet</code> are strings
+     *     <li>the values of <code>valueSet</code> are Double objects
+     *     <li><code>defaultKey</code> is either <code>null</code>, a
+     *         key in <code>valueSet</code> or a double.
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
+     *         setDoubleAttribute(String, double)
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
+     *         getDoubleAttribute(String)
+     * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
+     *         getDoubleAttribute(String, double)
+     */
+    public double getDoubleAttribute(String    name,
+                                     Hashtable valueSet,
+                                     String    defaultKey,
+                                     boolean   allowLiteralNumbers)
+    {
+        if (this.ignoreCase) {
+            name = name.toUpperCase();
+        }
+        Object key = this.attributes.get(name);
+        Double result;
+        if (key == null) {
+            key = defaultKey;
+        }
+        try {
+            result = (Double) valueSet.get(key);
+        } catch (ClassCastException e) {
+            throw this.invalidValueSet(name);
+        }
+        if (result == null) {
+            if (! allowLiteralNumbers) {
+                throw this.invalidValue(name, (String) key);
+            }
+            try {
+                result = Double.valueOf((String) key);
+            } catch (NumberFormatException e) {
+                throw this.invalidValue(name, (String) key);
+            }
+        }
+        return result.doubleValue();
+    }
+
+
+    /**
+     * Returns an attribute of the element.
+     * If the attribute doesn't exist, <code>defaultValue</code> is returned.
+     * If the value of the attribute is equal to <code>trueValue</code>,
+     * <code>true</code> is returned.
+     * If the value of the attribute is equal to <code>falseValue</code>,
+     * <code>false</code> is returned.
+     * If the value doesn't match <code>trueValue</code> or
+     * <code>falseValue</code>, an exception is thrown.
+     *
+     * @param name         The name of the attribute.
+     * @param trueValue    The value associated with <code>true</code>.
+     * @param falseValue   The value associated with <code>true</code>.
+     * @param defaultValue Value to use if the attribute is missing.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     *     <li><code>trueValue</code> and <code>falseValue</code>
+     *         are different strings.
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
+     *         setAttribute(String, Object)
+     * @see nanoxml.XMLElement#removeAttribute(java.lang.String)
+     *         removeAttribute(String)
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     */
+    public boolean getBooleanAttribute(String  name,
+                                       String  trueValue,
+                                       String  falseValue,
+                                       boolean defaultValue)
+    {
+        if (this.ignoreCase) {
+            name = name.toUpperCase();
+        }
+        Object value = this.attributes.get(name);
+        if (value == null) {
+            return defaultValue;
+        } else if (value.equals(trueValue)) {
+            return true;
+        } else if (value.equals(falseValue)) {
+            return false;
+        } else {
+            throw this.invalidValue(name, (String) value);
+        }
+    }
+
+
+    /**
+     * Returns the name of the element.
+     *
+     * @see nanoxml.XMLElement#setName(java.lang.String) setName(String)
+     */
+    public String getName()
+    {
+        return this.name;
+    }
+
+
+    /**
+     * Reads one XML element from a java.io.Reader and parses it.
+     *
+     * @param reader
+     *     The reader from which to retrieve the XML data.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>reader != null</code>
+     *     <li><code>reader</code> is not closed
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>the state of the receiver is updated to reflect the XML element
+     *         parsed from the reader
+     *     <li>the reader points to the first character following the last
+     *         '&gt;' character of the XML element
+     * </ul></dd></dl><dl>
+     *
+     * @throws java.io.IOException
+     *     If an error occured while reading the input.
+     * @throws nanoxml.XMLParseException
+     *     If an error occured while parsing the read data.
+     */
+    public void parseFromReader(Reader reader)
+    throws IOException, XMLParseException
+    {
+        this.parseFromReader(reader, /*startingLineNr*/ 1);
+    }
+
+
+    /**
+     * Reads one XML element from a java.io.Reader and parses it.
+     *
+     * @param reader
+     *     The reader from which to retrieve the XML data.
+     * @param startingLineNr
+     *     The line number of the first line in the data.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>reader != null</code>
+     *     <li><code>reader</code> is not closed
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>the state of the receiver is updated to reflect the XML element
+     *         parsed from the reader
+     *     <li>the reader points to the first character following the last
+     *         '&gt;' character of the XML element
+     * </ul></dd></dl><dl>
+     *
+     * @throws java.io.IOException
+     *     If an error occured while reading the input.
+     * @throws nanoxml.XMLParseException
+     *     If an error occured while parsing the read data.
+     */
+    public void parseFromReader(Reader reader2,
+                                int    startingLineNr)
+        throws IOException, XMLParseException
+    {
+        this.name = null;
+        this.contents = "";
+        this.attributes = new Hashtable();
+        this.children = new Vector();
+        this.charReadTooMuch = '\0';
+        this.reader = reader2;
+        this.parserLineNr = startingLineNr;
+
+        for (;;) {
+            char ch = this.scanWhitespace();
+
+            if (ch != '<') {
+                throw this.expectedInput("<");
+            }
+
+            ch = this.readChar();
+
+            if ((ch == '!') || (ch == '?')) {
+                this.skipSpecialTag(0);
+            } else {
+                this.unreadChar(ch);
+                this.scanElement(this);
+                return;
+            }
+        }
+    }
+
+
+    /**
+     * Reads one XML element from a String and parses it.
+     *
+     * @param reader
+     *     The reader from which to retrieve the XML data.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>string != null</code>
+     *     <li><code>string.length() &gt; 0</code>
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>the state of the receiver is updated to reflect the XML element
+     *         parsed from the reader
+     * </ul></dd></dl><dl>
+     *
+     * @throws nanoxml.XMLParseException
+     *     If an error occured while parsing the string.
+     */
+    public void parseString(String string)
+        throws XMLParseException
+    {
+        try {
+            this.parseFromReader(new StringReader(string),
+                                 /*startingLineNr*/ 1);
+        } catch (IOException e) {
+            // Java exception handling suxx
+        }
+    }
+
+
+    /**
+     * Reads one XML element from a String and parses it.
+     *
+     * @param reader
+     *     The reader from which to retrieve the XML data.
+     * @param offset
+     *     The first character in <code>string</code> to scan.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>string != null</code>
+     *     <li><code>offset &lt; string.length()</code>
+     *     <li><code>offset &gt;= 0</code>
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>the state of the receiver is updated to reflect the XML element
+     *         parsed from the reader
+     * </ul></dd></dl><dl>
+     *
+     * @throws nanoxml.XMLParseException
+     *     If an error occured while parsing the string.
+     */
+    public void parseString(String string,
+                            int    offset)
+        throws XMLParseException
+    {
+        this.parseString(string.substring(offset));
+    }
+
+
+    /**
+     * Reads one XML element from a String and parses it.
+     *
+     * @param reader
+     *     The reader from which to retrieve the XML data.
+     * @param offset
+     *     The first character in <code>string</code> to scan.
+     * @param end
+     *     The character where to stop scanning.
+     *     This character is not scanned.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>string != null</code>
+     *     <li><code>end &lt;= string.length()</code>
+     *     <li><code>offset &lt; end</code>
+     *     <li><code>offset &gt;= 0</code>
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>the state of the receiver is updated to reflect the XML element
+     *         parsed from the reader
+     * </ul></dd></dl><dl>
+     *
+     * @throws nanoxml.XMLParseException
+     *     If an error occured while parsing the string.
+     */
+    public void parseString(String string,
+                            int    offset,
+                            int    end)
+        throws XMLParseException
+    {
+        this.parseString(string.substring(offset, end));
+    }
+
+
+    /**
+     * Reads one XML element from a String and parses it.
+     *
+     * @param reader
+     *     The reader from which to retrieve the XML data.
+     * @param offset
+     *     The first character in <code>string</code> to scan.
+     * @param end
+     *     The character where to stop scanning.
+     *     This character is not scanned.
+     * @param startingLineNr
+     *     The line number of the first line in the data.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>string != null</code>
+     *     <li><code>end &lt;= string.length()</code>
+     *     <li><code>offset &lt; end</code>
+     *     <li><code>offset &gt;= 0</code>
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>the state of the receiver is updated to reflect the XML element
+     *         parsed from the reader
+     * </ul></dd></dl><dl>
+     *
+     * @throws nanoxml.XMLParseException
+     *     If an error occured while parsing the string.
+     */
+    public void parseString(String string,
+                            int    offset,
+                            int    end,
+                            int    startingLineNr)
+        throws XMLParseException
+    {
+        string = string.substring(offset, end);
+        try {
+            this.parseFromReader(new StringReader(string), startingLineNr);
+        } catch (IOException e) {
+            // Java exception handling suxx
+        }
+    }
+
+
+    /**
+     * Reads one XML element from a char array and parses it.
+     *
+     * @param reader
+     *     The reader from which to retrieve the XML data.
+     * @param offset
+     *     The first character in <code>string</code> to scan.
+     * @param end
+     *     The character where to stop scanning.
+     *     This character is not scanned.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>input != null</code>
+     *     <li><code>end &lt;= input.length</code>
+     *     <li><code>offset &lt; end</code>
+     *     <li><code>offset &gt;= 0</code>
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>the state of the receiver is updated to reflect the XML element
+     *         parsed from the reader
+     * </ul></dd></dl><dl>
+     *
+     * @throws nanoxml.XMLParseException
+     *     If an error occured while parsing the string.
+     */
+    public void parseCharArray(char[] input,
+                               int    offset,
+                               int    end)
+        throws XMLParseException
+    {
+        this.parseCharArray(input, offset, end, /*startingLineNr*/ 1);
+    }
+
+
+    /**
+     * Reads one XML element from a char array and parses it.
+     *
+     * @param reader
+     *     The reader from which to retrieve the XML data.
+     * @param offset
+     *     The first character in <code>string</code> to scan.
+     * @param end
+     *     The character where to stop scanning.
+     *     This character is not scanned.
+     * @param startingLineNr
+     *     The line number of the first line in the data.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>input != null</code>
+     *     <li><code>end &lt;= input.length</code>
+     *     <li><code>offset &lt; end</code>
+     *     <li><code>offset &gt;= 0</code>
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>the state of the receiver is updated to reflect the XML element
+     *         parsed from the reader
+     * </ul></dd></dl><dl>
+     *
+     * @throws nanoxml.XMLParseException
+     *     If an error occured while parsing the string.
+     */
+    public void parseCharArray(char[] input,
+                               int    offset,
+                               int    end,
+                               int    startingLineNr)
+        throws XMLParseException
+    {
+        try {
+            Reader reader = new CharArrayReader(input, offset, end);
+            this.parseFromReader(reader, startingLineNr);
+        } catch (IOException e) {
+            // This exception will never happen.
+        }
+    }
+
+
+    /**
+     * Removes a child element.
+     *
+     * @param child
+     *     The child element to remove.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>child != null</code>
+     *     <li><code>child</code> is a child element of the receiver
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>countChildren() => old.countChildren() - 1
+     *     <li>enumerateChildren() => old.enumerateChildren() - child
+     *     <li>getChildren() => old.enumerateChildren() - child
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#addChild(nanoxml.XMLElement)
+     *         addChild(XMLElement)
+     * @see nanoxml.XMLElement#countChildren()
+     * @see nanoxml.XMLElement#enumerateChildren()
+     * @see nanoxml.XMLElement#getChildren()
+     */
+    public void removeChild(XMLElement child)
+    {
+        this.children.removeElement(child);
+    }
+
+
+    /**
+     * Removes an attribute.
+     *
+     * @param name
+     *     The name of the attribute.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>enumerateAttributeNames()
+     *         => old.enumerateAttributeNames() - name
+     *     <li>getAttribute(name) => <code>null</code>
+     * </ul></dd></dl><dl>
+     *
+     * @see nanoxml.XMLElement#enumerateAttributeNames()
+     * @see nanoxml.XMLElement#setDoubleAttribute(java.lang.String, double)
+     *         setDoubleAttribute(String, double)
+     * @see nanoxml.XMLElement#setIntAttribute(java.lang.String, int)
+     *         setIntAttribute(String, int)
+     * @see nanoxml.XMLElement#setAttribute(java.lang.String, java.lang.Object)
+     *         setAttribute(String, Object)
+     * @see nanoxml.XMLElement#getAttribute(java.lang.String)
+     *         getAttribute(String)
+     * @see nanoxml.XMLElement#getAttribute(java.lang.String, java.lang.Object)
+     *         getAttribute(String, Object)
+     * @see nanoxml.XMLElement#getAttribute(java.lang.String,
+     *                                      java.util.Hashtable,
+     *                                      java.lang.String, boolean)
+     *         getAttribute(String, Hashtable, String, boolean)
+     * @see nanoxml.XMLElement#getStringAttribute(java.lang.String)
+     *         getStringAttribute(String)
+     * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
+     *                                            java.lang.String)
+     *         getStringAttribute(String, String)
+     * @see nanoxml.XMLElement#getStringAttribute(java.lang.String,
+     *                                            java.util.Hashtable,
+     *                                            java.lang.String, boolean)
+     *         getStringAttribute(String, Hashtable, String, boolean)
+     * @see nanoxml.XMLElement#getIntAttribute(java.lang.String)
+     *         getIntAttribute(String)
+     * @see nanoxml.XMLElement#getIntAttribute(java.lang.String, int)
+     *         getIntAttribute(String, int)
+     * @see nanoxml.XMLElement#getIntAttribute(java.lang.String,
+     *                                         java.util.Hashtable,
+     *                                         java.lang.String, boolean)
+     *         getIntAttribute(String, Hashtable, String, boolean)
+     * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String)
+     *         getDoubleAttribute(String)
+     * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String, double)
+     *         getDoubleAttribute(String, double)
+     * @see nanoxml.XMLElement#getDoubleAttribute(java.lang.String,
+     *                                            java.util.Hashtable,
+     *                                            java.lang.String, boolean)
+     *         getDoubleAttribute(String, Hashtable, String, boolean)
+     * @see nanoxml.XMLElement#getBooleanAttribute(java.lang.String,
+     *                                             java.lang.String,
+     *                                             java.lang.String, boolean)
+     *         getBooleanAttribute(String, String, String, boolean)
+     */
+    public void removeAttribute(String name)
+    {
+        if (this.ignoreCase) {
+            name = name.toUpperCase();
+        }
+        this.attributes.remove(name);
+    }
+
+
+    /**
+     * Creates a new similar XML element.
+     * <P>
+     * You should override this method when subclassing XMLElement.
+     */
+    protected XMLElement createAnotherElement()
+    {
+        return new XMLElement(this.entities,
+                              this.ignoreWhitespace,
+                              false,
+                              this.ignoreCase);
+    }
+
+
+    /**
+     * Changes the content string.
+     *
+     * @param content
+     *     The new content string.
+     */
+    public void setContent(String content)
+    {
+        this.contents = content;
+    }
+
+
+    /**
+     * Changes the name of the element.
+     *
+     * @param name
+     *     The new name.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name</code> is a valid XML identifier
+     * </ul></dd></dl>
+     *
+     * @see nanoxml.XMLElement#getName()
+     */
+    public void setName(String name2)
+    {
+        this.name = name2;
+    }
+
+
+    /**
+     * Writes the XML element to a string.
+     *
+     * @see nanoxml.XMLElement#write(java.io.Writer) write(Writer)
+     */
+    public String toString()
+    {
+        try {
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            OutputStreamWriter writer = new OutputStreamWriter(out);
+            this.write(writer);
+            writer.flush();
+            return new String(out.toByteArray());
+        } catch (IOException e) {
+            // Java exception handling suxx
+            return super.toString();
+        }
+    }
+
+
+    /**
+     * Writes the XML element to a writer.
+     *
+     * @param writer
+     *     The writer to write the XML data to.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>writer != null</code>
+     *     <li><code>writer</code> is not closed
+     * </ul></dd></dl>
+     *
+     * @throws java.io.IOException
+     *      If the data could not be written to the writer.
+     *
+     * @see nanoxml.XMLElement#toString()
+     */
+    public void write(Writer writer)
+        throws IOException
+    {
+        if (this.name == null) {
+            this.writeEncoded(writer, this.contents);
+            return;
+        }
+        writer.write('<');
+        writer.write(this.name);
+        if (! this.attributes.isEmpty()) {
+            Enumeration e = this.attributes.keys();
+            while (e.hasMoreElements()) {
+                writer.write(' ');
+                String key = (String) e.nextElement();
+                String value = (String) this.attributes.get(key);
+                writer.write(key);
+                writer.write('='); writer.write('"');
+                this.writeEncoded(writer, value);
+                writer.write('"');
+            }
+        }
+        if ((this.contents != null) && (this.contents.length() > 0)) {
+            writer.write('>');
+            this.writeEncoded(writer, this.contents);
+            writer.write('<'); writer.write('/');
+            writer.write(this.name);
+            writer.write('>');
+        } else if (this.children.isEmpty()) {
+            writer.write('/'); writer.write('>');
+        } else {
+            writer.write('>');
+            Enumeration e = this.enumerateChildren();
+            while (e.hasMoreElements()) {
+                XMLElement child = (XMLElement) e.nextElement();
+                child.write(writer);
+            }
+            writer.write('<'); writer.write('/');
+            writer.write(this.name);
+            writer.write('>');
+        }
+    }
+
+
+    /**
+     * Writes a string encoded to a writer.
+     *
+     * @param writer
+     *     The writer to write the XML data to.
+     * @param str
+     *     The string to write encoded.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>writer != null</code>
+     *     <li><code>writer</code> is not closed
+     *     <li><code>str != null</code>
+     * </ul></dd></dl>
+     */
+    protected void writeEncoded(Writer writer,
+                                String str)
+        throws IOException
+    {
+        for (int i = 0; i < str.length(); i += 1) {
+            char ch = str.charAt(i);
+            switch (ch) {
+                case '<':
+                    writer.write('&'); writer.write('l'); writer.write('t');
+                    writer.write(';');
+                    break;
+                case '>':
+                    writer.write('&'); writer.write('g'); writer.write('t');
+                    writer.write(';');
+                    break;
+                case '&':
+                    writer.write('&'); writer.write('a'); writer.write('m');
+                    writer.write('p'); writer.write(';');
+                    break;
+                case '"':
+                    writer.write('&'); writer.write('q'); writer.write('u');
+                    writer.write('o'); writer.write('t'); writer.write(';');
+                    break;
+                case '\'':
+                    writer.write('&'); writer.write('a'); writer.write('p');
+                    writer.write('o'); writer.write('s'); writer.write(';');
+                    break;
+                default:
+                    int unicode = (int) ch;
+                    if ((unicode < 32) || (unicode > 126)) {
+                        writer.write('&'); writer.write('#');
+                        writer.write('x');
+                        writer.write(Integer.toString(unicode, 16));
+                        writer.write(';');
+                    } else {
+                        writer.write(ch);
+                    }
+            }
+        }
+    }
+
+
+    /**
+     * Scans an identifier from the current reader.
+     * The scanned identifier is appended to <code>result</code>.
+     *
+     * @param result
+     *     The buffer in which the scanned identifier will be put.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>result != null</code>
+     *     <li>The next character read from the reader is a valid first
+     *         character of an XML identifier.
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>The next character read from the reader won't be an identifier
+     *         character.
+     * </ul></dd></dl><dl>
+     */
+    protected void scanIdentifier(StringBuffer result)
+        throws IOException
+    {
+        for (;;) {
+            char ch = this.readChar();
+            if (((ch < 'A') || (ch > 'Z')) && ((ch < 'a') || (ch > 'z'))
+                && ((ch < '0') || (ch > '9')) && (ch != '_') && (ch != '.')
+                && (ch != ':') && (ch != '-') && (ch <= '\u007E')) {
+                this.unreadChar(ch);
+                return;
+            }
+            result.append(ch);
+        }
+    }
+
+
+    /**
+     * This method scans an identifier from the current reader.
+     *
+     * @return the next character following the whitespace.
+     */
+    protected char scanWhitespace()
+        throws IOException
+    {
+        for (;;) {
+            char ch = this.readChar();
+            switch (ch) {
+                case ' ':
+                case '\t':
+                case '\n':
+                case '\r':
+                    break;
+                default:
+                    return ch;
+            }
+        }
+    }
+
+
+    /**
+     * This method scans an identifier from the current reader.
+     * The scanned whitespace is appended to <code>result</code>.
+     *
+     * @return the next character following the whitespace.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>result != null</code>
+     * </ul></dd></dl>
+     */
+    protected char scanWhitespace(StringBuffer result)
+        throws IOException
+    {
+        for (;;) {
+            char ch = this.readChar();
+            switch (ch) {
+                case ' ':
+                case '\t':
+                case '\n':
+                    result.append(ch);
+                case '\r':
+                    break;
+                default:
+                    return ch;
+            }
+        }
+    }
+
+
+    /**
+     * This method scans a delimited string from the current reader.
+     * The scanned string without delimiters is appended to
+     * <code>string</code>.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>string != null</code>
+     *     <li>the next char read is the string delimiter
+     * </ul></dd></dl>
+     */
+    protected void scanString(StringBuffer string)
+        throws IOException
+    {
+        char delimiter = this.readChar();
+        if ((delimiter != '\'') && (delimiter != '"')) {
+            throw this.expectedInput("' or \"");
+        }
+        for (;;) {
+            char ch = this.readChar();
+            if (ch == delimiter) {
+                return;
+            } else if (ch == '&') {
+                this.resolveEntity(string);
+            } else {
+                string.append(ch);
+            }
+        }
+    }
+
+
+    /**
+     * Scans a #PCDATA element. CDATA sections and entities are resolved.
+     * The next &lt; char is skipped.
+     * The scanned data is appended to <code>data</code>.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>data != null</code>
+     * </ul></dd></dl>
+     */
+    protected void scanPCData(StringBuffer data)
+        throws IOException
+    {
+        for (;;) {
+            char ch = this.readChar();
+            if (ch == '<') {
+                ch = this.readChar();
+                if (ch == '!') {
+                    this.checkCDATA(data);
+                } else {
+                    this.unreadChar(ch);
+                    return;
+                }
+            } else if (ch == '&') {
+                this.resolveEntity(data);
+            } else {
+                data.append(ch);
+            }
+        }
+    }
+
+
+    /**
+     * Scans a special tag and if the tag is a CDATA section, append its
+     * content to <code>buf</code>.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>buf != null</code>
+     *     <li>The first &lt; has already been read.
+     * </ul></dd></dl>
+     */
+    protected boolean checkCDATA(StringBuffer buf)
+        throws IOException
+    {
+        char ch = this.readChar();
+        if (ch != '[') {
+            this.unreadChar(ch);
+            this.skipSpecialTag(0);
+            return false;
+        } else if (! this.checkLiteral("CDATA[")) {
+            this.skipSpecialTag(1); // one [ has already been read
+            return false;
+        } else {
+            int delimiterCharsSkipped = 0;
+            while (delimiterCharsSkipped < 3) {
+                ch = this.readChar();
+                switch (ch) {
+                    case ']':
+                        if (delimiterCharsSkipped < 2) {
+                            delimiterCharsSkipped += 1;
+                        } else {
+                            buf.append(']');
+                            buf.append(']');
+                            delimiterCharsSkipped = 0;
+                        }
+                        break;
+                    case '>':
+                        if (delimiterCharsSkipped < 2) {
+                            for (int i = 0; i < delimiterCharsSkipped; i++) {
+                                buf.append(']');
+                            }
+                            delimiterCharsSkipped = 0;
+                            buf.append('>');
+                        } else {
+                            delimiterCharsSkipped = 3;
+                        }
+                        break;
+                    default:
+                        for (int i = 0; i < delimiterCharsSkipped; i += 1) {
+                            buf.append(']');
+                        }
+                        buf.append(ch);
+                        delimiterCharsSkipped = 0;
+                }
+            }
+            return true;
+        }
+    }
+
+
+    /**
+     * Skips a comment.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li>The first &lt;!-- has already been read.
+     * </ul></dd></dl>
+     */
+    protected void skipComment()
+        throws IOException
+    {
+        int dashesToRead = 2;
+        while (dashesToRead > 0) {
+            char ch = this.readChar();
+            if (ch == '-') {
+                dashesToRead -= 1;
+            } else {
+                dashesToRead = 2;
+            }
+        }
+        if (this.readChar() != '>') {
+            throw this.expectedInput(">");
+        }
+    }
+
+
+    /**
+     * Skips a special tag or comment.
+     *
+     * @param bracketLevel The number of open square brackets ([) that have
+     *                     already been read.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li>The first &lt;! has already been read.
+     *     <li><code>bracketLevel >= 0</code>
+     * </ul></dd></dl>
+     */
+    protected void skipSpecialTag(int bracketLevel)
+        throws IOException
+    {
+        int tagLevel = 1; // <
+        char stringDelimiter = '\0';
+        if (bracketLevel == 0) {
+            char ch = this.readChar();
+            if (ch == '[') {
+                bracketLevel += 1;
+            } else if (ch == '-') {
+                ch = this.readChar();
+                if (ch == '[') {
+                    bracketLevel += 1;
+                } else if (ch == ']') {
+                    bracketLevel -= 1;
+                } else if (ch == '-') {
+                    this.skipComment();
+                    return;
+                }
+            }
+        }
+        while (tagLevel > 0) {
+            char ch = this.readChar();
+            if (stringDelimiter == '\0') {
+                if ((ch == '"') || (ch == '\'')) {
+                    stringDelimiter = ch;
+                } else if (bracketLevel <= 0) {
+                    if (ch == '<') {
+                        tagLevel += 1;
+                    } else if (ch == '>') {
+                        tagLevel -= 1;
+                    }
+                }
+                if (ch == '[') {
+                    bracketLevel += 1;
+                } else if (ch == ']') {
+                    bracketLevel -= 1;
+                }
+            } else {
+                if (ch == stringDelimiter) {
+                    stringDelimiter = '\0';
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Scans the data for literal text.
+     * Scanning stops when a character does not match or after the complete
+     * text has been checked, whichever comes first.
+     *
+     * @param literal the literal to check.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>literal != null</code>
+     * </ul></dd></dl>
+     */
+    protected boolean checkLiteral(String literal)
+        throws IOException
+    {
+        int length = literal.length();
+        for (int i = 0; i < length; i += 1) {
+            if (this.readChar() != literal.charAt(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+
+    /**
+     * Reads a character from a reader.
+     */
+    protected char readChar()
+        throws IOException
+    {
+        if (this.charReadTooMuch != '\0') {
+            char ch = this.charReadTooMuch;
+            this.charReadTooMuch = '\0';
+            return ch;
+        } else {
+            int i = this.reader.read();
+            if (i < 0) {
+                throw this.unexpectedEndOfData();
+            } else if (i == 10) {
+                this.parserLineNr += 1;
+                return '\n';
+            } else {
+                return (char) i;
+            }
+        }
+    }
+
+
+    /**
+     * Scans an XML element.
+     *
+     * @param elt The element that will contain the result.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li>The first &lt; has already been read.
+     *     <li><code>elt != null</code>
+     * </ul></dd></dl>
+     */
+    protected void scanElement(XMLElement elt)
+        throws IOException
+    {
+        StringBuffer buf = new StringBuffer();
+        this.scanIdentifier(buf);
+        String name = buf.toString();
+        elt.setName(name);
+        char ch = this.scanWhitespace();
+        while ((ch != '>') && (ch != '/')) {
+            buf.setLength(0);
+            this.unreadChar(ch);
+            this.scanIdentifier(buf);
+            String key = buf.toString();
+            ch = this.scanWhitespace();
+            if (ch != '=') {
+                throw this.expectedInput("=");
+            }
+            this.unreadChar(this.scanWhitespace());
+            buf.setLength(0);
+            this.scanString(buf);
+            elt.setAttribute(key, buf);
+            ch = this.scanWhitespace();
+        }
+        if (ch == '/') {
+            ch = this.readChar();
+            if (ch != '>') {
+                throw this.expectedInput(">");
+            }
+            return;
+        }
+        buf.setLength(0);
+        ch = this.scanWhitespace(buf);
+        if (ch != '<') {
+            this.unreadChar(ch);
+            this.scanPCData(buf);
+        } else {
+            for (;;) {
+                ch = this.readChar();
+                if (ch == '!') {
+                    if (this.checkCDATA(buf)) {
+                        this.scanPCData(buf);
+                        break;
+                    } else {
+                        ch = this.scanWhitespace(buf);
+                        if (ch != '<') {
+                            this.unreadChar(ch);
+                            this.scanPCData(buf);
+                            break;
+                        }
+                    }
+                } else {
+                    if ((ch != '/') || this.ignoreWhitespace) {
+                        buf.setLength(0);
+                    }
+                    if (ch == '/') {
+                        this.unreadChar(ch);
+                    }
+                    break;
+                }
+            }
+        }
+        if (buf.length() == 0) {
+            while (ch != '/') {
+                if (ch == '!') {
+                    ch = this.readChar();
+                    if (ch != '-') {
+                        throw this.expectedInput("Comment or Element");
+                    }
+                    ch = this.readChar();
+                    if (ch != '-') {
+                        throw this.expectedInput("Comment or Element");
+                    }
+                    this.skipComment();
+                } else {
+                    this.unreadChar(ch);
+                    XMLElement child = this.createAnotherElement();
+                    this.scanElement(child);
+                    elt.addChild(child);
+                }
+                ch = this.scanWhitespace();
+                if (ch != '<') {
+                    throw this.expectedInput("<");
+                }
+                ch = this.readChar();
+            }
+            this.unreadChar(ch);
+        } else {
+            if (this.ignoreWhitespace) {
+                elt.setContent(buf.toString().trim());
+            } else {
+                elt.setContent(buf.toString());
+            }
+        }
+        ch = this.readChar();
+        if (ch != '/') {
+            throw this.expectedInput("/");
+        }
+        this.unreadChar(this.scanWhitespace());
+        if (! this.checkLiteral(name)) {
+            throw this.expectedInput(name);
+        }
+        if (this.scanWhitespace() != '>') {
+            throw this.expectedInput(">");
+        }
+    }
+
+
+    /**
+     * Resolves an entity. The name of the entity is read from the reader.
+     * The value of the entity is appended to <code>buf</code>.
+     *
+     * @param buf Where to put the entity value.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li>The first &amp; has already been read.
+     *     <li><code>buf != null</code>
+     * </ul></dd></dl>
+     */
+    protected void resolveEntity(StringBuffer buf)
+        throws IOException
+    {
+        char ch = '\0';
+        StringBuffer keyBuf = new StringBuffer();
+        for (;;) {
+            ch = this.readChar();
+            if (ch == ';') {
+                break;
+            }
+            keyBuf.append(ch);
+        }
+        String key = keyBuf.toString();
+        if (key.charAt(0) == '#') {
+            try {
+                if (key.charAt(1) == 'x') {
+                    ch = (char) Integer.parseInt(key.substring(2), 16);
+                } else {
+                    ch = (char) Integer.parseInt(key.substring(1), 10);
+                }
+            } catch (NumberFormatException e) {
+                throw this.unknownEntity(key);
+            }
+            buf.append(ch);
+        } else {
+            char[] value = (char[]) this.entities.get(key);
+            if (value == null) {
+                throw this.unknownEntity(key);
+            }
+            buf.append(value);
+        }
+    }
+
+
+    /**
+     * Pushes a character back to the read-back buffer.
+     *
+     * @param ch The character to push back.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li>The read-back buffer is empty.
+     *     <li><code>ch != '\0'</code>
+     * </ul></dd></dl>
+     */
+    protected void unreadChar(char ch)
+    {
+        this.charReadTooMuch = ch;
+    }
+
+
+    /**
+     * Creates a parse exception for when an invalid valueset is given to
+     * a method.
+     *
+     * @param name The name of the entity.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     * </ul></dd></dl>
+     */
+    protected XMLParseException invalidValueSet(String name)
+    {
+        String msg = "Invalid value set (entity name = \"" + name + "\")";
+        return new XMLParseException(this.getName(), this.parserLineNr, msg);
+    }
+
+
+    /**
+     * Creates a parse exception for when an invalid value is given to a
+     * method.
+     *
+     * @param name  The name of the entity.
+     * @param value The value of the entity.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>value != null</code>
+     * </ul></dd></dl>
+     */
+    protected XMLParseException invalidValue(String name,
+                                             String value)
+    {
+        String msg = "Attribute \"" + name + "\" does not contain a valid "
+                   + "value (\"" + value + "\")";
+        return new XMLParseException(this.getName(), this.parserLineNr, msg);
+    }
+
+
+    /**
+     * Creates a parse exception for when the end of the data input has been
+     * reached.
+     */
+    protected XMLParseException unexpectedEndOfData()
+    {
+        String msg = "Unexpected end of data reached";
+        return new XMLParseException(this.getName(), this.parserLineNr, msg);
+    }
+
+
+    /**
+     * Creates a parse exception for when a syntax error occured.
+     *
+     * @param context The context in which the error occured.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>context != null</code>
+     *     <li><code>context.length() &gt; 0</code>
+     * </ul></dd></dl>
+     */
+    protected XMLParseException syntaxError(String context)
+    {
+        String msg = "Syntax error while parsing " + context;
+        return new XMLParseException(this.getName(), this.parserLineNr, msg);
+    }
+
+
+    /**
+     * Creates a parse exception for when the next character read is not
+     * the character that was expected.
+     *
+     * @param charSet The set of characters (in human readable form) that was
+     *                expected.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>charSet != null</code>
+     *     <li><code>charSet.length() &gt; 0</code>
+     * </ul></dd></dl>
+     */
+    protected XMLParseException expectedInput(String charSet)
+    {
+        String msg = "Expected: " + charSet;
+        return new XMLParseException(this.getName(), this.parserLineNr, msg);
+    }
+
+
+    /**
+     * Creates a parse exception for when an entity could not be resolved.
+     *
+     * @param name The name of the entity.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name.length() &gt; 0</code>
+     * </ul></dd></dl>
+     */
+    protected XMLParseException unknownEntity(String name)
+    {
+        String msg = "Unknown or invalid entity: &" + name + ";";
+        return new XMLParseException(this.getName(), this.parserLineNr, msg);
+    }
+    
+}

Added: trunk/plugins/ThawIndexBrowser/nanoxml/XMLParseException.java
===================================================================
--- trunk/plugins/ThawIndexBrowser/nanoxml/XMLParseException.java               
                (rev 0)
+++ trunk/plugins/ThawIndexBrowser/nanoxml/XMLParseException.java       
2008-03-30 15:23:56 UTC (rev 18860)
@@ -0,0 +1,130 @@
+/* XMLParseException.java
+ *
+ * $Revision: 1.4 $
+ * $Date: 2002/03/24 10:27:59 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 Lite.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ *****************************************************************************/
+
+
+package plugins.ThawIndexBrowser.nanoxml;
+
+
+/**
+ * An XMLParseException is thrown when an error occures while parsing an XML
+ * string.
+ * <P>
+ * $Revision: 1.4 $<BR>
+ * $Date: 2002/03/24 10:27:59 $<P>
+ *
+ * @see nanoxml.XMLElement
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
+ */
+public class XMLParseException
+    extends RuntimeException
+{
+
+    /**
+     * Indicates that no line number has been associated with this exception.
+     */
+    public static final int NO_LINE = -1;
+
+
+    /**
+     * The line number in the source code where the error occurred, or
+     * <code>NO_LINE</code> if the line number is unknown.
+     *
+     * <dl><dt><b>Invariants:</b></dt><dd>
+     * <ul><li><code>lineNr &gt 0 || lineNr == NO_LINE</code>
+     * </ul></dd></dl>
+     */
+    private int lineNr;
+
+
+    /**
+     * Creates an exception.
+     *
+     * @param name    The name of the element where the error is located.
+     * @param message A message describing what went wrong.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>message != null</code>
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>getLineNr() => NO_LINE
+     * </ul></dd></dl><dl>
+     */
+    public XMLParseException(String name,
+                             String message)
+    {
+        super("XML Parse Exception during parsing of "
+              + ((name == null) ? "the XML definition"
+                                : ("a " + name + " element"))
+              + ": " + message);
+        this.lineNr = XMLParseException.NO_LINE;
+    }
+
+
+    /**
+     * Creates an exception.
+     *
+     * @param name    The name of the element where the error is located.
+     * @param lineNr  The number of the line in the input.
+     * @param message A message describing what went wrong.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>message != null</code>
+     *     <li><code>lineNr &gt; 0</code>
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>getLineNr() => lineNr
+     * </ul></dd></dl><dl>
+     */
+    public XMLParseException(String name,
+                             int    lineNr,
+                             String message)
+    {
+        super("XML Parse Exception during parsing of "
+              + ((name == null) ? "the XML definition"
+                                : ("a " + name + " element"))
+              + " at line " + lineNr + ": " + message);
+        this.lineNr = lineNr;
+    }
+
+
+    /**
+     * Where the error occurred, or <code>NO_LINE</code> if the line number is
+     * unknown.
+     *
+     * @see nanoxml.XMLParseException#NO_LINE
+     */
+    public int getLineNr()
+    {
+        return this.lineNr;
+    }
+
+}


Reply via email to