[freenet-dev] [freenet-cvs] r18860 - in trunk/plugins/ThawIndexBrowser: . nanoxml

Matthew Toseland toad at amphibian.dyndns.org
Mon Mar 31 20:54:58 UTC 2008


Where can I get a clean copy of nanoxml to compare with?

On Sunday 30 March 2008 16:23, saces at freenetproject.org wrote:
> 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;
> +                }
> +