[libnanoxml2-java] 01/09: Imported Upstream version 2.2.3.dfsg

Andreas Tille tille at debian.org
Wed Dec 23 17:43:33 UTC 2015


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository libnanoxml2-java.

commit 94ea16987c6534d3965d622d699c8450f8a2e414
Author: Sveinung Kvilhaugsvik <sveinung84 at users.sourceforge.net>
Date:   Wed Dec 23 15:56:20 2015 +0100

    Imported Upstream version 2.2.3.dfsg
---
 Documentation/NanoXML-Java/LaTeX/Makefile          |   17 +
 Documentation/NanoXML-Java/LaTeX/NanoXML-Java.tex  |   36 +
 Documentation/NanoXML-Java/LaTeX/advanced.tex      |  482 ++++
 Documentation/NanoXML-Java/LaTeX/defs.tex          |   27 +
 Documentation/NanoXML-Java/LaTeX/introduction.tex  |  123 +
 .../NanoXML-Java/LaTeX/retrieving_data.tex         |  323 +++
 .../NanoXML-Java/LaTeX/streaming_data.tex          |  133 +
 Documentation/NanoXML-Java/LaTeX/structure.graffle |  769 ++++++
 Documentation/NanoXML-Lite/LaTeX/Makefile          |   16 +
 Documentation/NanoXML-Lite/LaTeX/NanoXML-Lite.tex  |   30 +
 Documentation/NanoXML-Lite/LaTeX/defs.tex          |   24 +
 Documentation/NanoXML-Lite/LaTeX/introduction.tex  |  106 +
 .../NanoXML-Lite/LaTeX/retrieving_data.tex         |  172 ++
 Examples/Java/Chapter02-01/DumpXML.java            |   47 +
 Examples/Java/Chapter02-01/test.xml                |    6 +
 Examples/Java/Chapter03-01/DumpXML.java            |   46 +
 Examples/Java/Chapter03-01/MyBuilder.java          |   94 +
 Examples/Java/Chapter03-01/test.xml                |    6 +
 Examples/Java/Chapter04-01/Demo.java               |   51 +
 Examples/Java/Chapter04-01/MyValidator.java        |   68 +
 Examples/Java/Chapter04-01/test.xml                |    6 +
 Examples/Java/Chapter04-02/Demo.java               |   48 +
 Examples/Java/Chapter04-02/MyResolver.java         |   47 +
 Examples/Java/Chapter04-02/test.xml                |    6 +
 Examples/Java/Chapter04-03/Demo.java               |   49 +
 Examples/Java/Chapter04-03/MyResolver.java         |   47 +
 Examples/Java/Chapter04-03/test.xml                |   10 +
 Examples/Java/Chapter04-04/Chapter.java            |   51 +
 Examples/Java/Chapter04-04/Chapter.java.xml        |    8 +
 Examples/Java/Chapter04-04/DocumentBuilder.java    |  134 +
 Examples/Java/Chapter04-04/DocumentElement.java    |   57 +
 Examples/Java/Chapter04-04/Paragraph.java          |   61 +
 Examples/Java/Chapter04-04/XML2HTML.java           |   62 +
 Examples/Java/Chapter04-04/document.dtd            |   11 +
 Examples/Java/Chapter04-04/test.xml                |    6 +
 Sources/Java/net/n3/nanoxml/CDATAReader.java       |  193 ++
 Sources/Java/net/n3/nanoxml/ContentReader.java     |  212 ++
 Sources/Java/net/n3/nanoxml/IXMLBuilder.java       |  216 ++
 Sources/Java/net/n3/nanoxml/IXMLElement.java       |  529 ++++
 .../Java/net/n3/nanoxml/IXMLEntityResolver.java    |   91 +
 Sources/Java/net/n3/nanoxml/IXMLParser.java        |  124 +
 Sources/Java/net/n3/nanoxml/IXMLReader.java        |  178 ++
 Sources/Java/net/n3/nanoxml/IXMLValidator.java     |  165 ++
 Sources/Java/net/n3/nanoxml/NonValidator.java      |  632 +++++
 Sources/Java/net/n3/nanoxml/PIReader.java          |  157 ++
 Sources/Java/net/n3/nanoxml/StdXMLBuilder.java     |  335 +++
 Sources/Java/net/n3/nanoxml/StdXMLParser.java      |  689 +++++
 Sources/Java/net/n3/nanoxml/StdXMLReader.java      |  626 +++++
 Sources/Java/net/n3/nanoxml/ValidatorPlugin.java   |  422 +++
 Sources/Java/net/n3/nanoxml/XMLAttribute.java      |  153 ++
 Sources/Java/net/n3/nanoxml/XMLElement.java        | 1042 +++++++
 Sources/Java/net/n3/nanoxml/XMLEntityResolver.java |  174 ++
 Sources/Java/net/n3/nanoxml/XMLException.java      |  286 ++
 Sources/Java/net/n3/nanoxml/XMLParseException.java |   69 +
 Sources/Java/net/n3/nanoxml/XMLParserFactory.java  |  139 +
 Sources/Java/net/n3/nanoxml/XMLUtil.java           |  763 ++++++
 .../net/n3/nanoxml/XMLValidationException.java     |  190 ++
 Sources/Java/net/n3/nanoxml/XMLWriter.java         |  311 +++
 Sources/Lite/nanoxml/XMLElement.java               | 2875 ++++++++++++++++++++
 Sources/Lite/nanoxml/XMLParseException.java        |  130 +
 Sources/SAX/net/n3/nanoxml/sax/SAXAdapter.java     |  334 +++
 .../SAX/net/n3/nanoxml/sax/SAXEntityResolver.java  |  144 +
 Sources/SAX/net/n3/nanoxml/sax/SAXParser.java      |  263 ++
 Test/Java/DumpXML.java                             |   50 +
 Test/Java/attributes.xml                           |   26 +
 Test/Java/attributes.xml.out                       |    4 +
 Test/Java/complex_pe.xml                           |   12 +
 Test/Java/complex_pe.xml.out                       |    1 +
 Test/Java/double_dtd.xml                           |    7 +
 Test/Java/double_dtd.xml.out                       |    1 +
 Test/Java/external_dtd.xml                         |    4 +
 Test/Java/external_dtd.xml.out                     |    2 +
 Test/Java/include.ent                              |    1 +
 Test/Java/internal_dtd.xml                         |   16 +
 Test/Java/internal_dtd.xml.out                     |    3 +
 Test/Java/namespaces.xml                           |   22 +
 Test/Java/namespaces.xml.out                       |    1 +
 Test/Java/simple.xml                               |   17 +
 Test/Java/simple.xml.out                           |    4 +
 Test/Java/test.dtd                                 |    7 +
 Test/Lite/DumpXML_Lite.java                        |   57 +
 Test/Lite/comments.xml                             |   10 +
 Test/Lite/comments.xml.out                         |    1 +
 Test/Lite/simple.xml                               |   19 +
 Test/Lite/simple.xml.out                           |    1 +
 build.sh                                           |   42 +
 86 files changed, 14929 insertions(+)

diff --git a/Documentation/NanoXML-Java/LaTeX/Makefile b/Documentation/NanoXML-Java/LaTeX/Makefile
new file mode 100644
index 0000000..31f4612
--- /dev/null
+++ b/Documentation/NanoXML-Java/LaTeX/Makefile
@@ -0,0 +1,17 @@
+MAIN=NanoXML-Java
+COMPONENTS=NanoXML-Java.tex defs.tex introduction.tex \
+           retrieving_data.tex streaming_data.tex
+PDFLATEX=/usr/local/teTeX/bin/powerpc-apple-darwin-current/pdflatex
+
+all: $(MAIN).pdf
+
+clean:
+	rm -f $(MAIN).pdf $(COMPONENTS:.tex=.aux) $(MAIN).log $(MAIN).toc
+
+$(MAIN).pdf: $(COMPONENTS)
+	sed "s/ModDate (D:[0-9]*)/ModDate (D:`date +'%Y%m%d%H%M%S'`)/" \
+	        < $(MAIN).tex >$(MAIN).tmp
+	mv $(MAIN).tmp $(MAIN).tex
+	$(PDFLATEX) $(MAIN).tex
+	$(PDFLATEX) $(MAIN).tex >/dev/null 2>&1
+	$(PDFLATEX) $(MAIN).tex >/dev/null 2>&1
diff --git a/Documentation/NanoXML-Java/LaTeX/NanoXML-Java.tex b/Documentation/NanoXML-Java/LaTeX/NanoXML-Java.tex
new file mode 100644
index 0000000..eb78907
--- /dev/null
+++ b/Documentation/NanoXML-Java/LaTeX/NanoXML-Java.tex
@@ -0,0 +1,36 @@
+\documentclass[a4paper]{book}
+\usepackage[pdftex]{graphicx}
+\usepackage{hyperref}
+\DeclareGraphicsExtensions{.pdf}
+\title{NanoXML/Java 2.2}
+\author{Marc De Scheemaecker $<$cyberelf at mac.com$>$}
+
+\pdfinfo {
+    /Title (NanoXML/Java 2.2)
+    /Creator (LaTeX)
+    /Producer (pdfTeX 0.14a)
+    /Author (Marc De Scheemaecker)
+    /CreationDate (D:20020205000000)
+    /ModDate (D:20030201171151)
+    /Subject (NanoXML/Java)
+    /Keywords (NanoXML,XML)
+}
+
+\include{defs}
+
+\begin{document}
+
+\titlepage
+\maketitle
+
+\tableofcontents
+
+\include{introduction}
+
+\include{retrieving_data}
+
+\include{streaming_data}
+
+\include{advanced}
+
+\end{document}
diff --git a/Documentation/NanoXML-Java/LaTeX/advanced.tex b/Documentation/NanoXML-Java/LaTeX/advanced.tex
new file mode 100644
index 0000000..33c8e45
--- /dev/null
+++ b/Documentation/NanoXML-Java/LaTeX/advanced.tex
@@ -0,0 +1,482 @@
+\chapter{Advanced Topics}
+
+This chapter explains how you can customize the \NanoXML{} parser setup.
+Unlike \ltext{NanoXML 1}, \ltext{NanoXML/Java 2} is designed as a framework: it is composed of many different components which you can plug together.
+It's possible to change the reader, the builder, the validator and even the parser.
+
+\ltext{NanoXML/Java} comes with one set of components.
+Except for \ltext{NanoXML/Lite}, every branch offers its own set of components customized for a certain purpose.
+\ltext{NanoXML/SAX} offers components for using \NanoXML{} as a parser for the \ltext{SAX} framework.
+
+The following figure gives a short representation of the major components.
+
+\begin{figure}[ht]
+\begin{center}
+\includegraphics[width=8cm]{structure.pdf}
+\end{center}
+\caption{Design of NanoXML/Java}
+\end{figure}
+
+The \emph{reader} retrieves data from a Java input stream and provides character data to the other components.
+
+The \emph{parser} converts the character data it retrieves from the reader to \XML{} events which it sends to the builder.
+
+The \emph{validator} parses a \ltext{DTD} and validates the \XML{} data.
+The current validator does only the minimum necessary for a non-validating parser.
+
+The \emph{entity resolvers} converts entity references (\&\ldots;) and parameter entity references (\%\ldots;) to character data.
+The resolver uses the reader to access external entities.
+
+The \emph{builder} interpretes \XML{} events coming from the parser and builds a tree of \XML{} elements.
+The standard builder creates a tree of \classname{IXMLElement}.
+You can provide your own builder to create a custom tree or if you are interested in the \XML{} events themselves, \acronym{e.g.} to use \XML{} streaming.
+
+
+\section{The \NanoXML{} Reader}
+
+The reader retrieves data from some source and feeds it to the other components.
+
+The reader is basically a stack of push-back readers.
+Every time a new data stream becomes active, the current reader is pushed on a stack.
+When the current reader has no more data left, the parent reader is popped from the stack.
+
+If you want to implement public IDs using \acronym{e.g.} a catalog file similar to SGML, you could implement a reader by overriding the method \methodname{openStream} of \classname{StdXMLReader}:
+
+\begin{example}
+\xkeyword{public class} MyReader
+~~\xkeyword{extends} StdXMLReader
+\{
+~~\xkeyword{private} Properties publicIDs;
+~
+~~\xkeyword{public} MyReader(Properties publicIDs)
+~~\{
+~~~~\xkeyword{this}.publicIDs = publicIDs;
+~~\}
+~
+~~\xkeyword{public} Reader openStream(String publicID,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~String systemID)
+~~~~\xkeyword{throws} MalformedURLException,
+~~~~~~~~~~~FileNotFoundException,
+~~~~~~~~~~~IOException
+~~\{
+~~~~\xkeyword{if} (publicID != \xkeyword{null}) \{
+~~~~~~systemID = publicIDs.getProperty(publicID, systemID);
+~~~~\}
+~~~~\xkeyword{return super}.openStream(publicID, systemID);
+~~\}
+\}
+\end{example}
+
+In this example, you have to provide a properties object which maps public IDs to system IDs.
+
+
+\section{The \NanoXML{} Parser}
+
+The parser analyzes the character stream it retrieves from the reader and sends \XML{} events to the builder.
+It uses a validator to validate the data and an entity resolver to resolve general entities.
+You rarely need to create a custom parser.
+If you need to, you have to implement \classname{IXMLParser}.
+
+
+\section{The \NanoXML{} Validator}
+
+The validator parses the \ltext{DTD} and checks the \XML{} data.
+\ltext{NanoXML 2.0} uses a \classname{NonValidator} implementation that only performs the minimum necessary for a non-validating parser.
+
+As a \ltext{DTD} is very vague, you can implement your own validator to perform a more fine-grained check of the \XML{} data.
+The easiest way to create your own validator is to create a subclass of \classname{ValidatorPlugin}.
+
+The following example shows how to implement a validator.
+It checks that every attribute named ``id'' starts with three capital letters.
+
+\begin{example}
+\xkeyword{public class} MyValidator
+~~\xkeyword{extends} ValidatorPlugin
+\{
+~~\xkeyword{public void} attributeAdded(String key,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~String value,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~String systemID,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\xkeyword{int} lineNr)
+~~\{
+~~~~\xkeyword{boolean} valid = true;
+~~~~\xkeyword{if} (key.equals("id")) \{
+~~~~~~\xkeyword{if} (value.length() < 3) \{
+~~~~~~~~valid = \xkeyword{false};
+~~~~~~\} \xkeyword{else} \{
+~~~~~~~~\xkeyword{for} (\xkeyword{int} i = 0; i < 3; i++) \{
+~~~~~~~~~~\xkeyword{char} ch = value.charAt(i);
+~~~~~~~~~~\xkeyword{if} ((ch < 'A') || (ch > 'Z')) \{
+~~~~~~~~~~~~valid = \xkeyword{false};
+~~~~~~~~~~\}
+~~~~~~~~\}
+~~~~~~\}
+~~~~\}
+~~~~\xkeyword{if} (valid) \{
+~~~~~~\xkeyword{super}.attributeAdded(key, value, systemID, lineNr);
+~~~~\} \xkeyword{else} \{
+~~~~~~\xkeyword{this}.attributeWithInvalidValue(systemID, lineNr, \xkeyword{null}, key, value);
+~~~~\}
+~~\}
+\}
+\end{example}
+
+To register the validator to a parser, use the following code:
+
+\begin{example}
+IXMLParser parser \ldots
+\ldots
+IXMLValidator val1 = parser.getValidator();
+MyValidator val2 = \xkeyword{new} MyValidator();
+val2.setDelegate(val1);
+parser.setValidator(val2);
+\end{example}
+
+
+\section{The \NanoXML{} Entity Resolvers}
+
+The entity resolver converts entity references to \XML{} data.
+If you want \acronym{e.g.} to retrieve entity values from a database, you have to create your own resolver.
+
+Entity resolvers have to implement \classname{IXMLEntityResolver}.
+Usually, you only have to make a subclass of \classname{XMLEntityResolver} and implement the method \methodname{getEntity} or \methodname{openExternalEntity}.
+
+Entities can be used in the \XML{} data and in the \ltext{DTD}.
+As these entities are independent of each other, there are two entity resolvers.
+
+
+\subsection{Standard Entities}
+
+The resolver for standard entities has to be registered to the parser by calling \methodname{setResolver}.
+The following example registers a resolver that forces the entity ``\&foo;'' to be resolved to ``bar'':
+
+\begin{example}
+\xkeyword{import} net.n3.nanoxml.*;
+\xkeyword{import} java.io.*;
+~
+\xkeyword{class} MyResolver
+~~\xkeyword{extends} XMLEntityResolver
+\{
+~~\xkeyword{public} Reader getEntity(IXMLReader xmlReader,
+~~~~~~~~~~~~~~~~~~~~~~~~~~String name)
+~~~~\xkeyword{throws} XMLParseException
+~~\{
+~~~~\xkeyword{if} (name.equals("foo")) \{
+~~~~~~\xkeyword{return new} StringReader("bar");
+~~~~\} \xkeyword{else} \{
+~~~~~~\xkeyword{return super}.getEntity(xmlReader, name);
+~~~~\}
+~~\}
+\}
+~
+\xkeyword{public class} Demo
+\{
+~~\xkeyword{public static void} main(String[] args)
+~~~~\xkeyword{throws} Exception
+~~\{
+~~~~IXMLParser parser = XMLParserFactory.createDefaultXMLParser();
+~~~~parser.setResolver(new MyResolver());
+~~~~IXMLReader reader = StdXMLReader.fileReader("test.xml");
+~~~~parser.setReader(reader);
+~~~~IXMLElement xml = (IXMLElement) parser.parse();
+~~~~XMLWriter writer = new XMLWriter(System.out);
+~~~~writer.write(xml);
+~~\}
+\}
+\end{example}
+
+
+\subsection{Parameter Entities}
+
+The resolver for parameter entities has to be registered to the validator by calling \methodname{setParameterEntityResolver}.
+The following example show a custom version of the \classname{Demo} class that registers \methodname{MyResolver} as a parameter entity resolver.
+
+\begin{example}
+\xkeyword{public class} Demo
+\{
+~~\xkeyword{public static void} main(String[] args)
+~~~~\xkeyword{throws} Exception
+~~\{
+~~~~IXMLParser parser = XMLParserFactory.createDefaultXMLParser();
+~~~~IXMLValidator validator = parser.getValidator();
+~~~~validator.setParameterEntityResolver(new MyResolver());
+~~~~IXMLReader reader = StdXMLReader.fileReader("test.xml");
+~~~~parser.setReader(reader);
+~~~~IXMLElement xml = (IXMLElement) parser.parse();
+~~~~XMLWriter writer = new XMLWriter(System.out);
+~~~~writer.write(xml);
+~~\}
+\}
+\end{example}
+
+
+\section{The \NanoXML{} Builder}
+
+The builder interpretes \XML{} events coming from the parser and builds a tree of \ltext{Java} objects.
+When the parsing is done, the builder hands over its result to the parser.
+
+As explained in chapter 3, the builder can also be used to read \XML{} data while it's being streamed.
+This feature is useful if you don't want to wait until all the data has been read before processing the information.
+
+As an example, we have the following XML structure (document.dtd):
+
+\begin{example}
+$<$!ELEMENT Chapter (Paragraph*)$>$
+$<$!ATTLIST Chapter
+~~~~~~~~title~CDATA~\#REQUIRED
+~~~~~~~~id~~~~CDATA~\#REQUIRED$>$
+$<$!ELEMENT Paragraph (\#PCDATA)$>$
+$<$!ATTLIST Paragraph
+~~~~~~~~align~(left|center|right)~"left"$>$
+\end{example}
+
+The elements are put in the \ltext{Java} classes \classname{Chapter} and \classname{Paragraph} which, for convenience, extend the following base class:
+
+\begin{example}
+\xkeyword{public class} DocumentElement
+\{
+~~\xkeyword{protected} Properties attrs;
+~~\xkeyword{protected} Vector children;
+~
+~~\xkeyword{public} DocumentElement()
+~~\{
+~~~~\xkeyword{this}.attrs = \xkeyword{new} Properties();
+~~~~\xkeyword{this}.children = \xkeyword{new} Vector();
+~~\}
+~
+~~\xkeyword{public void} setAttribute(String attrName,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~String value)
+~~\{
+~~~~\xkeyword{this}.attrs.put(attrName, value);
+~~\}
+~
+~~\xkeyword{public void} addChild(DocumentElement elt)
+~~\{
+~~~~\xkeyword{this}.children.addElement(elt);
+~~\}
+\}
+\end{example}
+
+This base class simply makes it easy for our builder to set attributes and to add children to an element.
+
+The \classname{Chapter} and \classname{Paragraph} classes extend this base class to give more practical access to their attributes and children:
+
+\begin{example}
+\xkeyword{public class} Chapter
+~~\xkeyword{extends} DocumentElement
+\{
+~~\xkeyword{public} String getTitle()
+~~\{
+~~~~\xkeyword{return this}.attrs.getProperty("title");
+~~\}
+~
+~~\xkeyword{public} String getID()
+~~\{
+~~~~\xkeyword{return this}.attrs.getProperty("id");
+~~\}
+~
+~~\xkeyword{public} Enumeration getParagraphs()
+~~\{
+~~~~\xkeyword{return this}.children.elements();
+~~\}
+\}
+~
+\xkeyword{public class} Paragraph
+~~\xkeyword{extends} DocumentElement
+\{
+~~\xkeyword{public static final int} LEFT = 0;
+~~\xkeyword{public static final int} CENTER = 1;
+~~\xkeyword{public static final int} RIGHT = 2;
+~
+~~\xkeyword{private static} Hashtable alignments;
+~
+~~\xkeyword{static}
+~~\{
+~~~~alignments = \xkeyword{new} Hashtable();
+~~~~alignments.put("left", \xkeyword{new} Integer(LEFT));
+~~~~alignments.put("center", \xkeyword{new} Integer(CENTER));
+~~~~alignments.put("right", \xkeyword{new} Integer(RIGHT));
+~~\}
+~
+~~\xkeyword{public} String getContent()
+~~\{
+~~~~\xkeyword{return this}.attrs.getProperty("\#PCDATA");
+~~\}
+~
+~~\xkeyword{public int} getAlignment()
+~~\{
+~~~~String str = \xkeyword{this}.attrs.getProperty("align");
+~~~~Integer align = alignments.get(str);
+~~~~\xkeyword{return} align.intValue();
+~~\}
+\}
+\end{example}
+
+The builder creates the data structure based on the \XML{} events it receives from the parser.
+Because both \classname{Chapter} and \classname{Paragraph} extend \classname{DocumentElement}, the builder is fairly simple.
+
+\begin{example}
+\xkeyword{import} net.n3.nanoxml.*;
+\xkeyword{import} java.util.*;
+\xkeyword{import} java.io.*;
+~
+\xkeyword{public class} DocumentBuilder
+~~\xkeyword{implements} IXMLBuilder
+\{
+~~\xkeyword{private static} Hashtable classes;
+~~\xkeyword{private} Stack elements;
+~~\xkeyword{private} DocumentElement topElement;
+~
+~~\xkeyword{static}
+~~\{
+~~~~classes = \xkeyword{new} Hashtable();
+~~~~classes.put("Chapter", Chapter.\xkeyword{class});
+~~~~classes.put("Paragraph", Paragraph.\xkeyword{class});
+~~\}
+~
+~~\xkeyword{public void} startBuilding(String systemID,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~\xkeyword{int} lineNr)
+~~\{
+~~~~\xkeyword{this}.elements = \xkeyword{new} Stack();
+~~~~\xkeyword{this}.topElement = \xkeyword{null};
+~~\}
+~
+~~\xkeyword{public void} newProcessingInstruction(String target,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Reader reader)
+~~\{
+~~~~// nothing to do
+~~\}
+~
+~~\xkeyword{public void} startElement(String name,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~String nsPrefix,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~String nsSystemID,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~String systemID,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~\xkeyword{int} lineNr)
+~~\{
+~~~~DocumentElement elt = \xkeyword{null};
+~~~~\xkeyword{try} \{
+~~~~~~Class cls = (Class) classes.get(name);
+~~~~~~elt = (DocumentElement) cls.newInstance();
+~~~~\} \xkeyword{catch} (Exception e) \{
+~~~~~~// ignore the exception
+~~~~\}
+~~~~\xkeyword{this}.elements.push(elt);
+~~~~\xkeyword{if} (\xkeyword{this}.topElement == \xkeyword{null}) \{
+~~~~~~\xkeyword{this}.topElement = elt;
+~~~~\}
+~~\}
+~
+~~\xkeyword{public void} endElement(String name,
+~~~~~~~~~~~~~~~~~~~~~~~~~String nsPrefix,
+~~~~~~~~~~~~~~~~~~~~~~~~~String nsSystemID)
+~~\{
+~~~~DocumentElement child = (DocumentElement) \xkeyword{this}.elements.pop();
+~~~~\xkeyword{if} (! \xkeyword{this}.elements.isEmpty()) \{
+~~~~~~DocumentElement parent = (DocumentElement) \xkeyword{this}.elements.peek();
+~~~~~~parent.addChild(child);
+~~~~\}
+~~\}
+~
+~~\xkeyword{public void} addAttribute(String key,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~String nsPrefix,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~String nsSystemID,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~String value,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~String type)
+~~\{
+~~~~DocumentElement child = (DocumentElement) \xkeyword{this}.elements.peek();
+~~~~child.setAttribute(key, value);
+~~\}
+~
+~~\xkeyword{public void} elementAttributesProcessed(String name,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~String nsPrefix,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~String nsSystemID)
+~~\{
+~~~~// nothing to do
+~~\}
+~
+~~\xkeyword{public void} addPCData(Reader reader,
+~~~~~~~~~~~~~~~~~~~~~~~~String systemID,
+~~~~~~~~~~~~~~~~~~~~~~~~\xkeyword{int} lineNr)
+~~~~\xkeyword{throws} IOException
+~~\{
+~~~~StringBuffer str = \xkeyword{new} StringBuffer(1024);
+~~~~\xkeyword{char}[] buf = \xkeyword{new char}[bufSize];
+~~~~\xkeyword{for} (;;) \{
+~~~~~~\xkeyword{int} size = reader.read(buf);
+~~~~~~\xkeyword{if} (size < 0) \{
+~~~~~~~~\xkeyword{break};
+~~~~~~\}
+~~~~~~str.append(buf, 0, size);
+~~~~\}
+~~~~\xkeyword{this}.addAttribute("\#PCDATA", \xkeyword{null}, \xkeyword{null}, str.toString(), "CDATA");
+~~\}
+~
+~~\xkeyword{public} Object getResult()
+~~\{
+~~~~\xkeyword{return} topElement;
+~~\}
+\}
+\end{example}
+
+Note that, for simplicity, error and exception handling is not present in this example.
+The builder holds a stack of the current elements it builds.
+Character data is read from a reader.
+The method \methodname{addPCData} reads this data in blocks of 1K.
+
+Finally, this application sets up the \NanoXML{} parser and converts an \XML{} document to \ltext{HTML} which it dumps on the standard output:
+
+\begin{example}
+\xkeyword{import} java.util.*;
+\xkeyword{import} net.n3.nanoxml.*;
+~
+\xkeyword{public class} XML2HTML
+\{
+~~\xkeyword{public static void} main(String[] params)
+~~~~\xkeyword{throws} Exception
+~~\{
+~~~~IXMLBuilder builder = \xkeyword{new} DocumentBuilder();
+~~~~IXMLParser parser = XMLParserFactory.createDefaultXMLParser();
+~~~~parser.setBuilder(builder);
+~~~~IXMLReader reader = StdXMLReader.fileReader(param[0]);
+~~~~parser.setReader(reader);
+~~~~Chapter chapter = (Chapter) parser.parse();
+~~~~System.out.println("$<$!DOCTYPE \ldots{} $>$");
+~~~~System.out.print("$<$HTML$><$HEAD$><$TITLE$>$");
+~~~~System.out.print(chapter.getTitle());
+~~~~System.out.println("</TITLE></HEAD><BODY>");
+~~~~System.out.print("$<$H1$>$");
+~~~~System.out.print(chapter.getTitle());
+~~~~System.out.println("$<$/H1$>$");
+~~~~Enumeration enum = chapter.getParagraphs();
+~~~~\xkeyword{while} (enum.hasMoreElements()) \{
+~~~~~~Paragraph para = (Paragraph) enum.nextElement();
+~~~~~~System.out.print("$<$P$>$");
+~~~~~~System.out.print(para.getContent());
+~~~~~~System.out.println("$<$/P$>$");
+~~~~\}
+~~~~System.out.println("$<$/BODY$><$/HTML$>$");
+~~\}
+\}
+\end{example}
+
+If we run the example on the following \XML{} file:
+
+\begin{example}
+$<$!DOCTYPE Chapter SYSTEM "document.dtd"$>$
+~
+$<$Chapter id="ch01" title="The Title"$>$
+~~~~$<$Paragraph$>$First paragraph...$<$/Paragraph$>$
+~~~~$<$Paragraph$>$Second paragraph...$<$/Paragraph$>$
+$<$/Chapter$>$
+\end{example}
+
+The output will be:
+
+\begin{example}
+$<$!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN'
+~~'http://www.w3.org/TR/html4/strict.dtd'>
+$<$HTML$><$HEAD$><$TITLE$>$The Title$<$/TITLE$><$/HEAD$><$BODY$>$
+$<$H1$>$The Title$<$/H1$>$
+$<$P$>$First paragraph...$<$/P$>$
+$<$P$>$Second paragraph...$<$/P$>$
+$<$/BODY$>$
+\end{example}
diff --git a/Documentation/NanoXML-Java/LaTeX/defs.tex b/Documentation/NanoXML-Java/LaTeX/defs.tex
new file mode 100644
index 0000000..6ebfbcb
--- /dev/null
+++ b/Documentation/NanoXML-Java/LaTeX/defs.tex
@@ -0,0 +1,27 @@
+\newcommand\ltext[1]{\mbox{#1}}
+\newcommand\XML{\ltext{XML}}
+\newcommand\NanoXML{\ltext{NanoXML}}
+\newcommand\term[1]{\emph{\mbox{#1}}}
+\newcommand\acronym[1]{\ltext{#1}}
+\newcommand\classname[1]{\mbox{\tt #1}}
+\newcommand\variable[1]{\mbox{\tt #1}}
+\newcommand\propertykey[1]{\mbox{\tt #1}}
+\newcommand\propertyvalue[1]{\mbox{\tt #1}}
+\newcommand\methodname[1]{\mbox{\tt #1}}
+\newcommand\filename[1]{\mbox{\sl #1}}
+\newcommand\packagename[1]{\mbox{\tt\sl #1}}
+
+\newcommand\xcallout[1]{\hfill\textcircled{#1}}
+\newcommand\xkeyword[1]{{\bf #1}}
+
+\newcounter{calloutindex}
+\newenvironment{callout}%
+{\begin{itemize}\setcounter{calloutindex}{1}}%
+{\end{itemize}}
+\newcommand\coitem{\item[\textcircled{\arabic{calloutindex}}%
+\addtocounter{calloutindex}{1}]}
+
+\newenvironment{example}%
+{\par\vskip 6pt\tt\small\obeylines\obeyspaces}%
+{\vskip 6pt}
+
diff --git a/Documentation/NanoXML-Java/LaTeX/introduction.tex b/Documentation/NanoXML-Java/LaTeX/introduction.tex
new file mode 100644
index 0000000..a21e7a7
--- /dev/null
+++ b/Documentation/NanoXML-Java/LaTeX/introduction.tex
@@ -0,0 +1,123 @@
+\chapter{Introduction}
+
+This chapter gives a short introduction to \XML{} and \NanoXML{}.
+
+\section{About \XML{}}
+
+The extensible markup language,
+\href{http://www.w3c.org/TR/REC-xml}{\XML{}}, is a way to mark up text in
+a structured document.
+
+\XML  is a simplification of the complex \ltext{SGML} standard.
+\ltext{SGML}, the Standard Generalized Markup Language, is an international
+(\ltext{ISO}) standard for marking up text and graphics.
+The best known application of \ltext{SGML} is \ltext{HTML}.
+
+Although \ltext{SGML} data is very easy to write, it's very difficult to write a
+generic \ltext{SGML} parser.
+When designing \XML{} however, the authors removed much of the flexibility
+of \ltext{SGML} making it much easier to parse \XML{} documents correctly.
+
+\XML{} data is structured as a tree of \term{entities}.
+An entity can be a string of character data or an element which can contain other
+entities.
+Elements can optionally have a set of attributes.
+Attributes are key/value pairs which set some properties of an element.
+
+The following example shows some \XML{} data:
+
+\begin{example}
+$<$book$>$
+~~$<$chapter id="my chapter"$>$
+~~~~$<$title$>$The title$<$/title$>$
+~~~~Some text.
+~~$<$/chapter$>$
+$<$/book$>$
+\end{example}
+
+At the root of the tree, you can find the element ``book''.
+This element contains one child element: ``chapter''.
+The chapter element has one attribute which maps the key ``id'' to
+``my chapter''.
+The chapter element has two child entities: the element ``title'' and the
+character data ``Some text.''.
+Finally, the title element has one child, the string ``The title''.
+
+\section{About \NanoXML{}}
+
+In April 2000, \NanoXML{} was first released as a spin-off project of
+\ltext{AUIT}, the Abstract User Interface Toolkit.
+
+The intent of NanoXML was to be a small parser which was easy to use.
+\ltext{SAX} and \ltext{DOM} are much too complex for what I needed and the
+mainstream parsers were either much too big or had a very restrictive license.
+
+\ltext{NanoXML 1} has all the features I needed: it is very small (about 6K),
+is reasonably fast for small \XML{} documents, is very easy to use and is
+free (\ltext{zlib/libpng} license).
+As I never intended to use \NanoXML{} to parse \ltext{DocBook} documents,
+there was no support for mixed data or \ltext{DTD} parsing.
+
+\NanoXML{} was released as a \ltext{SourceForge} project and, because of the
+very good response from its users, it matured to a small and stable parser.
+The final version, release \ltext{1.6.8} was released in May 2001.
+
+Because of its small size, people started to use \NanoXML{} for embedded
+systems (\ltext{KVM}, \ltext{J2ME}) and kindly submitted patches to make
+\NanoXML{} work in such restricted environment.
+
+\section{\NanoXML{} 2}
+
+In July 2001, \ltext{NanoXML 2} has been released.
+Unlike \ltext{NanoXML 1}, speed and \XML{} compliancy were considered to be
+very important when the new parser was designed.
+\ltext{NanoXML 2} is also very modular: you can easily replace the different
+components in the parser to customize it to your needs.
+The modularity of \ltext{NanoXML 2} also benefits extensions like \acronym{e.g.} \ltext{SAX} support which can now directly access the parser.
+In \ltext{NanoXML 1}, the \ltext{SAX} adapter had to iterate the data structure built by the base product.
+
+Although many features were added to \NanoXML{}, the second release was
+still very small.
+The full parser with builder fits in a \ltext{JAR} file of about 32K.
+This is still very tiny, especially when you compare this with the ``standard'' parsers of more than four times its size.
+
+As there is still need for a tiny parser like \ltext{NanoXML 1}, there is a
+special branch of \ltext{NanoXML 2}: \ltext{NanoXML/Lite}. This parser is source compatible with \ltext{NanoXML 1} but features a new parsing algorithm which makes it more than twice as fast as the older version.
+It is however more restrictive on the \XML{} data it parses: the older
+version allowed some not-wellformed data to be parsed.
+
+There are three branches of \ltext{NanoXML 2}:
+\begin{itemize}
+  \item[$\bullet$]
+    \term{NanoXML/Lite} is the successor of \ltext{NanoXML 1}.
+    It features an almost compatible parser which is extremely small.
+  \item[$\bullet$]
+    \term{NanoXML/Java} is the standard parser.
+  \item[$\bullet$]
+    \term{NanoXML/SAX} is the \ltext{SAX} adapter for \ltext{NanoXML/Java}.
+\end{itemize}
+
+The latest version of \NanoXML{} is \ltext{NanoXML 2.2.1}, which has been
+released in April 2002.
+
+\section{\NanoXML{} Extension to the \XML{} System ID}
+
+Because it's convenient to put data files into jar files, we need some way to specify that we want some resource which can be found in the class path.
+There is no support for such resources in the \XML{} 1.0 specification.
+NanoXML allows you to specify such resources using the
+\emph{reference part} of a \ltext{URL}.
+
+This means that if the \ltext{DTD} of the \XML{} data is put in the
+resource \filename{/data/foo.dtd}, you can specify such path using the 
+following document type declaration:
+
+\begin{example}
+$<$!DOCTYPE foo SYSTEM 'file:\#/data/foo.dtd'$>$
+\end{example}
+
+It's even possible to specify a resource found in a particular jar, like in the following example:
+
+\begin{example}
+$<$!DOCTYPE foo SYSTEM 'http://myserver.com/dtds.jar\#/foo.dtd'$>$
+\end{example}
+
diff --git a/Documentation/NanoXML-Java/LaTeX/retrieving_data.tex b/Documentation/NanoXML-Java/LaTeX/retrieving_data.tex
new file mode 100644
index 0000000..bcad88c
--- /dev/null
+++ b/Documentation/NanoXML-Java/LaTeX/retrieving_data.tex
@@ -0,0 +1,323 @@
+\chapter{Retrieving Data From An \XML{} Datasource}
+
+This chapter shows how to retrieve \XML{} data from a standard data
+source.
+Such source can be a file, an \ltext{HTTP} object or a text string.
+The method described in this chapter is the simplest way to retrieve
+\XML{} data.
+More advanced ways are described in the next chapters.
+
+\section{A Very Simple Example}
+
+This section describes a very simple \XML{} application.
+It parses \XML{} data from a stream and dumps it ``pretty-printed'' to
+the standard output.
+While its use is very limited, it shows how to set up a parser and parse an
+\XML{} document.
+
+\begin{example}
+\xkeyword{import} net.n3.nanoxml.*;\xcallout{1}
+\xkeyword{import} java.io.*;
+
+\xkeyword{public class} DumpXML
+\{
+~~\xkeyword{public static void} main(String[] args)
+~~~~\xkeyword{throws} Exception
+~~\{
+~~~~IXMLParser parser = XMLParserFactory.createDefaultXMLParser();\xcallout{2}
+~~~~IXMLReader reader = StdXMLReader.fileReader("test.xml");\xcallout{3}
+~~~~parser.setReader(reader);
+~~~~IXMLElement xml = (IXMLElement) parser.parse();\xcallout{4}
+~~~~XMLWriter writer = new XMLWriter(System.out);\xcallout{5}
+~~~~writer.write(xml);
+~~\}
+\}
+\end{example}
+
+\begin{callout}
+  \coitem
+    The \NanoXML{} classes are located in the package 
+    \packagename{net.n3.nanoxml}.
+  \coitem
+    This command creates an \XML{} parser.
+    The actual class of the parser is dependent on the value of the system
+    property \propertykey{net.n3.nanoxml.XMLParser}, which is by default
+    \propertyvalue{net.n3.nanoxml.StdXMLParser}.
+  \coitem
+    The command creates a ``standard'' reader which reads its data from the
+    file called \filename{test.xml}.
+
+    Usually you can use \classname{StdXMLReader} to feed the \XML{} data to
+    the parser.
+    The default reader is able to set up HTTP connections when retrieving
+    \ltext{DTDs} or entities from different machines.
+    If necessary, you can supply your own reader to \acronym{e.g.} provide
+    support for \ltext{PUBLIC} identifiers.
+  \coitem
+    The \XML{} parser now parses the data read from \filename{test.xml}
+    and creates a tree of parsed \XML{} elements.
+
+    The structure of those elements will be described in the next section.
+  \coitem
+    An \classname{XMLWriter} can be used to dump a ``pretty-printed'' view
+    of the parsed \XML  data on an output stream.
+    In this case, we dump the read data to the standard output
+    \ltext{(System.out)}.
+\end{callout}
+
+
+\section{Analyzing The Data}
+
+You can easily traverse the logical tree generated by the parser.
+If you need to create your own object tree, you can create your custom
+% TODO: make "chapter 3" a xref
+builder, which is described in chapter 3.
+
+The default \XML{} builder, \classname{StdXMLBuilder} generates a tree of
+\classname{IXMLElement} objects.
+Every such object has a name and can have attributes, \ltext{\#PCDATA} content and child objects.
+
+The following XML data:
+
+\begin{example}
+$<$FOO attr1="fred" attr2="barney"$>$
+~~~~$<$BAR a1="flintstone" a2="rubble"$>$
+~~~~~~~~Some data.
+~~~~$<$/BAR$>$
+~~~~$<$QUUX/$>$
+$<$/FOO$>$
+\end{example}
+
+is parsed to the following objects:
+
+\begin{itemize}
+  \item[] Element FOO:
+    \begin{itemize}
+      \item[] Attributes = \{ "attr1"="fred", "attr2"="barney" \}
+      \item[] Children = \{ BAR, QUUX \}
+      \item[] PCData = null
+    \end{itemize}
+  \item[] Element BAR:
+    \begin{itemize}
+      \item[] Attributes = \{ "a1"="flintstone", "a2"="rubble" \}
+      \item[] Children = \{\}
+      \item[] PCData = "Some data."
+    \end{itemize}
+  \item[] Element QUUX:
+    \begin{itemize}
+      \item[] Attributes = \{\}
+      \item[] Children = \{\}
+      \item[] PCData = null
+    \end{itemize}
+\end{itemize}
+
+You can retrieve the name of an element using \methodname{getFullName}, thus:
+
+\begin{example}
+FOO.getFullName() $\to$ "FOO"
+\end{example}
+
+You can enumerate the attribute keys using 
+\methodname{enumerateAttributeNames}:
+
+\begin{example}
+Enumeration enum = FOO.enumerateAttributeNames();
+\xkeyword{while} (enum.hasMoreElements()) \{
+~~System.out.print(enum.nextElement());
+~~System.out.print(' ');
+\}
+$\to$ attr1 attr2
+\end{example}
+
+You can retrieve the value of an attribute using \methodname{getAttribute}:
+
+\begin{example}
+FOO.getAttribute ("attr1", null) $\to$ "fred"
+\end{example}
+
+The child elements can be enumerated using \methodname{enumerateChildren}:
+
+\begin{example}
+Enumeration enum = FOO.enumerateChildren();
+\xkeyword{while} (enum.hasMoreElements()) \{
+~~System.out.print(enum.nextElement() + ' ');
+\}
+$\to$ BAR QUUX
+\end{example}
+
+If the element contains parsed character data \ltext{(\#PCDATA)} as its only
+child.
+You can retrieve that data using \methodname{getContent}:
+
+\begin{example}
+BAR.getContent() $\to$ "Some data."
+\end{example}
+
+If an element contains both \ltext{\#PCDATA} and \XML  elements as its
+children, the character data segments will be put in untitled \XML
+elements (whose name is \ltext{null}).
+
+\classname{IXMLElement} contains many convenience methods for retrieving data
+and traversing the \XML  tree.
+
+
+\section{Generating \XML}
+
+You can very easily create a tree of \XML  elements or modify an
+existing one.
+
+To create a new tree, just create an \classname{IXMLElement} object:
+
+\begin{example}
+IXMLElement elt = \xkeyword{new} XMLElement("ElementName");
+\end{example}
+
+You can add an attribute to the element by calling \methodname{setAttribute}.
+
+\begin{example}
+elt.setAttribute("key", "value");
+\end{example}
+
+You can add a child element to an element by calling \methodname{addChild}:
+
+\begin{example}
+IXMLElement child = elt.createElement("Child");
+elt.addChild(child);
+\end{example}
+
+Note that the child element is created calling \methodname{createElement}.
+This insures that the child instance is compatible with its new parent.
+
+If an element has no children, you can add \ltext{\#PCDATA} content to it using
+\methodname{setContent}:
+
+\begin{example}
+child.setContent("Some content");
+\end{example}
+
+If the element does have children, you can add \ltext{\#PCDATA} content to it
+by adding an untitled element, which you create by calling
+\methodname{createPCDataElement}:
+
+\begin{example}
+IXMLElement pcdata = elt.createPCDataElement();
+pcdata.setContent("Blah blah");
+elt.addChild(pcdata);
+\end{example}
+
+When you have created or edited the XML element tree, you can write it out to
+an output stream or writer using an \classname{XMLWriter}:
+
+\begin{example}
+java.io.Writer output = \ldots;
+IXMLElement xmltree = \ldots;
+XMLWriter xmlwriter = new XMLWriter(output);
+writer.write(xmltree);
+\end{example}
+
+
+\section{Namespaces}
+
+As of version 2.1, \ltext{NanoXML} has support for namespaces.
+Namespaces allow you to attach a \ltext{URI} to the name of an element name or an attribute.
+This \ltext{URI} allows you to make a distinction between similary named
+entities coming from different sources.
+More information about namespaces can be found in the XML Namespaces
+recommendation, which can be found at
+\href{http://www.w3c.org/TR/REC-xml-names/}%
+{http://www.w3c.org/TR/REC-xml-names/}.
+
+Please note that a \ltext{DTD} has no support for namespaces.
+It is important to understand that an \XML  document can have only one
+\ltext{DTD}.
+Though the namespace \ltext{URI} is often presented as a \ltext{URL}, that
+\ltext{URL} is not a system id for a \ltext{DTD}.
+The only function of a namespace \ltext{URI} is to provide a globally unique
+name.
+
+As an example, let�s have the following \XML  data:
+
+\begin{example}
+$<$doc:book xmlns:doc="http://nanoxml.n3.net/book"$>$
+~~$<$chapter xmlns="http://nanoxml.n3.net/chapter"
+~~~~~~~~~~~title="Introduction"
+~~~~~~~~~~~doc:id="chapter1"/$>$
+$<$/doc:book$>$
+\end{example}
+
+The top-level element uses the namespace \ltext{``http://nanoxml.n3.net/book''}.
+The prefix is used as an alias for the namespace, which is defined in the
+attribute \ltext{xmlns:doc}.
+This prefix is defined for the \ltext{doc:book} element and its child elements.
+
+The chapter element uses the namespace
+\ltext{``http://nanoxml.n3.net/chapter''}.
+Because the namespace \ltext{URI} has been defined as the value of the xmlns
+attribute, the namespace is the default namespace for the chapter element.
+Default namespaces are inherited by the child elements, but only for their
+names.
+Attributes never have a default namespace.
+
+The chapter element has an attribute \ltext{doc:id}, which is defined in the
+same namespace as \ltext{doc:book} because of the doc prefix.
+
+\ltext{NanoXML 2.1} offers some variants on the standard retrieval methods to allow the application to access the namespace information.
+
+In the following examples, we assume the variable book to contain the
+\ltext{doc:book} element and the variable chapter to contain the chapter
+element.
+
+To get the full name, which includes the namespace prefix, of the element, use \methodname{getFullName}:
+
+\begin{example}
+book.getFullName() $\to$ "doc:book"
+chapter.getFullName() $\to$ "chapter"
+\end{example}
+
+To get the short name, which excludes the namespace prefix, of the element,
+use \methodname{getName}:
+
+\begin{example}
+book.getName() $\to$ "book"
+chapter.getName $\to$ "chapter"
+\end{example}
+
+For elements that have no associated namespace, \methodname{getName} and
+\methodname{getFullName} are equivalent.
+
+To get the namespace \ltext{URI} associated with the name of the element, use \methodname{getNamespace}:
+
+\begin{example}
+book.getNamespace() $\to$ "http://nanoxml.n3.net/book"
+chapter.getNamespace() $\to$ "http://nanoxml.n3.net/chapter"
+\end{example}
+
+If no namespace is associated with the name of the element, this method returns \variable{null}.
+
+You can get an attribute of an element using either its full name (which
+includes its prefix) or its short name together with its namespace \ltext{URI}, so the following two instructions are equivalent:
+
+\begin{example}
+chapter.getAttribute("doc:id", null)
+chapter.getAttribute("id", "http://nanoxml.n3.net/book", null)
+\end{example}
+
+Note that the title attribute of chapter has no namespace, even though the
+chapter element name has a default namespace.
+
+You can create a new element which uses a namespace this way:
+
+\begin{example}
+book = \xkeyword{new} XMLElement("doc:book", "http://nanoxml.n3.net/book");
+chapter = book.createElement("chapter",
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"http://nanoxml.n3.net/chapter");
+\end{example}
+
+You can add an attribute which uses a namespace this way:
+
+\begin{example}
+chapter.setAttribute("doc:id",
+~~~~~~~~~~~~~~~~~~~~~"http://nanoxml.n3.net/book",
+~~~~~~~~~~~~~~~~~~~~~chapterId);
+\end{example}
+
diff --git a/Documentation/NanoXML-Java/LaTeX/streaming_data.tex b/Documentation/NanoXML-Java/LaTeX/streaming_data.tex
new file mode 100644
index 0000000..fc0976e
--- /dev/null
+++ b/Documentation/NanoXML-Java/LaTeX/streaming_data.tex
@@ -0,0 +1,133 @@
+\chapter{Retrieving Data From An \XML{} Stream}
+
+If you're retrieving data from a stream, but you don't want to wait to process the data until it's completely read, you can use streaming.
+
+\section{The \XML{} Builder}
+
+The \XML{} data tree is created using an \XML{} builder. By default, the builder creates a tree of \classname{IXMLElement}.
+
+While the parser parses the data, it notifies the builder of any elements it encounters. Using this information, the builder generate the object tree. When the parser is done processing the data, it retrieves the object tree from the builder using \methodname{getResult}.
+
+The following example shows a simple builder that prints the notifications on the standard output.
+
+\begin{example}
+\xkeyword{import} java.io.*;
+\xkeyword{import} net.n3.nanoxml.*;
+~
+\xkeyword{public class} MyBuilder
+~~~~\xkeyword{implements} IXMLBuilder
+~~\{
+~~\xkeyword{public void} startBuilding(String systemID,\xcallout{1}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~\xkeyword{int} lineNr)
+~~\{
+~~~~System.out.println("Document started");
+~~\}
+~
+~~\xkeyword{public void} newProcessingInstruction(String target,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Reader reader)
+~~~~\xkeyword{throws} IOException
+~~\{
+~~~~System.out.println("New PI with target " + target);
+~~\}
+~
+~~\xkeyword{public void} startElement(String name,\xcallout{3}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~String nsPrefix,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~String nsSystemID,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~String systemID,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~\xkeyword{int} lineNr)
+~~\{
+~~~~System.out.println("Element started: " + name);
+~~\}
+~
+~~\xkeyword{public void} endElement(String name,\xcallout{4}
+~~~~~~~~~~~~~~~~~~~~~~~~~String nsPrefix,
+~~~~~~~~~~~~~~~~~~~~~~~~~String nsSystemID)
+~~\{
+~~~~System.out.println("Element ended: " + name);
+~~\}
+~
+~~\xkeyword{public void} addAttribute(String key,\xcallout{5}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~String nsPrefix,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~String nsSystemID,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~String value,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~String type)
+~~\{
+~~~~System.out.println(" " + key + ": " + type + " = " + value);
+~~\}
+~
+~~\xkeyword{public void} elementAttributesProcessed(String name,\xcallout{6}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~String nsPrefix,
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~String nsSystemID)
+~~\{
+~~~~// nothing to do
+~~\}
+~
+~~\xkeyword{public void} addPCData(Reader reader,\xcallout{7}
+~~~~~~~~~~~~~~~~~~~~~~~~String systemID,
+~~~~~~~~~~~~~~~~~~~~~~~~\xkeyword{int} lineNr)
+~~~~\xkeyword{throws} IOException
+~~\{
+~~~~System.out.println("\#PCDATA");
+~~\}
+~
+~~\xkeyword{public} Object getResult()\xcallout{8}
+~~\{
+~~~~\xkeyword{return null};
+~~\}
+\}
+\end{example}
+
+\begin{callout}
+  \coitem
+    The \XML{} parser started parsing the document. The \variable{lineNr}
+    parameter contains the line number where the document starts.
+  \coitem
+    The \XML{} parser encountered a processing instruction (PI) which is not
+    handled by the parser itself.
+    The target contains the target of the PI.
+    The contents of the PI can be read from reader.
+  \coitem
+    A new element has been started at line \variable{lineNr}.
+    The name of the element is stored in \variable{name}.
+  \coitem
+    The current element has ended.
+    For convenience, the name of that element is put in the parameter
+    \variable{name}.
+  \coitem
+    An attribute is added to the current element.
+  \coitem
+    This method is called when all the attributes of the current element
+    have been processed.
+  \coitem
+    A \ltext{\#PCDATA} section has been encountered.
+    The contents of the section can be read from reader.
+  \coitem
+    This method is called when the parsing has finished.
+    If the builder has a result, it has to return it to the parser in this
+    method.
+\end{callout}
+
+
+\section{Registering an XML Builder}
+
+You can register the builder to the parser using the method \methodname{setBuilder}.
+
+The following example shows how to create a parser which uses the builder we created in the previous section:
+
+\begin{example}
+\xkeyword{import} net.n3.nanoxml.*;
+\xkeyword{import} java.io.*;
+~
+\xkeyword{public class} DumpXML
+\{
+~~\xkeyword{public static void} main(String args[])
+~~~~\xkeyword{throws} Exception
+~~\{
+~~~~IXMLParser parser = XMLParserFactory.createDefaultXMLParser();
+~~~~IXMLReader reader = StdXMLReader.fileReader("test.xml");
+~~~~parser.setReader(reader);
+~~~~parser.setBuilder(new MyBuilder());
+~~~~parser.parse();
+~~\}
+\}
+\end{example}
diff --git a/Documentation/NanoXML-Java/LaTeX/structure.graffle b/Documentation/NanoXML-Java/LaTeX/structure.graffle
new file mode 100755
index 0000000..f469c27
--- /dev/null
+++ b/Documentation/NanoXML-Java/LaTeX/structure.graffle
@@ -0,0 +1,769 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+	<key>CanvasColor</key>
+	<dict>
+		<key>w</key>
+		<real>1.000000e+00</real>
+	</dict>
+	<key>ColumnAlign</key>
+	<integer>0</integer>
+	<key>ColumnSpacing</key>
+	<real>3.600000e+01</real>
+	<key>GraphDocumentVersion</key>
+	<integer>2</integer>
+	<key>GraphicsList</key>
+	<array>
+		<dict>
+			<key>Class</key>
+			<string>LineGraphic</string>
+			<key>Head</key>
+			<dict>
+				<key>ID</key>
+				<integer>3</integer>
+			</dict>
+			<key>ID</key>
+			<integer>21</integer>
+			<key>Points</key>
+			<array>
+				<string>{271.396, 149.4}</string>
+				<string>{271.405, 198.45}</string>
+			</array>
+			<key>Style</key>
+			<dict>
+				<key>stroke</key>
+				<dict>
+					<key>HeadArrow</key>
+					<string>FilledArrow</string>
+					<key>LineType</key>
+					<integer>1</integer>
+					<key>TailArrow</key>
+					<string>0</string>
+				</dict>
+			</dict>
+			<key>Tail</key>
+			<dict>
+				<key>ID</key>
+				<integer>2</integer>
+			</dict>
+		</dict>
+		<dict>
+			<key>Class</key>
+			<string>LineGraphic</string>
+			<key>Head</key>
+			<dict>
+				<key>ID</key>
+				<integer>2</integer>
+				<key>Info</key>
+				<integer>2</integer>
+			</dict>
+			<key>ID</key>
+			<integer>20</integer>
+			<key>Points</key>
+			<array>
+				<string>{270.442, 76.475}</string>
+				<string>{271.404, 113.4}</string>
+			</array>
+			<key>Style</key>
+			<dict>
+				<key>stroke</key>
+				<dict>
+					<key>HeadArrow</key>
+					<string>FilledArrow</string>
+					<key>LineType</key>
+					<integer>1</integer>
+					<key>TailArrow</key>
+					<string>0</string>
+				</dict>
+			</dict>
+			<key>Tail</key>
+			<dict>
+				<key>ID</key>
+				<integer>1</integer>
+			</dict>
+		</dict>
+		<dict>
+			<key>Class</key>
+			<string>LineGraphic</string>
+			<key>Head</key>
+			<dict>
+				<key>ID</key>
+				<integer>8</integer>
+			</dict>
+			<key>ID</key>
+			<integer>19</integer>
+			<key>Points</key>
+			<array>
+				<string>{271.396, 305.325}</string>
+				<string>{272.238, 340.2}</string>
+			</array>
+			<key>Style</key>
+			<dict>
+				<key>stroke</key>
+				<dict>
+					<key>HeadArrow</key>
+					<string>FilledArrow</string>
+					<key>LineType</key>
+					<integer>1</integer>
+					<key>TailArrow</key>
+					<string>0</string>
+				</dict>
+			</dict>
+			<key>Tail</key>
+			<dict>
+				<key>ID</key>
+				<integer>7</integer>
+			</dict>
+		</dict>
+		<dict>
+			<key>Class</key>
+			<string>LineGraphic</string>
+			<key>Head</key>
+			<dict>
+				<key>ID</key>
+				<integer>7</integer>
+			</dict>
+			<key>ID</key>
+			<integer>18</integer>
+			<key>Points</key>
+			<array>
+				<string>{271.395, 234.45}</string>
+				<string>{271.404, 269.325}</string>
+			</array>
+			<key>Style</key>
+			<dict>
+				<key>stroke</key>
+				<dict>
+					<key>HeadArrow</key>
+					<string>FilledArrow</string>
+					<key>LineType</key>
+					<integer>1</integer>
+					<key>TailArrow</key>
+					<string>0</string>
+				</dict>
+			</dict>
+			<key>Tail</key>
+			<dict>
+				<key>ID</key>
+				<integer>3</integer>
+			</dict>
+		</dict>
+		<dict>
+			<key>Class</key>
+			<string>LineGraphic</string>
+			<key>Head</key>
+			<dict>
+				<key>ID</key>
+				<integer>5</integer>
+			</dict>
+			<key>ID</key>
+			<integer>17</integer>
+			<key>Points</key>
+			<array>
+				<string>{246.967, 149.4}</string>
+				<string>{178.856, 212.625}</string>
+			</array>
+			<key>Style</key>
+			<dict>
+				<key>stroke</key>
+				<dict>
+					<key>HeadArrow</key>
+					<string>FilledArrow</string>
+					<key>LineType</key>
+					<integer>1</integer>
+					<key>TailArrow</key>
+					<string>0</string>
+				</dict>
+			</dict>
+			<key>Tail</key>
+			<dict>
+				<key>ID</key>
+				<integer>2</integer>
+				<key>Info</key>
+				<integer>5</integer>
+			</dict>
+		</dict>
+		<dict>
+			<key>Class</key>
+			<string>LineGraphic</string>
+			<key>Head</key>
+			<dict>
+				<key>ID</key>
+				<integer>3</integer>
+			</dict>
+			<key>ID</key>
+			<integer>16</integer>
+			<key>Points</key>
+			<array>
+				<string>{160.875, 159.371}</string>
+				<string>{226.8, 216.227}</string>
+			</array>
+			<key>Style</key>
+			<dict>
+				<key>stroke</key>
+				<dict>
+					<key>HeadArrow</key>
+					<string>FilledArrow</string>
+					<key>LineType</key>
+					<integer>1</integer>
+					<key>TailArrow</key>
+					<string>FilledArrow</string>
+				</dict>
+			</dict>
+			<key>Tail</key>
+			<dict>
+				<key>ID</key>
+				<integer>4</integer>
+			</dict>
+		</dict>
+		<dict>
+			<key>Class</key>
+			<string>LineGraphic</string>
+			<key>Head</key>
+			<dict>
+				<key>ID</key>
+				<integer>5</integer>
+			</dict>
+			<key>ID</key>
+			<integer>15</integer>
+			<key>Points</key>
+			<array>
+				<string>{115.428, 177.75}</string>
+				<string>{115.397, 212.625}</string>
+			</array>
+			<key>Style</key>
+			<dict>
+				<key>stroke</key>
+				<dict>
+					<key>HeadArrow</key>
+					<string>FilledArrow</string>
+					<key>LineType</key>
+					<integer>1</integer>
+					<key>TailArrow</key>
+					<string>0</string>
+				</dict>
+			</dict>
+			<key>Tail</key>
+			<dict>
+				<key>ID</key>
+				<integer>4</integer>
+			</dict>
+		</dict>
+		<dict>
+			<key>Class</key>
+			<string>LineGraphic</string>
+			<key>Head</key>
+			<dict>
+				<key>ID</key>
+				<integer>6</integer>
+			</dict>
+			<key>ID</key>
+			<integer>14</integer>
+			<key>Points</key>
+			<array>
+				<string>{316.8, 131.198}</string>
+				<string>{368.55, 166.139}</string>
+			</array>
+			<key>Style</key>
+			<dict>
+				<key>stroke</key>
+				<dict>
+					<key>HeadArrow</key>
+					<string>FilledArrow</string>
+					<key>LineType</key>
+					<integer>1</integer>
+					<key>TailArrow</key>
+					<string>0</string>
+				</dict>
+			</dict>
+			<key>Tail</key>
+			<dict>
+				<key>ID</key>
+				<integer>2</integer>
+			</dict>
+		</dict>
+		<dict>
+			<key>Class</key>
+			<string>LineGraphic</string>
+			<key>Head</key>
+			<dict>
+				<key>ID</key>
+				<integer>4</integer>
+			</dict>
+			<key>ID</key>
+			<integer>13</integer>
+			<key>Points</key>
+			<array>
+				<string>{226.8, 131.202}</string>
+				<string>{160.875, 159.371}</string>
+			</array>
+			<key>Style</key>
+			<dict>
+				<key>stroke</key>
+				<dict>
+					<key>HeadArrow</key>
+					<string>FilledArrow</string>
+					<key>LineType</key>
+					<integer>1</integer>
+					<key>TailArrow</key>
+					<string>0</string>
+				</dict>
+			</dict>
+			<key>Tail</key>
+			<dict>
+				<key>ID</key>
+				<integer>2</integer>
+			</dict>
+		</dict>
+		<dict>
+			<key>Class</key>
+			<string>LineGraphic</string>
+			<key>Head</key>
+			<dict>
+				<key>ID</key>
+				<integer>10</integer>
+			</dict>
+			<key>ID</key>
+			<integer>12</integer>
+			<key>Points</key>
+			<array>
+				<string>{272.231, 368.55}</string>
+				<string>{315, 396.9}</string>
+			</array>
+			<key>Style</key>
+			<dict>
+				<key>stroke</key>
+				<dict>
+					<key>HeadArrow</key>
+					<string>0</string>
+					<key>LineType</key>
+					<integer>1</integer>
+					<key>TailArrow</key>
+					<string>0</string>
+				</dict>
+			</dict>
+			<key>Tail</key>
+			<dict>
+				<key>ID</key>
+				<integer>8</integer>
+				<key>Info</key>
+				<integer>1</integer>
+			</dict>
+		</dict>
+		<dict>
+			<key>Class</key>
+			<string>LineGraphic</string>
+			<key>Head</key>
+			<dict>
+				<key>ID</key>
+				<integer>9</integer>
+				<key>Info</key>
+				<integer>2</integer>
+			</dict>
+			<key>ID</key>
+			<integer>11</integer>
+			<key>Points</key>
+			<array>
+				<string>{272.231, 368.55}</string>
+				<string>{223.33, 396.9}</string>
+			</array>
+			<key>Style</key>
+			<dict>
+				<key>stroke</key>
+				<dict>
+					<key>HeadArrow</key>
+					<string>0</string>
+					<key>LineType</key>
+					<integer>1</integer>
+					<key>TailArrow</key>
+					<string>0</string>
+				</dict>
+			</dict>
+			<key>Tail</key>
+			<dict>
+				<key>ID</key>
+				<integer>12</integer>
+			</dict>
+		</dict>
+		<dict>
+			<key>Bounds</key>
+			<string>{{283.5, 396.9}, {63, 42.525}}</string>
+			<key>Class</key>
+			<string>ShapedGraphic</string>
+			<key>ID</key>
+			<integer>10</integer>
+			<key>Magnets</key>
+			<array>
+				<string>{0, 1}</string>
+				<string>{0, -1}</string>
+				<string>{1, 0}</string>
+				<string>{-1, 0}</string>
+			</array>
+			<key>Shape</key>
+			<string>RoundedRectangle</string>
+			<key>Text</key>
+			<dict>
+				<key>Text</key>
+				<string>{\rtf1\mac\ansicpg10000\cocoartf100
+{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;\f1\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 BAR\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f1\b0 \cf0 id='2'}</string>
+			</dict>
+		</dict>
+		<dict>
+			<key>Bounds</key>
+			<string>{{192.15, 396.9}, {63, 42.525}}</string>
+			<key>Class</key>
+			<string>ShapedGraphic</string>
+			<key>ID</key>
+			<integer>9</integer>
+			<key>Magnets</key>
+			<array>
+				<string>{0, 1}</string>
+				<string>{0, -1}</string>
+				<string>{1, 0}</string>
+				<string>{-1, 0}</string>
+			</array>
+			<key>Shape</key>
+			<string>RoundedRectangle</string>
+			<key>Text</key>
+			<dict>
+				<key>Text</key>
+				<string>{\rtf1\mac\ansicpg10000\cocoartf100
+{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;\f1\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 BAR\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f1\b0 \cf0 id='1'}</string>
+			</dict>
+		</dict>
+		<dict>
+			<key>Bounds</key>
+			<string>{{240.975, 340.2}, {63, 28.35}}</string>
+			<key>Class</key>
+			<string>ShapedGraphic</string>
+			<key>ID</key>
+			<integer>8</integer>
+			<key>Magnets</key>
+			<array>
+				<string>{0, 1}</string>
+				<string>{0, -1}</string>
+				<string>{1, 0}</string>
+				<string>{-1, 0}</string>
+			</array>
+			<key>Shape</key>
+			<string>RoundedRectangle</string>
+			<key>Text</key>
+			<dict>
+				<key>Text</key>
+				<string>{\rtf1\mac\ansicpg10000\cocoartf100
+{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 FOO}</string>
+			</dict>
+		</dict>
+		<dict>
+			<key>Bounds</key>
+			<string>{{226.8, 269.325}, {90, 36}}</string>
+			<key>Class</key>
+			<string>ShapedGraphic</string>
+			<key>ID</key>
+			<integer>7</integer>
+			<key>Magnets</key>
+			<array>
+				<string>{0, 1}</string>
+				<string>{0, -1}</string>
+				<string>{1, 0}</string>
+				<string>{-1, 0}</string>
+			</array>
+			<key>Shape</key>
+			<string>Rectangle</string>
+			<key>Text</key>
+			<dict>
+				<key>Text</key>
+				<string>{\rtf1\mac\ansicpg10000\cocoartf100
+{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 Builder}</string>
+			</dict>
+		</dict>
+		<dict>
+			<key>Bounds</key>
+			<string>{{368.55, 148.275}, {113.4, 36}}</string>
+			<key>Class</key>
+			<string>ShapedGraphic</string>
+			<key>ID</key>
+			<integer>6</integer>
+			<key>Magnets</key>
+			<array>
+				<string>{0, 1}</string>
+				<string>{0, -1}</string>
+				<string>{1, 0}</string>
+				<string>{-1, 0}</string>
+			</array>
+			<key>Shape</key>
+			<string>Rectangle</string>
+			<key>Text</key>
+			<dict>
+				<key>Text</key>
+				<string>{\rtf1\mac\ansicpg10000\cocoartf100
+{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 Entity Resolver}</string>
+			</dict>
+		</dict>
+		<dict>
+			<key>Bounds</key>
+			<string>{{52, 212.625}, {127.575, 42.525}}</string>
+			<key>Class</key>
+			<string>ShapedGraphic</string>
+			<key>ID</key>
+			<integer>5</integer>
+			<key>Magnets</key>
+			<array>
+				<string>{1, 1}</string>
+				<string>{1, -1}</string>
+				<string>{-1, -1}</string>
+				<string>{-1, 1}</string>
+				<string>{0, 1}</string>
+				<string>{0, -1}</string>
+				<string>{1, 0}</string>
+				<string>{-1, 0}</string>
+			</array>
+			<key>Shape</key>
+			<string>Rectangle</string>
+			<key>Text</key>
+			<dict>
+				<key>Text</key>
+				<string>{\rtf1\mac\ansicpg10000\cocoartf100
+{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;\f1\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 Entity Resolver\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f1\b0 \cf0 (parameter entities)}</string>
+			</dict>
+		</dict>
+		<dict>
+			<key>Bounds</key>
+			<string>{{70.875, 141.75}, {90, 36}}</string>
+			<key>Class</key>
+			<string>ShapedGraphic</string>
+			<key>ID</key>
+			<integer>4</integer>
+			<key>Magnets</key>
+			<array>
+				<string>{0, 1}</string>
+				<string>{0, -1}</string>
+				<string>{1, 0}</string>
+				<string>{-1, 0}</string>
+			</array>
+			<key>Shape</key>
+			<string>Rectangle</string>
+			<key>Text</key>
+			<dict>
+				<key>Text</key>
+				<string>{\rtf1\mac\ansicpg10000\cocoartf100
+{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 Validator}</string>
+			</dict>
+		</dict>
+		<dict>
+			<key>Bounds</key>
+			<string>{{226.8, 198.45}, {90, 36}}</string>
+			<key>Class</key>
+			<string>ShapedGraphic</string>
+			<key>ID</key>
+			<integer>3</integer>
+			<key>Magnets</key>
+			<array>
+				<string>{0, 1}</string>
+				<string>{0, -1}</string>
+				<string>{1, 0}</string>
+				<string>{-1, 0}</string>
+			</array>
+			<key>Shape</key>
+			<string>Rectangle</string>
+			<key>Text</key>
+			<dict>
+				<key>Text</key>
+				<string>{\rtf1\mac\ansicpg10000\cocoartf100
+{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 Parser}</string>
+			</dict>
+		</dict>
+		<dict>
+			<key>Bounds</key>
+			<string>{{226.8, 113.4}, {90, 36}}</string>
+			<key>Class</key>
+			<string>ShapedGraphic</string>
+			<key>ID</key>
+			<integer>2</integer>
+			<key>Magnets</key>
+			<array>
+				<string>{0, 1}</string>
+				<string>{0, -1}</string>
+				<string>{1, 0}</string>
+				<string>{-1, 0}</string>
+				<string>{-0.325556, 0.613889}</string>
+			</array>
+			<key>Shape</key>
+			<string>Rectangle</string>
+			<key>Text</key>
+			<dict>
+				<key>Text</key>
+				<string>{\rtf1\mac\ansicpg10000\cocoartf100
+{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 Reader}</string>
+			</dict>
+		</dict>
+		<dict>
+			<key>Bounds</key>
+			<string>{{220.7, 16.475}, {98, 60}}</string>
+			<key>Class</key>
+			<string>ShapedGraphic</string>
+			<key>FitText</key>
+			<string>YES</string>
+			<key>ID</key>
+			<integer>1</integer>
+			<key>Shape</key>
+			<string>Rectangle</string>
+			<key>Style</key>
+			<dict>
+				<key>fill</key>
+				<dict>
+					<key>Draws</key>
+					<string>NO</string>
+				</dict>
+				<key>shadow</key>
+				<dict>
+					<key>Draws</key>
+					<string>NO</string>
+				</dict>
+				<key>stroke</key>
+				<dict>
+					<key>Draws</key>
+					<string>NO</string>
+				</dict>
+			</dict>
+			<key>Text</key>
+			<dict>
+				<key>Text</key>
+				<string>{\rtf1\mac\ansicpg10000\cocoartf100
+{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
+
+\f0\fs24 \cf0 <FOO>\
+    <BAR id='1'/>\
+    <BAR id='2'/>\
+</FOO>}</string>
+			</dict>
+		</dict>
+	</array>
+	<key>GridInfo</key>
+	<dict>
+		<key>GridSpacing</key>
+		<real>1.417500e+01</real>
+		<key>MajorGridSpacing</key>
+		<integer>10</integer>
+	</dict>
+	<key>HPages</key>
+	<integer>1</integer>
+	<key>ImageCounter</key>
+	<integer>2</integer>
+	<key>IsPalette</key>
+	<string>NO</string>
+	<key>Layers</key>
+	<array>
+		<dict>
+			<key>Lock</key>
+			<string>NO</string>
+			<key>Name</key>
+			<string>Layer 1</string>
+			<key>Print</key>
+			<string>YES</string>
+			<key>View</key>
+			<string>YES</string>
+		</dict>
+	</array>
+	<key>LayoutInfo</key>
+	<dict>
+		<key>AutoAdjust</key>
+		<string>YES</string>
+		<key>MagneticFieldCenter</key>
+		<string>{0, 0}</string>
+	</dict>
+	<key>MagnetsEnabled</key>
+	<string>YES</string>
+	<key>MagnetsVisible</key>
+	<string>YES</string>
+	<key>PageBreakColor</key>
+	<dict>
+		<key>w</key>
+		<real>6.666667e-01</real>
+	</dict>
+	<key>PageBreaks</key>
+	<string>YES</string>
+	<key>PageSetup</key>
+	<data>
+	BAt0eXBlZHN0cmVhbYED6IQBQISEhAtOU1ByaW50SW5mbwGEhAhOU09iamVjdACFkoSE
+	hBNOU011dGFibGVEaWN0aW9uYXJ5AISEDE5TRGljdGlvbmFyeQCUhAFpFZKEhIQITlNT
+	dHJpbmcBlIQBKw9OU1BhZ2VzUGVyU2hlZXSGkoSEhAhOU051bWJlcgCEhAdOU1ZhbHVl
+	AJSEASqEhAFznQGGkoSZmRBOU0pvYkRpc3Bvc2l0aW9uhpKEmZkPTlNQcmludFNwb29s
+	Sm9ihpKEmZkOTlNCb3R0b21NYXJnaW6GkoSbnISEAWaeJIaShJmZC05TUGFwZXJOYW1l
+	hpKEmZkCQTSGkoSZmQ9OU1ByaW50QWxsUGFnZXOGkoSbnJ2dAIaShJmZDU5TSm9iRmVh
+	dHVyZXOGkoSWlwCGkoSZmRVOU0hvcml6b25hbFBhZ2luYXRpb26GkoSbnJ2dAIaShJmZ
+	CE5TQ29waWVzhpKEm5yEhAFTnwGGkoSZmQ9OU1NjYWxpbmdGYWN0b3KGkoSbnISEAWSg
+	AYaShJmZC05TRmlyc3RQYWdlhpKEm5ytnwGGkoSZmRROU1ZlcnRpY2FsUGFnaW5hdGlv
+	boaShJucnZ0AhpKEmZkSTlNSZXZlcnNlUGFnZU9yZGVyhpKmkoSZmQ1OU1JpZ2h0TWFy
+	Z2luhpKEm5yiniSGkoSZmRZOU0hvcml6b250YWxseUNlbnRlcmVkhpKEm5ydnQGGkoSZ
+	mQxOU0xlZnRNYXJnaW6GkoSbnKKeJIaShJmZDU5TT3JpZW50YXRpb26GkoSbnJ2dAIaS
+	hJmZGU5TUHJpbnRSZXZlcnNlT3JpZW50YXRpb26GkqaShJmZCk5TTGFzdFBhZ2WGkoSb
+	nISXl4J/////hpKEmZkLTlNUb3BNYXJnaW6GkoSbnKKeJIaShJmZFE5TVmVydGljYWxs
+	eUNlbnRlcmVkhpK5koSZmQtOU1BhcGVyU2l6ZYaShJychIQGez89ZmZ9oYECU4EDSoaG
+	hg==
+	</data>
+	<key>RowAlign</key>
+	<integer>0</integer>
+	<key>RowSpacing</key>
+	<real>3.600000e+01</real>
+	<key>VPages</key>
+	<integer>1</integer>
+	<key>WindowInfo</key>
+	<dict>
+		<key>Frame</key>
+		<string>{{21, 0}, {538, 746}}</string>
+		<key>VisibleRegion</key>
+		<string>{{0, 0}, {523, 669}}</string>
+		<key>Zoom</key>
+		<string>1</string>
+	</dict>
+</dict>
+</plist>
diff --git a/Documentation/NanoXML-Lite/LaTeX/Makefile b/Documentation/NanoXML-Lite/LaTeX/Makefile
new file mode 100644
index 0000000..b5780d4
--- /dev/null
+++ b/Documentation/NanoXML-Lite/LaTeX/Makefile
@@ -0,0 +1,16 @@
+MAIN=NanoXML-Lite
+COMPONENTS=NanoXML-Lite.tex defs.tex introduction.tex retrieving_data.tex
+PDFLATEX=/usr/local/teTeX/bin/powerpc-apple-darwin-current/pdflatex
+
+all: $(MAIN).pdf
+
+clean:
+	rm -f $(MAIN).pdf $(COMPONENTS:.tex=.aux) $(MAIN).log $(MAIN).toc
+
+$(MAIN).pdf: $(COMPONENTS)
+	sed "s/ModDate (D:[0-9]*)/ModDate (D:`date +'%Y%m%d%H%M%S'`)/" \
+	        < $(MAIN).tex >$(MAIN).tmp
+	mv $(MAIN).tmp $(MAIN).tex
+	$(PDFLATEX) $(MAIN).tex
+	$(PDFLATEX) $(MAIN).tex >/dev/null 2>&1
+	$(PDFLATEX) $(MAIN).tex >/dev/null 2>&1
diff --git a/Documentation/NanoXML-Lite/LaTeX/NanoXML-Lite.tex b/Documentation/NanoXML-Lite/LaTeX/NanoXML-Lite.tex
new file mode 100644
index 0000000..8ea6ba9
--- /dev/null
+++ b/Documentation/NanoXML-Lite/LaTeX/NanoXML-Lite.tex
@@ -0,0 +1,30 @@
+\documentclass[a4paper]{book}
+\usepackage{hyperref}
+\title{NanoXML/Lite 2.2}
+\author{Marc De Scheemaecker $<$cyberelf at mac.com$>$}
+
+\pdfinfo {
+    /Title (NanoXML/Lite 2.2)
+    /Creator (LaTeX)
+    /Producer (pdfTeX 0.14a)
+    /Author (Marc De Scheemaecker)
+    /CreationDate (D:20010204000000)
+    /ModDate (D:20030201171156)
+    /Subject (NanoXML/Lite)
+    /Keywords (NanoXML,XML)
+}
+
+\include{defs}
+
+\begin{document}
+
+\titlepage
+\maketitle
+
+\tableofcontents
+
+\include{introduction}
+
+\include{retrieving_data}
+
+\end{document}
diff --git a/Documentation/NanoXML-Lite/LaTeX/defs.tex b/Documentation/NanoXML-Lite/LaTeX/defs.tex
new file mode 100644
index 0000000..f187f2e
--- /dev/null
+++ b/Documentation/NanoXML-Lite/LaTeX/defs.tex
@@ -0,0 +1,24 @@
+\newcommand\ltext[1]{\mbox{#1}}
+\newcommand\term[1]{\emph{\mbox{#1}}}
+\newcommand\acronym[1]{\ltext{#1}}
+\newcommand\classname[1]{\mbox{\tt #1}}
+\newcommand\propertykey[1]{\mbox{\tt #1}}
+\newcommand\propertyvalue[1]{\mbox{\tt #1}}
+\newcommand\methodname[1]{\mbox{\tt #1}}
+\newcommand\filename[1]{\mbox{\sl #1}}
+\newcommand\packagename[1]{\mbox{\tt\sl #1}}
+
+\newcommand\xcallout[1]{\hfill\textcircled{#1}}
+\newcommand\xkeyword[1]{{\bf #1}}
+
+\newcounter{calloutindex}
+\newenvironment{callout}%
+{\begin{itemize}\setcounter{calloutindex}{1}}%
+{\end{itemize}}
+\newcommand\coitem{\item[\textcircled{\arabic{calloutindex}}%
+\addtocounter{calloutindex}{1}]}
+
+\newenvironment{example}%
+{\par\vskip 6pt\tt\small\obeylines\obeyspaces}%
+{\vskip 6pt}
+
diff --git a/Documentation/NanoXML-Lite/LaTeX/introduction.tex b/Documentation/NanoXML-Lite/LaTeX/introduction.tex
new file mode 100644
index 0000000..e6e012e
--- /dev/null
+++ b/Documentation/NanoXML-Lite/LaTeX/introduction.tex
@@ -0,0 +1,106 @@
+\chapter{Introduction}
+
+This chapter gives a short introduction to XML and NanoXML.
+
+\section{About \ltext{XML}}
+
+The extensible markup language,
+\href{http://www.w3c.org/TR/REC-xml}{\ltext{XML}}, is a way to mark up text in
+a structured document.
+
+\ltext{XML} is a simplification of the complex \ltext{SGML} standard.
+\ltext{SGML}, the Standard Generalized Markup Language, is an international
+(\ltext{ISO}) standard for marking up text and graphics.
+The best known application of \ltext{SGML} is \ltext{HTML}.
+
+Although \ltext{SGML} data is very easy to write, it's very difficult to write a
+generic \ltext{SGML} parser.
+When designing \ltext{XML} however, the authors removed much of the flexibility
+of \ltext{SGML} making it much easier to parse \ltext{XML} documents correctly.
+
+\ltext{XML} data is structured as a tree of \term{entities}.
+An entity can be a string of character data or an element which can contain other
+entities.
+Elements can optionally have a set of attributes.
+Attributes are key/value pairs which set some properties of an element.
+
+The following example shows some XML data:
+
+\begin{example}
+$<$book$>$
+~~$<$chapter id="my chapter"$>$
+~~~~$<$title$>$The title$<$/title$>$
+~~~~Some text.
+~~$<$/chapter$>$
+$<$/book$>$
+\end{example}
+
+At the root of the tree, you can find the element ``book''.
+This element contains one child element: ``chapter''.
+The chapter element has one attribute which maps the key ``id'' to
+``my chapter''.
+The chapter element has two child entities: the element ``title'' and the
+character data ``Some text.''.
+Finally, the title element has one child, the string ``The title''.
+
+\section{About \ltext{NanoXML}}
+
+In April 2000, \ltext{NanoXML} was first released as a spin-off project of
+\ltext{AUIT}, the Abstract User Interface Toolkit.
+
+The intent of NanoXML was to be a small parser which was easy to use.
+\ltext{SAX} and \ltext{DOM} are much too complex for what I needed and the
+mainstream parsers were either much too big or had a very restrictive license.
+
+\ltext{NanoXML 1} has all the features I needed: it is very small (about 6K),
+is reasonably fast for small \ltext{XML} documents, is very easy to use and is
+free (\ltext{zlib/libpng} license).
+As I never intended to use \ltext{NanoXML} to parse \ltext{DocBook} documents,
+there was no support for mixed data or \ltext{DTD} parsing.
+
+\ltext{NanoXML} was released as a \ltext{SourceForge} project and, because of the
+very good response from its users, it matured to a small and stable parser.
+The final version, release \ltext{1.6.8} was released in May 2001.
+
+Because of its small size, people started to use \ltext{NanoXML} for embedded
+systems (\ltext{KVM}, \ltext{J2ME}) and kindly submitted patches to make
+\ltext{NanoXML} work in such restricted environment.
+
+\section{\ltext{NanoXML} 2}
+
+In July 2001, \ltext{NanoXML} 2 has been released.
+Unlike \ltext{NanoXML 1}, speed and \ltext{XML} compliancy were considered to be
+very important when the new parser was designed.
+\ltext{NanoXML 2} is also very modular: you can easily replace the different
+components in the parser to customize it to your needs.
+The modularity of \ltext{NanoXML 2} also benefits extensions like \acronym{e.g.}
+\ltext{SAX} support which can now directly access the parser.
+In \ltext{NanoXML 1}, the \ltext{SAX} adapter had to iterate the data structure
+built by the base product.
+
+Although many features were added to \ltext{NanoXML}, the second release was
+still very small.
+The full parser with builder fits in a \ltext{JAR} file of about 32K.
+This is still very tiny, especially when you compare this with the ``standard''
+parsers of more than four times its size.
+
+As there is still need for a tiny parser like \ltext{NanoXML 1}, there is a
+special branch of \ltext{NanoXML 2}: \ltext{NanoXML/Lite}. This parser is source
+compatible with \ltext{NanoXML 1} but features a new parsing algorithm which
+makes it more than twice as fast as the older version.
+It is however more restrictive on the \ltext{XML} data it parses: the older
+version allowed some not-wellformed data to be parsed.
+
+There are three branches of NanoXML 2:
+\begin{itemize}
+  \item[$\bullet$]
+    \term{NanoXML/Lite} is the successor of \ltext{NanoXML 1}.
+    It features an almost compatible parser which is extremely small.
+  \item[$\bullet$]
+    \term{NanoXML/Java} is the standard parser.
+  \item[$\bullet$]
+    \term{NanoXML/SAX} is the \ltext{SAX} adapter for \ltext{NanoXML/Java}.
+\end{itemize}
+
+The latest version of \ltext{NanoXML} is \ltext{NanoXML 2.2.1}, which has been
+released in February 2002.
diff --git a/Documentation/NanoXML-Lite/LaTeX/retrieving_data.tex b/Documentation/NanoXML-Lite/LaTeX/retrieving_data.tex
new file mode 100644
index 0000000..6034833
--- /dev/null
+++ b/Documentation/NanoXML-Lite/LaTeX/retrieving_data.tex
@@ -0,0 +1,172 @@
+\chapter{Retrieving Data From An \ltext{XML} Datasource}
+
+This chapter shows how to retrieve \ltext{XML} data from a standard data source.
+Such source can be a file, an \ltext{HTTP} object or a text string.
+
+\section{A Very Simple Example}
+
+This section describes a very simple \ltext{XML} application.
+It parses \ltext{XML} data from a stream and dumps it to the standard output.
+While its use is very limited, it shows how to set up a parser and parse an
+\ltext{XML} document.
+
+\begin{example}
+\xkeyword{import} nanoxml.*;\xcallout{1}
+\xkeyword{import} java.io.*;
+
+\xkeyword{public class} DumpXML
+\{
+~~\xkeyword{public static void} main(String[] args)
+~~~~\xkeyword{throws} Exception
+~~\{
+~~~~XMLElement xml = new XMLElement();\xcallout{2}
+~~~~FileReader reader = new FileReader("test.xml");
+~~~~xml.parseFromReader(reader);\xcallout{3}
+~~~~System.out.println(xml);\xcallout{4}
+~~\}
+\}
+\end{example}
+
+\begin{callout}
+  \coitem
+    The \ltext{NanoXML} classes are located in the package \packagename{nanoxml}.
+  \coitem
+    This command creates an empty \ltext{XML} element.
+  \coitem
+    The method \methodname{parseFromReader} parses the data in the file
+    \filename{test.xml} and fills the empty element.
+  \coitem
+    The \ltext{XML} element is dumped to the standard output.
+\end{callout}
+
+\section{Analyzing The Data}
+
+You can easily traverse the logical tree generated by the parser.
+By calling one of the \methodname{parse*} methods, you fill an empty
+\ltext{XML} element with the parsed contents.
+Every such object can have a name, attributes, \ltext{\#PCDATA} content and child
+objects.
+
+The following XML data:
+
+\begin{example}
+$<$FOO attr1="fred" attr2="barney"$>$
+~~$<$BAR a1="flintstone" a2="rubble"$>$
+~~~~Some data.
+~~$<$/BAR$>$
+~~$<$QUUX/$>$
+$<$/FOO$>$
+\end{example}
+
+is parsed to the following objects:
+
+\begin{itemize}
+  \item[] Element FOO:
+    \begin{itemize}
+      \item[] Attributes = \{ "attr1"="fred", "attr2"="barney" \}
+      \item[] Children = \{ BAR, QUUX \}
+      \item[] PCData = null
+    \end{itemize}
+  \item[] Element BAR:
+    \begin{itemize}
+      \item[] Attributes = \{ "a1"="flintstone", "a2"="rubble" \}
+      \item[] Children = \{\}
+      \item[] PCData = "Some data."
+    \end{itemize}
+  \item[] Element QUUX:
+    \begin{itemize}
+      \item[] Attributes = \{\}
+      \item[] Children = \{\}
+      \item[] PCData = null
+    \end{itemize}
+\end{itemize}
+
+You can retrieve the name of an element using the method \methodname{getName},
+thus:
+
+\begin{example}
+FOO.getName() $\to$ "FOO"
+\end{example}
+
+You can enumerate the attribute names using the method
+\methodname{enumerateAttributeNames}:
+
+\begin{example}
+Enumeration enum = FOO.enumerateAttributeNames();
+\xkeyword{while} (enum.hasMoreElements()) \{
+~~System.out.print(enum.nextElement());
+~~System.out.print(' ');
+\}
+$\to$ attr1 attr2
+\end{example}
+
+You can retrieve the value of an attribute using \methodname{getAttribute}:
+
+\begin{example}
+FOO.getAttribute("attr1") $\to$ "fred"
+\end{example}
+
+The child elements can be enumerated using the method
+\methodname{enumerateChildren}:
+
+\begin{example}
+Enumeration enum = FOO.enumerateChildren();
+\xkeyword{while} (enum.hasMoreElements()) \{
+~~XMLElement child = (XMLElement) enum.nextElement();
+~~System.out.print(child.getName() + ' ');
+\}
+$\to$ BAR QUUX
+\end{example}
+
+If the element contains parsed character data (\ltext{\#PCDATA}) as its only
+child.
+You can retrieve that data using \methodname{getContent}:
+
+\begin{example}
+BAR.getContent() $\to$ "Some data."
+\end{example}
+
+Note that in \ltext{NanoXML/Lite}, a child cannot have children and
+\ltext{\#PCDATA} content at the same time.
+
+\section{Generating \ltext{XML}}
+
+You can very easily create a tree of \ltext{XML} elements or modify an existing
+one.
+To create a new tree, just create an \classname{XMLElement} object:
+
+\begin{example}
+XMLElement elt = new XMLElement("ElementName");
+\end{example}
+
+You can add an attribute to the element by calling \methodname{setAttribute}:
+
+\begin{example}
+elt.setAttribute("key", "value");
+\end{example}
+
+You can add a child element to an element by calling \methodname{addChild}:
+
+\begin{example}
+XMLElement child = new XMLElement("Child");
+elt.addChild(child);
+\end{example}
+
+If an element has no children, you can add \ltext{\#PCDATA} content to it using
+\methodname{setContent}:
+
+\begin{example}
+child.setContent("Some content");
+\end{example}
+
+Note that in \ltext{NanoXML/Lite}, a child cannot have children and
+\ltext{\#PCDATA} content at the same time.
+
+When you have created or edited the \ltext{XML} element tree, you can write it
+out to an output stream or writer using the method \methodname{toString}:
+
+\begin{example}
+java.io.PrintWriter output = ...;
+XMLElement xmltree = ...;
+output.println(xmltree);
+\end{example}
diff --git a/Examples/Java/Chapter02-01/DumpXML.java b/Examples/Java/Chapter02-01/DumpXML.java
new file mode 100644
index 0000000..0519f00
--- /dev/null
+++ b/Examples/Java/Chapter02-01/DumpXML.java
@@ -0,0 +1,47 @@
+/* DumpXML.java                                                    NanoXML/Java
+ *
+ * $Revision: 1.1.1.1 $
+ * $Date: 2001/07/16 17:46:19 $
+ * $Name:  $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2001 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.
+ */
+
+import net.n3.nanoxml.*;
+import java.io.*;
+
+public class DumpXML
+{
+
+    public static void main(String[] args)
+        throws Exception
+    {
+        IXMLParser parser = XMLParserFactory.createDefaultXMLParser();
+        IXMLReader reader = StdXMLReader.fileReader("test.xml");
+        parser.setReader(reader);
+        XMLElement xml = (XMLElement) parser.parse();
+        XMLWriter writer = new XMLWriter(System.out);
+        writer.write(xml);
+    }
+
+}
+
diff --git a/Examples/Java/Chapter02-01/test.xml b/Examples/Java/Chapter02-01/test.xml
new file mode 100644
index 0000000..3a3aaec
--- /dev/null
+++ b/Examples/Java/Chapter02-01/test.xml
@@ -0,0 +1,6 @@
+<book>
+    <chapter id="my chapter">
+        <title>The title</title>
+        Some text.
+    </chapter>
+</book>
diff --git a/Examples/Java/Chapter03-01/DumpXML.java b/Examples/Java/Chapter03-01/DumpXML.java
new file mode 100644
index 0000000..005052d
--- /dev/null
+++ b/Examples/Java/Chapter03-01/DumpXML.java
@@ -0,0 +1,46 @@
+/* DumpXML.java                                                    NanoXML/Java
+ *
+ * $Revision: 1.1.1.1 $
+ * $Date: 2001/07/16 18:18:58 $
+ * $Name:  $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2001 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.
+ */
+
+import net.n3.nanoxml.*;
+import java.io.*;
+
+public class DumpXML
+{
+
+    public static void main(String[] args)
+        throws Exception
+    {
+        IXMLParser parser = XMLParserFactory.createDefaultXMLParser();
+        IXMLReader reader = StdXMLReader.fileReader("test.xml");
+        parser.setReader(reader);
+        parser.setBuilder(new MyBuilder());
+        parser.parse();
+    }
+
+}
+
diff --git a/Examples/Java/Chapter03-01/MyBuilder.java b/Examples/Java/Chapter03-01/MyBuilder.java
new file mode 100644
index 0000000..1d10a77
--- /dev/null
+++ b/Examples/Java/Chapter03-01/MyBuilder.java
@@ -0,0 +1,94 @@
+/* XMLWriter.java                                                  NanoXML/Java
+ *
+ * $Revision: 1.1.1.1 $
+ * $Date: 2001/07/16 18:17:15 $
+ * $Name:  $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2001 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.
+ */
+
+import java.io.*;
+import net.n3.nanoxml.*;
+
+
+public class MyBuilder
+    implements IXMLBuilder
+{
+
+    public void startBuilding(String systemID,
+                              int    lineNr)
+    {
+        System.out.println("Document started");
+    }
+
+    public void newProcessingInstruction(String target,
+                                         Reader reader)
+    {
+        System.out.println("New PI with target " + target);
+    }
+
+    public void startElement(String name,
+                             String nsPrefix,
+                             String nsSystemID,
+                             String systemID,
+                             int    lineNr)
+    {
+        System.out.println("Element started: " + name);
+    }
+
+    public void endElement(String name,
+                           String nsPrefix,
+                           String nsSystemID)
+    {
+        System.out.println("Element ended: " + name);
+    }
+
+    public void addAttribute(String key,
+                             String nsPrefix,
+                             String nsSystemID,
+                             String value,
+                             String type)
+    {
+        System.out.println("    " + key + ": " + type + " = " + value);
+    }
+
+    public void elementAttributesProcessed(String name,
+                                           String nsPrefix,
+                                           String nsSystemID)
+    {
+        // nothing to do
+    }
+    
+    public void addPCData(Reader reader,
+                          String systemID,
+                          int    lineNr)
+    {
+        System.out.println("#PCDATA");
+    }
+
+    public Object getResult()
+    {
+        return null;
+    }
+
+}
+
diff --git a/Examples/Java/Chapter03-01/test.xml b/Examples/Java/Chapter03-01/test.xml
new file mode 100644
index 0000000..3a3aaec
--- /dev/null
+++ b/Examples/Java/Chapter03-01/test.xml
@@ -0,0 +1,6 @@
+<book>
+    <chapter id="my chapter">
+        <title>The title</title>
+        Some text.
+    </chapter>
+</book>
diff --git a/Examples/Java/Chapter04-01/Demo.java b/Examples/Java/Chapter04-01/Demo.java
new file mode 100644
index 0000000..d7dfca7
--- /dev/null
+++ b/Examples/Java/Chapter04-01/Demo.java
@@ -0,0 +1,51 @@
+/* Demo.java                                                       NanoXML/Java
+ *
+ * $Revision: 1.1.1.1 $
+ * $Date: 2001/07/16 18:53:26 $
+ * $Name:  $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2001 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.
+ */
+
+import net.n3.nanoxml.*;
+import java.io.*;
+
+public class Demo
+{
+
+    public static void main(String[] args)
+        throws Exception
+    {
+        IXMLParser parser = XMLParserFactory.createDefaultXMLParser();
+        IXMLValidator val1 = parser.getValidator();
+        MyValidator val2 = new MyValidator();
+        val2.setDelegate(val1);
+        parser.setValidator(val2);
+        IXMLReader reader = StdXMLReader.fileReader("test.xml");
+        parser.setReader(reader);
+        XMLElement xml = (XMLElement) parser.parse();
+        XMLWriter writer = new XMLWriter(System.out);
+        writer.write(xml);
+    }
+
+}
+
diff --git a/Examples/Java/Chapter04-01/MyValidator.java b/Examples/Java/Chapter04-01/MyValidator.java
new file mode 100644
index 0000000..5914977
--- /dev/null
+++ b/Examples/Java/Chapter04-01/MyValidator.java
@@ -0,0 +1,68 @@
+/* MyValidator.java                                                NanoXML/Java
+ *
+ * $Revision: 1.1.1.1 $
+ * $Date: 2001/07/16 22:10:51 $
+ * $Name:  $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2001 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.
+ */
+
+import net.n3.nanoxml.*;
+import java.io.*;
+
+public class MyValidator
+    extends ValidatorPlugin
+{
+
+    public void attributeAdded(String key,
+                               String nsPrefix,
+                               String nsSystemID,
+                               String value,
+                               String systemID,
+                               int    lineNr)
+        throws Exception
+    {
+        boolean valid = true;
+
+        if (key.equals("id")) {
+            if (value.length() < 3) {
+                valid = false;
+            } else {
+                for (int i = 0; i < 3; i++) {
+                    char ch = value.charAt(i);
+
+                    if ((ch < 'A') || (ch > 'Z')) {
+                        valid = false;
+                    }
+                }
+            }
+        }
+
+        if (valid) {
+            super.attributeAdded(key, nsPrefix, nsSystemID, value,
+                                 systemID, lineNr);
+        } else {
+            this.invalidAttributeValue(systemID, lineNr, null, key, value);
+        }
+    }
+}
+
diff --git a/Examples/Java/Chapter04-01/test.xml b/Examples/Java/Chapter04-01/test.xml
new file mode 100644
index 0000000..a3a5531
--- /dev/null
+++ b/Examples/Java/Chapter04-01/test.xml
@@ -0,0 +1,6 @@
+<book>
+    <chapter id="CHA001">
+        <title>The title</title>
+        Some text.
+    </chapter>
+</book>
diff --git a/Examples/Java/Chapter04-02/Demo.java b/Examples/Java/Chapter04-02/Demo.java
new file mode 100644
index 0000000..c005b2f
--- /dev/null
+++ b/Examples/Java/Chapter04-02/Demo.java
@@ -0,0 +1,48 @@
+/* Demo.java                                                       NanoXML/Java
+ *
+ * $Revision: 1.1.1.1 $
+ * $Date: 2001/07/16 22:19:40 $
+ * $Name:  $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2001 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.
+ */
+
+import net.n3.nanoxml.*;
+import java.io.*;
+
+public class Demo
+{
+
+    public static void main(String[] args)
+        throws Exception
+    {
+        IXMLParser parser = XMLParserFactory.createDefaultXMLParser();
+        parser.setResolver(new MyResolver());
+        IXMLReader reader = StdXMLReader.fileReader("test.xml");
+        parser.setReader(reader);
+        XMLElement xml = (XMLElement) parser.parse();
+        XMLWriter writer = new XMLWriter(System.out);
+        writer.write(xml);
+    }
+
+}
+
diff --git a/Examples/Java/Chapter04-02/MyResolver.java b/Examples/Java/Chapter04-02/MyResolver.java
new file mode 100644
index 0000000..5a6102b
--- /dev/null
+++ b/Examples/Java/Chapter04-02/MyResolver.java
@@ -0,0 +1,47 @@
+/* MyResolver.java                                                 NanoXML/Java
+ *
+ * $Revision: 1.1.1.1 $
+ * $Date: 2001/07/16 22:21:21 $
+ * $Name:  $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2001 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.
+ */
+
+import net.n3.nanoxml.*;
+import java.io.*;
+
+public class MyResolver
+    extends XMLEntityResolver
+{
+
+    public Reader getEntity(IXMLReader reader,
+                            String     name)
+        throws XMLParseException
+    {
+        if (name.equals("foo")) {
+            return new StringReader("bar");
+        } else {
+            return super.getEntity(reader, name);
+        }
+    }
+}
+
diff --git a/Examples/Java/Chapter04-02/test.xml b/Examples/Java/Chapter04-02/test.xml
new file mode 100644
index 0000000..8ee58ef
--- /dev/null
+++ b/Examples/Java/Chapter04-02/test.xml
@@ -0,0 +1,6 @@
+<book>
+    <chapter id="&foo;">
+        <title>The title</title>
+        Some text.
+    </chapter>
+</book>
diff --git a/Examples/Java/Chapter04-03/Demo.java b/Examples/Java/Chapter04-03/Demo.java
new file mode 100644
index 0000000..ddbc42f
--- /dev/null
+++ b/Examples/Java/Chapter04-03/Demo.java
@@ -0,0 +1,49 @@
+/* Demo.java                                                       NanoXML/Java
+ *
+ * $Revision: 1.1.1.1 $
+ * $Date: 2001/07/16 22:24:40 $
+ * $Name:  $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2001 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.
+ */
+
+import net.n3.nanoxml.*;
+import java.io.*;
+
+public class Demo
+{
+
+    public static void main(String[] args)
+        throws Exception
+    {
+        IXMLParser parser = XMLParserFactory.createDefaultXMLParser();
+        IXMLValidator validator = parser.getValidator();
+        validator.setParameterEntityResolver(new MyResolver());
+        IXMLReader reader = StdXMLReader.fileReader("test.xml");
+        parser.setReader(reader);
+        XMLElement xml = (XMLElement) parser.parse();
+        XMLWriter writer = new XMLWriter(System.out);
+        writer.write(xml);
+    }
+
+}
+
diff --git a/Examples/Java/Chapter04-03/MyResolver.java b/Examples/Java/Chapter04-03/MyResolver.java
new file mode 100644
index 0000000..09dc431
--- /dev/null
+++ b/Examples/Java/Chapter04-03/MyResolver.java
@@ -0,0 +1,47 @@
+/* MyResolver.java                                                 NanoXML/Java
+ *
+ * $Revision: 1.1.1.1 $
+ * $Date: 2001/07/16 22:22:59 $
+ * $Name:  $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2001 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.
+ */
+
+import net.n3.nanoxml.*;
+import java.io.*;
+
+public class MyResolver
+    extends XMLEntityResolver
+{
+
+    public Reader getEntity(IXMLReader reader,
+                            String     name)
+        throws XMLParseException
+    {
+        if (name.equals("foo")) {
+            return new StringReader("bar");
+        } else {
+            return super.getEntity(reader, name);
+        }
+    }
+}
+
diff --git a/Examples/Java/Chapter04-03/test.xml b/Examples/Java/Chapter04-03/test.xml
new file mode 100644
index 0000000..4316983
--- /dev/null
+++ b/Examples/Java/Chapter04-03/test.xml
@@ -0,0 +1,10 @@
+<!DOCTYPE book [
+    <!ENTITY blah "%foo;">
+]>
+
+<book>
+    <chapter id="&blah;">
+        <title>The title</title>
+        Some text.
+    </chapter>
+</book>
diff --git a/Examples/Java/Chapter04-04/Chapter.java b/Examples/Java/Chapter04-04/Chapter.java
new file mode 100644
index 0000000..3154509
--- /dev/null
+++ b/Examples/Java/Chapter04-04/Chapter.java
@@ -0,0 +1,51 @@
+/* Chapter.java                                                    NanoXML/Java
+ *
+ * $Revision: 1.1.1.1 $
+ * $Date: 2001/07/16 22:38:06 $
+ * $Name:  $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2001 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.
+ */
+
+import net.n3.nanoxml.*;
+import java.util.*;
+import java.io.*;
+
+public class Chapter
+    extends DocumentElement
+{
+
+    public String getTitle()
+    {
+        return this.attrs.getProperty("title");
+    }
+
+    public String getID()
+    {
+        return this.attrs.getProperty("id");
+    }
+
+    public Enumeration getParagraphs()
+    {
+        return this.children.elements();
+    }
+}
diff --git a/Examples/Java/Chapter04-04/Chapter.java.xml b/Examples/Java/Chapter04-04/Chapter.java.xml
new file mode 100644
index 0000000..3971530
--- /dev/null
+++ b/Examples/Java/Chapter04-04/Chapter.java.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?><java2xml_root><java_source_code><import_declaration><comment><comment_line>/* Chapter.java                                                    NanoXML/Java</comment_line><comment_line> *</comment_line><comment_line> * $Revision: 1.1.1.1 $</comment_line><comment_line> * $Date: 2001/07/17 07:33:37 $</comment_line><comment_line> * $Name:  $</comment_line><comment_line> *</comment_line><comment_line> * This file is part of NanoXML 2 for Java.</comment_li [...]
+<statistics>
+<nr_of_codelines nr="49"/>
+<nr_of_classes nr="1"/>
+<nr_of_methods nr="3"/>
+<nr_of_statements nr="3"/>
+</statistics>
+</java2xml_root>
\ No newline at end of file
diff --git a/Examples/Java/Chapter04-04/DocumentBuilder.java b/Examples/Java/Chapter04-04/DocumentBuilder.java
new file mode 100644
index 0000000..e97fead
--- /dev/null
+++ b/Examples/Java/Chapter04-04/DocumentBuilder.java
@@ -0,0 +1,134 @@
+/* DocumentBuilder.java                                            NanoXML/Java
+ *
+ * $Revision: 1.1.1.1 $
+ * $Date: 2001/07/17 18:37:28 $
+ * $Name:  $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2001 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.
+ */
+
+import java.io.*;
+import java.util.*;
+import net.n3.nanoxml.*;
+
+
+public class DocumentBuilder
+    implements IXMLBuilder
+{
+
+    private static Hashtable classes;
+
+    private Stack elements;
+
+    private DocumentElement topElement;
+
+    static {
+        classes = new Hashtable();
+        classes.put("Chapter", Chapter.class);
+        classes.put("Paragraph", Paragraph.class);
+    }
+
+
+    public void startBuilding(String systemID,
+                              int    lineNr)
+    {
+        this.elements = new Stack();
+        this.topElement = null;
+    }
+
+    public void newProcessingInstruction(String target,
+                                         Reader reader)
+    {
+        // nothing to do
+    }
+
+    public void startElement(String name,
+                             String nsPrefix,
+                             String nsSystemID,
+                             String systemID,
+                             int    lineNr)
+    {
+        DocumentElement elt = null;
+        try {
+            Class cls = (Class) classes.get(name);
+            elt = (DocumentElement) cls.newInstance();
+        } catch (Exception e) {
+            // ignore the exception
+        }
+        this.elements.push(elt);
+        if (this.topElement == null) {
+            this.topElement = elt;
+        }
+    }
+
+    public void endElement(String name,
+                           String nsPrefix,
+                           String nsSystemID)
+    {
+        DocumentElement child = (DocumentElement) this.elements.pop();
+        if (! this.elements.isEmpty()) {
+            DocumentElement parent = (DocumentElement) this.elements.peek();
+            parent.addChild(child);
+        }
+    }
+
+    public void addAttribute(String key,
+                             String nsPrefix,
+                             String nsSystemID,
+                             String value,
+                             String type)
+    {
+        DocumentElement child = (DocumentElement) this.elements.peek();
+        child.setAttribute(key, value);
+    }
+
+    public void elementAttributesProcessed(String name,
+                                           String nsPrefix,
+                                           String nsSystemID)
+    {
+        // nothing to do
+    }
+    
+    public void addPCData(Reader reader,
+                          String systemID,
+                          int    lineNr)
+        throws IOException
+    {
+        StringBuffer str = new StringBuffer(1024);
+        char[] buf = new char[1024];
+        for (;;) {
+            int size = reader.read(buf);
+            if (size < 0) {
+                break;
+            }
+            str.append(buf, 0, size);
+        }
+        this.addAttribute("#PCDATA", null, null, str.toString(), "CDATA");
+    }
+
+    public Object getResult()
+    {
+        return topElement;
+    }
+
+}
+
diff --git a/Examples/Java/Chapter04-04/DocumentElement.java b/Examples/Java/Chapter04-04/DocumentElement.java
new file mode 100644
index 0000000..36e10d1
--- /dev/null
+++ b/Examples/Java/Chapter04-04/DocumentElement.java
@@ -0,0 +1,57 @@
+/* DocumentElement.java                                            NanoXML/Java
+ *
+ * $Revision: 1.1.1.1 $
+ * $Date: 2001/07/17 17:55:44 $
+ * $Name:  $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2001 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.
+ */
+
+import net.n3.nanoxml.*;
+import java.util.*;
+import java.io.*;
+
+public class DocumentElement
+{
+
+    protected Properties attrs;
+
+    protected Vector children;
+
+    public DocumentElement()
+    {
+        this.attrs = new Properties();
+        this.children = new Vector();
+    }
+
+    public void setAttribute(String attrName,
+                             String value)
+    {
+        this.attrs.put(attrName, value);
+    }
+
+    public void addChild(DocumentElement elt)
+    {
+        this.children.addElement(elt);
+    }
+}
+
diff --git a/Examples/Java/Chapter04-04/Paragraph.java b/Examples/Java/Chapter04-04/Paragraph.java
new file mode 100644
index 0000000..cd9d4be
--- /dev/null
+++ b/Examples/Java/Chapter04-04/Paragraph.java
@@ -0,0 +1,61 @@
+/* Paragraph.java                                                  NanoXML/Java
+ *
+ * $Revision: 1.1.1.1 $
+ * $Date: 2001/07/17 18:00:08 $
+ * $Name:  $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2001 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.
+ */
+
+import net.n3.nanoxml.*;
+import java.util.*;
+import java.io.*;
+
+public class Paragraph
+    extends DocumentElement
+{
+
+    public static final int LEFT = 0;
+    public static final int CENTER = 1;
+    public static final int RIGHT = 2;
+
+    private static final Hashtable alignments;
+
+    static {
+        alignments = new Hashtable();
+        alignments.put("left", new Integer(LEFT));
+        alignments.put("center", new Integer(CENTER));
+        alignments.put("right", new Integer(RIGHT));
+    }
+
+    public String getContent()
+    {
+        return this.attrs.getProperty("#PCDATA");
+    }
+
+    public int getAlignment()
+    {
+        String str = this.attrs.getProperty("align");
+        Integer align = (Integer) alignments.get(str);
+        return align.intValue();
+    }
+}
diff --git a/Examples/Java/Chapter04-04/XML2HTML.java b/Examples/Java/Chapter04-04/XML2HTML.java
new file mode 100644
index 0000000..2199afb
--- /dev/null
+++ b/Examples/Java/Chapter04-04/XML2HTML.java
@@ -0,0 +1,62 @@
+/* XML2HTML.java                                                   NanoXML/Java
+ *
+ * $Revision: 1.1.1.1 $
+ * $Date: 2001/07/17 18:33:34 $
+ * $Name:  $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2001 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.
+ */
+
+import net.n3.nanoxml.*;
+import java.util.*;
+
+public class XML2HTML
+{
+
+    public static void main(String[] param)
+        throws Exception
+    {
+        IXMLBuilder builder = new DocumentBuilder();
+        IXMLParser parser = XMLParserFactory.createDefaultXMLParser();
+        parser.setBuilder(builder);
+        IXMLReader reader = StdXMLReader.fileReader(param[0]);
+        parser.setReader(reader);
+        Chapter chapter = (Chapter) parser.parse();
+        System.out.println("<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' 'http://www.w3.org/TR/html4/strict.dtd'>");
+        System.out.print("<HTML><HEAD><TITLE>");
+        System.out.print(chapter.getTitle());
+        System.out.println("</TITLE></HEAD><BODY>");
+        System.out.print("<H1>");
+        System.out.print(chapter.getTitle());
+        System.out.println("</H1>");
+        Enumeration enum = chapter.getParagraphs();
+        while (enum.hasMoreElements()) {
+            Paragraph para = (Paragraph) enum.nextElement();
+            System.out.print("<P>");
+            System.out.print(para.getContent());
+            System.out.println("</P>");
+        }
+        System.out.println("</BODY></HTML>");
+    }
+
+}
+
diff --git a/Examples/Java/Chapter04-04/document.dtd b/Examples/Java/Chapter04-04/document.dtd
new file mode 100644
index 0000000..82a74f3
--- /dev/null
+++ b/Examples/Java/Chapter04-04/document.dtd
@@ -0,0 +1,11 @@
+<!ELEMENT Chapter (Paragraph*)>
+
+<!ATTLIST Chapter
+        title CDATA #REQUIRED
+        id CDATA #REQUIRED>
+
+<!ELEMENT Paragraph (#PCDATA)>
+
+<!ATTLIST Paragraph
+        align (left|center|right) 'left'>
+
diff --git a/Examples/Java/Chapter04-04/test.xml b/Examples/Java/Chapter04-04/test.xml
new file mode 100644
index 0000000..7f30916
--- /dev/null
+++ b/Examples/Java/Chapter04-04/test.xml
@@ -0,0 +1,6 @@
+<!DOCTYPE Chapter SYSTEM "document.dtd">
+<Chapter id="ch01" title="The Title">
+    <Paragraph>First paragraph...</Paragraph>
+    <Paragraph>Second paragraph...</Paragraph>
+</Chapter>
+
diff --git a/Sources/Java/net/n3/nanoxml/CDATAReader.java b/Sources/Java/net/n3/nanoxml/CDATAReader.java
new file mode 100644
index 0000000..ffd90ba
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/CDATAReader.java
@@ -0,0 +1,193 @@
+/* CDATAReader.java                                                NanoXML/Java
+ *
+ * $Revision: 1.3 $
+ * $Date: 2002/01/04 21:03:28 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.Reader;
+import java.io.IOException;
+
+
+/**
+ * This reader reads data from another reader until the end of a CDATA section
+ * (]]>) has been encountered.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.3 $
+ */
+class CDATAReader
+   extends Reader
+{
+
+   /**
+    * The encapsulated reader.
+    */
+   private IXMLReader reader;
+    
+    
+   /**
+    * Saved char.
+    */
+   private char savedChar;
+    
+
+   /**
+    * True if the end of the stream has been reached.
+    */
+   private boolean atEndOfData;
+
+
+   /**
+    * Creates the reader.
+    *
+    * @param reader the encapsulated reader
+    */
+   CDATAReader(IXMLReader reader)
+   {
+      this.reader = reader;
+      this.savedChar = 0;
+      this.atEndOfData = false;
+   }
+
+
+   /**
+    * Cleans up the object when it's destroyed.
+    */
+   protected void finalize()
+      throws Throwable
+   {
+      this.reader = null;
+      super.finalize();
+   }
+    
+
+   /**
+    * Reads a block of data.
+    *
+    * @param buffer where to put the read data
+    * @param offset first position in buffer to put the data
+    * @param size maximum number of chars to read
+    *
+    * @return the number of chars read, or -1 if at EOF
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   public int read(char[] buffer,
+                   int    offset,
+                   int    size)
+      throws IOException
+   {
+      int charsRead = 0;
+
+      if (this.atEndOfData) {
+         return -1;
+      }
+
+      if ((offset + size) > buffer.length) {
+         size = buffer.length - offset;
+      }
+
+      while (charsRead < size) {
+         char ch = this.savedChar;
+
+         if (ch == 0) {
+            ch = this.reader.read();
+         } else {
+            this.savedChar = 0;
+         }
+
+         if (ch == ']') {
+            char ch2 = this.reader.read();
+            
+            if (ch2 == ']') {
+               char ch3 = this.reader.read();
+
+               if (ch3 == '>') {
+                  this.atEndOfData = true;
+                  break;
+               }
+
+               this.savedChar = ch2;
+               this.reader.unread(ch3);
+            } else {
+               this.reader.unread(ch2);
+            }
+         }
+         buffer[charsRead] = ch;
+         charsRead++;
+      }
+
+      if (charsRead == 0) {
+         charsRead = -1;
+      }
+
+      return charsRead;
+   }
+    
+
+   /**
+    * Skips remaining data and closes the stream.
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   public void close()
+      throws IOException
+   {
+      while (! this.atEndOfData) {
+         char ch = this.savedChar;
+
+         if (ch == 0) {
+            ch = this.reader.read();
+         } else {
+            this.savedChar = 0;
+         }
+
+         if (ch == ']') {
+            char ch2 = this.reader.read();
+
+            if (ch2 == ']') {
+               char ch3 = this.reader.read();
+
+               if (ch3 == '>') {
+                  break;
+               }
+
+               this.savedChar = ch2;
+               this.reader.unread(ch3);
+            } else {
+               this.reader.unread(ch2);
+            }
+         }
+      }
+
+      this.atEndOfData = true;
+   }
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/ContentReader.java b/Sources/Java/net/n3/nanoxml/ContentReader.java
new file mode 100644
index 0000000..6fea6f8
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/ContentReader.java
@@ -0,0 +1,212 @@
+/* ContentReader.java                                              NanoXML/Java
+ *
+ * $Revision: 1.4 $
+ * $Date: 2002/01/04 21:03:28 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.Reader;
+import java.io.IOException;
+
+
+/**
+ * This reader reads data from another reader until a new element has
+ * been encountered.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
+ */
+class ContentReader
+   extends Reader
+{
+
+   /**
+    * The encapsulated reader.
+    */
+   private IXMLReader reader;
+
+
+   /**
+    * Buffer.
+    */
+   private String buffer;
+
+
+   /**
+    * Pointer into the buffer.
+    */
+   private int bufferIndex;
+
+
+   /**
+    * The entity resolver.
+    */
+   private IXMLEntityResolver resolver;
+    
+
+   /**
+    * Creates the reader.
+    *
+    * @param reader the encapsulated reader
+    * @param resolver the entity resolver
+    * @param buffer data that has already been read from <code>reader</code>
+    */
+   ContentReader(IXMLReader         reader,
+                 IXMLEntityResolver resolver,
+                 String             buffer)
+   {
+      this.reader = reader;
+      this.resolver = resolver;
+      this.buffer = buffer;
+      this.bufferIndex = 0;
+   }
+
+
+   /**
+    * Cleans up the object when it's destroyed.
+    */
+   protected void finalize()
+      throws Throwable
+   {
+      this.reader = null;
+      this.resolver = null;
+      this.buffer = null;
+      super.finalize();
+   }
+
+
+   /**
+    * Reads a block of data.
+    *
+    * @param outputBuffer where to put the read data
+    * @param offset first position in buffer to put the data
+    * @param size maximum number of chars to read
+    *
+    * @return the number of chars read, or -1 if at EOF
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   public int read(char[] outputBuffer,
+                   int    offset,
+                   int    size)
+      throws IOException
+   {
+      try {
+         int charsRead = 0;
+         int bufferLength = this.buffer.length();
+
+         if ((offset + size) > outputBuffer.length) {
+            size = outputBuffer.length - offset;
+         }
+
+         while (charsRead < size) {
+            String str = "";
+            char ch;
+
+            if (this.bufferIndex >= bufferLength) {
+               str = XMLUtil.read(this.reader, '&');
+               ch = str.charAt(0);
+            } else {
+               ch = this.buffer.charAt(this.bufferIndex);
+               this.bufferIndex++;
+               outputBuffer[charsRead] = ch;
+               charsRead++;
+               continue; // don't interprete chars in the buffer
+            }
+
+            if (ch == '<') {
+               this.reader.unread(ch);
+               break;
+            }
+
+            if ((ch == '&') && (str.length() > 1)) {
+               if (str.charAt(1) == '#') {
+                  ch = XMLUtil.processCharLiteral(str);
+               } else {
+                  XMLUtil.processEntity(str, this.reader, this.resolver);
+                  continue;
+               }
+            }
+
+            outputBuffer[charsRead] = ch;
+            charsRead++;
+         }
+
+         if (charsRead == 0) {
+            charsRead = -1;
+         }
+
+         return charsRead;
+      } catch (XMLParseException e) {
+         throw new IOException(e.getMessage());
+      }
+   }
+
+
+   /**
+    * Skips remaining data and closes the stream.
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   public void close()
+      throws IOException
+   {
+      try {
+         int bufferLength = this.buffer.length();
+
+         for (;;) {
+            String str = "";
+            char ch;
+
+            if (this.bufferIndex >= bufferLength) {
+               str = XMLUtil.read(this.reader, '&');
+               ch = str.charAt(0);
+            } else {
+               ch = this.buffer.charAt(this.bufferIndex);
+               this.bufferIndex++;
+               continue; // don't interprete chars in the buffer
+            }
+
+            if (ch == '<') {
+               this.reader.unread(ch);
+               break;
+            }
+
+            if ((ch == '&') && (str.length() > 1)) {
+               if (str.charAt(1) != '#') {
+                  XMLUtil.processEntity(str, this.reader, this.resolver);
+               }
+            }
+         }
+      } catch (XMLParseException e) {
+         throw new IOException(e.getMessage());
+      }
+   }
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/IXMLBuilder.java b/Sources/Java/net/n3/nanoxml/IXMLBuilder.java
new file mode 100644
index 0000000..5e5eee4
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/IXMLBuilder.java
@@ -0,0 +1,216 @@
+/* IXMLBuilder.java                                                NanoXML/Java
+ *
+ * $Revision: 1.3 $
+ * $Date: 2002/01/04 21:03:28 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.Reader;
+import java.io.IOException;
+
+
+/**
+ * NanoXML uses IXMLBuilder to construct the XML data structure it retrieved
+ * from its data source. You can supply your own builder or you can use the
+ * default builder of NanoXML.
+ * <P>
+ * If a method of the builder throws an exception, the parsing is aborted and
+ * {@link net.n3.nanoxml.IXMLParser#parse} throws an
+ * {@link net.n3.nanoxml.XMLException} which encasulates the original
+ * exception.
+ *
+ * @see net.n3.nanoxml.IXMLParser
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.3 $
+ */
+public interface IXMLBuilder
+{
+
+   /**
+    * This method is called before the parser starts processing its input.
+    *
+    * @param systemID the system ID of the XML data source.
+    * @param lineNr   the line on which the parsing starts.
+    *
+    * @throws java.lang.Exception
+    *     If an exception occurred while processing the event.
+    */
+   public void startBuilding(String systemID,
+                             int    lineNr)
+      throws Exception;
+
+
+   /**
+    * This method is called when a processing instruction is encountered.
+    * A PI with a reserved target ("xml" with any case) is never reported.
+    *
+    * @param target the processing instruction target.
+    * @param reader the method can retrieve the parameter of the PI from this
+    *               reader. You may close the reader before reading all its
+    *               data and you cannot read too much data.
+    *
+    * @throws java.lang.Exception
+    *     If an exception occurred while processing the event.
+    */
+   public void newProcessingInstruction(String target,
+                                        Reader reader)
+      throws Exception;
+
+
+   /**
+    * This method is called when a new XML element is encountered.
+    *
+    * @see #endElement
+    *
+    * @param name       the name of the element.
+    * @param nsPrefix   the prefix used to identify the namespace. If no
+    *                   namespace has been specified, this parameter is null.
+    * @param nsURI      the URI associated with the namespace. If no
+    *                   namespace has been specified, or no URI is
+    *                   associated with nsPrefix, this parameter is null.
+    * @param systemID   the system ID of the XML data source.
+    * @param lineNr     the line in the source where the element starts.
+    *
+    * @throws java.lang.Exception
+    *     If an exception occurred while processing the event.
+    */
+   public void startElement(String name,
+                            String nsPrefix,
+                            String nsURI,
+                            String systemID,
+                            int    lineNr)
+      throws Exception;
+
+
+   /**
+    * This method is called when a new attribute of an XML element is
+    * encountered.
+    *
+    * @param key        the key (name) of the attribute.
+    * @param nsPrefix   the prefix used to identify the namespace. If no
+    *                   namespace has been specified, this parameter is null.
+    * @param nsURI      the URI associated with the namespace. If no
+    *                   namespace has been specified, or no URI is
+    *                   associated with nsPrefix, this parameter is null.
+    * @param value      the value of the attribute.
+    * @param type       the type of the attribute. If no type is known,
+    *                   "CDATA" is returned.
+    *
+    * @throws java.lang.Exception
+    *     If an exception occurred while processing the event.
+    */
+   public void addAttribute(String key,
+                            String nsPrefix,
+                            String nsURI,
+                            String value,
+                            String type)
+      throws Exception;
+
+
+   /**
+    * This method is called when the attributes of an XML element have been
+    * processed.
+    *
+    * @see #startElement
+    * @see #addAttribute
+    *
+    * @param name       the name of the element.
+    * @param nsPrefix   the prefix used to identify the namespace. If no
+    *                   namespace has been specified, this parameter is null.
+    * @param nsURI      the URI associated with the namespace. If no
+    *                   namespace has been specified, or no URI is
+    *                   associated with nsPrefix, this parameter is null.
+    *
+    * @throws java.lang.Exception
+    *     If an exception occurred while processing the event.
+    */
+   public void elementAttributesProcessed(String name,
+                                          String nsPrefix,
+                                          String nsURI)
+      throws Exception;
+
+
+   /**
+    * This method is called when the end of an XML elemnt is encountered.
+    *
+    * @see #startElement
+    *
+    * @param name       the name of the element.
+    * @param nsPrefix   the prefix used to identify the namespace. If no
+    *                   namespace has been specified, this parameter is null.
+    * @param nsURI      the URI associated with the namespace. If no
+    *                   namespace has been specified, or no URI is
+    *                   associated with nsPrefix, this parameter is null.
+    *
+    * @throws java.lang.Exception
+    *     If an exception occurred while processing the event.
+    */
+   public void endElement(String name,
+                          String nsPrefix,
+                          String nsURI)
+      throws Exception;
+
+
+   /**
+    * This method is called when a PCDATA element is encountered. A Java
+    * reader is supplied from which you can read the data. The reader will
+    * only read the data of the element. You don't need to check for
+    * boundaries. If you don't read the full element, the rest of the data
+    * is skipped. You also don't have to care about entities: they are
+    * resolved by the parser.
+    *
+    * @param reader   the method can retrieve the data from this reader. You
+    *                 may close the reader before reading all its data and you
+    *                 cannot read too much data.
+    * @param systemID the system ID of the XML data source.
+    * @param lineNr   the line in the source where the element starts.
+    *
+    * @throws java.lang.Exception
+    *     If an exception occurred while processing the event.
+    */
+   public void addPCData(Reader reader,
+                         String systemID,
+                         int    lineNr)
+      throws Exception;
+
+
+   /**
+    * Returns the result of the building process. This method is called just
+    * before the <I>parse</I> method of IXMLParser returns.
+    *
+    * @see net.n3.nanoxml.IXMLParser#parse
+    *
+    * @return the result of the building process.
+    *
+    * @throws java.lang.Exception
+    *     If an exception occurred while processing the event.
+    */
+   public Object getResult()
+      throws Exception;
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/IXMLElement.java b/Sources/Java/net/n3/nanoxml/IXMLElement.java
new file mode 100644
index 0000000..9081860
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/IXMLElement.java
@@ -0,0 +1,529 @@
+/* IXMLElement.java                                                NanoXML/Java
+ *
+ * $Revision: 1.4 $
+ * $Date: 2002/01/04 21:03:28 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.Serializable;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Vector;
+
+
+/**
+ * IXMLElement is an XML element. It provides an easy to use generic interface
+ * on top of an XML like data structure like e.g. a DOM like structure.
+ * Elements returned by StdXMLBuilder also implement this interface.
+ *
+ * @see net.n3.nanoxml.StdXMLBuilder
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
+ */
+public interface IXMLElement
+{
+
+   /**
+    * No line number defined.
+    */
+   public static final int NO_LINE = -1;
+
+
+   /**
+    * Creates an element to be used for #PCDATA content.
+    */
+   public IXMLElement createPCDataElement();
+
+
+   /**
+    * Creates an empty element.
+    *
+    * @param fullName the name of the element.
+    */
+   public IXMLElement createElement(String fullName);
+
+
+   /**
+    * Creates an empty element.
+    *
+    * @param fullName the name of the element.
+    * @param systemID the system ID of the XML data where the element starts.
+    * @param lineNr   the line in the XML data where the element starts.
+    */
+   public IXMLElement createElement(String fullName,
+                                    String systemID,
+                                    int    lineNr);
+
+
+   /**
+    * Creates an empty element.
+    *
+    * @param fullName  the full name of the element
+    * @param namespace the namespace URI.
+    */
+   public IXMLElement createElement(String fullName,
+                                    String namespace);
+
+
+   /**
+    * Creates an empty element.
+    *
+    * @param fullName  the full name of the element
+    * @param namespace the namespace URI.
+    * @param systemID  the system ID of the XML data where the element starts.
+    * @param lineNr    the line in the XML data where the element starts.
+    */
+   public IXMLElement createElement(String fullName,
+                                    String namespace,
+                                    String systemID,
+                                    int    lineNr);
+
+
+   /**
+    * Returns the parent element. This method returns null for the root
+    * element.
+    */
+   public IXMLElement getParent();
+
+
+   /**
+    * Returns the full name (i.e. the name including an eventual namespace
+    * prefix) of the element.
+    *
+    * @return the name, or null if the element only contains #PCDATA.
+    */
+   public String getFullName();
+
+
+   /**
+    * Returns the name of the element.
+    *
+    * @return the name, or null if the element only contains #PCDATA.
+    */
+   public String getName();
+
+
+   /**
+    * Returns the namespace of the element.
+    *
+    * @return the namespace, or null if no namespace is associated with the
+    *         element.
+    */
+   public String getNamespace();
+
+
+   /**
+    * Sets the full name. This method also sets the short name and clears the
+    * namespace URI.
+    *
+    * @param name the non-null name.
+    */
+   public void setName(String name);
+
+
+   /**
+    * Sets the name.
+    *
+    * @param fullName  the non-null full name.
+    * @param namespace the namespace URI, which may be null.
+    */
+   public void setName(String fullName,
+                       String namespace);
+
+
+   /**
+    * Adds a child element.
+    *
+    * @param child the non-null child to add.
+    */
+   public void addChild(IXMLElement child);
+
+
+   /**
+    * Removes a child element.
+    *
+    * @param child the non-null child to remove.
+    */
+   public void removeChild(IXMLElement child);
+
+
+   /**
+    * Removes the child located at a certain index.
+    *
+    * @param index the index of the child, where the first child has index 0.
+    */
+   public void removeChildAtIndex(int index);
+
+
+   /**
+    * Returns an enumeration of all child elements.
+    *
+    * @return the non-null enumeration
+    */
+   public Enumeration enumerateChildren();
+
+
+   /**
+    * Returns whether the element is a leaf element.
+    *
+    * @return true if the element has no children.
+    */
+   public boolean isLeaf();
+
+
+   /**
+    * Returns whether the element has children.
+    *
+    * @return true if the element has children.
+    */
+   public boolean hasChildren();
+
+
+   /**
+    * Returns the number of children.
+    *
+    * @return the count.
+    */
+   public int getChildrenCount();
+
+
+   /**
+    * Returns a vector containing all the child elements.
+    *
+    * @return the vector.
+    */
+   public Vector getChildren();
+
+
+   /**
+    * Returns the child at a specific index.
+    *
+    * @param index the index of the child
+    *
+    * @return the non-null child
+    *
+    * @throws java.lang.ArrayIndexOutOfBoundsException
+    *		if the index is out of bounds.
+    */
+   public IXMLElement getChildAtIndex(int index)
+      throws ArrayIndexOutOfBoundsException;
+
+
+   /**
+    * Searches a child element.
+    *
+    * @param name the full name of the child to search for.
+    *
+    * @return the child element, or null if no such child was found.
+    */
+   public IXMLElement getFirstChildNamed(String name);
+
+
+   /**
+    * Searches a child element.
+    *
+    * @param name      the name of the child to search for.
+    * @param namespace the namespace, which may be null.
+    *
+    * @return the child element, or null if no such child was found.
+    */
+   public IXMLElement getFirstChildNamed(String name,
+                                         String namespace);
+
+
+   /**
+    * Returns a vector of all child elements named <I>name</I>.
+    *
+    * @param name the full name of the children to search for.
+    *
+    * @return the non-null vector of child elements.
+    */
+   public Vector getChildrenNamed(String name);
+
+
+   /**
+    * Returns a vector of all child elements named <I>name</I>.
+    *
+    * @param name      the name of the children to search for.
+    * @param namespace the namespace, which may be null.
+    *
+    * @return the non-null vector of child elements.
+    */
+   public Vector getChildrenNamed(String name,
+                                  String namespace);
+
+
+
+   /**
+    * Returns the number of attributes.
+    */
+   public int getAttributeCount();
+
+
+   /**
+    * @deprecated As of NanoXML/Java 2.0.1, replaced by
+    *             {@link #getAttribute(java.lang.String,java.lang.String)}
+    * Returns the value of an attribute.
+    *
+    * @param name the non-null name of the attribute.
+    *
+    * @return the value, or null if the attribute does not exist.
+    */
+   public String getAttribute(String name);
+
+
+   /**
+    * Returns the value of an attribute.
+    *
+    * @param name the non-null full name of the attribute.
+    * @param defaultValue the default value of the attribute.
+    *
+    * @return the value, or defaultValue if the attribute does not exist.
+    */
+   public String getAttribute(String name,
+                              String defaultValue);
+
+
+   /**
+    * Returns the value of an attribute.
+    *
+    * @param name the non-null name of the attribute.
+    * @param namespace the namespace URI, which may be null.
+    * @param defaultValue the default value of the attribute.
+    *
+    * @return the value, or defaultValue if the attribute does not exist.
+    */
+   public String getAttribute(String name,
+                              String namespace,
+                              String defaultValue);
+
+
+   /**
+    * Returns the value of an attribute.
+    *
+    * @param name the non-null full name of the attribute.
+    * @param defaultValue the default value of the attribute.
+    *
+    * @return the value, or defaultValue if the attribute does not exist.
+    */
+   public int getAttribute(String name,
+                           int    defaultValue);
+
+
+   /**
+    * Returns the value of an attribute.
+    *
+    * @param name the non-null name of the attribute.
+    * @param namespace the namespace URI, which may be null.
+    * @param defaultValue the default value of the attribute.
+    *
+    * @return the value, or defaultValue if the attribute does not exist.
+    */
+   public int getAttribute(String name,
+                           String namespace,
+                           int    defaultValue);
+
+
+   /**
+    * Returns the type of an attribute.
+    *
+    * @param name the non-null full name of the attribute.
+    *
+    * @return the type, or null if the attribute does not exist.
+    */
+   public String getAttributeType(String name);
+
+
+   /**
+    * Returns the namespace of an attribute.
+    *
+    * @param name the non-null full name of the attribute.
+    *
+    * @return the namespace, or null if there is none associated.
+    */
+   public String getAttributeNamespace(String name);
+
+
+   /**
+    * Returns the type of an attribute.
+    *
+    * @param name the non-null name of the attribute.
+    * @param namespace the namespace URI, which may be null.
+    *
+    * @return the type, or null if the attribute does not exist.
+    */
+   public String getAttributeType(String name,
+                                  String namespace);
+
+
+   /**
+    * Sets an attribute.
+    *
+    * @param name the non-null full name of the attribute.
+    * @param value the non-null value of the attribute.
+    */
+   public void setAttribute(String name,
+                            String value);
+
+
+   /**
+    * Sets an attribute.
+    *
+    * @param fullName the non-null full name of the attribute.
+    * @param namespace the namespace URI of the attribute, which may be null.
+    * @param value the non-null value of the attribute.
+    */
+   public void setAttribute(String fullName,
+                            String namespace,
+                            String value);
+
+
+   /**
+    * Removes an attribute.
+    *
+    * @param name the non-null name of the attribute.
+    */
+   public void removeAttribute(String name);
+
+
+   /**
+    * Removes an attribute.
+    *
+    * @param name the non-null name of the attribute.
+    * @param namespace the namespace URI of the attribute, which may be null.
+    */
+   public void removeAttribute(String name,
+                               String namespace);
+
+
+   /**
+    * Returns an enumeration of all attribute names.
+    *
+    * @return the non-null enumeration.
+    */
+   public Enumeration enumerateAttributeNames();
+
+
+   /**
+    * Returns whether an attribute exists.
+    *
+    * @param name the non-null name of the attribute.
+    *
+    * @return true if the attribute exists.
+    */
+   public boolean hasAttribute(String name);
+
+
+   /**
+    * Returns whether an attribute exists.
+    *
+    * @param name the non-null name of the attribute.
+    * @param namespace the namespace URI of the attribute, which may be null.
+    *
+    * @return true if the attribute exists.
+    */
+   public boolean hasAttribute(String name,
+                               String namespace);
+
+
+   /**
+    * Returns all attributes as a Properties object.
+    *
+    * @return the non-null set.
+    */
+   public Properties getAttributes();
+
+
+   /**
+    * Returns all attributes in a specific namespace as a Properties object.
+    *
+    * @param namespace the namespace URI of the attributes, which may be null.
+    *
+    * @return the non-null set.
+    */
+   public Properties getAttributesInNamespace(String namespace);
+
+
+   /**
+    * Returns the system ID of the data where the element started.
+    *
+    * @return the system ID, or null if unknown.
+    *
+    * @see #getLineNr
+    */
+   public String getSystemID();
+
+
+   /**
+    * Returns the line number in the data where the element started.
+    *
+    * @return the line number, or NO_LINE if unknown.
+    *
+    * @see #NO_LINE
+    * @see #getSystemID
+    */
+   public int getLineNr();
+
+
+   /**
+    * Return the #PCDATA content of the element. If the element has a
+    * combination of #PCDATA content and child elements, the #PCDATA
+    * sections can be retrieved as unnamed child objects. In this case,
+    * this method returns null.
+    *
+    * @return the content.
+    */
+   public String getContent();
+
+
+   /**
+    * Sets the #PCDATA content. It is an error to call this method with a
+    * non-null value if there are child objects.
+    *
+    * @param content the (possibly null) content.
+    */
+   public void setContent(String content);
+
+
+   /**
+    * Returns true if the element equals another element.
+    *
+    * @param rawElement the element to compare to
+    */
+   public boolean equals(Object rawElement);
+
+
+   /**
+    * Returns true if the element equals another element.
+    *
+    * @param rawElement the element to compare to
+    */
+   public boolean equalsXMLElement(IXMLElement elt);
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/IXMLEntityResolver.java b/Sources/Java/net/n3/nanoxml/IXMLEntityResolver.java
new file mode 100644
index 0000000..2eb541e
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/IXMLEntityResolver.java
@@ -0,0 +1,91 @@
+/* IXMLEntityResolver.java                                         NanoXML/Java
+ *
+ * $Revision: 1.4 $
+ * $Date: 2002/01/04 21:03:28 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.util.Hashtable;
+import java.io.Reader;
+import java.io.StringReader;
+
+
+/**
+ * An IXMLEntityResolver resolves entities.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
+ */
+public interface IXMLEntityResolver
+{
+
+   /**
+    * Adds an internal entity.
+    *
+    * @param name  the name of the entity.
+    * @param value the value of the entity.
+    */
+   public void addInternalEntity(String name,
+                                 String value);
+
+
+   /**
+    * Adds an external entity.
+    *
+    * @param name     the name of the entity.
+    * @param publicID the public ID of the entity, which may be null.
+    * @param systemID the system ID of the entity.
+    */
+   public void addExternalEntity(String name,
+                                 String publicID,
+                                 String systemID);
+
+
+   /**
+    * Returns a Java reader containing the value of an entity.
+    *
+    * @param xmlReader the current NanoXML reader.
+    * @param name      the name of the entity.
+    *
+    * @return the reader, or null if the entity could not be resolved.
+    *
+    * @throws net.n3.nanoxml.XMLParseException
+    *     If an exception occurred while resolving the entity.
+    */
+   public Reader getEntity(IXMLReader xmlReader,
+                           String     name)
+      throws XMLParseException;
+
+
+   /**
+    * Returns true if an entity is external.
+    *
+    * @param name the name of the entity.
+    */
+   public boolean isExternalEntity(String name);
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/IXMLParser.java b/Sources/Java/net/n3/nanoxml/IXMLParser.java
new file mode 100644
index 0000000..dc6b518
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/IXMLParser.java
@@ -0,0 +1,124 @@
+/* IXMLParser.java                                                 NanoXML/Java
+ *
+ * $Revision: 1.3 $
+ * $Date: 2002/01/04 21:03:28 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.IOException;
+
+
+/**
+ * IXMLParser is the core parser of NanoXML.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.3 $
+ */
+public interface IXMLParser
+{
+
+   /**
+    * Sets the reader from which the parser retrieves its data.
+    *
+    * @param reader the reader.
+    */
+   public void setReader(IXMLReader reader);
+
+
+   /**
+    * Returns the reader from which the parser retrieves its data.
+    *
+    * @return the reader.
+    */
+   public IXMLReader getReader();
+
+
+   /**
+    * Sets the builder which creates the logical structure of the XML data.
+    *
+    * @param builder the builder.
+    */
+   public void setBuilder(IXMLBuilder builder);
+
+
+   /**
+    * Returns the builder which creates the logical structure of the XML data.
+    *
+    * @return the builder.
+    */
+   public IXMLBuilder getBuilder();
+
+
+   /**
+    * Sets the validator that validates the XML data.
+    *
+    * @param validator the validator.
+    */
+   public void setValidator(IXMLValidator validator);
+
+
+   /**
+    * Returns the validator that validates the XML data.
+    *
+    * @return the validator.
+    */
+   public IXMLValidator getValidator();
+
+
+   /**
+    * Sets the entity resolver.
+    *
+    * @param resolver the non-null resolver.
+    */
+   public void setResolver(IXMLEntityResolver resolver);
+
+
+   /**
+    * Returns the entity resolver.
+    *
+    * @return the non-null resolver.
+    */
+   public IXMLEntityResolver getResolver();
+
+
+   /**
+    * Parses the data and lets the builder create the logical data structure.
+    * The method returns the result of <I>getResult</I> of the builder. if an
+    * error occurred while reading or parsing the data, the method may throw
+    * an XMLException.
+    *
+    * @see net.n3.nanoxml.IXMLBuilder#getResult
+    *
+    * @return the logical structure built by the builder.
+    *
+    * @throws net.n3.nanoxml.XMLException
+    *		if an error occurred reading or parsing the data
+    */
+   public Object parse()
+      throws XMLException;
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/IXMLReader.java b/Sources/Java/net/n3/nanoxml/IXMLReader.java
new file mode 100644
index 0000000..cf581a3
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/IXMLReader.java
@@ -0,0 +1,178 @@
+/* IXMLReader.java                                                 NanoXML/Java
+ *
+ * $Revision: 1.4 $
+ * $Date: 2002/01/04 21:03:28 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.Reader;
+import java.io.IOException;
+import java.io.FileNotFoundException;
+import java.net.MalformedURLException;
+
+
+/**
+ * IXMLReader reads the data to be parsed.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
+ */
+public interface IXMLReader
+{
+
+   /**
+    * Reads a character.
+    *
+    * @return the character
+    *
+    * @throws java.io.IOException
+    *		If no character could be read.
+    */
+   public char read()
+      throws IOException;
+
+
+   /**
+    * Returns true if the current stream has no more characters left to be
+    * read.
+    *
+    * @throws java.io.IOException
+    *		If an I/O error occurred.
+    */
+   public boolean atEOFOfCurrentStream()
+      throws IOException;
+
+
+   /**
+    * Returns true if there are no more characters left to be read.
+    *
+    * @throws java.io.IOException
+    *		If an I/O error occurred.
+    */
+   public boolean atEOF()
+      throws IOException;
+
+
+   /**
+    * Pushes the last character read back to the stream.
+    *
+    * @param ch the character to push back.
+    *
+    * @throws java.io.IOException
+    *		If an I/O error occurred.
+    */
+   public void unread(char ch)
+      throws IOException;
+
+
+   /**
+    * Returns the line number of the data in the current stream.
+    */
+   public int getLineNr();
+
+
+   /**
+    * Opens a stream from a public and system ID.
+    *
+    * @param publicID the public ID, which may be null.
+    * @param systemID the system ID, which is never null.
+    *
+    * @throws java.net.MalformedURLException
+    *     If the system ID does not contain a valid URL.
+    * @throws java.io.FileNotFoundException
+    *     If the system ID refers to a local file which does not exist.
+    * @throws java.io.IOException
+    *     If an error occurred opening the stream.
+    */
+   public Reader openStream(String publicID,
+                            String systemID)
+      throws MalformedURLException,
+             FileNotFoundException,
+             IOException;
+
+
+   /**
+    * Starts a new stream from a Java reader. The new stream is used
+    * temporary to read data from. If that stream is exhausted, control
+    * returns to the "parent" stream.
+    *
+    * @param reader the reader to read the new data from.
+    */
+   public void startNewStream(Reader reader);
+
+
+   /**
+    * Starts a new stream from a Java reader. The new stream is used
+    * temporary to read data from. If that stream is exhausted, control
+    * returns to the parent stream.
+    *
+    * @param reader the non-null reader to read the new data from
+    * @param isInternalEntity true if the reader is produced by resolving
+    *                         an internal entity
+    */
+   public void startNewStream(Reader  reader,
+                              boolean isInternalEntity);
+
+
+   /**
+    * Returns the current "level" of the stream on the stack of streams.
+    */
+   public int getStreamLevel();
+
+
+   /**
+    * Sets the system ID of the current stream.
+    *
+    * @param systemID the system ID.
+    *
+    * @throws java.net.MalformedURLException
+    *     If the system ID does not contain a valid URL.
+    */
+   public void setSystemID(String systemID)
+      throws MalformedURLException;
+
+
+   /**
+    * Sets the public ID of the current stream.
+    *
+    * @param publicID the public ID.
+    */
+   public void setPublicID(String publicID);
+
+
+   /**
+    * Returns the current system ID.
+    */
+   public String getSystemID();
+
+
+   /**
+    * Returns the current public ID.
+    */
+   public String getPublicID();
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/IXMLValidator.java b/Sources/Java/net/n3/nanoxml/IXMLValidator.java
new file mode 100644
index 0000000..8b2e948
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/IXMLValidator.java
@@ -0,0 +1,165 @@
+/* IXMLValidator.java                                              NanoXML/Java
+ *
+ * $Revision: 1.3 $
+ * $Date: 2002/01/04 21:03:28 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.Reader;
+import java.io.IOException;
+import java.util.Properties;
+
+
+/**
+ * IXMLValidator processes the DTD and handles entity references.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.3 $
+ */
+public interface IXMLValidator
+{
+
+   /**
+    * Sets the parameter entity resolver.
+    *
+    * @param resolver the entity resolver.
+    */
+   public void setParameterEntityResolver(IXMLEntityResolver resolver);
+
+
+   /**
+    * Returns the parameter entity resolver.
+    *
+    * @return the entity resolver.
+    */
+   public IXMLEntityResolver getParameterEntityResolver();
+
+
+   /**
+    * Parses the DTD. The validator object is responsible for reading the
+    * full DTD.
+    *
+    * @param publicID       the public ID, which may be null.
+    * @param reader         the reader to read the DTD from.
+    * @param entityResolver the entity resolver.
+    * @param external       true if the DTD is external.
+    *
+    * @throws java.lang.Exception
+    *     If something went wrong.
+    */
+   public void parseDTD(String             publicID,
+                        IXMLReader         reader,
+                        IXMLEntityResolver entityResolver,
+                        boolean            external)
+      throws Exception;
+
+
+   /**
+    * Indicates that an element has been started.
+    *
+    * @param name       the name of the element.
+    * @param systemId   the system ID of the XML data of the element.
+    * @param lineNr     the line number in the XML data of the element.
+    *
+    * @throws java.lang.Exception
+    *     If the element could not be validated.
+    */
+   public void elementStarted(String name,
+                              String systemId,
+                              int    lineNr)
+      throws Exception;
+
+
+   /**
+    * Indicates that the current element has ended.
+    *
+    * @param name       the name of the element.
+    * @param systemId   the system ID of the XML data of the element.
+    * @param lineNr     the line number in the XML data of the element.
+    *
+    * @throws java.lang.Exception
+    *     If the element could not be validated.
+    */
+   public void elementEnded(String name,
+                            String systemId,
+                            int    lineNr)
+      throws Exception;
+
+
+   /**
+    * Indicates that an attribute has been added to the current element.
+    *
+    * @param key        the name of the attribute.
+    * @param value      the value of the attribute.
+    * @param systemId   the system ID of the XML data of the element.
+    * @param lineNr     the line number in the XML data of the element.
+    *
+    * @throws java.lang.Exception
+    *     If the attribute could not be validated.
+    */
+   public void attributeAdded(String key,
+                              String value,
+                              String systemId,
+                              int    lineNr)
+      throws Exception;
+
+
+   /**
+    * This method is called when the attributes of an XML element have been
+    * processed.
+    * If there are attributes with a default value which have not been
+    * specified yet, they have to be put into <I>extraAttributes</I>.
+    *
+    * @param name            the name of the element.
+    * @param extraAttributes where to put extra attributes.
+    * @param systemId        the system ID of the XML data of the element.
+    * @param lineNr          the line number in the XML data of the element.
+    *
+    * @throws java.lang.Exception
+    *     if the element could not be validated.
+    */
+   public void elementAttributesProcessed(String     name,
+                                          Properties extraAttributes,
+                                          String     systemId,
+                                          int        lineNr)
+      throws Exception;
+
+
+   /**
+    * Indicates that a new #PCDATA element has been encountered.
+    *
+    * @param systemId the system ID of the XML data of the element.
+    * @param lineNr   the line number in the XML data of the element.
+    *
+    * @throws java.lang.Exception
+    *     if the element could not be validated.
+    */
+   public void PCDataAdded(String systemId,
+                           int    lineNr)
+      throws Exception;
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/NonValidator.java b/Sources/Java/net/n3/nanoxml/NonValidator.java
new file mode 100644
index 0000000..472716f
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/NonValidator.java
@@ -0,0 +1,632 @@
+/* NonValidator.java                                               NanoXML/Java
+ *
+ * $Revision: 1.4 $
+ * $Date: 2002/02/03 21:19:38 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.Reader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Stack;
+
+
+/**
+ * NonValidator is a concrete implementation of IXMLValidator which processes
+ * the DTD and handles entity definitions. It does not do any validation
+ * itself.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
+ */
+public class NonValidator
+   implements IXMLValidator
+{
+
+   /**
+    * The parameter entity resolver.
+    */
+   protected IXMLEntityResolver parameterEntityResolver;
+
+
+   /**
+    * Contains the default values for attributes for the different element
+    * types.
+    */
+   protected Hashtable attributeDefaultValues;
+
+
+   /**
+    * The stack of elements to be processed.
+    */
+   protected Stack currentElements;
+
+
+   /**
+    * Creates the "validator".
+    */
+   public NonValidator()
+   {
+      this.attributeDefaultValues = new Hashtable();
+      this.currentElements = new Stack();
+      this.parameterEntityResolver = new XMLEntityResolver();
+   }
+
+
+   /**
+    * Cleans up the object when it's destroyed.
+    */
+   protected void finalize()
+      throws Throwable
+   {
+      this.parameterEntityResolver = null;
+      this.attributeDefaultValues.clear();
+      this.attributeDefaultValues = null;
+      this.currentElements.clear();
+      this.currentElements = null;
+      super.finalize();
+   }
+
+
+   /**
+    * Sets the parameter entity resolver.
+    *
+    * @param resolver the entity resolver.
+    */
+   public void setParameterEntityResolver(IXMLEntityResolver resolver)
+   {
+      this.parameterEntityResolver = resolver;
+   }
+
+
+   /**
+    * Returns the parameter entity resolver.
+    *
+    * @return the entity resolver.
+    */
+   public IXMLEntityResolver getParameterEntityResolver()
+   {
+      return this.parameterEntityResolver;
+   }
+
+
+   /**
+    * Parses the DTD. The validator object is responsible for reading the
+    * full DTD.
+    *
+    * @param publicID       the public ID, which may be null.
+    * @param reader         the reader to read the DTD from.
+    * @param entityResolver the entity resolver.
+    * @param external       true if the DTD is external.
+    *
+    * @throws java.lang.Exception
+    *     If something went wrong.
+    */
+   public void parseDTD(String             publicID,
+                        IXMLReader         reader,
+                        IXMLEntityResolver entityResolver,
+                        boolean            external)
+      throws Exception
+   {
+      XMLUtil.skipWhitespace(reader, null);
+      int origLevel = reader.getStreamLevel();
+
+      for (;;) {
+         String str = XMLUtil.read(reader, '%');
+         char ch = str.charAt(0);
+
+         if (ch == '%') {
+            XMLUtil.processEntity(str, reader,
+                                  this.parameterEntityResolver);
+            continue;
+         } else if (ch == '<') {
+            this.processElement(reader, entityResolver);
+         } else if (ch == ']') {
+            return; // end internal DTD
+         } else {
+            XMLUtil.errorInvalidInput(reader.getSystemID(),
+                                      reader.getLineNr(),
+                                      str);
+         }
+
+         do {
+            ch = reader.read();
+
+            if (external && (reader.getStreamLevel() < origLevel)) {
+               reader.unread(ch);
+               return; // end external DTD
+            }
+         } while ((ch == ' ') || (ch == '\t') || (ch == '\n')
+                  || (ch == '\r'));
+
+         reader.unread(ch);
+      }
+   }
+
+
+   /**
+    * Processes an element in the DTD.
+    *
+    * @param reader         the reader to read data from.
+    * @param entityResolver the entity resolver.
+    *
+    * @throws java.lang.Exception
+    *     If something went wrong.
+    */
+   protected void processElement(IXMLReader         reader,
+                                 IXMLEntityResolver entityResolver)
+      throws Exception
+   {
+      String str = XMLUtil.read(reader, '%');
+      char ch = str.charAt(0);
+
+      if (ch != '!') {
+         XMLUtil.skipTag(reader);
+         return;
+      }
+
+      str = XMLUtil.read(reader, '%');
+      ch = str.charAt(0);
+
+      switch (ch) {
+         case '-':
+            XMLUtil.skipComment(reader);
+            break;
+
+         case '[':
+            this.processConditionalSection(reader, entityResolver);
+            break;
+
+         case 'E':
+            this.processEntity(reader, entityResolver);
+            break;
+
+         case 'A':
+            this.processAttList(reader, entityResolver);
+            break;
+
+         default:
+            XMLUtil.skipTag(reader);
+      }
+   }
+
+
+   /**
+    * Processes a conditional section.
+    *
+    * @param reader         the reader to read data from.
+    * @param entityResolver the entity resolver.
+    *
+    * @throws java.lang.Exception
+    *     If something went wrong.
+    */
+   protected void processConditionalSection(IXMLReader         reader,
+                                            IXMLEntityResolver entityResolver)
+      throws Exception
+   {
+      XMLUtil.skipWhitespace(reader, null);
+
+      String str = XMLUtil.read(reader, '%');
+      char ch = str.charAt(0);
+
+      if (ch != 'I') {
+         XMLUtil.skipTag(reader);
+         return;
+      }
+
+      str = XMLUtil.read(reader, '%');
+      ch = str.charAt(0);
+
+      switch (ch) {
+         case 'G':
+            this.processIgnoreSection(reader, entityResolver);
+            return;
+
+         case 'N':
+            break;
+
+         default:
+            XMLUtil.skipTag(reader);
+            return;
+      }
+
+      if (! XMLUtil.checkLiteral(reader, "CLUDE")) {
+         XMLUtil.skipTag(reader);
+         return;
+      }
+
+      XMLUtil.skipWhitespace(reader, null);
+
+      str = XMLUtil.read(reader, '%');
+      ch = str.charAt(0);
+
+      if (ch != '[') {
+         XMLUtil.skipTag(reader);
+         return;
+      }
+
+      Reader subreader = new CDATAReader(reader);
+      StringBuffer buf = new StringBuffer(1024);
+
+      for (;;) {
+         int ch2 = subreader.read();
+
+         if (ch2 < 0) {
+            break;
+         }
+
+         buf.append((char) ch2);
+      }
+
+      subreader.close();
+      reader.startNewStream(new StringReader(buf.toString()));
+   }
+
+
+   /**
+    * Processes an ignore section.
+    *
+    * @param reader         the reader to read data from.
+    * @param entityResolver the entity resolver.
+    *
+    * @throws java.lang.Exception
+    *     If something went wrong.
+    */
+   protected void processIgnoreSection(IXMLReader         reader,
+                                       IXMLEntityResolver entityResolver)
+      throws Exception
+   {
+      if (! XMLUtil.checkLiteral(reader, "NORE")) {
+         XMLUtil.skipTag(reader);
+         return;
+      }
+
+      XMLUtil.skipWhitespace(reader, null);
+
+      String str = XMLUtil.read(reader, '%');
+      char ch = str.charAt(0);
+
+      if (ch != '[') {
+         XMLUtil.skipTag(reader);
+         return;
+      }
+
+      Reader subreader = new CDATAReader(reader);
+      subreader.close();
+   }
+
+
+   /**
+    * Processes an ATTLIST element.
+    *
+    * @param reader         the reader to read data from.
+    * @param entityResolver the entity resolver.
+    *
+    * @throws java.lang.Exception
+    *     If something went wrong.
+    */
+   protected void processAttList(IXMLReader         reader,
+                                 IXMLEntityResolver entityResolver)
+      throws Exception
+   {
+      if (! XMLUtil.checkLiteral(reader,  "TTLIST")) {
+         XMLUtil.skipTag(reader);
+         return;
+      }
+
+      XMLUtil.skipWhitespace(reader, null);
+      String str = XMLUtil.read(reader, '%');
+      char ch = str.charAt(0);
+      while (ch == '%') {
+         XMLUtil.processEntity(str, reader,
+                               this.parameterEntityResolver);
+         str = XMLUtil.read(reader, '%');
+         ch = str.charAt(0);
+      }
+      reader.unread(ch);
+      String elementName = XMLUtil.scanIdentifier(reader);
+      XMLUtil.skipWhitespace(reader, null);
+      
+      str = XMLUtil.read(reader, '%');
+      ch = str.charAt(0);
+      while (ch == '%') {
+         XMLUtil.processEntity(str, reader,
+                               this.parameterEntityResolver);
+         str = XMLUtil.read(reader, '%');
+         ch = str.charAt(0);
+      }
+
+      Properties props = new Properties();
+
+      while (ch != '>') {
+         reader.unread(ch);
+         String attName = XMLUtil.scanIdentifier(reader);
+         XMLUtil.skipWhitespace(reader, null);
+         str = XMLUtil.read(reader, '%');
+         ch = str.charAt(0);
+         while (ch == '%') {
+             XMLUtil.processEntity(str, reader,
+                                   this.parameterEntityResolver);
+             str = XMLUtil.read(reader, '%');
+             ch = str.charAt(0);
+         }
+
+         if (ch == '(') {
+             while (ch != ')') {
+                 str = XMLUtil.read(reader, '%');
+                 ch = str.charAt(0);
+                 while (ch == '%') {
+                     XMLUtil.processEntity(str, reader,
+                                           this.parameterEntityResolver);
+                     str = XMLUtil.read(reader, '%');
+                     ch = str.charAt(0);
+                 }
+             }
+         } else {
+            reader.unread(ch);
+            XMLUtil.scanIdentifier(reader);
+         }
+
+         XMLUtil.skipWhitespace(reader, null);
+         str = XMLUtil.read(reader, '%');
+         ch = str.charAt(0);
+         while (ch == '%') {
+             XMLUtil.processEntity(str, reader,
+                                   this.parameterEntityResolver);
+             str = XMLUtil.read(reader, '%');
+             ch = str.charAt(0);
+         }
+         
+         if (ch == '#') {
+            str = XMLUtil.scanIdentifier(reader);
+            XMLUtil.skipWhitespace(reader, null);
+
+            if (! str.equals("FIXED")) {
+               XMLUtil.skipWhitespace(reader, null);
+
+               str = XMLUtil.read(reader, '%');
+               ch = str.charAt(0);
+               while (ch == '%') {
+                  XMLUtil.processEntity(str, reader,
+                                        this.parameterEntityResolver);
+                  str = XMLUtil.read(reader, '%');
+                  ch = str.charAt(0);
+               }
+
+               continue;
+            }
+         } else {
+            reader.unread(ch);
+         }
+
+         String value = XMLUtil.scanString(reader, '%',
+                                           this.parameterEntityResolver);
+         props.put(attName, value);
+         XMLUtil.skipWhitespace(reader, null);
+
+         str = XMLUtil.read(reader, '%');
+         ch = str.charAt(0);
+         while (ch == '%') {
+            XMLUtil.processEntity(str, reader,
+                                  this.parameterEntityResolver);
+            str = XMLUtil.read(reader, '%');
+            ch = str.charAt(0);
+         }
+      }
+
+      if (! props.isEmpty()) {
+         this.attributeDefaultValues.put(elementName, props);
+      }
+   }
+
+
+   /**
+    * Processes an ENTITY element.
+    *
+    * @param reader         the reader to read data from.
+    * @param entityResolver the entity resolver.
+    *
+    * @throws java.lang.Exception
+    *     If something went wrong.
+    */
+   protected void processEntity(IXMLReader         reader,
+                                IXMLEntityResolver entityResolver)
+      throws Exception
+   {
+      if (! XMLUtil.checkLiteral(reader, "NTITY")) {
+         XMLUtil.skipTag(reader);
+         return;
+      }
+
+      XMLUtil.skipWhitespace(reader, null);
+      char ch = XMLUtil.readChar(reader, '\0');
+
+      if (ch == '%') {
+         XMLUtil.skipWhitespace(reader, null);
+         entityResolver = this.parameterEntityResolver;
+      } else {
+         reader.unread(ch);
+      }
+
+      String key = XMLUtil.scanIdentifier(reader);
+      XMLUtil.skipWhitespace(reader, null);
+      ch = XMLUtil.readChar(reader, '%');
+      String systemID = null;
+      String publicID = null;
+
+      switch (ch) {
+         case 'P':
+            if (! XMLUtil.checkLiteral(reader, "UBLIC")) {
+               XMLUtil.skipTag(reader);
+               return;
+            }
+
+            XMLUtil.skipWhitespace(reader, null);
+            publicID = XMLUtil.scanString(reader, '%',
+                                          this.parameterEntityResolver);
+            XMLUtil.skipWhitespace(reader, null);
+            systemID = XMLUtil.scanString(reader, '%',
+                                          this.parameterEntityResolver);
+            XMLUtil.skipWhitespace(reader, null);
+            XMLUtil.readChar(reader, '%');
+            break;
+
+         case 'S':
+            if (! XMLUtil.checkLiteral(reader, "YSTEM")) {
+               XMLUtil.skipTag(reader);
+               return;
+            }
+
+            XMLUtil.skipWhitespace(reader, null);
+            systemID = XMLUtil.scanString(reader, '%',
+                                          this.parameterEntityResolver);
+            XMLUtil.skipWhitespace(reader, null);
+            XMLUtil.readChar(reader, '%');
+            break;
+
+         case '"':
+         case '\'':
+            reader.unread(ch);
+            String value = XMLUtil.scanString(reader, '%',
+                                              this.parameterEntityResolver);
+            entityResolver.addInternalEntity(key, value);
+            XMLUtil.skipWhitespace(reader, null);
+            XMLUtil.readChar(reader, '%');
+            break;
+
+         default:
+            XMLUtil.skipTag(reader);
+      }
+
+      if (systemID != null) {
+         entityResolver.addExternalEntity(key, publicID, systemID);
+      }
+   }
+
+
+   /**
+    * Indicates that an element has been started.
+    *
+    * @param name       the name of the element.
+    * @param systemId   the system ID of the XML data of the element.
+    * @param lineNr     the line number in the XML data of the element.
+    */
+   public void elementStarted(String name,
+                              String systemId,
+                              int    lineNr)
+   {
+      Properties attribs
+         = (Properties) this.attributeDefaultValues.get(name);
+
+      if (attribs == null) {
+         attribs = new Properties();
+      } else {
+         attribs = (Properties) attribs.clone();
+      }
+
+      this.currentElements.push(attribs);
+   }
+
+
+   /**
+    * Indicates that the current element has ended.
+    *
+    * @param name       the name of the element.
+    * @param systemId   the system ID of the XML data of the element.
+    * @param lineNr     the line number in the XML data of the element.
+    */
+   public void elementEnded(String name,
+                            String systemId,
+                            int    lineNr)
+   {
+      // nothing to do
+   }
+
+
+   /**
+    * This method is called when the attributes of an XML element have been
+    * processed.
+    * If there are attributes with a default value which have not been
+    * specified yet, they have to be put into <I>extraAttributes</I>.
+    *
+    * @param name            the name of the element.
+    * @param extraAttributes where to put extra attributes.
+    * @param systemId        the system ID of the XML data of the element.
+    * @param lineNr          the line number in the XML data of the element.
+    */
+   public void elementAttributesProcessed(String     name,
+                                          Properties extraAttributes,
+                                          String     systemId,
+                                          int        lineNr)
+   {
+      Properties props = (Properties) this.currentElements.pop();
+      Enumeration enum = props.keys();
+
+      while (enum.hasMoreElements()) {
+         String key = (String) enum.nextElement();
+         extraAttributes.put(key, props.get(key));
+      }
+   }
+
+
+   /**
+    * Indicates that an attribute has been added to the current element.
+    *
+    * @param key        the name of the attribute.
+    * @param value      the value of the attribute.
+    * @param systemId   the system ID of the XML data of the element.
+    * @param lineNr     the line number in the XML data of the element.
+    */
+   public void attributeAdded(String key,
+                              String value,
+                              String systemId,
+                              int    lineNr)
+   {
+      Properties props = (Properties) this.currentElements.peek();
+
+      if (props.containsKey(key)) {
+         props.remove(key);
+      }
+   }
+
+
+   /**
+    * Indicates that a new #PCDATA element has been encountered.
+    *
+    * @param systemId the system ID of the XML data of the element.
+    * @param lineNr   the line number in the XML data of the element.
+    */
+   public void PCDataAdded(String systemId,
+                           int    lineNr)
+   {
+      // nothing to do
+   }
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/PIReader.java b/Sources/Java/net/n3/nanoxml/PIReader.java
new file mode 100644
index 0000000..9d1bc1c
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/PIReader.java
@@ -0,0 +1,157 @@
+/* PIReader.java                                                   NanoXML/Java
+ *
+ * $Revision: 1.3 $
+ * $Date: 2002/01/04 21:03:28 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.Reader;
+import java.io.IOException;
+
+
+/**
+ * This reader reads data from another reader until the end of a processing
+ * instruction (?>) has been encountered.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.3 $
+ */
+class PIReader
+   extends Reader
+{
+
+   /**
+    * The encapsulated reader.
+    */
+   private IXMLReader reader;
+
+
+   /**
+    * True if the end of the stream has been reached.
+    */
+   private boolean atEndOfData;
+
+
+   /**
+    * Creates the reader.
+    *
+    * @param reader the encapsulated reader
+    */
+   PIReader(IXMLReader reader)
+   {
+      this.reader = reader;
+      this.atEndOfData = false;
+   }
+
+
+   /**
+    * Cleans up the object when it's destroyed.
+    */
+   protected void finalize()
+      throws Throwable
+   {
+      this.reader = null;
+      super.finalize();
+   }
+
+
+   /**
+    * Reads a block of data.
+    *
+    * @param buffer where to put the read data
+    * @param offset first position in buffer to put the data
+    * @param size maximum number of chars to read
+    *
+    * @return the number of chars read, or -1 if at EOF
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   public int read(char[] buffer,
+                   int    offset,
+                   int    size)
+      throws IOException
+   {
+      if (this.atEndOfData) {
+         return -1;
+      }
+
+      int charsRead = 0;
+
+      if ((offset + size) > buffer.length) {
+         size = buffer.length - offset;
+      }
+
+      while (charsRead < size) {
+         char ch = this.reader.read();
+
+         if (ch == '?') {
+            char ch2 = this.reader.read();
+
+            if (ch2 == '>') {
+               this.atEndOfData = true;
+               break;
+            }
+
+            this.reader.unread(ch2);
+         }
+
+         buffer[charsRead] = ch;
+         charsRead++;
+      }
+
+      if (charsRead == 0) {
+         charsRead = -1;
+      }
+
+      return charsRead;
+   }
+
+
+   /**
+    * Skips remaining data and closes the stream.
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   public void close()
+      throws IOException
+   {
+      while (! this.atEndOfData) {
+         char ch = this.reader.read();
+
+         if (ch == '?') {
+            char ch2 = this.reader.read();
+
+            if (ch2 == '>') {
+               this.atEndOfData = true;
+            }
+         }
+      }
+   }
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/StdXMLBuilder.java b/Sources/Java/net/n3/nanoxml/StdXMLBuilder.java
new file mode 100644
index 0000000..da5d997
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/StdXMLBuilder.java
@@ -0,0 +1,335 @@
+/* StdXMLBuilder.java                                              NanoXML/Java
+ *
+ * $Revision: 1.3 $
+ * $Date: 2002/01/04 21:03:28 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Stack;
+
+
+/**
+ * StdXMLBuilder is a concrete implementation of IXMLBuilder which creates a
+ * tree of IXMLElement from an XML data source.
+ *
+ * @see net.n3.nanoxml.XMLElement
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.3 $
+ */
+public class StdXMLBuilder
+   implements IXMLBuilder
+{
+
+   /**
+    * This stack contains the current element and its parents.
+    */
+   private Stack stack;
+
+
+   /**
+    * The root element of the parsed XML tree.
+    */
+   private IXMLElement root;
+
+
+   /**
+    * Prototype element for creating the tree.
+    */
+   private IXMLElement prototype;
+
+
+   /**
+    * Creates the builder.
+    */
+   public StdXMLBuilder()
+   {
+      this(new XMLElement());
+   }
+
+
+   /**
+    * Creates the builder.
+    *
+    * @param prototype the prototype to use when building the tree.
+    */
+   public StdXMLBuilder(IXMLElement prototype)
+   {
+      this.stack = null;
+      this.root = null;
+      this.prototype = prototype;
+   }
+
+
+   /**
+    * Cleans up the object when it's destroyed.
+    */
+   protected void finalize()
+      throws Throwable
+   {
+      this.prototype = null;
+      this.root = null;
+      this.stack.clear();
+      this.stack = null;
+      super.finalize();
+   }
+
+
+   /**
+    * This method is called before the parser starts processing its input.
+    *
+    * @param systemID the system ID of the XML data source.
+    * @param lineNr   the line on which the parsing starts.
+    */
+   public void startBuilding(String systemID,
+                             int    lineNr)
+   {
+      this.stack = new Stack();
+      this.root = null;
+   }
+
+
+   /**
+    * This method is called when a processing instruction is encountered.
+    * PIs with target "xml" are handled by the parser.
+    *
+    * @param target the PI target.
+    * @param reader to read the data from the PI.
+    */
+   public void newProcessingInstruction(String target,
+                                        Reader reader)
+   {
+      // nothing to do
+   }
+
+
+   /**
+    * This method is called when a new XML element is encountered.
+    *
+    * @see #endElement
+    *
+    * @param name       the name of the element.
+    * @param nsPrefix   the prefix used to identify the namespace. If no
+    *                   namespace has been specified, this parameter is null.
+    * @param nsURI      the URI associated with the namespace. If no
+    *                   namespace has been specified, or no URI is
+    *                   associated with nsPrefix, this parameter is null.
+    * @param systemID   the system ID of the XML data source.
+    * @param lineNr     the line in the source where the element starts.
+    */
+   public void startElement(String name,
+                            String nsPrefix,
+                            String nsURI,
+                            String systemID,
+                            int    lineNr)
+   {
+      String fullName = name;
+
+      if (nsPrefix != null) {
+         fullName = nsPrefix + ':' + name;
+      }
+
+      IXMLElement elt = this.prototype.createElement(fullName, nsURI,
+                                                     systemID, lineNr);
+
+      if (this.stack.empty()) {
+         this.root = elt;
+      } else {
+         IXMLElement top = (IXMLElement) this.stack.peek();
+         top.addChild(elt);
+      }
+
+      this.stack.push(elt);
+   }
+
+
+   /**
+    * This method is called when the attributes of an XML element have been
+    * processed.
+    *
+    * @see #startElement
+    * @see #addAttribute
+    *
+    * @param name       the name of the element.
+    * @param nsPrefix   the prefix used to identify the namespace. If no
+    *                   namespace has been specified, this parameter is null.
+    * @param nsURI      the URI associated with the namespace. If no
+    *                   namespace has been specified, or no URI is
+    *                   associated with nsPrefix, this parameter is null.
+    */
+   public void elementAttributesProcessed(String name,
+                                          String nsPrefix,
+                                          String nsURI)
+   {
+      // nothing to do
+   }
+
+
+   /**
+    * This method is called when the end of an XML elemnt is encountered.
+    *
+    * @see #startElement
+    *
+    * @param name       the name of the element.
+    * @param nsPrefix   the prefix used to identify the namespace. If no
+    *                   namespace has been specified, this parameter is null.
+    * @param nsURI      the URI associated with the namespace. If no
+    *                   namespace has been specified, or no URI is
+    *                   associated with nsPrefix, this parameter is null.
+    */
+   public void endElement(String name,
+                          String nsPrefix,
+                          String nsURI)
+   {
+      IXMLElement elt = (IXMLElement) this.stack.pop();
+
+      if (elt.getChildrenCount() == 1) {
+         IXMLElement child = elt.getChildAtIndex(0);
+
+         if (child.getName() == null) {
+            elt.setContent(child.getContent());
+            elt.removeChildAtIndex(0);
+         }
+      }
+   }
+
+
+   /**
+    * This method is called when a new attribute of an XML element is
+    * encountered.
+    *
+    * @param key        the key (name) of the attribute.
+    * @param nsPrefix   the prefix used to identify the namespace. If no
+    *                   namespace has been specified, this parameter is null.
+    * @param nsURI      the URI associated with the namespace. If no
+    *                   namespace has been specified, or no URI is
+    *                   associated with nsPrefix, this parameter is null.
+    * @param value      the value of the attribute.
+    * @param type       the type of the attribute. If no type is known,
+    *                   "CDATA" is returned.
+    *
+    * @throws java.lang.Exception
+    *     If an exception occurred while processing the event.
+    */
+   public void addAttribute(String key,
+                            String nsPrefix,
+                            String nsURI,
+                            String value,
+                            String type)
+      throws Exception
+   {
+      String fullName = key;
+
+      if (nsPrefix != null) {
+         fullName = nsPrefix + ':' + key;
+      }
+
+      IXMLElement top = (IXMLElement) this.stack.peek();
+
+      if (top.hasAttribute(fullName)) {
+         throw new XMLParseException(top.getSystemID(),
+                                     top.getLineNr(),
+                                     "Duplicate attribute: " + key);
+      }
+
+      if (nsPrefix != null) {
+         top.setAttribute(fullName, nsURI, value);
+      } else {
+         top.setAttribute(fullName, value);
+      }
+   }
+
+
+   /**
+    * This method is called when a PCDATA element is encountered. A Java
+    * reader is supplied from which you can read the data. The reader will
+    * only read the data of the element. You don't need to check for
+    * boundaries. If you don't read the full element, the rest of the data
+    * is skipped. You also don't have to care about entities; they are
+    * resolved by the parser.
+    *
+    * @param reader   the Java reader from which you can retrieve the data.
+    * @param systemID the system ID of the XML data source.
+    * @param lineNr   the line in the source where the element starts.
+    */
+   public void addPCData(Reader reader,
+                         String systemID,
+                         int    lineNr)
+   {
+      int bufSize = 2048;
+      int sizeRead = 0;
+      StringBuffer str = new StringBuffer(bufSize);
+      char[] buf = new char[bufSize];
+
+      for (;;) {
+         if (sizeRead >= bufSize) {
+            bufSize *= 2;
+            str.ensureCapacity(bufSize);
+         }
+
+         int size;
+
+         try {
+            size = reader.read(buf);
+         } catch (IOException e) {
+            break;
+         }
+
+         if (size < 0) {
+            break;
+         }
+
+         str.append(buf, 0, size);
+         sizeRead += size;
+      }
+
+      IXMLElement elt = this.prototype.createElement(null, systemID, lineNr);
+      elt.setContent(str.toString());
+
+      if (! this.stack.empty()) {
+         IXMLElement top = (IXMLElement) this.stack.peek();
+         top.addChild(elt);
+      }
+   }
+
+
+   /**
+    * Returns the result of the building process. This method is called just
+    * before the <I>parse</I> method of IXMLParser returns.
+    *
+    * @see net.n3.nanoxml.IXMLParser#parse
+    *
+    * @return the result of the building process.
+    */
+   public Object getResult()
+   {
+      return this.root;
+   }
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/StdXMLParser.java b/Sources/Java/net/n3/nanoxml/StdXMLParser.java
new file mode 100644
index 0000000..16eebbd
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/StdXMLParser.java
@@ -0,0 +1,689 @@
+/* StdXMLParser.java                                               NanoXML/Java
+ *
+ * $Revision: 1.5 $
+ * $Date: 2002/03/24 11:37:00 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.IOException;
+import java.io.CharArrayReader;
+import java.io.Reader;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Vector;
+
+
+/**
+ * StdXMLParser is the core parser of NanoXML.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.5 $
+ */
+public class StdXMLParser
+   implements IXMLParser
+{
+
+   /**
+    * The builder which creates the logical structure of the XML data.
+    */
+   private IXMLBuilder builder;
+
+
+   /**
+    * The reader from which the parser retrieves its data.
+    */
+   private IXMLReader reader;
+
+
+   /**
+    * The entity resolver.
+    */
+   private IXMLEntityResolver entityResolver;
+
+
+   /**
+    * The validator that will process entity references and validate the XML
+    * data.
+    */
+   private IXMLValidator validator;
+
+
+   /**
+    * Creates a new parser.
+    */
+   public StdXMLParser()
+   {
+      this.builder = null;
+      this.validator = null;
+      this.reader = null;
+      this.entityResolver = new XMLEntityResolver();
+   }
+
+
+   /**
+    * Cleans up the object when it's destroyed.
+    */
+   protected void finalize()
+      throws Throwable
+   {
+      this.builder = null;
+      this.reader = null;
+      this.entityResolver = null;
+      this.validator = null;
+      super.finalize();
+   }
+
+
+   /**
+    * Sets the builder which creates the logical structure of the XML data.
+    *
+    * @param builder the non-null builder
+    */
+   public void setBuilder(IXMLBuilder builder)
+   {
+      this.builder = builder;
+   }
+
+
+   /**
+    * Returns the builder which creates the logical structure of the XML data.
+    *
+    * @return the builder
+    */
+   public IXMLBuilder getBuilder()
+   {
+      return this.builder;
+   }
+
+
+   /**
+    * Sets the validator that validates the XML data.
+    *
+    * @param validator the non-null validator
+    */
+   public void setValidator(IXMLValidator validator)
+   {
+      this.validator = validator;
+   }
+
+
+   /**
+    * Returns the validator that validates the XML data.
+    *
+    * @return the validator
+    */
+   public IXMLValidator getValidator()
+   {
+      return this.validator;
+   }
+
+
+   /**
+    * Sets the entity resolver.
+    *
+    * @param resolver the non-null resolver
+    */
+   public void setResolver(IXMLEntityResolver resolver)
+   {
+      this.entityResolver = resolver;
+   }
+
+
+   /**
+    * Returns the entity resolver.
+    *
+    * @return the non-null resolver
+    */
+   public IXMLEntityResolver getResolver()
+   {
+      return this.entityResolver;
+   }
+
+
+   /**
+    * Sets the reader from which the parser retrieves its data.
+    *
+    * @param reader the reader
+    */
+   public void setReader(IXMLReader reader)
+   {
+      this.reader = reader;
+   }
+
+
+   /**
+    * Returns the reader from which the parser retrieves its data.
+    *
+    * @return the reader
+    */
+   public IXMLReader getReader()
+   {
+      return this.reader;
+   }
+
+
+   /**
+    * Parses the data and lets the builder create the logical data structure.
+    *
+    * @return the logical structure built by the builder
+    *
+    * @throws net.n3.nanoxml.XMLException
+    *		if an error occurred reading or parsing the data
+    */
+   public Object parse()
+      throws XMLException
+   {
+      try {
+         this.builder.startBuilding(this.reader.getSystemID(),
+                                    this.reader.getLineNr());
+         this.scanData();
+         return this.builder.getResult();
+      } catch (XMLException e) {
+         throw e;
+      } catch (Exception e) {
+         throw new XMLException(e);
+      }
+   }
+
+
+   /**
+    * Scans the XML data for elements.
+    *
+    * @throws java.lang.Exception
+    *     if something went wrong
+    */
+   protected void scanData()
+      throws Exception
+   {
+      while ((! this.reader.atEOF()) && (this.builder.getResult() == null)) {
+         String str = XMLUtil.read(this.reader, '&');
+         char ch = str.charAt(0);
+         if (ch == '&') {
+            XMLUtil.processEntity(str, this.reader, this.entityResolver);
+            continue;
+         }
+
+         switch (ch) {
+            case '<':
+               this.scanSomeTag(false, // don't allow CDATA
+                                null,  // no default namespace
+                                new Properties());
+               break;
+
+            case ' ':
+            case '\t':
+            case '\r':
+            case '\n':
+               // skip whitespace
+               break;
+
+            default:
+               XMLUtil.errorInvalidInput(reader.getSystemID(),
+                                         reader.getLineNr(),
+                                         "`" + ch + "' (0x"
+                                         + Integer.toHexString((int) ch)
+                                         + ')');
+         }
+      }
+   }
+
+
+   /**
+    * Scans an XML tag.
+    *
+    * @param allowCDATA true if CDATA sections are allowed at this point
+    * @param defaultNamespace the default namespace URI (or null)
+    * @param namespaces list of defined namespaces
+    *
+    * @throws java.lang.Exception
+    *     if something went wrong
+    */
+   protected void scanSomeTag(boolean    allowCDATA,
+                              String     defaultNamespace,
+                              Properties namespaces)
+      throws Exception
+   {
+      String str = XMLUtil.read(this.reader, '&');
+      char ch = str.charAt(0);
+
+      if (ch == '&') {
+         XMLUtil.errorUnexpectedEntity(reader.getSystemID(),
+                                       reader.getLineNr(),
+                                       str);
+      }
+
+      switch (ch) {
+         case '?':
+            this.processPI();
+            break;
+
+         case '!':
+            this.processSpecialTag(allowCDATA);
+            break;
+
+         default:
+            this.reader.unread(ch);
+            this.processElement(defaultNamespace, namespaces);
+      }
+   }
+
+
+   /**
+    * Processes a "processing instruction".
+    *
+    * @throws java.lang.Exception
+    *     if something went wrong
+    */
+   protected void processPI()
+      throws Exception
+   {
+      XMLUtil.skipWhitespace(this.reader, null);
+      String target = XMLUtil.scanIdentifier(this.reader);
+      XMLUtil.skipWhitespace(this.reader, null);
+      Reader reader = new PIReader(this.reader);
+
+      if (! target.equalsIgnoreCase("xml")) {
+         this.builder.newProcessingInstruction(target, reader);
+      }
+
+      reader.close();
+   }
+
+
+   /**
+    * Processes a tag that starts with a bang (<!...>).
+    *
+    * @param allowCDATA true if CDATA sections are allowed at this point
+    *
+    * @throws java.lang.Exception
+    *     if something went wrong
+    */
+   protected void processSpecialTag(boolean allowCDATA)
+      throws Exception
+   {
+      String str = XMLUtil.read(this.reader, '&');
+      char ch = str.charAt(0);
+
+      if (ch == '&') {
+         XMLUtil.errorUnexpectedEntity(reader.getSystemID(),
+                                       reader.getLineNr(),
+                                       str);
+      }
+
+      switch (ch) {
+         case '[':
+            if (allowCDATA) {
+               this.processCDATA();
+            } else {
+               XMLUtil.errorUnexpectedCDATA(reader.getSystemID(),
+                                            reader.getLineNr());
+            }
+
+            return;
+
+         case 'D':
+            this.processDocType();
+            return;
+
+         case '-':
+            XMLUtil.skipComment(this.reader);
+            return;
+      }
+   }
+
+
+   /**
+    * Processes a CDATA section.
+    *
+    * @throws java.lang.Exception
+    *     if something went wrong
+    */
+   protected void processCDATA()
+      throws Exception
+   {
+      if (! XMLUtil.checkLiteral(this.reader, "CDATA[")) {
+         XMLUtil.errorExpectedInput(reader.getSystemID(),
+                                    reader.getLineNr(),
+                                    "<![[CDATA[");
+      }
+
+      this.validator.PCDataAdded(this.reader.getSystemID(),
+                                 this.reader.getLineNr());
+      Reader reader = new CDATAReader(this.reader);
+      this.builder.addPCData(reader, this.reader.getSystemID(),
+                             this.reader.getLineNr());
+      reader.close();
+   }
+
+
+   /**
+    * Processes a document type declaration.
+    *
+    * @throws java.lang.Exception
+    *		if an error occurred reading or parsing the data
+    */
+   protected void processDocType()
+      throws Exception
+   {
+      if (! XMLUtil.checkLiteral(this.reader, "OCTYPE")) {
+         XMLUtil.errorExpectedInput(reader.getSystemID(),
+                                    reader.getLineNr(),
+                                    "<!DOCTYPE");
+         return;
+      }
+
+      XMLUtil.skipWhitespace(this.reader, null);
+      String systemID = null;
+      StringBuffer publicID = new StringBuffer();
+      String rootElement = XMLUtil.scanIdentifier(this.reader);
+      XMLUtil.skipWhitespace(this.reader, null);
+      char ch = this.reader.read();
+
+      if (ch == 'P') {
+         systemID = XMLUtil.scanPublicID(publicID, reader);
+         XMLUtil.skipWhitespace(this.reader, null);
+         ch = this.reader.read();
+      } else if (ch == 'S') {
+         systemID = XMLUtil.scanSystemID(reader);
+         XMLUtil.skipWhitespace(this.reader, null);
+         ch = this.reader.read();
+      }
+
+      if (ch == '[') {
+         this.validator.parseDTD(publicID.toString(),
+                                 this.reader,
+                                 this.entityResolver,
+                                 false);
+         XMLUtil.skipWhitespace(this.reader, null);
+         ch = this.reader.read();
+      }
+
+      if (ch != '>') {
+         XMLUtil.errorExpectedInput(reader.getSystemID(),
+                                    reader.getLineNr(),
+                                    "`>'");
+      }
+
+      if (systemID != null) {
+         Reader reader = this.reader.openStream(publicID.toString(),
+                                                systemID);
+         this.reader.startNewStream(reader);
+         this.reader.setSystemID(systemID);
+         this.reader.setPublicID(publicID.toString());
+         this.validator.parseDTD(publicID.toString(),
+                                 this.reader,
+                                 this.entityResolver,
+                                 true);
+      }
+   }
+
+
+   /**
+    * Processes a regular element.
+    *
+    * @param defaultNamespace the default namespace URI (or null)
+    * @param namespaces list of defined namespaces
+    *
+    * @throws java.lang.Exception
+    *     if something went wrong
+    */
+   protected void processElement(String     defaultNamespace,
+                                 Properties namespaces)
+      throws Exception
+   {
+      String fullName = XMLUtil.scanIdentifier(this.reader);
+      String name = fullName;
+      XMLUtil.skipWhitespace(this.reader, null);
+      String prefix = null;
+      int colonIndex = name.indexOf(':');
+
+      if (colonIndex > 0) {
+         prefix = name.substring(0, colonIndex);
+         name = name.substring(colonIndex + 1);
+      }
+
+      Vector attrNames = new Vector();
+      Vector attrValues = new Vector();
+      Vector attrTypes = new Vector();
+
+      this.validator.elementStarted(fullName,
+                                    this.reader.getSystemID(),
+                                    this.reader.getLineNr());
+      char ch;
+
+      for (;;) {
+         ch = this.reader.read();
+
+         if ((ch == '/') || (ch == '>')) {
+            break;
+         }
+
+         this.reader.unread(ch);
+         this.processAttribute(attrNames, attrValues, attrTypes);
+         XMLUtil.skipWhitespace(this.reader, null);
+      }
+
+      Properties extraAttributes = new Properties();
+      this.validator.elementAttributesProcessed(fullName,
+                                                extraAttributes,
+                                                this.reader.getSystemID(),
+                                                this.reader.getLineNr());
+      Enumeration enum = extraAttributes.keys();
+
+      while (enum.hasMoreElements()) {
+         String key = (String) enum.nextElement();
+         String value = extraAttributes.getProperty(key);
+         attrNames.addElement(key);
+         attrValues.addElement(value);
+         attrTypes.addElement("CDATA");
+      }
+
+      for (int i = 0; i < attrNames.size(); i++) {
+         String key = (String) attrNames.elementAt(i);
+         String value = (String) attrValues.elementAt(i);
+         String type = (String) attrTypes.elementAt(i);
+
+         if (key.equals("xmlns")) {
+            defaultNamespace = value;
+         } else if (key.startsWith("xmlns:")) {
+            namespaces.put(key.substring(6), value);
+         }
+      }
+
+      if (prefix == null) {
+         this.builder.startElement(name, prefix, defaultNamespace,
+                                   this.reader.getSystemID(),
+                                   this.reader.getLineNr());
+      } else {
+         this.builder.startElement(name, prefix,
+                                   namespaces.getProperty(prefix),
+                                   this.reader.getSystemID(),
+                                   this.reader.getLineNr());
+      }
+
+      for (int i = 0; i < attrNames.size(); i++) {
+         String key = (String) attrNames.elementAt(i);
+
+         if (key.startsWith("xmlns")) {
+            continue;
+         }
+
+         String value = (String) attrValues.elementAt(i);
+         String type = (String) attrTypes.elementAt(i);
+         colonIndex = key.indexOf(':');
+
+         if (colonIndex > 0) {
+            String attPrefix = key.substring(0, colonIndex);
+            key = key.substring(colonIndex + 1);
+            this.builder.addAttribute(key, attPrefix,
+                                      namespaces.getProperty(attPrefix),
+                                      value, type);
+         } else {
+            this.builder.addAttribute(key, null, null, value, type);
+         }
+      }
+
+      if (prefix == null) {
+         this.builder.elementAttributesProcessed(name, prefix,
+                                                 defaultNamespace);
+      } else {
+         this.builder.elementAttributesProcessed(name, prefix,
+                                                 namespaces
+                                                   .getProperty(prefix));
+      }
+
+      if (ch == '/') {
+         if (this.reader.read() != '>') {
+            XMLUtil.errorExpectedInput(reader.getSystemID(),
+                                       reader.getLineNr(),
+                                       "`>'");
+         }
+
+         this.validator.elementEnded(name,
+                                     this.reader.getSystemID(),
+                                     this.reader.getLineNr());
+
+         if (prefix == null) {
+            this.builder.endElement(name, prefix, defaultNamespace);
+         } else {
+            this.builder.endElement(name, prefix,
+                                    namespaces.getProperty(prefix));
+         }
+
+         return;
+      }
+
+      StringBuffer buffer = new StringBuffer(16);
+
+      for (;;) {
+         buffer.setLength(0);
+         String str;
+
+         for (;;) {
+            XMLUtil.skipWhitespace(this.reader, buffer);
+            str = XMLUtil.read(this.reader, '&');
+
+            if ((str.charAt(0) == '&') && (str.charAt(1) != '#')) {
+               XMLUtil.processEntity(str, this.reader,
+                                     this.entityResolver);
+            } else {
+               break;
+            }
+         }
+
+         if (str.charAt(0) == '<') {
+            str = XMLUtil.read(this.reader, '\0');
+
+            if (str.charAt(0) == '/') {
+               XMLUtil.skipWhitespace(this.reader, null);
+               str = XMLUtil.scanIdentifier(this.reader);
+
+               if (! str.equals(fullName)) {
+                  XMLUtil.errorWrongClosingTag(reader.getSystemID(),
+                                               reader.getLineNr(),
+                                               name, str);
+               }
+
+               XMLUtil.skipWhitespace(this.reader, null);
+
+               if (this.reader.read() != '>') {
+                  XMLUtil.errorClosingTagNotEmpty(reader.getSystemID(),
+                                                  reader.getLineNr());
+               }
+
+               this.validator.elementEnded(fullName,
+                                           this.reader.getSystemID(),
+                                           this.reader.getLineNr());
+               if (prefix == null) {
+                   this.builder.endElement(name, prefix, defaultNamespace);
+               } else {
+                   this.builder.endElement(name, prefix,
+                                           namespaces.getProperty(prefix));
+               }
+               break;
+            } else { // <[^/]
+               this.reader.unread(str.charAt(0));
+               this.scanSomeTag(true, //CDATA allowed
+                                defaultNamespace,
+                                (Properties) namespaces.clone());
+            }
+         } else { // [^<]
+            if (str.charAt(0) == '&') {
+               ch = XMLUtil.processCharLiteral(str);
+               buffer.append(ch);
+            } else {
+               reader.unread(str.charAt(0));
+            }
+            this.validator.PCDataAdded(this.reader.getSystemID(),
+                                       this.reader.getLineNr());
+            Reader r = new ContentReader(this.reader,
+                                         this.entityResolver,
+                                         buffer.toString());
+            this.builder.addPCData(r, this.reader.getSystemID(),
+                                   this.reader.getLineNr());
+            r.close();
+         }
+      }
+   }
+
+
+   /**
+    * Processes an attribute of an element.
+    *
+    * @param attrNames contains the names of the attributes.
+    * @param attrValues contains the values of the attributes.
+    * @param attrTypes contains the types of the attributes.
+    *
+    * @throws java.lang.Exception
+    *     if something went wrong
+    */
+   protected void processAttribute(Vector attrNames,
+                                   Vector attrValues,
+                                   Vector attrTypes)
+      throws Exception
+   {
+      String key = XMLUtil.scanIdentifier(this.reader);
+      XMLUtil.skipWhitespace(this.reader, null);
+
+      if (! XMLUtil.read(this.reader, '&').equals("=")) {
+         XMLUtil.errorExpectedInput(reader.getSystemID(),
+                                    reader.getLineNr(),
+                                    "`='");
+      }
+
+      XMLUtil.skipWhitespace(this.reader, null);
+      String value = XMLUtil.scanString(this.reader, '&',
+                                        this.entityResolver);
+      attrNames.addElement(key);
+      attrValues.addElement(value);
+      attrTypes.addElement("CDATA");
+      this.validator.attributeAdded(key, value,
+                                    this.reader.getSystemID(),
+                                    this.reader.getLineNr());
+   }
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/StdXMLReader.java b/Sources/Java/net/n3/nanoxml/StdXMLReader.java
new file mode 100644
index 0000000..637ba7e
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/StdXMLReader.java
@@ -0,0 +1,626 @@
+/* StdXMLReader.java                                               NanoXML/Java
+ *
+ * $Revision: 1.4 $
+ * $Date: 2002/01/04 21:03:28 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.LineNumberReader;
+import java.io.PushbackReader;
+import java.io.PushbackInputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Stack;
+
+
+/**
+ * StdXMLReader reads the data to be parsed.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
+ */
+public class StdXMLReader
+   implements IXMLReader
+{
+
+   /**
+    * A stacked reader.
+    *
+    * @author Marc De Scheemaecker
+    * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
+    */
+   private class StackedReader
+   {
+   
+      PushbackReader pbReader;
+   
+      LineNumberReader lineReader;
+   
+      URL systemId;
+   
+      String publicId;
+   
+   }
+
+
+   /**
+    * The stack of readers.
+    */
+   private Stack readers;
+
+
+   /**
+    * The current push-back reader.
+    */
+   private StackedReader currentReader;
+
+
+   /**
+    * Creates a new reader using a string as input.
+    *
+    * @param str the string containing the XML data
+    */
+   public static IXMLReader stringReader(String str)
+   {
+      return new StdXMLReader(new StringReader(str));
+   }
+
+
+   /**
+    * Creates a new reader using a file as input.
+    *
+    * @param filename the name of the file containing the XML data
+    *
+    * @throws java.io.FileNotFoundException
+    *     if the file could not be found
+    * @throws java.io.IOException
+    *     if an I/O error occurred
+    */
+   public static IXMLReader fileReader(String filename)
+      throws FileNotFoundException,
+             IOException
+   {
+      StdXMLReader r = new StdXMLReader(new FileInputStream(filename));
+      r.setSystemID(filename);
+
+      for (int i = 0; i < r.readers.size(); i++) {
+         StackedReader sr = (StackedReader) r.readers.elementAt(i);
+         sr.systemId = r.currentReader.systemId;
+      }
+
+      return r;
+   }
+
+
+   /**
+    * Initializes the reader from a system and public ID.
+    *
+    * @param publicID the public ID which may be null.
+    * @param systemID the non-null system ID.
+    *
+    * @throws MalformedURLException
+    *     if the system ID does not contain a valid URL
+    * @throws FileNotFoundException
+    *     if the system ID refers to a local file which does not exist
+    * @throws IOException
+    *     if an error occurred opening the stream
+    */
+   public StdXMLReader(String publicID,
+                       String systemID)
+      throws MalformedURLException,
+             FileNotFoundException,
+             IOException
+   {
+      URL systemIDasURL = null;
+
+      try {
+         systemIDasURL = new URL(systemID);
+      } catch (MalformedURLException e) {
+         systemID = "file:" + systemID;
+
+         try {
+            systemIDasURL = new URL(systemID);
+         } catch (MalformedURLException e2) {
+            throw e;
+         }
+      }
+
+      this.currentReader = new StackedReader();
+      this.readers = new Stack();
+      Reader reader = this.openStream(publicID, systemIDasURL.toString());
+      this.currentReader.lineReader = new LineNumberReader(reader);
+      this.currentReader.pbReader
+         = new PushbackReader(this.currentReader.lineReader, 2);
+   }
+
+
+   /**
+    * Initializes the XML reader.
+    *
+    * @param reader the input for the XML data.
+    */
+   public StdXMLReader(Reader reader)
+   {
+      this.currentReader = new StackedReader();
+      this.readers = new Stack();
+      this.currentReader.lineReader = new LineNumberReader(reader);
+      this.currentReader.pbReader
+         = new PushbackReader(this.currentReader.lineReader, 2);
+      this.currentReader.publicId = "";
+
+      try {
+         this.currentReader.systemId = new URL("file:.");
+      } catch (MalformedURLException e) {
+         // never happens
+      }
+   }
+
+
+   /**
+    * Cleans up the object when it's destroyed.
+    */
+   protected void finalize()
+      throws Throwable
+   {
+      this.currentReader.lineReader = null;
+      this.currentReader.pbReader = null;
+      this.currentReader.systemId = null;
+      this.currentReader.publicId = null;
+      this.currentReader = null;
+      this.readers.clear();
+      super.finalize();
+   }
+
+
+   /**
+    * Scans the encoding from an <?xml...?> tag.
+    *
+    * @param str the first tag in the XML data.
+    *
+    * @return the encoding, or null if no encoding has been specified.
+    */
+   protected String getEncoding(String str)
+   {
+      if (! str.startsWith("<?xml")) {
+         return null;
+      }
+
+      int index = 5;
+
+      while (index < str.length()) {
+         StringBuffer key = new StringBuffer();
+
+         while ((index < str.length()) && (str.charAt(index) <= ' ')) {
+            index++;
+         }
+
+         while ((index < str.length())
+                && (str.charAt(index) >= 'a')
+                && (str.charAt(index) <= 'z')) {
+            key.append(str.charAt(index));
+            index++;
+         }
+
+         while ((index < str.length()) && (str.charAt(index) <= ' ')) {
+            index++;
+         }
+
+         if ((index >= str.length()) || (str.charAt(index) != '=')) {
+            break;
+         }
+
+         while ((index < str.length()) && (str.charAt(index) != '\'')
+                && (str.charAt(index) != '"')) {
+            index++;
+         }
+
+         if (index >= str.length()) {
+            break;
+         }
+
+         char delimiter = str.charAt(index);
+         index++;
+         int index2 = str.indexOf(delimiter, index);
+
+         if (index2 < 0) {
+            break;
+         }
+
+         if (key.toString().equals("encoding")) {
+            return str.substring(index, index2);
+         }
+
+         index = index2 + 1;
+      }
+
+      return null;
+   }
+
+
+   /**
+    * Converts a stream to a reader while detecting the encoding.
+    *
+    * @param stream    the input for the XML data.
+    * @param charsRead buffer where to put characters that have been read
+    *
+    * @throws java.io.IOException
+    *     if an I/O error occurred
+    */
+   protected Reader stream2reader(InputStream  stream,
+                                  StringBuffer charsRead)
+      throws IOException
+   {
+      PushbackInputStream pbstream = new PushbackInputStream(stream);
+      int b = pbstream.read();
+
+      switch (b) {
+         case 0x00:
+         case 0xFE:
+         case 0xFF:
+            pbstream.unread(b);
+            return new InputStreamReader(pbstream, "UTF-16");
+
+         case 0xEF:
+            for (int i = 0; i < 2; i++) {
+               pbstream.read();
+            }
+
+            return new InputStreamReader(pbstream, "UTF-8");
+
+         case 0x3C:
+            b = pbstream.read();
+            charsRead.append('<');
+
+            while ((b > 0) && (b != 0x3E)) {
+               charsRead.append((char) b);
+               b = pbstream.read();
+            }
+
+            if (b > 0) {
+               charsRead.append((char) b);
+            }
+
+            String encoding = this.getEncoding(charsRead.toString());
+
+            if (encoding == null) {
+               return new InputStreamReader(pbstream, "UTF-8");
+            }
+
+            charsRead.setLength(0);
+
+            try {
+               return new InputStreamReader(pbstream, encoding);
+            } catch (UnsupportedEncodingException e) {
+               return new InputStreamReader(pbstream, "UTF-8");
+            }
+
+            default:
+               charsRead.append((char) b);
+               return new InputStreamReader(pbstream, "UTF-8");
+      }
+   }
+
+
+   /**
+    * Initializes the XML reader.
+    *
+    * @param stream the input for the XML data.
+    *
+    * @throws java.io.IOException
+    *		if an I/O error occurred
+    */
+   public StdXMLReader(InputStream stream)
+      throws IOException
+   {
+      PushbackInputStream pbstream = new PushbackInputStream(stream);
+      StringBuffer charsRead = new StringBuffer();
+      Reader reader = this.stream2reader(stream, charsRead);
+      this.currentReader = new StackedReader();
+      this.readers = new Stack();
+      this.currentReader.lineReader = new LineNumberReader(reader);
+      this.currentReader.pbReader
+         = new PushbackReader(this.currentReader.lineReader, 2);
+      this.currentReader.publicId = "";
+
+      try {
+         this.currentReader.systemId = new URL("file:.");
+      } catch (MalformedURLException e) {
+         // never happens
+      }
+
+      this.startNewStream(new StringReader(charsRead.toString()));
+   }
+
+
+   /**
+    * Reads a character.
+    *
+    * @return the character
+    *
+    * @throws java.io.IOException
+    *		if no character could be read
+    */
+   public char read()
+      throws IOException
+   {
+      int ch = this.currentReader.pbReader.read();
+
+      while (ch < 0) {
+         if (this.readers.empty()) {
+            throw new IOException("Unexpected EOF");
+         }
+
+         this.currentReader.pbReader.close();
+         this.currentReader = (StackedReader) this.readers.pop();
+         ch = this.currentReader.pbReader.read();
+      }
+
+      return (char) ch;
+   }
+
+
+   /**
+    * Returns true if the current stream has no more characters left to be
+    * read.
+    *
+    * @throws java.io.IOException
+    *		if an I/O error occurred
+    */
+   public boolean atEOFOfCurrentStream()
+      throws IOException
+   {
+      int ch = this.currentReader.pbReader.read();
+
+      if (ch < 0) {
+         return true;
+      } else {
+         this.currentReader.pbReader.unread(ch);
+         return false;
+      }
+   }
+
+
+   /**
+    * Returns true if there are no more characters left to be read.
+    *
+    * @throws java.io.IOException
+    *		if an I/O error occurred
+    */
+   public boolean atEOF()
+      throws IOException
+   {
+      int ch = this.currentReader.pbReader.read();
+
+      while (ch < 0) {
+         if (this.readers.empty()) {
+            return true;
+         }
+
+         this.currentReader.pbReader.close();
+         this.currentReader = (StackedReader) this.readers.pop();
+         ch = this.currentReader.pbReader.read();
+      }
+
+      this.currentReader.pbReader.unread(ch);
+      return false;
+   }
+
+
+   /**
+    * Pushes the last character read back to the stream.
+    *
+    * @param ch the character to push back.
+    *
+    * @throws java.io.IOException
+    *     if an I/O error occurred
+    */
+   public void unread(char ch)
+      throws IOException
+   {
+      this.currentReader.pbReader.unread(ch);
+   }
+
+
+   /**
+    * Opens a stream from a public and system ID.
+    *
+    * @param publicID the public ID, which may be null
+    * @param systemID the system ID, which is never null
+    *
+    * @throws java.net.MalformedURLException
+    *     if the system ID does not contain a valid URL
+    * @throws java.io.FileNotFoundException
+    *     if the system ID refers to a local file which does not exist
+    * @throws java.io.IOException
+    *     if an error occurred opening the stream
+    */
+   public Reader openStream(String publicID,
+                            String systemID)
+      throws MalformedURLException,
+             FileNotFoundException,
+             IOException
+   {
+      URL url = new URL(this.currentReader.systemId, systemID);
+
+      if (url.getRef() != null) {
+         String ref = url.getRef();
+
+         if (url.getFile().length() > 0) {
+            url = new URL(url.getProtocol(), url.getHost(), url.getPort(),
+                          url.getFile());
+            url = new URL("jar:" + url + '!' + ref);
+         } else {
+            url = StdXMLReader.class.getResource(ref);
+         }
+      }
+
+      this.currentReader.publicId = publicID;
+      this.currentReader.systemId = url;
+      StringBuffer charsRead = new StringBuffer();
+      Reader reader = this.stream2reader(url.openStream(), charsRead);
+
+      if (charsRead.length() == 0) {
+         return reader;
+      }
+
+      String charsReadStr = charsRead.toString();
+      PushbackReader pbreader = new PushbackReader(reader,
+                                                   charsReadStr.length());
+
+      for (int i = charsReadStr.length() - 1; i >= 0; i--) {
+         pbreader.unread(charsReadStr.charAt(i));
+      }
+
+      return pbreader;
+   }
+
+
+   /**
+    * Starts a new stream from a Java reader. The new stream is used
+    * temporary to read data from. If that stream is exhausted, control
+    * returns to the parent stream.
+    *
+    * @param reader the non-null reader to read the new data from
+    */
+   public void startNewStream(Reader reader)
+   {
+      this.startNewStream(reader, false);
+   }
+
+
+   /**
+    * Starts a new stream from a Java reader. The new stream is used
+    * temporary to read data from. If that stream is exhausted, control
+    * returns to the parent stream.
+    *
+    * @param reader the non-null reader to read the new data from
+    * @param isInternalEntity true if the reader is produced by resolving
+    *                         an internal entity
+    */
+   public void startNewStream(Reader  reader,
+                              boolean isInternalEntity)
+   {
+      StackedReader oldReader = this.currentReader;
+      this.readers.push(this.currentReader);
+      this.currentReader = new StackedReader();
+
+      if (isInternalEntity) {
+         this.currentReader.lineReader = null;
+         this.currentReader.pbReader = new PushbackReader(reader, 2);
+      } else {
+         this.currentReader.lineReader = new LineNumberReader(reader);
+         this.currentReader.pbReader
+            = new PushbackReader(this.currentReader.lineReader, 2);
+      }
+
+      this.currentReader.systemId = oldReader.systemId;
+      this.currentReader.publicId = oldReader.publicId;
+   }
+
+
+   /**
+    * Returns the current "level" of the stream on the stack of streams.
+    */
+   public int getStreamLevel()
+   {
+      return this.readers.size();
+   }
+
+
+   /**
+    * Returns the line number of the data in the current stream.
+    */
+   public int getLineNr()
+   {
+      if (this.currentReader.lineReader == null) {
+         StackedReader sr = (StackedReader) this.readers.peek();
+
+         if (sr.lineReader == null) {
+            return 0;
+         } else {
+            return sr.lineReader.getLineNumber() + 1;
+         }
+      }
+
+      return this.currentReader.lineReader.getLineNumber() + 1;
+   }
+
+
+   /**
+    * Sets the system ID of the current stream.
+    *
+    * @param systemID the system ID
+    *
+    * @throws java.net.MalformedURLException
+    *     if the system ID does not contain a valid URL
+    */
+   public void setSystemID(String systemID)
+      throws MalformedURLException
+   {
+      this.currentReader.systemId = new URL(this.currentReader.systemId,
+                                            systemID);
+   }
+
+
+   /**
+    * Sets the public ID of the current stream.
+    *
+    * @param publicID the public ID
+    */
+   public void setPublicID(String publicID)
+   {
+      this.currentReader.publicId = publicID;
+   }
+
+
+   /**
+    * Returns the current system ID.
+    */
+   public String getSystemID()
+   {
+      return this.currentReader.systemId.toString();
+   }
+
+
+   /**
+      * Returns the current public ID.
+    */
+   public String getPublicID()
+   {
+      return this.currentReader.publicId;
+   }
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/ValidatorPlugin.java b/Sources/Java/net/n3/nanoxml/ValidatorPlugin.java
new file mode 100644
index 0000000..246bf94
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/ValidatorPlugin.java
@@ -0,0 +1,422 @@
+/* ValidatorPlugin.java                                            NanoXML/Java
+ *
+ * $Revision: 1.3 $
+ * $Date: 2002/01/04 21:03:29 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.Reader;
+import java.io.IOException;
+import java.util.Properties;
+
+
+/**
+ * ValidatorPlugin allows the application to insert additional validators into
+ * NanoXML.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.3 $
+ */
+public class ValidatorPlugin
+   implements IXMLValidator
+{
+
+   /**
+    * The delegate.
+    */
+   private IXMLValidator delegate;
+
+
+   /**
+    * Initializes the plugin.
+    */
+   public ValidatorPlugin()
+   {
+      this.delegate = null;
+   }
+
+
+   /**
+    * Cleans up the object when it's destroyed.
+    */
+   protected void finalize()
+      throws Throwable
+   {
+      this.delegate = null;
+      super.finalize();
+   }
+
+
+   /**
+    * Returns the delegate.
+    */
+   public IXMLValidator getDelegate()
+   {
+      return this.delegate;
+   }
+
+
+   /**
+    * Sets the delegate.
+    *
+    * @param delegate the delegate
+    */
+   public void setDelegate(IXMLValidator delegate)
+   {
+      this.delegate = delegate;
+   }
+
+
+   /**
+    * Sets the parameter entity resolver.
+    *
+    * @param resolver the entity resolver.
+    */
+   public void setParameterEntityResolver(IXMLEntityResolver resolver)
+   {
+      this.delegate.setParameterEntityResolver(resolver);
+   }
+
+
+   /**
+    * Returns the parameter entity resolver.
+    *
+    * @return the entity resolver.
+    */
+   public IXMLEntityResolver getParameterEntityResolver()
+   {
+      return this.delegate.getParameterEntityResolver();
+   }
+
+
+   /**
+    * Parses the DTD. The validator object is responsible for reading the
+    * full DTD.
+    *
+    * @param publicID       the public ID, which may be null.
+    * @param reader         the reader to read the DTD from.
+    * @param entityResolver the entity resolver.
+    * @param external       true if the DTD is external.
+    *
+    * @throws java.lang.Exception
+    *     if something went wrong.
+    */
+   public void parseDTD(String             publicID,
+                        IXMLReader         reader,
+                        IXMLEntityResolver entityResolver,
+                        boolean            external)
+      throws Exception
+   {
+      this.delegate.parseDTD(publicID, reader, entityResolver, external);
+   }
+
+
+   /**
+    * Indicates that an element has been started.
+    *
+    * @param name       the name of the element.
+    * @param systemId   the system ID of the XML data of the element.
+    * @param lineNr     the line number in the XML data of the element.
+    *
+    * @throws java.lang.Exception
+    *     if the element could not be validated.
+    */
+   public void elementStarted(String name,
+                              String systemId,
+                              int    lineNr)
+      throws Exception
+   {
+      this.delegate.elementStarted(name, systemId, lineNr);
+   }
+
+
+   /**
+    * Indicates that the current element has ended.
+    *
+    * @param name       the name of the element.
+    * @param systemId   the system ID of the XML data of the element.
+    * @param lineNr     the line number in the XML data of the element.
+    *
+    * @throws java.lang.Exception
+    *     if the element could not be validated.
+    */
+   public void elementEnded(String name,
+                            String systemId,
+                            int    lineNr)
+      throws Exception
+   {
+      this.delegate.elementEnded(name,systemId, lineNr);
+   }
+
+
+   /**
+    * Indicates that an attribute has been added to the current element.
+    *
+    * @param key        the name of the attribute.
+    * @param value      the value of the attribute.
+    * @param systemId   the system ID of the XML data of the element.
+    * @param lineNr     the line number in the XML data of the element.
+    *
+    * @throws java.lang.Exception
+    *     if the attribute could not be validated.
+    */
+   public void elementAttributesProcessed(String     name,
+                                          Properties extraAttributes,
+                                          String     systemId,
+                                          int        lineNr)
+      throws Exception
+   {
+      this.delegate.elementAttributesProcessed(name, extraAttributes,
+                                               systemId, lineNr);
+   }
+
+
+   /**
+    * This method is called when the attributes of an XML element have been
+    * processed.
+    * If there are attributes with a default value which have not been
+    * specified yet, they have to be put into <I>extraAttributes</I>.
+    *
+    * @param name            the name of the element.
+    * @param extraAttributes where to put extra attributes.
+    * @param systemId        the system ID of the XML data of the element.
+    * @param lineNr          the line number in the XML data of the element.
+    *
+    * @throws java.lang.Exception
+    *     if the element could not be validated.
+    */
+   public void attributeAdded(String key,
+                              String value,
+                              String systemId,
+                              int    lineNr)
+      throws Exception
+   {
+      this.delegate.attributeAdded(key, value, systemId, lineNr);
+   }
+
+
+   /**
+    * Indicates that a new #PCDATA element has been encountered.
+    *
+    * @param systemId the system ID of the XML data of the element.
+    * @param lineNr   the line number in the XML data of the element.
+    *
+    * @throws java.lang.Exception
+    *     if the element could not be validated.
+    */
+   public void PCDataAdded(String systemId,
+                           int    lineNr)
+      throws Exception
+   {
+      this.delegate.PCDataAdded(systemId, lineNr);
+   }
+
+
+   /**
+    * Throws an XMLValidationException to indicate that an element is missing.
+    *
+    * @param systemID           the system ID of the XML data of the element
+    * @param lineNr             the line number in the XML data of the element
+    * @param parentElementName  the name of the parent element
+    * @param missingElementName the name of the missing element
+    *
+    * @throws net.n3.nanoxml.XMLValidationException
+    *      of course :-)
+    */
+   public void missingElement(String systemID,
+                              int    lineNr,
+                              String parentElementName,
+                              String missingElementName)
+      throws XMLValidationException
+   {
+      XMLUtil.errorMissingElement(systemID, lineNr, parentElementName,
+                                  missingElementName);
+   }
+
+
+   /**
+    * Throws an XMLValidationException to indicate that an element is
+    * unexpected.
+    *
+    * @param systemID              the system ID of the XML data of the
+    *                              element
+    * @param lineNr                the line number in the XML data of the
+    *                              element
+    * @param parentElementName     the name of the parent element
+    * @param unexpectedElementName the name of the missing element
+    *
+    * @throws net.n3.nanoxml.XMLValidationException
+    *      of course :-)
+    */
+   public void unexpectedElement(String systemID,
+                                 int    lineNr,
+                                 String parentElementName,
+                                 String unexpectedElementName)
+      throws XMLValidationException
+   {
+      XMLUtil.errorUnexpectedElement(systemID, lineNr, parentElementName,
+                                     unexpectedElementName);
+   }
+
+
+   /**
+    * Throws an XMLValidationException to indicate that an attribute is
+    * missing.
+    *
+    * @param systemID      the system ID of the XML data of the element
+    * @param lineNr        the line number in the XML data of the element
+    * @param elementName   the name of the element
+    * @param attributeName the name of the missing attribute
+    *
+    * @throws net.n3.nanoxml.XMLValidationException
+    *      of course :-)
+    */
+   public void missingAttribute(String systemID,
+                                int    lineNr,
+                                String elementName,
+                                String attributeName)
+      throws XMLValidationException
+   {
+      XMLUtil.errorMissingAttribute(systemID, lineNr, elementName,
+                                    attributeName);
+   }
+
+
+   /**
+    * Throws an XMLValidationException to indicate that an attribute is
+    * unexpected.
+    *
+    * @param systemID      the system ID of the XML data of the element
+    * @param lineNr        the line number in the XML data of the element
+    * @param elementName   the name of the element
+    * @param attributeName the name of the unexpected attribute
+    *
+    * @throws net.n3.nanoxml.XMLValidationException
+    *      of course :-)
+    */
+   public void unexpectedAttribute(String systemID,
+                                   int    lineNr,
+                                   String elementName,
+                                   String attributeName)
+      throws XMLValidationException
+   {
+      XMLUtil.errorUnexpectedAttribute(systemID, lineNr, elementName,
+                                       attributeName);
+   }
+
+
+   /**
+    * Throws an XMLValidationException to indicate that an attribute has an
+    * invalid value.
+    *
+    * @param systemID       the system ID of the XML data of the element
+    * @param lineNr         the line number in the XML data of the element
+    * @param elementName    the name of the element
+    * @param attributeName  the name of the attribute
+    * @param attributeValue the value of the attribute
+    *
+    * @throws net.n3.nanoxml.XMLValidationException
+    *      of course :-)
+    */
+   public void invalidAttributeValue(String systemID,
+                                     int    lineNr,
+                                     String elementName,
+                                     String attributeName,
+                                     String attributeValue)
+      throws XMLValidationException
+   {
+      XMLUtil.errorInvalidAttributeValue(systemID, lineNr, elementName,
+                                         attributeName, attributeValue);
+   }
+
+
+   /**
+    * Throws an XMLValidationException to indicate that a #PCDATA element was
+    * missing.
+    *
+    * @param systemID          the system ID of the XML data of the element
+    * @param lineNr            the line number in the XML data of the element
+    * @param parentElementName the name of the parent element
+    *
+    * @throws net.n3.nanoxml.XMLValidationException
+    *      of course :-)
+    */
+   public void missingPCData(String systemID,
+                             int    lineNr,
+                             String parentElementName)
+      throws XMLValidationException
+   {
+      XMLUtil.errorMissingPCData(systemID, lineNr, parentElementName);
+   }
+
+
+   /**
+    * Throws an XMLValidationException to indicate that a #PCDATA element was
+    * unexpected.
+    *
+    * @param systemID          the system ID of the XML data of the element
+    * @param lineNr            the line number in the XML data of the element
+    * @param parentElementName the name of the parent element
+    *
+    * @throws net.n3.nanoxml.XMLValidationException
+    *      of course :-)
+    */
+   public void unexpectedPCData(String systemID,
+                                int    lineNr,
+                                String parentElementName)
+      throws XMLValidationException
+   {
+      XMLUtil.errorUnexpectedPCData(systemID, lineNr, parentElementName);
+   }
+
+
+   /**
+    * Throws an XMLValidationException.
+    *
+    * @param systemID       the system ID of the XML data of the element
+    * @param lineNr         the line number in the XML data of the element
+    * @param message        the error message
+    * @param elementName    the name of the element (may be null)
+    * @param attributeName  the name of the attribute (may be null)
+    * @param attributeValue the value of the attribute (may be null)
+    *
+    * @throws net.n3.nanoxml.XMLValidationException
+    *      of course :-)
+    */
+   public void validationError(String systemID,
+                               int    lineNr,
+                               String message,
+                               String elementName,
+                               String attributeName,
+                               String attributeValue)
+      throws XMLValidationException
+   {
+      XMLUtil.validationError(systemID, lineNr, message, elementName,
+                              attributeName, attributeValue);
+   }
+   
+}
diff --git a/Sources/Java/net/n3/nanoxml/XMLAttribute.java b/Sources/Java/net/n3/nanoxml/XMLAttribute.java
new file mode 100644
index 0000000..bd49ecb
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/XMLAttribute.java
@@ -0,0 +1,153 @@
+/* XMLAttribute.java                                               NanoXML/Java
+ *
+ * $Revision: 1.4 $
+ * $Date: 2002/01/04 21:03:29 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+/**
+ * An attribute in an XML element. This is an internal class.
+ *
+ * @see net.n3.nanoxml.XMLElement
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
+ */
+class XMLAttribute
+{
+
+   /**
+    * The full name of the attribute.
+    */
+   private String fullName;
+
+
+   /**
+    * The short name of the attribute.
+    */
+   private String name;
+
+
+   /**
+    * The namespace URI of the attribute.
+    */
+   private String namespace;
+
+
+   /**
+    * The value of the attribute.
+    */
+   private String value;
+
+
+   /**
+    * The type of the attribute.
+    */
+   private String type;
+
+
+   /**
+    * Creates a new attribute.
+    *
+    * @param fullName  the non-null full name
+    * @param name      the non-null short name
+    * @param namespace the namespace URI, which may be null
+    * @param value     the value of the attribute
+    * @param type      the type of the attribute
+    */
+   XMLAttribute(String fullName,
+                String name,
+                String namespace,
+                String value,
+                String type)
+   {
+      this.fullName = fullName;
+      this.name = name;
+      this.namespace = namespace;
+      this.value = value;
+      this.type = type;
+   }
+
+
+   /**
+    * Returns the full name of the attribute.
+    */
+   String getFullName()
+   {
+      return this.fullName;
+   }
+
+
+   /**
+    * Returns the short name of the attribute.
+    */
+   String getName()
+   {
+      return this.name;
+   }
+
+
+   /**
+    * Returns the namespace of the attribute.
+    */
+   String getNamespace()
+   {
+      return this.namespace;
+   }
+
+
+   /**
+    * Returns the value of the attribute.
+    */
+   String getValue()
+   {
+      return this.value;
+   }
+
+
+   /**
+    * Sets the value of the attribute.
+    *
+    * @param value the new value.
+    */
+   void setValue(String value)
+   {
+      this.value = value;
+   }
+
+
+   /**
+    * Returns the type of the attribute.
+    *
+    * @param type the new type.
+    */
+   String getType()
+   {
+      return this.type;
+   }
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/XMLElement.java b/Sources/Java/net/n3/nanoxml/XMLElement.java
new file mode 100644
index 0000000..4686f53
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/XMLElement.java
@@ -0,0 +1,1042 @@
+/* XMLElement.java                                                 NanoXML/Java
+ *
+ * $Revision: 1.5 $
+ * $Date: 2002/02/06 18:50:12 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.Serializable;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Vector;
+
+
+/**
+ * XMLElement is an XML element. The standard NanoXML builder generates a
+ * tree of such elements.
+ *
+ * @see net.n3.nanoxml.StdXMLBuilder
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.5 $
+ */
+public class XMLElement implements IXMLElement, Serializable {
+
+    /**
+     * Necessary for serialization.
+     */
+    static final long serialVersionUID = -2383376380548624920L;
+
+
+    /**
+     * No line number defined.
+     */
+    public static final int NO_LINE = -1;
+
+
+    /**
+     * The parent element.
+     */
+    private IXMLElement parent;
+
+
+    /**
+     * The attributes of the element.
+     */
+    private Vector attributes;
+
+
+    /**
+     * The child elements.
+     */
+    private Vector children;
+
+
+    /**
+     * The name of the element.
+     */
+    private String name;
+
+
+    /**
+     * The full name of the element.
+     */
+    private String fullName;
+
+
+    /**
+     * The namespace URI.
+     */
+    private String namespace;
+
+
+    /**
+     * The content of the element.
+     */
+    private String content;
+
+
+    /**
+     * The system ID of the source data where this element is located.
+     */
+    private String systemID;
+
+
+    /**
+     * The line in the source data where this element starts.
+     */
+    private int lineNr;
+
+
+    /**
+     * Creates an empty element to be used for #PCDATA content.
+     */
+    public XMLElement() {
+        this(null, null, null, NO_LINE);
+    }
+
+
+    /**
+     * Creates an empty element.
+     *
+     * @param fullName the name of the element.
+     */
+    public XMLElement(String fullName) {
+        this(fullName, null, null, NO_LINE);
+    }
+
+
+    /**
+     * Creates an empty element.
+     *
+     * @param fullName the name of the element.
+     * @param systemID the system ID of the XML data where the element starts.
+     * @param lineNr   the line in the XML data where the element starts.
+     */
+    public XMLElement(String fullName,
+                      String systemID,
+                      int    lineNr) {
+        this(fullName, null, systemID, lineNr);
+    }
+
+
+    /**
+     * Creates an empty element.
+     *
+     * @param fullName  the full name of the element
+     * @param namespace the namespace URI.
+     */
+    public XMLElement(String fullName,
+                      String namespace) {
+        this(fullName, namespace, null, NO_LINE);
+    }
+
+
+    /**
+     * Creates an empty element.
+     *
+     * @param fullName  the full name of the element
+     * @param namespace the namespace URI.
+     * @param systemID  the system ID of the XML data where the element starts.
+     * @param lineNr    the line in the XML data where the element starts.
+     */
+    public XMLElement(String fullName,
+                      String namespace,
+                      String systemID,
+                      int    lineNr) {
+        this.attributes = new Vector();
+        this.children = new Vector(8);
+        this.fullName = fullName;
+        if (namespace == null) {
+            this.name = fullName;
+        } else {
+            int index = fullName.indexOf(':');
+            if (index >= 0) {
+                this.name = fullName.substring(index + 1);
+            } else {
+                this.name = fullName;
+            }
+        }
+        this.namespace = namespace;
+        this.content = null;
+        this.lineNr = lineNr;
+        this.systemID = systemID;
+        this.parent = null;
+    }
+
+
+    /**
+     * Creates an element to be used for #PCDATA content.
+     */
+    public IXMLElement createPCDataElement() {
+        return new XMLElement();
+    }
+
+
+    /**
+     * Creates an empty element.
+     *
+     * @param fullName the name of the element.
+     */
+    public IXMLElement createElement(String fullName) {
+        return new XMLElement(fullName);
+    }
+
+
+    /**
+     * Creates an empty element.
+     *
+     * @param fullName the name of the element.
+     * @param systemID the system ID of the XML data where the element starts.
+     * @param lineNr   the line in the XML data where the element starts.
+     */
+    public IXMLElement createElement(String fullName,
+                                     String systemID,
+                                     int    lineNr) {
+        return new XMLElement(fullName, systemID, lineNr);
+    }
+
+
+    /**
+     * Creates an empty element.
+     *
+     * @param fullName  the full name of the element
+     * @param namespace the namespace URI.
+     */
+    public IXMLElement createElement(String fullName,
+                                     String namespace) {
+        return new XMLElement(fullName, namespace);
+    }
+
+
+    /**
+     * Creates an empty element.
+     *
+     * @param fullName  the full name of the element
+     * @param namespace the namespace URI.
+     * @param systemID  the system ID of the XML data where the element starts.
+     * @param lineNr    the line in the XML data where the element starts.
+     */
+    public IXMLElement createElement(String fullName,
+                                     String namespace,
+                                     String systemID,
+                                     int    lineNr) {
+        return new XMLElement(fullName, namespace, systemID, lineNr);
+    }
+
+
+    /**
+     * Cleans up the object when it's destroyed.
+     */
+    protected void finalize() throws Throwable {
+        this.attributes.clear();
+        this.attributes = null;
+        this.children = null;
+        this.fullName = null;
+        this.name = null;
+        this.namespace = null;
+        this.content = null;
+        this.systemID = null;
+        this.parent = null;
+        super.finalize();
+    }
+
+
+    /**
+     * Returns the parent element. This method returns null for the root
+     * element.
+     */
+    public IXMLElement getParent() {
+        return this.parent;
+    }
+
+
+    /**
+     * Returns the full name (i.e. the name including an eventual namespace
+     * prefix) of the element.
+     *
+     * @return the name, or null if the element only contains #PCDATA.
+     */
+    public String getFullName() {
+        return this.fullName;
+    }
+
+
+    /**
+     * Returns the name of the element.
+     *
+     * @return the name, or null if the element only contains #PCDATA.
+     */
+    public String getName() {
+        return this.name;
+    }
+
+
+    /**
+     * Returns the namespace of the element.
+     *
+     * @return the namespace, or null if no namespace is associated with the
+     *         element.
+     */
+    public String getNamespace() {
+        return this.namespace;
+    }
+
+
+    /**
+     * Sets the full name. This method also sets the short name and clears the
+     * namespace URI.
+     *
+     * @param name the non-null name.
+     */
+    public void setName(String name) {
+        this.name = name;
+        this.fullName = name;
+        this.namespace = null;
+    }
+
+
+    /**
+     * Sets the name.
+     *
+     * @param fullName  the non-null full name.
+     * @param namespace the namespace URI, which may be null.
+     */
+    public void setName(String fullName,
+                        String namespace) {
+        int index = fullName.indexOf(':');
+        if ((namespace == null) || (index < 0)) {
+            this.name = fullName;
+        } else {
+            this.name = fullName.substring(index + 1);
+        }
+        this.fullName = fullName;
+        this.namespace = namespace;
+    }
+
+
+    /**
+     * Adds a child element.
+     *
+     * @param child the non-null child to add.
+     */
+    public void addChild(IXMLElement child) {
+        if (child == null) {
+            throw new IllegalArgumentException("child must not be null");
+        }
+        if ((child.getName() == null) && (! this.children.isEmpty())) {
+            IXMLElement lastChild = (IXMLElement) this.children.lastElement();
+
+            if (lastChild.getName() == null) {
+                lastChild.setContent(lastChild.getContent()
+                                     + child.getContent());
+                return;
+            }
+        }
+        ((XMLElement)child).parent = this;
+        this.children.addElement(child);
+    }
+
+
+    /**
+     * Inserts a child element.
+     *
+     * @param child the non-null child to add.
+     * @param index where to put the child.
+     */
+    public void insertChild(IXMLElement child,
+                            int         index) {
+        if (child == null) {
+            throw new IllegalArgumentException("child must not be null");
+        }
+        if ((child.getName() == null) && (! this.children.isEmpty())) {
+            IXMLElement lastChild = (IXMLElement) this.children.lastElement();
+            if (lastChild.getName() == null) {
+                lastChild.setContent(lastChild.getContent()
+                                     + child.getContent());
+                return;
+            }
+        }
+        ((XMLElement) child).parent = this;
+        this.children.insertElementAt(child, index);
+    }
+
+
+    /**
+     * Removes a child element.
+     *
+     * @param child the non-null child to remove.
+     */
+    public void removeChild(IXMLElement child) {
+        if (child == null) {
+            throw new IllegalArgumentException("child must not be null");
+        }
+        this.children.removeElement(child);
+    }
+
+
+    /**
+     * Removes the child located at a certain index.
+     *
+     * @param index the index of the child, where the first child has index 0.
+     */
+    public void removeChildAtIndex(int index) {
+        this.children.removeElementAt(index);
+    }
+
+
+    /**
+     * Returns an enumeration of all child elements.
+     *
+     * @return the non-null enumeration
+     */
+    public Enumeration enumerateChildren() {
+        return this.children.elements();
+    }
+
+
+    /**
+     * Returns whether the element is a leaf element.
+     *
+     * @return true if the element has no children.
+     */
+    public boolean isLeaf() {
+        return this.children.isEmpty();
+    }
+
+
+    /**
+     * Returns whether the element has children.
+     *
+     * @return true if the element has children.
+     */
+    public boolean hasChildren() {
+        return (! this.children.isEmpty());
+    }
+
+
+    /**
+     * Returns the number of children.
+     *
+     * @return the count.
+     */
+    public int getChildrenCount() {
+        return this.children.size();
+    }
+
+
+    /**
+     * Returns a vector containing all the child elements.
+     *
+     * @return the vector.
+     */
+    public Vector getChildren() {
+        return this.children;
+    }
+
+
+    /**
+     * Returns the child at a specific index.
+     *
+     * @param index the index of the child
+     *
+     * @return the non-null child
+     *
+     * @throws java.lang.ArrayIndexOutOfBoundsException
+     *		if the index is out of bounds.
+     */
+    public IXMLElement getChildAtIndex(int index)
+    throws ArrayIndexOutOfBoundsException {
+        return (IXMLElement) this.children.elementAt(index);
+    }
+    
+
+    /**
+     * Searches a child element.
+     *
+     * @param name the full name of the child to search for.
+     *
+     * @return the child element, or null if no such child was found.
+     */
+    public IXMLElement getFirstChildNamed(String name) {
+        Enumeration enum = this.children.elements();
+        while (enum.hasMoreElements()) {
+            IXMLElement child = (IXMLElement) enum.nextElement();
+            String childName = child.getFullName();
+            if ((childName != null) && childName.equals(name)) {
+                return child;
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * Searches a child element.
+     *
+     * @param name      the name of the child to search for.
+     * @param namespace the namespace, which may be null.
+     *
+     * @return the child element, or null if no such child was found.
+     */
+    public IXMLElement getFirstChildNamed(String name,
+                                          String namespace) {
+        Enumeration enum = this.children.elements();
+        while (enum.hasMoreElements()) {
+            IXMLElement child = (IXMLElement) enum.nextElement();
+            String str = child.getName();
+            boolean found = (str != null) && (str.equals(name));
+            str = child.getNamespace();
+            if (str == null) {
+                found &= (name == null);
+            } else {
+                found &= str.equals(namespace);
+            }
+            if (found) {
+                return child;
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * Returns a vector of all child elements named <I>name</I>.
+     *
+     * @param name the full name of the children to search for.
+     *
+     * @return the non-null vector of child elements.
+     */
+    public Vector getChildrenNamed(String name) {
+        Vector result = new Vector(this.children.size());
+        Enumeration enum = this.children.elements();
+        while (enum.hasMoreElements()) {
+            IXMLElement child = (IXMLElement) enum.nextElement();
+            String childName = child.getFullName();
+            if ((childName != null) && childName.equals(name)) {
+                result.addElement(child);
+            }
+        }
+        return result;
+    }
+
+
+    /**
+     * Returns a vector of all child elements named <I>name</I>.
+     *
+     * @param name      the name of the children to search for.
+     * @param namespace the namespace, which may be null.
+     *
+     * @return the non-null vector of child elements.
+     */
+    public Vector getChildrenNamed(String name,
+                                   String namespace) {
+        Vector result = new Vector(this.children.size());
+        Enumeration enum = this.children.elements();
+        while (enum.hasMoreElements()) {
+            IXMLElement child = (IXMLElement) enum.nextElement();
+            String str = child.getName();
+            boolean found = (str != null) && (str.equals(name));
+            str = child.getNamespace();
+            if (str == null) {
+                found &= (name == null);
+            } else {
+                found &= str.equals(namespace);
+            }
+
+            if (found) {
+                result.addElement(child);
+            }
+        }
+        return result;
+    }
+
+
+    /**
+     * Searches an attribute.
+     *
+     * @param fullName the non-null full name of the attribute.
+     *
+     * @return the attribute, or null if the attribute does not exist.
+     */
+    private XMLAttribute findAttribute(String fullName) {
+        Enumeration enum = this.attributes.elements();
+        while (enum.hasMoreElements()) {
+            XMLAttribute attr = (XMLAttribute) enum.nextElement();
+            if (attr.getFullName().equals(fullName)) {
+                return attr;
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * Searches an attribute.
+     *
+     * @param name the non-null short name of the attribute.
+     * @param namespace the name space, which may be null.
+     *
+     * @return the attribute, or null if the attribute does not exist.
+     */
+    private XMLAttribute findAttribute(String name,
+                                       String namespace) {
+        Enumeration enum = this.attributes.elements();
+        while (enum.hasMoreElements()) {
+            XMLAttribute attr = (XMLAttribute) enum.nextElement();
+            boolean found = attr.getName().equals(name);
+            if (namespace == null) {
+                found &= (attr.getNamespace() == null);
+            } else {
+                found &= namespace.equals(attr.getNamespace());
+            }
+
+            if (found) {
+                return attr;
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * Returns the number of attributes.
+     */
+    public int getAttributeCount() {
+        return this.attributes.size();
+    }
+
+
+    /**
+     * @deprecated As of NanoXML/Java 2.1, replaced by
+     *             {@link #getAttribute(java.lang.String,java.lang.String)}
+     * Returns the value of an attribute.
+     *
+     * @param name the non-null name of the attribute.
+     *
+     * @return the value, or null if the attribute does not exist.
+     */
+    public String getAttribute(String name) {
+        return this.getAttribute(name, null);
+    }
+
+
+    /**
+     * Returns the value of an attribute.
+     *
+     * @param name the non-null full name of the attribute.
+     * @param defaultValue the default value of the attribute.
+     *
+     * @return the value, or defaultValue if the attribute does not exist.
+     */
+    public String getAttribute(String name,
+                               String defaultValue) {
+        XMLAttribute attr = this.findAttribute(name);
+        if (attr == null) {
+            return defaultValue;
+        } else {
+            return attr.getValue();
+        }
+    }
+
+
+    /**
+     * Returns the value of an attribute.
+     *
+     * @param name the non-null name of the attribute.
+     * @param namespace the namespace URI, which may be null.
+     * @param defaultValue the default value of the attribute.
+     *
+     * @return the value, or defaultValue if the attribute does not exist.
+     */
+    public String getAttribute(String name,
+                               String namespace,
+                               String defaultValue) {
+        XMLAttribute attr = this.findAttribute(name, namespace);
+        if (attr == null) {
+            return defaultValue;
+        } else {
+            return attr.getValue();
+        }
+    }
+
+
+    /**
+     * Returns the value of an attribute.
+     *
+     * @param name the non-null full name of the attribute.
+     * @param defaultValue the default value of the attribute.
+     *
+     * @return the value, or defaultValue if the attribute does not exist.
+     */
+    public int getAttribute(String name,
+                            int    defaultValue) {
+        String value = this.getAttribute(name, Integer.toString(defaultValue));
+        return Integer.parseInt(value);
+    }
+
+
+    /**
+     * Returns the value of an attribute.
+     *
+     * @param name the non-null name of the attribute.
+     * @param namespace the namespace URI, which may be null.
+     * @param defaultValue the default value of the attribute.
+     *
+     * @return the value, or defaultValue if the attribute does not exist.
+     */
+    public int getAttribute(String name,
+                            String namespace,
+                            int    defaultValue) {
+        String value = this.getAttribute(name, namespace,
+                                         Integer.toString(defaultValue));
+        return Integer.parseInt(value);
+    }
+
+
+    /**
+     * Returns the type of an attribute.
+     *
+     * @param name the non-null full name of the attribute.
+     *
+     * @return the type, or null if the attribute does not exist.
+     */
+    public String getAttributeType(String name) {
+        XMLAttribute attr = this.findAttribute(name);
+        if (attr == null) {
+            return null;
+        } else {
+            return attr.getType();
+        }
+    }
+
+
+    /**
+     * Returns the namespace of an attribute.
+     *
+     * @param name the non-null full name of the attribute.
+     *
+     * @return the namespace, or null if there is none associated.
+     */
+    public String getAttributeNamespace(String name) {
+        XMLAttribute attr = this.findAttribute(name);
+        if (attr == null) {
+            return null;
+        } else {
+            return attr.getNamespace();
+        }
+    }
+
+
+    /**
+     * Returns the type of an attribute.
+     *
+     * @param name the non-null name of the attribute.
+     * @param namespace the namespace URI, which may be null.
+     *
+     * @return the type, or null if the attribute does not exist.
+     */
+    public String getAttributeType(String name,
+                                   String namespace) {
+        XMLAttribute attr = this.findAttribute(name, namespace);
+        if (attr == null) {
+            return null;
+        } else {
+            return attr.getType();
+        }
+    }
+
+
+    /**
+     * Sets an attribute.
+     *
+     * @param name the non-null full name of the attribute.
+     * @param value the non-null value of the attribute.
+     */
+    public void setAttribute(String name,
+                             String value) {
+        XMLAttribute attr = this.findAttribute(name);
+        if (attr == null) {
+            attr = new XMLAttribute(name, name, null, value, "CDATA");
+            this.attributes.addElement(attr);
+        } else {
+            attr.setValue(value);
+        }
+    }
+
+
+    /**
+     * Sets an attribute.
+     *
+     * @param fullName the non-null full name of the attribute.
+     * @param namespace the namespace URI of the attribute, which may be null.
+     * @param value the non-null value of the attribute.
+     */
+    public void setAttribute(String fullName,
+                             String namespace,
+                             String value) {
+        int index = fullName.indexOf(':');
+        String name = fullName.substring(index + 1);
+        XMLAttribute attr = this.findAttribute(name, namespace);
+        if (attr == null) {
+            attr = new XMLAttribute(fullName, name, namespace, value, "CDATA");
+            this.attributes.addElement(attr);
+        } else {
+            attr.setValue(value);
+        }
+    }
+
+
+    /**
+     * Removes an attribute.
+     *
+     * @param name the non-null name of the attribute.
+     */
+    public void removeAttribute(String name) {
+        for (int i = 0; i < this.attributes.size(); i++) {
+            XMLAttribute attr = (XMLAttribute) this.attributes.elementAt(i);
+            if (attr.getFullName().equals(name)) {
+                this.attributes.removeElementAt(i);
+                return;
+            }
+        }
+    }
+
+
+    /**
+     * Removes an attribute.
+     *
+     * @param name the non-null name of the attribute.
+     * @param namespace the namespace URI of the attribute, which may be null.
+     */
+    public void removeAttribute(String name,
+                                String namespace) {
+        for (int i = 0; i < this.attributes.size(); i++) {
+            XMLAttribute attr = (XMLAttribute) this.attributes.elementAt(i);
+            boolean found = attr.getName().equals(name);
+            if (namespace == null) {
+                found &= (attr.getNamespace() == null);
+            } else {
+                found &= attr.getNamespace().equals(namespace);
+            }
+
+            if (found) {
+                this.attributes.removeElementAt(i);
+                return;
+            }
+        }
+    }
+
+
+    /**
+     * Returns an enumeration of all attribute names.
+     *
+     * @return the non-null enumeration.
+     */
+    public Enumeration enumerateAttributeNames() {
+        Vector result = new Vector();
+        Enumeration enum = this.attributes.elements();
+        while (enum.hasMoreElements()) {
+            XMLAttribute attr = (XMLAttribute) enum.nextElement();
+            result.addElement(attr.getFullName());
+        }
+        return result.elements();
+    }
+
+
+    /**
+     * Returns whether an attribute exists.
+     *
+     * @return true if the attribute exists.
+     */
+    public boolean hasAttribute(String name) {
+        return this.findAttribute(name) != null;
+    }
+
+
+    /**
+     * Returns whether an attribute exists.
+     *
+     * @return true if the attribute exists.
+     */
+    public boolean hasAttribute(String name,
+                                String namespace) {
+        return this.findAttribute(name, namespace) != null;
+    }
+
+
+    /**
+     * Returns all attributes as a Properties object.
+     *
+     * @return the non-null set.
+     */
+    public Properties getAttributes() {
+        Properties result = new Properties();
+        Enumeration enum = this.attributes.elements();
+        while (enum.hasMoreElements()) {
+            XMLAttribute attr = (XMLAttribute) enum.nextElement();
+            result.put(attr.getFullName(), attr.getValue());
+        }
+        return result;
+    }
+
+
+    /**
+     * Returns all attributes in a specific namespace as a Properties object.
+     *
+     * @param namespace the namespace URI of the attributes, which may be null.
+     *
+     * @return the non-null set.
+     */
+    public Properties getAttributesInNamespace(String namespace) {
+        Properties result = new Properties();
+        Enumeration enum = this.attributes.elements();
+        while (enum.hasMoreElements()) {
+            XMLAttribute attr = (XMLAttribute) enum.nextElement();
+            if (namespace == null) {
+                if (attr.getNamespace() == null) {
+                    result.put(attr.getName(), attr.getValue());
+                }
+            } else {
+                if (namespace.equals(attr.getNamespace())) {
+                    result.put(attr.getName(), attr.getValue());
+                }
+            }
+        }
+        return result;
+    }
+
+
+    /**
+     * Returns the system ID of the data where the element started.
+     *
+     * @return the system ID, or null if unknown.
+     *
+     * @see #getLineNr
+     */
+    public String getSystemID() {
+        return this.systemID;
+    }
+
+
+    /**
+     * Returns the line number in the data where the element started.
+     *
+     * @return the line number, or NO_LINE if unknown.
+     *
+     * @see #NO_LINE
+     * @see #getSystemID
+     */
+    public int getLineNr() {
+        return this.lineNr;
+    }
+
+
+    /**
+     * Return the #PCDATA content of the element. If the element has a
+     * combination of #PCDATA content and child elements, the #PCDATA
+     * sections can be retrieved as unnamed child objects. In this case,
+     * this method returns null.
+     *
+     * @return the content.
+     */
+    public String getContent() {
+        return this.content;
+    }
+
+
+    /**
+     * Sets the #PCDATA content. It is an error to call this method with a
+     * non-null value if there are child objects.
+     *
+     * @param content the (possibly null) content.
+     */
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+
+    /**
+     * Returns true if the element equals another element.
+     *
+     * @param rawElement the element to compare to
+     */
+    public boolean equals(Object rawElement) {
+        try {
+            return this.equalsXMLElement((IXMLElement) rawElement);
+        } catch (ClassCastException e) {
+            return false;
+        }
+    }
+
+
+    /**
+     * Returns true if the element equals another element.
+     *
+     * @param rawElement the element to compare to
+     */
+    public boolean equalsXMLElement(IXMLElement elt) {
+        if (! this.name.equals(elt.getName())) {
+            return false;
+        }
+        if (this.attributes.size() != elt.getAttributeCount()) {
+            return false;
+        }
+        Enumeration enum = this.attributes.elements();
+        while (enum.hasMoreElements()) {
+            XMLAttribute attr = (XMLAttribute) enum.nextElement();
+            if (! elt.hasAttribute(attr.getName(), attr.getNamespace())) {
+                return false;
+            }
+            String value = elt.getAttribute(attr.getName(),
+                                            attr.getNamespace(),
+                                            null);
+            if (! attr.getValue().equals(value)) {
+                return false;
+            }
+            String type = elt.getAttributeType(attr.getName(),
+                                               attr.getNamespace());
+            if (! attr.getType().equals(type)) {
+                return false;
+            }
+        }
+        if (this.children.size() != elt.getChildrenCount()) {
+            return false;
+        }
+        for (int i = 0; i < this.children.size(); i++) {
+            IXMLElement child1 = this.getChildAtIndex(i);
+            IXMLElement child2 = elt.getChildAtIndex(i);
+
+            if (! child1.equalsXMLElement(child2)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/XMLEntityResolver.java b/Sources/Java/net/n3/nanoxml/XMLEntityResolver.java
new file mode 100644
index 0000000..4481e7f
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/XMLEntityResolver.java
@@ -0,0 +1,174 @@
+/* XMLEntityResolver.java                                          NanoXML/Java
+ *
+ * $Revision: 1.4 $
+ * $Date: 2002/01/04 21:03:29 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.util.Hashtable;
+import java.io.Reader;
+import java.io.StringReader;
+
+
+/**
+ * An XMLEntityResolver resolves entities.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
+ */
+public class XMLEntityResolver
+   implements IXMLEntityResolver
+{
+
+   /**
+    * The entities.
+    */
+   private Hashtable entities;
+
+
+   /**
+    * Initializes the resolver.
+    */
+   public XMLEntityResolver()
+   {
+      this.entities = new Hashtable();
+      this.entities.put("amp", "&");
+      this.entities.put("quot", """);
+      this.entities.put("apos", "'");
+      this.entities.put("lt", "<");
+      this.entities.put("gt", ">");
+   }
+
+
+   /**
+    * Cleans up the object when it's destroyed.
+    */
+   protected void finalize()
+      throws Throwable
+   {
+      this.entities.clear();
+      this.entities = null;
+      super.finalize();
+   }
+
+
+   /**
+    * Adds an internal entity.
+    *
+    * @param name the name of the entity.
+    * @param value the value of the entity.
+    */
+   public void addInternalEntity(String name,
+                                 String value)
+   {
+      if (! this.entities.containsKey(name)) {
+         this.entities.put(name, value);
+      }
+   }
+
+
+   /**
+    * Adds an external entity.
+    *
+    * @param name the name of the entity.
+    * @param publicID the public ID of the entity, which may be null.
+    * @param systemID the system ID of the entity.
+    */
+   public void addExternalEntity(String name,
+                                 String publicID,
+                                 String systemID)
+   {
+      if (! this.entities.containsKey(name)) {
+         this.entities.put(name, new String[] { publicID, systemID } );
+      }
+   }
+
+
+   /**
+    * Returns a Java reader containing the value of an entity.
+    *
+    * @param xmlReader the current XML reader
+    * @param name the name of the entity.
+    *
+    * @return the reader, or null if the entity could not be resolved.
+    */
+   public Reader getEntity(IXMLReader xmlReader,
+                           String     name)
+      throws XMLParseException
+   {
+      Object obj = this.entities.get(name);
+
+      if (obj == null) {
+         return null;
+      } else if (obj instanceof java.lang.String) {
+         return new StringReader((String)obj);
+      } else {
+         String[] id = (String[]) obj;
+         return this.openExternalEntity(xmlReader, id[0], id[1]);
+      }
+   }
+
+
+   /**
+    * Returns true if an entity is external.
+    *
+    * @param name the name of the entity.
+    */
+   public boolean isExternalEntity(String name)
+   {
+      Object obj = this.entities.get(name);
+      return ! (obj instanceof java.lang.String);
+   }
+
+
+   /**
+    * Opens an external entity.
+    *
+    * @param xmlReader the current XML reader
+    * @param publicID the public ID, which may be null
+    * @param systemID the system ID
+    *
+    * @return the reader, or null if the reader could not be created/opened
+    */
+   protected Reader openExternalEntity(IXMLReader xmlReader,
+                                       String     publicID,
+                                       String     systemID)
+      throws XMLParseException
+   {
+      String parentSystemID = xmlReader.getSystemID();
+
+      try {
+         return xmlReader.openStream(publicID, systemID);
+      } catch (Exception e) {
+         throw new XMLParseException(parentSystemID,
+                                     xmlReader.getLineNr(),
+                                     "Could not open external entity "
+                                     + "at system ID: " + systemID);
+      }
+   }
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/XMLException.java b/Sources/Java/net/n3/nanoxml/XMLException.java
new file mode 100644
index 0000000..459d66c
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/XMLException.java
@@ -0,0 +1,286 @@
+/* XMLException.java                                               NanoXML/Java
+ *
+ * $Revision: 1.4 $
+ * $Date: 2002/01/04 21:03:29 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+
+/**
+ * An XMLException is thrown when an exception occurred while processing the
+ * XML data.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
+ */
+public class XMLException
+   extends Exception
+{
+
+   /**
+    * The message of the exception.
+    */
+   private String msg;
+
+
+   /**
+    * The system ID of the XML data where the exception occurred.
+    */
+   private String systemID;
+
+
+   /**
+    * The line number in the XML data where the exception occurred.
+    */
+   private int lineNr;
+
+
+   /**
+    * Encapsulated exception.
+    */
+   private Exception encapsulatedException;
+
+
+   /**
+    * Creates a new exception.
+    *
+    * @param msg the message of the exception.
+    */
+   public XMLException(String msg)
+   {
+      this(null, -1, null, msg, false);
+   }
+
+
+   /**
+    * Creates a new exception.
+    *
+    * @param e the encapsulated exception.
+    */
+   public XMLException(Exception e)
+   {
+      this(null, -1, e, "Nested Exception", false);
+   }
+
+
+   /**
+    * Creates a new exception.
+    *
+    * @param systemID the system ID of the XML data where the exception
+    *                 occurred
+    * @param lineNr   the line number in the XML data where the exception
+    *                 occurred.
+    * @param e        the encapsulated exception.
+    */
+   public XMLException(String systemID,
+                       int    lineNr,
+                       Exception e)
+   {
+      this(systemID, lineNr, e, "Nested Exception", true);
+   }
+
+
+   /**
+    * Creates a new exception.
+    *
+    * @param systemID the system ID of the XML data where the exception
+    *                 occurred
+    * @param lineNr   the line number in the XML data where the exception
+    *                 occurred.
+    * @param msg      the message of the exception.
+    */
+   public XMLException(String systemID,
+                       int    lineNr,
+                       String msg)
+   {
+      this(systemID, lineNr, null, msg, true);
+   }
+
+
+   /**
+    * Creates a new exception.
+    *
+    * @param systemID     the system ID from where the data came
+    * @param lineNr       the line number in the XML data where the exception
+    *                     occurred.
+    * @param e            the encapsulated exception.
+    * @param msg          the message of the exception.
+    * @param reportParams true if the systemID, lineNr and e params need to be
+    *                     appended to the message
+    */
+   public XMLException(String    systemID,
+                       int       lineNr,
+                       Exception e,
+                       String    msg,
+                       boolean   reportParams)
+   {
+      super(XMLException.buildMessage(systemID, lineNr, e, msg,
+                                      reportParams));
+      this.systemID = systemID;
+      this.lineNr = lineNr;
+      this.encapsulatedException = e;
+      this.msg = XMLException.buildMessage(systemID, lineNr, e, msg,
+                                           reportParams);
+   }
+
+
+   /**
+    * Builds the exception message
+    *
+    * @param systemID     the system ID from where the data came
+    * @param lineNr       the line number in the XML data where the exception
+    *                     occurred.
+    * @param e            the encapsulated exception.
+    * @param msg          the message of the exception.
+    * @param reportParams true if the systemID, lineNr and e params need to be
+    *                     appended to the message
+    */
+   private static String buildMessage(String    systemID,
+                                      int       lineNr,
+                                      Exception e,
+                                      String    msg,
+                                      boolean   reportParams)
+   {
+      String str = msg;
+
+      if (reportParams) {
+         if (systemID != null) {
+            str += ", SystemID='" + systemID + "'";
+         }
+
+         if (lineNr >= 0) {
+            str += ", Line=" + lineNr;
+         }
+
+         if (e != null) {
+            str += ", Exception: " + e;
+         }
+      }
+
+      return str;
+   }
+
+
+   /**
+    * Cleans up the object when it's destroyed.
+    */
+   protected void finalize()
+      throws Throwable
+   {
+      this.systemID = null;
+      this.encapsulatedException = null;
+      super.finalize();
+   }
+
+
+   /**
+    * Returns the system ID of the XML data where the exception occurred.
+    * If there is no system ID known, null is returned.
+    */
+   public String getSystemID()
+   {
+      return this.systemID;
+   }
+
+
+   /**
+    * Returns the line number in the XML data where the exception occurred.
+    * If there is no line number known, -1 is returned.
+    */
+   public int getLineNr()
+   {
+      return this.lineNr;
+   }
+
+
+   /**
+    * Returns the encapsulated exception, or null if no exception is
+    * encapsulated.
+    */
+   public Exception getException()
+   {
+      return this.encapsulatedException;
+   }
+
+
+   /**
+    * Dumps the exception stack to a print writer.
+    *
+    * @param writer the print writer
+    */
+   public void printStackTrace(PrintWriter writer)
+   {
+      super.printStackTrace(writer);
+
+      if (this.encapsulatedException != null) {
+         writer.println("*** Nested Exception:");
+         this.encapsulatedException.printStackTrace(writer);
+      }
+   }
+
+
+   /**
+    * Dumps the exception stack to an output stream.
+    *
+    * @param stream the output stream
+    */
+   public void printStackTrace(PrintStream stream)
+   {
+      super.printStackTrace(stream);
+
+      if (this.encapsulatedException != null) {
+         stream.println("*** Nested Exception:");
+         this.encapsulatedException.printStackTrace(stream);
+      }
+   }
+
+
+   /**
+    * Dumps the exception stack to System.err.
+    */
+   public void printStackTrace()
+   {
+      super.printStackTrace();
+
+      if (this.encapsulatedException != null) {
+         System.err.println("*** Nested Exception:");
+         this.encapsulatedException.printStackTrace();
+      }
+   }
+
+
+   /**
+    * Returns a string representation of the exception.
+    */
+   public String toString()
+   {
+      return this.msg;
+   }
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/XMLParseException.java b/Sources/Java/net/n3/nanoxml/XMLParseException.java
new file mode 100644
index 0000000..cf14eea
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/XMLParseException.java
@@ -0,0 +1,69 @@
+/* XMLParseException.java                                          NanoXML/Java
+ *
+ * $Revision: 1.3 $
+ * $Date: 2002/01/04 21:03:29 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+/**
+ * An XMLParseException is thrown when the XML passed to the XML parser is not
+ * well-formed.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.3 $
+ */
+public class XMLParseException
+   extends XMLException
+{
+
+   /**
+    * Creates a new exception.
+    *
+    * @param msg the message of the exception.
+    */
+   public XMLParseException(String msg)
+   {
+      super(msg);
+   }
+
+
+   /**
+    * Creates a new exception.
+    *
+    * @param systemID the system ID from where the data came
+    * @param lineNr   the line number in the XML data where the exception
+    *                 occurred.
+    * @param msg      the message of the exception.
+    */
+   public XMLParseException(String systemID,
+                            int    lineNr,
+                            String msg)
+   {
+      super(systemID, lineNr, null, msg, true);
+   }
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/XMLParserFactory.java b/Sources/Java/net/n3/nanoxml/XMLParserFactory.java
new file mode 100644
index 0000000..e41df57
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/XMLParserFactory.java
@@ -0,0 +1,139 @@
+/* XMLParserFactory.java                                           NanoXML/Java
+ *
+ * $Revision: 1.3 $
+ * $Date: 2002/01/04 21:03:29 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.IOException;
+
+
+/**
+ * Creates an XML parser.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.3 $
+ */
+public class XMLParserFactory
+{
+
+   /**
+    * The class name of the default XML parser.
+    */
+   public static final String DEFAULT_CLASS = "net.n3.nanoxml.StdXMLParser";
+
+
+   /**
+    * The Java properties key of the XML parser class name.
+    */
+   public static final String CLASS_KEY = "net.n3.nanoxml.XMLParser";
+
+
+   /**
+    * Creates a default parser.
+    *
+    * @see #DEFAULT_CLASS
+    * @see #CLASS_KEY
+    *
+    * @return the non-null parser.
+    *
+    * @throws java.lang.ClassNotFoundException
+    *		if the class of the parser or validator could not be found.
+    * @throws java.lang.InstantiationException
+    *		if the parser could not be created
+    * @throws java.lang.IllegalAccessException
+    *		if the parser could not be created
+    */
+   public static IXMLParser createDefaultXMLParser()
+      throws ClassNotFoundException,
+             InstantiationException,
+             IllegalAccessException
+   {
+      String className = System.getProperty(XMLParserFactory.CLASS_KEY,
+                                            XMLParserFactory.DEFAULT_CLASS);
+      return XMLParserFactory.createXMLParser(className,
+                                              new StdXMLBuilder());
+   }
+
+
+   /**
+    * Creates a default parser.
+    *
+    * @see #DEFAULT_CLASS
+    * @see #CLASS_KEY
+    *
+    * @param builder the XML builder.
+    *
+    * @return the non-null parser.
+    *
+    * @throws java.lang.ClassNotFoundException
+    *		if the class of the parser could not be found.
+    * @throws java.lang.InstantiationException
+    *		if the parser could not be created
+    * @throws java.lang.IllegalAccessException
+    *		if the parser could not be created
+    */
+   public static IXMLParser createDefaultXMLParser(IXMLBuilder builder)
+      throws ClassNotFoundException,
+             InstantiationException,
+             IllegalAccessException
+   {
+      String className = System.getProperty(XMLParserFactory.CLASS_KEY,
+                                            XMLParserFactory.DEFAULT_CLASS);
+      return XMLParserFactory.createXMLParser(className, builder);
+   }
+
+
+   /**
+    * Creates a parser.
+    *
+    * @param className the name of the class of the XML parser
+    * @param builder the XML builder.
+    *
+    * @return the non-null parser.
+    *
+    * @throws java.lang.ClassNotFoundException
+    *		if the class of the parser could not be found.
+    * @throws java.lang.InstantiationException
+    *		if the parser could not be created
+    * @throws java.lang.IllegalAccessException
+    *		if the parser could not be created
+    */
+   public static IXMLParser createXMLParser(String      className,
+                                            IXMLBuilder builder)
+      throws ClassNotFoundException,
+             InstantiationException,
+             IllegalAccessException
+   {
+      Class cls = Class.forName(className);
+      IXMLParser parser = (IXMLParser) cls.newInstance();
+      parser.setBuilder(builder);
+      parser.setValidator(new NonValidator());
+      return parser;
+   }
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/XMLUtil.java b/Sources/Java/net/n3/nanoxml/XMLUtil.java
new file mode 100644
index 0000000..a54a7d9
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/XMLUtil.java
@@ -0,0 +1,763 @@
+/* XMLUtil.java                                                    NanoXML/Java
+ *
+ * $Revision: 1.5 $
+ * $Date: 2002/02/03 21:19:38 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.CharArrayReader;
+
+
+/**
+ * Utility methods for NanoXML.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.5 $
+ */
+class XMLUtil
+{
+
+   /**
+    * Skips the remainder of a comment.
+    * It is assumed that <!- is already read.
+    *
+    * @param reader the reader
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   static void skipComment(IXMLReader reader)
+      throws IOException,
+             XMLParseException
+   {
+      if (reader.read() != '-') {
+         XMLUtil.errorExpectedInput(reader.getSystemID(),
+                                    reader.getLineNr(),
+                                    "<!--");
+      }
+      
+      int dashesRead = 0;
+
+      for (;;) {
+         char ch = reader.read();
+
+         switch (ch) {
+            case '-':
+               dashesRead++;
+               break;
+
+            case '>':
+               if (dashesRead == 2) {
+                  return;
+               }
+
+            default:
+               dashesRead = 0;
+         }
+      }
+   }
+
+
+   /**
+    * Skips the remainder of the current XML tag.
+    *
+    * @param reader         the reader
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   static void skipTag(IXMLReader reader)
+      throws IOException,
+             XMLParseException
+   {
+      int level = 1;
+
+      while (level > 0) {
+         char ch = reader.read();
+
+         switch (ch) {
+            case '<':
+               ++level;
+               break;
+
+            case '>':
+               --level;
+               break;
+         }
+      }
+   }
+
+
+   /**
+    * Scans a public ID.
+    *
+    * @param publicID       will contain the public ID
+    * @param reader         the reader
+    *
+    * @return the system ID
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   static String scanPublicID(StringBuffer publicID,
+                              IXMLReader   reader)
+      throws IOException,
+             XMLParseException
+   {
+      if (! XMLUtil.checkLiteral(reader, "UBLIC")) {
+         return null;
+      }
+
+      XMLUtil.skipWhitespace(reader, null);
+      publicID.append(XMLUtil.scanString(reader, '\0', null));
+      XMLUtil.skipWhitespace(reader, null);
+      return XMLUtil.scanString(reader, '\0', null);
+   }
+
+
+   /**
+    * Scans a system ID.
+    *
+    * @param reader         the reader
+    *
+    * @return the system ID
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   static String scanSystemID(IXMLReader reader)
+      throws IOException,
+            XMLParseException
+   {
+      if (! XMLUtil.checkLiteral(reader, "YSTEM")) {
+         return null;
+      }
+
+      XMLUtil.skipWhitespace(reader, null);
+      return XMLUtil.scanString(reader, '\0', null);
+   }
+
+
+   /**
+    * Retrieves an identifier from the data.
+    *
+    * @param reader         the reader
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   static String scanIdentifier(IXMLReader reader)
+      throws IOException,
+             XMLParseException
+   {
+      StringBuffer result = new StringBuffer();
+
+      for (;;) {
+         char ch = reader.read();
+
+         if ((ch == '_') || (ch == ':') || (ch == '-') || (ch == '.')
+             || ((ch >= 'a') && (ch <= 'z'))
+             || ((ch >= 'A') && (ch <= 'Z'))
+             || ((ch >= '0') && (ch <= '9')) || (ch > '\u007E')) {
+            result.append(ch);
+         } else {
+            reader.unread(ch);
+            break;
+         }
+      }
+
+      return result.toString();
+   }
+
+
+   /**
+    * Retrieves a delimited string from the data.
+    *
+    * @param reader              the reader
+    * @param entityChar          the escape character (& or %)
+    * @param entityResolver      the entity resolver
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   static String scanString(IXMLReader         reader,
+                            char               entityChar,
+                            IXMLEntityResolver entityResolver)
+      throws IOException,
+             XMLParseException
+   {
+      StringBuffer result = new StringBuffer();
+      int startingLevel = reader.getStreamLevel();
+      char delim = reader.read();
+
+      if ((delim != '\'') && (delim != '"')) {
+         XMLUtil.errorExpectedInput(reader.getSystemID(),
+                                    reader.getLineNr(),
+                                    "delimited string");
+      }
+
+      for (;;) {
+         String str = XMLUtil.read(reader, entityChar);
+         char ch = str.charAt(0);
+
+         if (ch == entityChar) {
+            if (str.charAt(1) == '#') {
+               result.append(XMLUtil.processCharLiteral(str));
+            } else {
+               XMLUtil.processEntity(str, reader, entityResolver);
+            }
+         } else if (ch == '&') {
+            reader.unread(ch);
+            str = XMLUtil.read(reader, '&');
+            if (str.charAt(1) == '#') {
+               result.append(XMLUtil.processCharLiteral(str));
+            } else {
+               result.append(str);
+            }
+         } else if (reader.getStreamLevel() == startingLevel) {
+            if (ch == delim) {
+               break;
+            } else if ((ch == 9) || (ch == 10) || (ch == 13)) {
+               result.append(' ');
+            } else {
+               result.append(ch);
+            }
+         } else {
+            result.append(ch);
+         }
+      }
+
+      return result.toString();
+   }
+
+
+   /**
+    * Processes an entity.
+    *
+    * @param entity         the entity
+    * @param reader         the reader
+    * @param entityResolver the entity resolver
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   static void processEntity(String             entity,
+                             IXMLReader         reader,
+                             IXMLEntityResolver entityResolver)
+      throws IOException,
+             XMLParseException
+   {
+      entity = entity.substring(1, entity.length() - 1);
+      Reader entityReader = entityResolver.getEntity(reader, entity);
+
+      if (entityReader == null) {
+         XMLUtil.errorInvalidEntity(reader.getSystemID(),
+                                    reader.getLineNr(),
+                                    entity);
+      }
+
+      boolean externalEntity = entityResolver.isExternalEntity(entity);
+      reader.startNewStream(entityReader, !externalEntity);
+   }
+
+
+   /**
+    * Processes a character literal.
+    *
+    * @param entity         the entity
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   static char processCharLiteral(String entity)
+      throws IOException,
+             XMLParseException
+   {
+      if (entity.charAt(2) == 'x') {
+         entity = entity.substring(3, entity.length() - 1);
+         return (char) Integer.parseInt(entity, 16);
+      } else {
+         entity = entity.substring(2, entity.length() - 1);
+         return (char) Integer.parseInt(entity, 10);
+      }
+   }
+   
+
+   /**
+    * Skips whitespace from the reader.
+    *
+    * @param reader         the reader
+    * @param buffer         where to put the whitespace; null if the
+    *                       whitespace does not have to be stored.
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   static void skipWhitespace(IXMLReader   reader,
+                              StringBuffer buffer)
+      throws IOException
+   {
+      char ch;
+
+      if (buffer == null) {
+         do {
+            ch = reader.read();
+         } while ((ch == ' ') || (ch == '\t') || (ch == '\n'));
+      } else {
+         for (;;) {
+            ch = reader.read();
+
+            if ((ch != ' ') && (ch != '\t') && (ch != '\n')) {
+               break;
+            }
+
+            if (ch == '\n') {
+               buffer.append('\n');
+            } else {
+               buffer.append(' ');
+            }
+         }
+      }
+
+      reader.unread(ch);
+   }
+   
+
+   /**
+    * Reads a character from the reader.
+    *
+    * @param reader         the reader
+    * @param entityChar     the escape character (& or %) used to indicate
+    *                       an entity
+    *
+    * @return the character, or an entity expression (like e.g. &lt;)
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   static String read(IXMLReader         reader,
+                      char               entityChar)
+      throws IOException,
+             XMLParseException
+   {
+      char ch = reader.read();
+      StringBuffer buf = new StringBuffer();
+      buf.append(ch);
+
+      if (ch == entityChar) {
+         while (ch != ';') {
+            ch = reader.read();
+            buf.append(ch);
+         }
+      }
+
+      return buf.toString();
+   }
+
+
+   /**
+    * Reads a character from the reader disallowing entities.
+    *
+    * @param reader         the reader
+    * @param entityChar     the escape character (& or %) used to indicate
+    *                       an entity
+    */
+   static char readChar(IXMLReader reader,
+                        char       entityChar)
+      throws IOException,
+             XMLParseException
+   {
+      String str = XMLUtil.read(reader, entityChar);
+      char ch = str.charAt(0);
+
+      if (ch == entityChar) {
+         XMLUtil.errorUnexpectedEntity(reader.getSystemID(),
+                                       reader.getLineNr(),
+                                       str);
+      }
+
+      return ch;
+   }
+
+
+   /**
+    * Returns true if the data starts with <I>literal</I>.
+    * Enough chars are read to determine this result.
+    *
+    * @param reader         the reader
+    * @param literal        the literal to check
+    *
+    * @throws java.io.IOException
+    *		if an error occurred reading the data
+    */
+   static boolean checkLiteral(IXMLReader         reader,
+                               String             literal)
+      throws IOException,
+             XMLParseException
+   {
+      for (int i = 0; i < literal.length(); i++) {
+         if (reader.read() != literal.charAt(i)) {
+            return false;
+         }
+      }
+
+      return true;
+   }
+   
+
+   /**
+    * Throws an XMLParseException to indicate that an expected string is not
+    * encountered.
+    *
+    * @param systemID       the system ID of the data source
+    * @param lineNr         the line number in the data source
+    * @param expectedString the string that is expected
+    */
+   static void errorExpectedInput(String systemID,
+                                  int    lineNr,
+                                  String expectedString)
+      throws XMLParseException
+   {
+      throw new XMLParseException(systemID, lineNr,
+                                  "Expected: " + expectedString);
+   }
+
+
+   /**
+    * Throws an XMLParseException to indicate that an entity could not be
+    * resolved.
+    *
+    * @param systemID       the system ID of the data source
+    * @param lineNr         the line number in the data source
+    * @param entity    the name of the entity
+    */
+   static void errorInvalidEntity(String systemID,
+                                  int    lineNr,
+                                  String     entity)
+      throws XMLParseException
+   {
+      throw new XMLParseException(systemID, lineNr,
+                                  "Invalid entity: `&" + entity + ";'");
+   }
+
+
+   /**
+    * Throws an XMLParseException to indicate that an entity reference is
+    * unexpected at this point.
+    *
+    * @param systemID       the system ID of the data source
+    * @param lineNr         the line number in the data source
+    * @param entity    the name of the entity
+    */
+   static void errorUnexpectedEntity(String systemID,
+                                     int    lineNr,
+                                     String entity)
+      throws XMLParseException
+   {
+      throw new XMLParseException(systemID, lineNr,
+                                  "No entity reference is expected here ("
+                                  + entity + ")");
+   }
+
+
+   /**
+    * Throws an XMLParseException to indicate that a CDATA section is
+    * unexpected at this point.
+    *
+    * @param systemID       the system ID of the data source
+    * @param lineNr         the line number in the data source
+    */
+   static void errorUnexpectedCDATA(String systemID,
+                                    int    lineNr)
+      throws XMLParseException
+   {
+      throw new XMLParseException(systemID, lineNr,
+                                  "No CDATA section is expected here");
+   }
+
+
+   /**
+    * Throws an XMLParseException to indicate that a string is not expected
+    * at this point.
+    *
+    * @param systemID       the system ID of the data source
+    * @param lineNr         the line number in the data source
+    * @param unexpectedString the string that is unexpected
+    */
+   static void errorInvalidInput(String systemID,
+                                 int    lineNr,
+                                 String     unexpectedString)
+      throws XMLParseException
+   {
+      throw new XMLParseException(systemID, lineNr,
+                                  "Invalid input: " + unexpectedString);
+   }
+
+
+   /**
+    * Throws an XMLParseException to indicate that the closing tag of an
+    * element does not match the opening tag.
+    *
+    * @param systemID       the system ID of the data source
+    * @param lineNr         the line number in the data source
+    * @param expectedName the name of the opening tag
+    * @param wrongName    the name of the closing tag
+    */
+   static void errorWrongClosingTag(String systemID,
+                                    int    lineNr,
+                                    String     expectedName,
+                                    String     wrongName)
+      throws XMLParseException
+   {
+      throw new XMLParseException(systemID, lineNr,
+                                  "Closing tag does not match opening tag: `"
+                                  + wrongName + "' != `" + expectedName
+                                  + "'");
+   }
+
+
+   /**
+    * Throws an XMLParseException to indicate that extra data is encountered
+    * in a closing tag.
+    *
+    * @param systemID       the system ID of the data source
+    * @param lineNr         the line number in the data source
+    */
+   static void errorClosingTagNotEmpty(String systemID,
+                                       int    lineNr)
+      throws XMLParseException
+   {
+      throw new XMLParseException(systemID, lineNr,
+                                  "Closing tag must be empty");
+   }
+
+
+   /**
+    * Throws an XMLValidationException to indicate that an element is missing.
+    *
+    * @param systemID       the system ID of the data source
+    * @param lineNr         the line number in the data source
+    * @param parentElementName the name of the parent element
+    * @param missingElementName the name of the missing element
+    */
+   static void errorMissingElement(String systemID,
+                                   int    lineNr,
+                                   String parentElementName,
+                                   String missingElementName)
+      throws XMLValidationException
+   {
+      throw new XMLValidationException(
+                              XMLValidationException.MISSING_ELEMENT,
+                              systemID, lineNr,
+                              missingElementName,
+                              /*attributeName*/ null,
+                              /*attributeValue*/ null,
+                              "Element " + parentElementName
+                              + " expects to have a " + missingElementName);
+   }
+
+
+   /**
+    * Throws an XMLValidationException to indicate that an element is
+    * unexpected.
+    *
+    * @param systemID       the system ID of the data source
+    * @param lineNr         the line number in the data source
+    * @param parentElementName the name of the parent element
+    * @param unexpectedElementName the name of the unexpected element
+    */
+   static void errorUnexpectedElement(String systemID,
+                                      int    lineNr,
+                                      String parentElementName,
+                                      String unexpectedElementName)
+      throws XMLValidationException
+   {
+      throw new XMLValidationException(
+                              XMLValidationException.UNEXPECTED_ELEMENT,
+                              systemID, lineNr,
+                              unexpectedElementName,
+                              /*attributeName*/ null,
+                              /*attributeValue*/ null,
+                              "Unexpected " + unexpectedElementName + " in a "
+                              + parentElementName);
+   }
+
+
+   /**
+    * Throws an XMLValidationException to indicate that an attribute is
+    * missing.
+    *
+    * @param systemID       the system ID of the data source
+    * @param lineNr         the line number in the data source
+    * @param elementName    the name of the element
+    * @param attributeName  the name of the missing attribute
+    */
+   static void errorMissingAttribute(String systemID,
+                                     int    lineNr,
+                                     String elementName,
+                                     String attributeName)
+      throws XMLValidationException
+   {
+      throw new XMLValidationException(
+                     XMLValidationException.MISSING_ATTRIBUTE,
+                     systemID, lineNr,
+                     elementName,
+                     attributeName,
+                     /*attributeValue*/ null,
+                     "Element " + elementName + " expects an attribute named "
+                     + attributeName);
+   }
+
+
+   /**
+    * Throws an XMLValidationException to indicate that an attribute is
+    * unexpected.
+    *
+    * @param systemID       the system ID of the data source
+    * @param lineNr         the line number in the data source
+    * @param elementName    the name of the element
+    * @param attributeName  the name of the unexpected attribute
+    */
+   static void errorUnexpectedAttribute(String systemID,
+                                        int    lineNr,
+                                        String elementName,
+                                        String attributeName)
+      throws XMLValidationException
+   {
+      throw new XMLValidationException(
+                     XMLValidationException.UNEXPECTED_ATTRIBUTE,
+                     systemID, lineNr,
+                     elementName,
+                     attributeName,
+                     /*attributeValue*/ null,
+                     "Element " + elementName + " did not expect an attribute "
+                     + "named " + attributeName);
+   }
+
+
+   /**
+    * Throws an XMLValidationException to indicate that an attribute has an
+    * invalid value.
+    *
+    * @param systemID       the system ID of the data source
+    * @param lineNr         the line number in the data source
+    * @param elementName    the name of the element
+    * @param attributeName  the name of the attribute
+    * @param attributeValue the value of that attribute
+    */
+   static void errorInvalidAttributeValue(String systemID,
+                                          int    lineNr,
+                                          String elementName,
+                                          String attributeName,
+                                          String attributeValue)
+      throws XMLValidationException
+   {
+      throw new XMLValidationException(
+                           XMLValidationException.ATTRIBUTE_WITH_INVALID_VALUE,
+                           systemID, lineNr,
+                           elementName,
+                           attributeName,
+                           attributeValue,
+                           "Invalid value for attribute " + attributeName);
+   }
+
+
+   /**
+    * Throws an XMLValidationException to indicate that a #PCDATA element was
+    * missing.
+    *
+    * @param systemID       the system ID of the data source
+    * @param lineNr         the line number in the data source
+    * @param parentElementName the name of the parent element
+    */
+   static void errorMissingPCData(String systemID,
+                                  int    lineNr,
+                                  String parentElementName)
+      throws XMLValidationException
+   {
+      throw new XMLValidationException(
+                           XMLValidationException.MISSING_PCDATA,
+                           systemID, lineNr,
+                           /*elementName*/ null,
+                           /*attributeName*/ null,
+                           /*attributeValue*/ null,
+                           "Missing #PCDATA in element " + parentElementName);
+   }
+   
+
+   /**
+    * Throws an XMLValidationException to indicate that a #PCDATA element was
+    * unexpected.
+    *
+    * @param systemID       the system ID of the data source
+    * @param lineNr         the line number in the data source
+    * @param parentElementName the name of the parent element
+    */
+   static void errorUnexpectedPCData(String systemID,
+                                     int    lineNr,
+                                     String parentElementName)
+      throws XMLValidationException
+   {
+      throw new XMLValidationException(
+                        XMLValidationException.UNEXPECTED_PCDATA,
+                        systemID, lineNr,
+                        /*elementName*/ null,
+                        /*attributeName*/ null,
+                        /*attributeValue*/ null,
+                        "Unexpected #PCDATA in element " + parentElementName);
+   }
+
+
+   /**
+    * Throws an XMLValidationException.
+    *
+    * @param systemID       the system ID of the data source
+    * @param lineNr         the line number in the data source
+    * @param message        the error message
+    * @param elementName    the name of the element
+    * @param attributeName  the name of the attribute
+    * @param attributeValue the value of that attribute
+    */
+   static void validationError(String systemID,
+                               int    lineNr,
+                               String message,
+                               String elementName,
+                               String attributeName,
+                               String attributeValue)
+      throws XMLValidationException
+   {
+      throw new XMLValidationException(XMLValidationException.MISC_ERROR,
+                                       systemID, lineNr,
+                                       elementName,
+                                       attributeName,
+                                       attributeValue,
+                                       message);
+   }
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/XMLValidationException.java b/Sources/Java/net/n3/nanoxml/XMLValidationException.java
new file mode 100644
index 0000000..a01dcff
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/XMLValidationException.java
@@ -0,0 +1,190 @@
+/* XMLValidationException.java                                     NanoXML/Java
+ *
+ * $Revision: 1.3 $
+ * $Date: 2002/01/04 21:03:29 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+/**
+ * An XMLValidationException is thrown when the XML passed to the XML parser is
+ * well-formed but not valid.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.3 $
+ */
+public class XMLValidationException
+   extends XMLException
+{
+
+   /**
+    * An element was missing.
+    */
+   public static final int MISSING_ELEMENT = 1;
+
+
+   /**
+    * An unexpected element was encountered.
+    */
+   public static final int UNEXPECTED_ELEMENT = 2;
+
+
+   /**
+    * An attribute was missing.
+    */
+   public static final int MISSING_ATTRIBUTE = 3;
+
+
+   /**
+    * An unexpected attribute was encountered.
+    */
+   public static final int UNEXPECTED_ATTRIBUTE = 4;
+
+
+   /**
+    * An attribute has an invalid value.
+    */
+   public static final int ATTRIBUTE_WITH_INVALID_VALUE = 5;
+
+
+   /**
+    * A PCDATA element was missing.
+    */
+   public static final int MISSING_PCDATA = 6;
+
+
+   /**
+    * An unexpected PCDATA element was encountered.
+    */
+   public static final int UNEXPECTED_PCDATA = 7;
+
+
+   /**
+    * Another error than those specified in this class was encountered.
+    */
+   public static final int MISC_ERROR = 0;
+
+
+   /**
+    * Which error occurred.
+    */
+   private int errorType;
+
+
+   /**
+    * The name of the element where the exception occurred.
+    */
+   private String elementName;
+
+
+   /**
+    * The name of the attribute where the exception occurred.
+    */
+   private String attributeName;
+
+
+   /**
+    * The value of the attribute where the exception occurred.
+    */
+   private String attributeValue;
+
+
+   /**
+    * Creates a new exception.
+    *
+    * @param errorType      the type of validity error
+    * @param systemID       the system ID from where the data came
+    * @param lineNr         the line number in the XML data where the
+    *                       exception occurred.
+    * @param elementName    the name of the offending element
+    * @param attributeName  the name of the offending attribute
+    * @param attributeValue the value of the offending attribute
+    * @param msg            the message of the exception.
+    */
+   public XMLValidationException(int    errorType,
+                                 String systemID,
+                                 int    lineNr,
+                                 String elementName,
+                                 String attributeName,
+                                 String attributeValue,
+                                 String msg)
+   {
+      super(systemID, lineNr, null,
+            msg + ((elementName == null) ? "" : (", element=" + elementName))
+            + ((attributeName == null) ? ""
+                                       : (", attribute=" + attributeName))
+            + ((attributeValue == null) ? ""
+                                       : (", value='" + attributeValue + "'")),
+            false);
+      this.elementName = elementName;
+      this.attributeName = attributeName;
+      this.attributeValue = attributeValue;
+   }
+
+
+   /**
+    * Cleans up the object when it's destroyed.
+    */
+   protected void finalize()
+      throws Throwable
+   {
+      this.elementName = null;
+      this.attributeName = null;
+      this.attributeValue = null;
+      super.finalize();
+   }
+
+
+   /**
+    * Returns the name of the element in which the validation is violated.
+    * If there is no current element, null is returned.
+    */
+   public String getElementName()
+   {
+      return this.elementName;
+   }
+
+
+   /**
+    * Returns the name of the attribute in which the validation is violated.
+    * If there is no current attribute, null is returned.
+    */
+   public String getAttributeName()
+   {
+      return this.attributeName;
+   }
+
+
+   /**
+    * Returns the value of the attribute in which the validation is violated.
+    * If there is no current attribute, null is returned.
+    */
+   public String getAttributeValue()
+   {
+      return this.attributeValue;
+   }
+
+}
diff --git a/Sources/Java/net/n3/nanoxml/XMLWriter.java b/Sources/Java/net/n3/nanoxml/XMLWriter.java
new file mode 100644
index 0000000..23abc92
--- /dev/null
+++ b/Sources/Java/net/n3/nanoxml/XMLWriter.java
@@ -0,0 +1,311 @@
+/* XMLWriter.java                                                  NanoXML/Java
+ *
+ * $Revision: 1.4 $
+ * $Date: 2002/03/24 11:37:51 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml;
+
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Enumeration;
+import java.util.Vector;
+
+
+/**
+ * An XMLWriter writes XML data to a stream.
+ *
+ * @see net.n3.nanoxml.IXMLElement
+ * @see java.io.Writer
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
+ */
+public class XMLWriter
+{
+
+   /**
+    * Where to write the output to.
+    */
+   private PrintWriter writer;
+
+
+   /**
+    * Creates a new XML writer.
+    *
+    * @param writer where to write the output to.
+    */
+   public XMLWriter(Writer writer)
+   {
+      if (writer instanceof PrintWriter) {
+         this.writer = (PrintWriter) writer;
+      } else {
+         this.writer = new PrintWriter(writer);
+      }
+   }
+
+
+   /**
+    * Creates a new XML writer.
+    *
+    * @param stream where to write the output to.
+    */
+   public XMLWriter(OutputStream stream)
+   {
+      this.writer = new PrintWriter(stream);
+   }
+
+
+   /**
+    * Cleans up the object when it's destroyed.
+    */
+   protected void finalize()
+      throws Throwable
+   {
+      this.writer = null;
+      super.finalize();
+   }
+
+
+   /**
+    * Writes an XML element.
+    *
+    * @param xml the non-null XML element to write.
+    */
+   public void write(IXMLElement xml)
+      throws IOException
+   {
+      this.write(xml, false, 0, true);
+   }
+
+
+   /**
+    * Writes an XML element.
+    *
+    * @param xml the non-null XML element to write.
+    * @param prettyPrint if spaces need to be inserted to make the output more
+    *                    readable
+    */
+   public void write(IXMLElement xml,
+                     boolean     prettyPrint)
+      throws IOException
+   {
+      this.write(xml, prettyPrint, 0, true);
+   }
+
+
+   /**
+    * Writes an XML element.
+    *
+    * @param xml the non-null XML element to write.
+    * @param prettyPrint if spaces need to be inserted to make the output more
+    *                    readable
+    * @param indent how many spaces to indent the element.
+    */
+   public void write(IXMLElement xml,
+                     boolean     prettyPrint,
+                     int         indent)
+       throws IOException
+   {
+       this.write(xml, prettyPrint, indent, true);
+   }
+
+   
+   /**
+    * Writes an XML element.
+    *
+    * @param xml the non-null XML element to write.
+    * @param prettyPrint if spaces need to be inserted to make the output more
+    *                    readable
+    * @param indent how many spaces to indent the element.
+    */
+   public void write(IXMLElement xml,
+                     boolean     prettyPrint,
+                     int         indent,
+                     boolean     collapseEmptyElements)
+      throws IOException
+   {
+      if (prettyPrint) {
+         for (int i = 0; i < indent; i++) {
+            this.writer.print(' ');
+         }
+      }
+
+      if (xml.getName() == null) {
+         if (xml.getContent() != null) {
+            if (prettyPrint) {
+               this.writeEncoded(xml.getContent().trim());
+               writer.println();
+            } else {
+               this.writeEncoded(xml.getContent());
+            }
+         }
+      } else {
+         this.writer.print('<');
+         this.writer.print(xml.getFullName());
+         Vector nsprefixes = new Vector();
+
+         if (xml.getNamespace() != null) {
+            if (xml.getName().equals(xml.getFullName())) {
+               this.writer.print(" xmlns=\"" + xml.getNamespace() + '"');
+            } else {
+               String prefix = xml.getFullName();
+               prefix = prefix.substring(0, prefix.indexOf(':'));
+               nsprefixes.addElement(prefix);
+               this.writer.print(" xmlns:" + prefix);
+               this.writer.print("=\"" + xml.getNamespace() + "\"");
+            }
+         }
+
+         Enumeration enum = xml.enumerateAttributeNames();
+
+         while (enum.hasMoreElements()) {
+            String key = (String) enum.nextElement();
+            int index = key.indexOf(':');
+
+            if (index >= 0) {
+               String namespace = xml.getAttributeNamespace(key);
+
+               if (namespace != null) {
+                  String prefix = key.substring(0, index);
+
+                  if (! nsprefixes.contains(prefix)) {
+                     this.writer.print(" xmlns:" + prefix);
+                     this.writer.print("=\"" + namespace + '"');
+                     nsprefixes.addElement(prefix);
+                  }
+               }
+            }
+         }
+
+         enum = xml.enumerateAttributeNames();
+
+         while (enum.hasMoreElements()) {
+            String key = (String) enum.nextElement();
+            String value = xml.getAttribute(key, null);
+            this.writer.print(" " + key + "=\"");
+            this.writeEncoded(value);
+            this.writer.print('"');
+         }
+
+         if ((xml.getContent() != null)
+             && (xml.getContent().length() > 0)) {
+            writer.print('>');
+            this.writeEncoded(xml.getContent());
+            writer.print("</" + xml.getFullName() + '>');
+
+            if (prettyPrint) {
+               writer.println();
+            }
+         } else if (xml.hasChildren() || (! collapseEmptyElements)) {
+            writer.print('>');
+
+            if (prettyPrint) {
+               writer.println();
+            }
+
+            enum = xml.enumerateChildren();
+
+            while (enum.hasMoreElements()) {
+               IXMLElement child = (IXMLElement) enum.nextElement();
+               this.write(child, prettyPrint, indent + 4,
+                          collapseEmptyElements);
+            }
+
+            if (prettyPrint) {
+               for (int i = 0; i < indent; i++) {
+                  this.writer.print(' ');
+               }
+            }
+
+            this.writer.print("</" + xml.getFullName() + ">");
+
+            if (prettyPrint) {
+               writer.println();
+            }
+         } else {
+            this.writer.print("/>");
+
+            if (prettyPrint) {
+               writer.println();
+            }
+         }
+      }
+
+      this.writer.flush();
+   }
+
+
+   /**
+    * Writes a string encoding reserved characters.
+    *
+    * @param str the string to write.
+    */
+   private void writeEncoded(String str)
+   {
+      for (int i = 0; i < str.length(); i++) {
+         char c = str.charAt(i);
+
+         switch (c) {
+            case 0x0A:
+               this.writer.print(c);
+               break;
+
+            case '<':
+               this.writer.print("<");
+               break;
+
+            case '>':
+               this.writer.print(">");
+               break;
+
+            case '&':
+               this.writer.print("&");
+               break;
+
+            case '\'':
+               this.writer.print("'");
+               break;
+
+            case '"':
+               this.writer.print(""");
+               break;
+
+            default:
+               if ((c < ' ') || (c > 0x7E)) {
+                  this.writer.print("&#x");
+                  this.writer.print(Integer.toString(c, 16));
+                  this.writer.print(';');
+               } else {
+                  this.writer.print(c);
+               }
+         }
+      }
+   }
+
+}
diff --git a/Sources/Lite/nanoxml/XMLElement.java b/Sources/Lite/nanoxml/XMLElement.java
new file mode 100644
index 0000000..d90934d
--- /dev/null
+++ b/Sources/Lite/nanoxml/XMLElement.java
@@ -0,0 +1,2875 @@
+/* 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.
+ *****************************************************************************/
+
+
+package 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>
+ *     String key = (String) enum.nextElement();<BR>
+ *     String value = element.getStringAttribute(key);<BR>
+ *     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>
+ * <title>The Title</title>
+ * </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
+ *         <<A href="mailto:cyberelf at mac.com">cyberelf at mac.com</A>>
+ * @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 &...; entities. The keys are the entity names
+     * without the & 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" => "<", "gt" => ">",
+     *         "quot" => "\"", "apos" => "'",
+     *         "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 > 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 entities)
+    {
+        this(entities, 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 entities,
+                      boolean   skipLeadingWhitespace)
+    {
+        this(entities, 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 entities,
+                      boolean   skipLeadingWhitespace,
+                      boolean   ignoreCase)
+    {
+        this(entities, skipLeadingWhitespace, true, ignoreCase);
+    }
+
+
+    /**
+     * 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 entities,
+                         boolean   skipLeadingWhitespace,
+                         boolean   fillBasicConversionTable,
+                         boolean   ignoreCase)
+    {
+        this.ignoreWhitespace = skipLeadingWhitespace;
+        this.ignoreCase = ignoreCase;
+        this.name = null;
+        this.contents = "";
+        this.attributes = new Hashtable();
+        this.children = new Vector();
+        this.entities = entities;
+        this.lineNr = 0;
+        Enumeration enum = this.entities.keys();
+        while (enum.hasMoreElements()) {
+            Object key = enum.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.
+     *
+     * @deprecated Use {@link #setAttribute(java.lang.String, java.lang.Object)
+     *             setAttribute} instead.
+     */
+    public void addProperty(String name,
+                            Object value)
+    {
+        this.setAttribute(name, 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>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.
+     *
+     * @deprecated Use {@link #setIntAttribute(java.lang.String, int)
+     *             setIntAttribute} instead.
+     */
+    public void addProperty(String key,
+                            int    value)
+    {
+        this.setIntAttribute(key, 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));
+    }
+
+
+    /**
+     * Adds or modifies an attribute.
+     *
+     * @param name
+     *     The name of the attribute.
+     * @param value
+     *     The value of the attribute.
+     *
+     * @deprecated Use {@link #setDoubleAttribute(java.lang.String, double)
+     *             setDoubleAttribute} instead.
+     */
+    public void addProperty(String name,
+                            double value)
+    {
+        this.setDoubleAttribute(name, 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 attribute names.
+     *
+     * @deprecated Use {@link #enumerateAttributeNames()
+     *             enumerateAttributeNames} instead.
+     */
+    public Enumeration enumeratePropertyNames()
+    {
+        return this.enumerateAttributeNames();
+    }
+
+
+    /**
+     * 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.
+     *
+     * @deprecated Use {@link #getContent() getContent} instead.
+     */
+    public String getContents()
+    {
+        return this.getContent();
+    }
+
+
+    /**
+     * 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" =>
+     * 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 an attribute by looking up a key in a hashtable.
+     *
+     * @deprecated Use {@link #getIntAttribute(java.lang.String,
+     *             java.util.Hashtable, java.lang.String, boolean)
+     *             getIntAttribute} instead.
+     */
+    public int getIntProperty(String    name,
+                              Hashtable valueSet,
+                              String    defaultKey)
+    {
+        return this.getIntAttribute(name, valueSet, defaultKey, false);
+    }
+
+
+    /**
+     * Returns an attribute.
+     *
+     * @deprecated Use {@link #getStringAttribute(java.lang.String)
+     *             getStringAttribute} instead.
+     */
+    public String getProperty(String name)
+    {
+        return this.getStringAttribute(name);
+    }
+
+
+    /**
+     * Returns an attribute.
+     *
+     * @deprecated Use {@link #getStringAttribute(java.lang.String,
+     *             java.lang.String) getStringAttribute} instead.
+     */
+    public String getProperty(String name,
+                              String defaultValue)
+    {
+        return this.getStringAttribute(name, defaultValue);
+    }
+
+
+    /**
+     * Returns an attribute.
+     *
+     * @deprecated Use {@link #getIntAttribute(java.lang.String, int)
+     *             getIntAttribute} instead.
+     */
+    public int getProperty(String name,
+                           int    defaultValue)
+    {
+        return this.getIntAttribute(name, defaultValue);
+    }
+
+
+    /**
+     * Returns an attribute.
+     *
+     * @deprecated Use {@link #getDoubleAttribute(java.lang.String, double)
+     *             getDoubleAttribute} instead.
+     */
+    public double getProperty(String name,
+                              double defaultValue)
+    {
+        return this.getDoubleAttribute(name, defaultValue);
+    }
+
+
+    /**
+     * Returns an attribute.
+     *
+     * @deprecated Use {@link #getBooleanAttribute(java.lang.String,
+     *             java.lang.String, java.lang.String, boolean)
+     *             getBooleanAttribute} instead.
+     */
+    public boolean getProperty(String  key,
+                               String  trueValue,
+                               String  falseValue,
+                               boolean defaultValue)
+    {
+        return this.getBooleanAttribute(key, trueValue, falseValue,
+                                        defaultValue);
+    }
+
+
+    /**
+     * Returns an attribute by looking up a key in a hashtable.
+     *
+     * @deprecated Use {@link #getAttribute(java.lang.String,
+     *             java.util.Hashtable, java.lang.String, boolean)
+     *             getAttribute} instead.
+     */
+    public Object getProperty(String    name,
+                              Hashtable valueSet,
+                              String    defaultKey)
+    {
+        return this.getAttribute(name, valueSet, defaultKey, false);
+    }
+
+
+    /**
+     * Returns an attribute by looking up a key in a hashtable.
+     *
+     * @deprecated Use {@link #getStringAttribute(java.lang.String,
+     *             java.util.Hashtable, java.lang.String, boolean)
+     *             getStringAttribute} instead.
+     */
+    public String getStringProperty(String    name,
+                                    Hashtable valueSet,
+                                    String    defaultKey)
+    {
+        return this.getStringAttribute(name, valueSet, defaultKey, false);
+    }
+
+
+    /**
+     * Returns an attribute by looking up a key in a hashtable.
+     *
+     * @deprecated Use {@link #getIntAttribute(java.lang.String,
+     *             java.util.Hashtable, java.lang.String, boolean)
+     *             getIntAttribute} instead.
+     */
+    public int getSpecialIntProperty(String    name,
+                                     Hashtable valueSet,
+                                     String    defaultKey)
+    {
+        return this.getIntAttribute(name, valueSet, defaultKey, true);
+    }
+
+
+    /**
+     * Returns an attribute by looking up a key in a hashtable.
+     *
+     * @deprecated Use {@link #getDoubleAttribute(java.lang.String,
+     *             java.util.Hashtable, java.lang.String, boolean)
+     *             getDoubleAttribute} instead.
+     */
+    public double getSpecialDoubleProperty(String    name,
+                                           Hashtable valueSet,
+                                           String    defaultKey)
+    {
+        return this.getDoubleAttribute(name, valueSet, defaultKey, true);
+    }
+
+
+    /**
+     * Returns the name of the element.
+     *
+     * @see nanoxml.XMLElement#setName(java.lang.String) setName(String)
+     */
+    public String getName()
+    {
+        return this.name;
+    }
+
+
+    /**
+     * Returns the name of the element.
+     *
+     * @deprecated Use {@link #getName() getName} instead.
+     */
+    public String getTagName()
+    {
+        return this.getName();
+    }
+
+
+    /**
+     * 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
+     *         '>' 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
+     *         '>' 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,
+                                int    startingLineNr)
+        throws IOException, XMLParseException
+    {
+        this.name = null;
+        this.contents = "";
+        this.attributes = new Hashtable();
+        this.children = new Vector();
+        this.charReadTooMuch = '\0';
+        this.reader = reader;
+        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() > 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 < string.length()</code>
+     *     <li><code>offset >= 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 <= string.length()</code>
+     *     <li><code>offset < end</code>
+     *     <li><code>offset >= 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 <= string.length()</code>
+     *     <li><code>offset < end</code>
+     *     <li><code>offset >= 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 <= input.length</code>
+     *     <li><code>offset < end</code>
+     *     <li><code>offset >= 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 <= input.length</code>
+     *     <li><code>offset < end</code>
+     *     <li><code>offset >= 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);
+    }
+
+
+    /**
+     * Removes an attribute.
+     *
+     * @param name
+     *     The name of the attribute.
+     *
+     * @deprecated Use {@link #removeAttribute(java.lang.String)
+     *             removeAttribute} instead.
+     */
+    public void removeProperty(String name)
+    {
+        this.removeAttribute(name);
+    }
+
+
+    /**
+     * Removes an attribute.
+     *
+     * @param name
+     *     The name of the attribute.
+     *
+     * @deprecated Use {@link #removeAttribute(java.lang.String)
+     *             removeAttribute} instead.
+     */
+    public void removeChild(String name)
+    {
+        this.removeAttribute(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.
+     *
+     * @deprecated Use {@link #setName(java.lang.String) setName} instead.
+     */
+    public void setTagName(String name)
+    {
+        this.setName(name);
+    }
+
+
+    /**
+     * 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 name)
+    {
+        this.name = name;
+    }
+
+
+    /**
+     * 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 enum = this.attributes.keys();
+            while (enum.hasMoreElements()) {
+                writer.write(' ');
+                String key = (String) enum.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 enum = this.enumerateChildren();
+            while (enum.hasMoreElements()) {
+                XMLElement child = (XMLElement) enum.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 < char is skipped.
+     * The scanned data is appended to <code>data</code>.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>data != null</code>
+     * </ul></dd></dl>
+     */
+    protected void scanPCData(StringBuffer data)
+        throws IOException
+    {
+        for (;;) {
+            char ch = this.readChar();
+            if (ch == '<') {
+                ch = this.readChar();
+                if (ch == '!') {
+                    this.checkCDATA(data);
+                } else {
+                    this.unreadChar(ch);
+                    return;
+                }
+            } else if (ch == '&') {
+                this.resolveEntity(data);
+            } else {
+                data.append(ch);
+            }
+        }
+    }
+
+
+    /**
+     * Scans a special tag and if the tag is a CDATA section, append its
+     * content to <code>buf</code>.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>buf != null</code>
+     *     <li>The first < has already been read.
+     * </ul></dd></dl>
+     */
+    protected boolean checkCDATA(StringBuffer buf)
+        throws IOException
+    {
+        char ch = this.readChar();
+        if (ch != '[') {
+            this.unreadChar(ch);
+            this.skipSpecialTag(0);
+            return false;
+        } else if (! this.checkLiteral("CDATA[")) {
+            this.skipSpecialTag(1); // one [ has already been read
+            return false;
+        } else {
+            int delimiterCharsSkipped = 0;
+            while (delimiterCharsSkipped < 3) {
+                ch = this.readChar();
+                switch (ch) {
+                    case ']':
+                        if (delimiterCharsSkipped < 2) {
+                            delimiterCharsSkipped += 1;
+                        } else {
+                            buf.append(']');
+                            buf.append(']');
+                            delimiterCharsSkipped = 0;
+                        }
+                        break;
+                    case '>':
+                        if (delimiterCharsSkipped < 2) {
+                            for (int i = 0; i < delimiterCharsSkipped; i++) {
+                                buf.append(']');
+                            }
+                            delimiterCharsSkipped = 0;
+                            buf.append('>');
+                        } else {
+                            delimiterCharsSkipped = 3;
+                        }
+                        break;
+                    default:
+                        for (int i = 0; i < delimiterCharsSkipped; i += 1) {
+                            buf.append(']');
+                        }
+                        buf.append(ch);
+                        delimiterCharsSkipped = 0;
+                }
+            }
+            return true;
+        }
+    }
+
+
+    /**
+     * Skips a comment.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li>The first <!-- has already been read.
+     * </ul></dd></dl>
+     */
+    protected void skipComment()
+        throws IOException
+    {
+        int dashesToRead = 2;
+        while (dashesToRead > 0) {
+            char ch = this.readChar();
+            if (ch == '-') {
+                dashesToRead -= 1;
+            } else {
+                dashesToRead = 2;
+            }
+        }
+        if (this.readChar() != '>') {
+            throw this.expectedInput(">");
+        }
+    }
+
+
+    /**
+     * Skips a special tag or comment.
+     *
+     * @param bracketLevel The number of open square brackets ([) that have
+     *                     already been read.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li>The first <! has already been read.
+     *     <li><code>bracketLevel >= 0</code>
+     * </ul></dd></dl>
+     */
+    protected void skipSpecialTag(int bracketLevel)
+        throws IOException
+    {
+        int tagLevel = 1; // <
+        char stringDelimiter = '\0';
+        if (bracketLevel == 0) {
+            char ch = this.readChar();
+            if (ch == '[') {
+                bracketLevel += 1;
+            } else if (ch == '-') {
+                ch = this.readChar();
+                if (ch == '[') {
+                    bracketLevel += 1;
+                } else if (ch == ']') {
+                    bracketLevel -= 1;
+                } else if (ch == '-') {
+                    this.skipComment();
+                    return;
+                }
+            }
+        }
+        while (tagLevel > 0) {
+            char ch = this.readChar();
+            if (stringDelimiter == '\0') {
+                if ((ch == '"') || (ch == '\'')) {
+                    stringDelimiter = ch;
+                } else if (bracketLevel <= 0) {
+                    if (ch == '<') {
+                        tagLevel += 1;
+                    } else if (ch == '>') {
+                        tagLevel -= 1;
+                    }
+                }
+                if (ch == '[') {
+                    bracketLevel += 1;
+                } else if (ch == ']') {
+                    bracketLevel -= 1;
+                }
+            } else {
+                if (ch == stringDelimiter) {
+                    stringDelimiter = '\0';
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Scans the data for literal text.
+     * Scanning stops when a character does not match or after the complete
+     * text has been checked, whichever comes first.
+     *
+     * @param literal the literal to check.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>literal != null</code>
+     * </ul></dd></dl>
+     */
+    protected boolean checkLiteral(String literal)
+        throws IOException
+    {
+        int length = literal.length();
+        for (int i = 0; i < length; i += 1) {
+            if (this.readChar() != literal.charAt(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+
+    /**
+     * Reads a character from a reader.
+     */
+    protected char readChar()
+        throws IOException
+    {
+        if (this.charReadTooMuch != '\0') {
+            char ch = this.charReadTooMuch;
+            this.charReadTooMuch = '\0';
+            return ch;
+        } else {
+            int i = this.reader.read();
+            if (i < 0) {
+                throw this.unexpectedEndOfData();
+            } else if (i == 10) {
+                this.parserLineNr += 1;
+                return '\n';
+            } else {
+                return (char) i;
+            }
+        }
+    }
+
+
+    /**
+     * Scans an XML element.
+     *
+     * @param elt The element that will contain the result.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li>The first < has already been read.
+     *     <li><code>elt != null</code>
+     * </ul></dd></dl>
+     */
+    protected void scanElement(XMLElement elt)
+        throws IOException
+    {
+        StringBuffer buf = new StringBuffer();
+        this.scanIdentifier(buf);
+        String name = buf.toString();
+        elt.setName(name);
+        char ch = this.scanWhitespace();
+        while ((ch != '>') && (ch != '/')) {
+            buf.setLength(0);
+            this.unreadChar(ch);
+            this.scanIdentifier(buf);
+            String key = buf.toString();
+            ch = this.scanWhitespace();
+            if (ch != '=') {
+                throw this.expectedInput("=");
+            }
+            this.unreadChar(this.scanWhitespace());
+            buf.setLength(0);
+            this.scanString(buf);
+            elt.setAttribute(key, buf);
+            ch = this.scanWhitespace();
+        }
+        if (ch == '/') {
+            ch = this.readChar();
+            if (ch != '>') {
+                throw this.expectedInput(">");
+            }
+            return;
+        }
+        buf.setLength(0);
+        ch = this.scanWhitespace(buf);
+        if (ch != '<') {
+            this.unreadChar(ch);
+            this.scanPCData(buf);
+        } else {
+            for (;;) {
+                ch = this.readChar();
+                if (ch == '!') {
+                    if (this.checkCDATA(buf)) {
+                        this.scanPCData(buf);
+                        break;
+                    } else {
+                        ch = this.scanWhitespace(buf);
+                        if (ch != '<') {
+                            this.unreadChar(ch);
+                            this.scanPCData(buf);
+                            break;
+                        }
+                    }
+                } else {
+                    if ((ch != '/') || this.ignoreWhitespace) {
+                        buf.setLength(0);
+                    }
+                    if (ch == '/') {
+                        this.unreadChar(ch);
+                    }
+                    break;
+                }
+            }
+        }
+        if (buf.length() == 0) {
+            while (ch != '/') {
+                if (ch == '!') {
+                    ch = this.readChar();
+                    if (ch != '-') {
+                        throw this.expectedInput("Comment or Element");
+                    }
+                    ch = this.readChar();
+                    if (ch != '-') {
+                        throw this.expectedInput("Comment or Element");
+                    }
+                    this.skipComment();
+                } else {
+                    this.unreadChar(ch);
+                    XMLElement child = this.createAnotherElement();
+                    this.scanElement(child);
+                    elt.addChild(child);
+                }
+                ch = this.scanWhitespace();
+                if (ch != '<') {
+                    throw this.expectedInput("<");
+                }
+                ch = this.readChar();
+            }
+            this.unreadChar(ch);
+        } else {
+            if (this.ignoreWhitespace) {
+                elt.setContent(buf.toString().trim());
+            } else {
+                elt.setContent(buf.toString());
+            }
+        }
+        ch = this.readChar();
+        if (ch != '/') {
+            throw this.expectedInput("/");
+        }
+        this.unreadChar(this.scanWhitespace());
+        if (! this.checkLiteral(name)) {
+            throw this.expectedInput(name);
+        }
+        if (this.scanWhitespace() != '>') {
+            throw this.expectedInput(">");
+        }
+    }
+
+
+    /**
+     * Resolves an entity. The name of the entity is read from the reader.
+     * The value of the entity is appended to <code>buf</code>.
+     *
+     * @param buf Where to put the entity value.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li>The first & has already been read.
+     *     <li><code>buf != null</code>
+     * </ul></dd></dl>
+     */
+    protected void resolveEntity(StringBuffer buf)
+        throws IOException
+    {
+        char ch = '\0';
+        StringBuffer keyBuf = new StringBuffer();
+        for (;;) {
+            ch = this.readChar();
+            if (ch == ';') {
+                break;
+            }
+            keyBuf.append(ch);
+        }
+        String key = keyBuf.toString();
+        if (key.charAt(0) == '#') {
+            try {
+                if (key.charAt(1) == 'x') {
+                    ch = (char) Integer.parseInt(key.substring(2), 16);
+                } else {
+                    ch = (char) Integer.parseInt(key.substring(1), 10);
+                }
+            } catch (NumberFormatException e) {
+                throw this.unknownEntity(key);
+            }
+            buf.append(ch);
+        } else {
+            char[] value = (char[]) this.entities.get(key);
+            if (value == null) {
+                throw this.unknownEntity(key);
+            }
+            buf.append(value);
+        }
+    }
+
+
+    /**
+     * Pushes a character back to the read-back buffer.
+     *
+     * @param ch The character to push back.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li>The read-back buffer is empty.
+     *     <li><code>ch != '\0'</code>
+     * </ul></dd></dl>
+     */
+    protected void unreadChar(char ch)
+    {
+        this.charReadTooMuch = ch;
+    }
+
+
+    /**
+     * Creates a parse exception for when an invalid valueset is given to
+     * a method.
+     *
+     * @param name The name of the entity.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     * </ul></dd></dl>
+     */
+    protected XMLParseException invalidValueSet(String name)
+    {
+        String msg = "Invalid value set (entity name = \"" + name + "\")";
+        return new XMLParseException(this.getName(), this.parserLineNr, msg);
+    }
+
+
+    /**
+     * Creates a parse exception for when an invalid value is given to a
+     * method.
+     *
+     * @param name  The name of the entity.
+     * @param value The value of the entity.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>value != null</code>
+     * </ul></dd></dl>
+     */
+    protected XMLParseException invalidValue(String name,
+                                             String value)
+    {
+        String msg = "Attribute \"" + name + "\" does not contain a valid "
+                   + "value (\"" + value + "\")";
+        return new XMLParseException(this.getName(), this.parserLineNr, msg);
+    }
+
+
+    /**
+     * Creates a parse exception for when the end of the data input has been
+     * reached.
+     */
+    protected XMLParseException unexpectedEndOfData()
+    {
+        String msg = "Unexpected end of data reached";
+        return new XMLParseException(this.getName(), this.parserLineNr, msg);
+    }
+
+
+    /**
+     * Creates a parse exception for when a syntax error occured.
+     *
+     * @param context The context in which the error occured.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>context != null</code>
+     *     <li><code>context.length() > 0</code>
+     * </ul></dd></dl>
+     */
+    protected XMLParseException syntaxError(String context)
+    {
+        String msg = "Syntax error while parsing " + context;
+        return new XMLParseException(this.getName(), this.parserLineNr, msg);
+    }
+
+
+    /**
+     * Creates a parse exception for when the next character read is not
+     * the character that was expected.
+     *
+     * @param charSet The set of characters (in human readable form) that was
+     *                expected.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>charSet != null</code>
+     *     <li><code>charSet.length() > 0</code>
+     * </ul></dd></dl>
+     */
+    protected XMLParseException expectedInput(String charSet)
+    {
+        String msg = "Expected: " + charSet;
+        return new XMLParseException(this.getName(), this.parserLineNr, msg);
+    }
+
+
+    /**
+     * Creates a parse exception for when an entity could not be resolved.
+     *
+     * @param name The name of the entity.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>name != null</code>
+     *     <li><code>name.length() > 0</code>
+     * </ul></dd></dl>
+     */
+    protected XMLParseException unknownEntity(String name)
+    {
+        String msg = "Unknown or invalid entity: &" + name + ";";
+        return new XMLParseException(this.getName(), this.parserLineNr, msg);
+    }
+    
+}
diff --git a/Sources/Lite/nanoxml/XMLParseException.java b/Sources/Lite/nanoxml/XMLParseException.java
new file mode 100644
index 0000000..86b1b1a
--- /dev/null
+++ b/Sources/Lite/nanoxml/XMLParseException.java
@@ -0,0 +1,130 @@
+/* XMLParseException.java
+ *
+ * $Revision: 1.4 $
+ * $Date: 2002/03/24 10:27:59 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of NanoXML 2 Lite.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ *****************************************************************************/
+
+
+package nanoxml;
+
+
+/**
+ * An XMLParseException is thrown when an error occures while parsing an XML
+ * string.
+ * <P>
+ * $Revision: 1.4 $<BR>
+ * $Date: 2002/03/24 10:27:59 $<P>
+ *
+ * @see nanoxml.XMLElement
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
+ */
+public class XMLParseException
+    extends RuntimeException
+{
+
+    /**
+     * Indicates that no line number has been associated with this exception.
+     */
+    public static final int NO_LINE = -1;
+
+
+    /**
+     * The line number in the source code where the error occurred, or
+     * <code>NO_LINE</code> if the line number is unknown.
+     *
+     * <dl><dt><b>Invariants:</b></dt><dd>
+     * <ul><li><code>lineNr &gt 0 || lineNr == NO_LINE</code>
+     * </ul></dd></dl>
+     */
+    private int lineNr;
+
+
+    /**
+     * Creates an exception.
+     *
+     * @param name    The name of the element where the error is located.
+     * @param message A message describing what went wrong.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>message != null</code>
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>getLineNr() => NO_LINE
+     * </ul></dd></dl><dl>
+     */
+    public XMLParseException(String name,
+                             String message)
+    {
+        super("XML Parse Exception during parsing of "
+              + ((name == null) ? "the XML definition"
+                                : ("a " + name + " element"))
+              + ": " + message);
+        this.lineNr = XMLParseException.NO_LINE;
+    }
+
+
+    /**
+     * Creates an exception.
+     *
+     * @param name    The name of the element where the error is located.
+     * @param lineNr  The number of the line in the input.
+     * @param message A message describing what went wrong.
+     *
+     * </dl><dl><dt><b>Preconditions:</b></dt><dd>
+     * <ul><li><code>message != null</code>
+     *     <li><code>lineNr > 0</code>
+     * </ul></dd></dl>
+     *
+     * <dl><dt><b>Postconditions:</b></dt><dd>
+     * <ul><li>getLineNr() => lineNr
+     * </ul></dd></dl><dl>
+     */
+    public XMLParseException(String name,
+                             int    lineNr,
+                             String message)
+    {
+        super("XML Parse Exception during parsing of "
+              + ((name == null) ? "the XML definition"
+                                : ("a " + name + " element"))
+              + " at line " + lineNr + ": " + message);
+        this.lineNr = lineNr;
+    }
+
+
+    /**
+     * Where the error occurred, or <code>NO_LINE</code> if the line number is
+     * unknown.
+     *
+     * @see nanoxml.XMLParseException#NO_LINE
+     */
+    public int getLineNr()
+    {
+        return this.lineNr;
+    }
+
+}
diff --git a/Sources/SAX/net/n3/nanoxml/sax/SAXAdapter.java b/Sources/SAX/net/n3/nanoxml/sax/SAXAdapter.java
new file mode 100644
index 0000000..66bd96b
--- /dev/null
+++ b/Sources/SAX/net/n3/nanoxml/sax/SAXAdapter.java
@@ -0,0 +1,334 @@
+/* SAXAdapter.java                                                  NanoXML/SAX
+ *
+ * $Revision: 1.4 $
+ * $Date: 2002/01/04 21:03:28 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of the SAX adapter for NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml.sax;
+
+
+import java.io.Reader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import net.n3.nanoxml.IXMLBuilder;
+import net.n3.nanoxml.IXMLReader;
+import org.xml.sax.AttributeList;
+import org.xml.sax.DocumentHandler;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.HandlerBase;
+import org.xml.sax.Locator;
+import org.xml.sax.helpers.AttributeListImpl;
+import org.xml.sax.helpers.LocatorImpl;
+
+
+/**
+ * SAXAdapter is the core adapter for using NanoXML/Java with SAX, the "Simple"
+ * API For XML.
+ *
+ * @see net.n3.nanoxml.IXMLBuilder
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
+ */
+public class SAXAdapter
+   implements IXMLBuilder
+{
+
+   /**
+    * The NanoXML reader.
+    */
+   private IXMLReader nanoxmlReader;
+
+
+   /**
+    * The SAX document handler.
+    */
+   private DocumentHandler saxDocumentHandler;
+
+
+   /**
+    * The SAX error handler.
+    */
+   private ErrorHandler saxErrorHandler;
+
+
+   /**
+    * The SAX locator.
+    */
+   private LocatorImpl saxLocator;
+
+
+   /**
+    * The SAX attribute list.
+    */
+   private AttributeListImpl saxAttributes;
+
+
+   /**
+    * Creates the adapter.
+    */
+   public SAXAdapter()
+   {
+      this.saxDocumentHandler = new HandlerBase();
+      this.saxLocator = new LocatorImpl();
+      this.saxLocator.setColumnNumber(-1);
+   }
+
+
+   /**
+    * Cleans up the object when it's destroyed.
+    */
+   protected void finalize()
+      throws Throwable
+   {
+      this.nanoxmlReader = null;
+      this.saxDocumentHandler = null;
+      this.saxErrorHandler = null;
+      this.saxLocator = null;
+      this.saxAttributes = null;
+      super.finalize();
+   }
+
+
+   /**
+    * Sets the document handler.
+    *
+    * @param handler the document handler
+    */
+   public void setDocumentHandler(DocumentHandler handler)
+   {
+      this.saxDocumentHandler = handler;
+   }
+
+
+   /**
+    * Sets the reader.
+    *
+    * @param reader the reader.
+    */
+   public void setReader(IXMLReader reader)
+   {
+      this.nanoxmlReader = reader;
+   }
+
+
+   /**
+    * This method is called before the parser starts processing its input.
+    *
+    * @param systemID the system ID of the data source
+    * @param lineNr the line on which the parsing starts
+    */
+   public void startBuilding(String systemID,
+                             int    lineNr)
+      throws Exception
+   {
+      this.saxLocator.setLineNumber(lineNr);
+      this.saxLocator.setSystemId(systemID);
+      this.saxDocumentHandler.setDocumentLocator(this.saxLocator);
+      this.saxDocumentHandler.startDocument();
+   }
+
+
+   /**
+    * This method is called when a processing instruction is encountered.
+    * PIs with target "xml" are handled by the parser.
+    *
+    * @param target the PI target
+    * @param reader to read the data from the PI
+    */
+   public void newProcessingInstruction(String target,
+                                        Reader reader)
+      throws Exception
+   {
+      StringBuffer data = new StringBuffer();
+      char[] chars = new char[1024];
+      int charsRead = reader.read(chars);
+
+      while (charsRead > 0) {
+         data.append(chars, 0, charsRead);
+         charsRead = reader.read(chars);
+      }
+
+      this.saxDocumentHandler.processingInstruction(target, data.toString());
+   }
+
+
+   /**
+    * This method is called when a new XML element is encountered.
+    *
+    * @see #endElement
+    *
+    * @param name the name of the element
+    * @param nsPrefix the prefix used to identify the namespace
+    * @param nsSystemId the system ID associated with the namespace
+    * @param systemID the system ID of the data source
+    * @param lineNr the line in the source where the element starts
+    */
+   public void startElement(String name,
+                            String nsPrefix,
+                            String nsSystemId,
+                            String systemID,
+                            int    lineNr)
+      throws Exception
+   {
+      if (nsPrefix != null) {
+         name = nsPrefix + ':' + name;
+      }
+
+      this.saxLocator.setLineNumber(lineNr);
+      this.saxLocator.setSystemId(systemID);
+      this.saxAttributes = new AttributeListImpl();
+   }
+
+
+   /**
+    * This method is called when the attributes of an XML element have been
+    * processed.
+    *
+    * @see #startElement
+    * @see #addAttribute
+    *
+    * @param name the name of the element
+    * @param nsPrefix the prefix used to identify the namespace
+    * @param nsSystemId the system ID associated with the namespace
+    */
+   public void elementAttributesProcessed(String name,
+                                          String nsPrefix,
+                                          String nsSystemId)
+      throws Exception
+   {
+      if (nsPrefix != null) {
+         name = nsPrefix + ':' + name;
+      }
+
+      this.saxDocumentHandler.startElement(name, this.saxAttributes);
+   }
+
+
+   /**
+    * This method is called when the end of an XML elemnt is encountered.
+    *
+    * @see #startElement
+    *
+    * @param name the name of the element
+    * @param nsPrefix the prefix used to identify the namespace
+    * @param nsSystemId the system ID associated with the namespace
+    */
+   public void endElement(String name,
+                          String nsPrefix,
+                          String nsSystemId)
+      throws Exception
+   {
+      if (nsPrefix != null) {
+         name = nsPrefix + ':' + name;
+      }
+
+      this.saxDocumentHandler.endElement(name);
+   }
+
+
+   /**
+    * This method is called when a new attribute of an XML element is
+    * encountered.
+    *
+    * @param key the key (name) of the attribute
+    * @param nsPrefix the prefix used to identify the namespace
+    * @param nsSystemId the system ID associated with the namespace
+    * @param value the value of the attribute
+    * @param type the type of the attribute ("CDATA" if unknown)
+    */
+   public void addAttribute(String key,
+                            String nsPrefix,
+                            String nsSystemId,
+                            String value,
+                            String type)
+      throws Exception
+   {
+      if (nsPrefix != null) {
+         key = nsPrefix + ':' + key;
+      }
+
+      this.saxAttributes.addAttribute(key, type, value);
+   }
+
+
+   /**
+    * This method is called when a PCDATA element is encountered. A Java
+    * reader is supplied from which you can read the data. The reader will
+    * only read the data of the element. You don't need to check for
+    * boundaries. If you don't read the full element, the rest of the data
+    * is skipped. You also don't have to care about entities; they are
+    * resolved by the parser.
+    *
+    * @param reader the Java reader from which you can retrieve the data
+    * @param systemID the system ID of the data source
+    * @param lineNr the line in the source where the element starts
+    *
+    * @throws java.io.IOException
+    *		when the reader throws such exception
+    */
+   public void addPCData(Reader reader,
+                         String systemID,
+                         int    lineNr)
+      throws Exception
+   {
+      this.saxLocator.setLineNumber(lineNr);
+      this.saxLocator.setSystemId(systemID);
+      char[] chars = new char[2048];
+      int charsRead = reader.read(chars);
+
+      while (charsRead > 0) {
+         this.saxDocumentHandler.characters(chars, 0, charsRead);
+         charsRead = reader.read(chars);
+      }
+   }
+
+
+   /**
+    * Returns the result of the building process. This method is called just
+    * before the parse() method of IXMLParser returns.
+    *
+    * @see net.n3.nanoxml.IXMLParser#parse
+    *
+    * @return the result of the building process.
+    */
+   public Object getResult()
+      throws Exception
+   {
+      return null;
+   }
+
+
+   /**
+    * Indicates that parsing has been completed.
+    */
+   public void endDocument()
+      throws Exception
+   {
+      this.saxDocumentHandler.endDocument();
+   }
+
+}
diff --git a/Sources/SAX/net/n3/nanoxml/sax/SAXEntityResolver.java b/Sources/SAX/net/n3/nanoxml/sax/SAXEntityResolver.java
new file mode 100644
index 0000000..196cf2e
--- /dev/null
+++ b/Sources/SAX/net/n3/nanoxml/sax/SAXEntityResolver.java
@@ -0,0 +1,144 @@
+/* SAXEntityResolver.java                                           NanoXML/SAX
+ *
+ * $Revision: 1.4 $
+ * $Date: 2002/01/04 21:03:28 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of the SAX adapter for NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml.sax;
+
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URL;
+import net.n3.nanoxml.XMLEntityResolver;
+import net.n3.nanoxml.IXMLReader;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+
+
+/**
+ * SAXEntityResolver is a subclass of XMLEntityResolver that supports the
+ * SAX EntityResolver listener.
+ *
+ * @see net.n3.nanoxml.IXMLEntityResolver
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.4 $
+ */
+public class SAXEntityResolver
+   extends XMLEntityResolver
+{
+
+   /**
+    * The SAX EntityResolver listener.
+    */
+   private EntityResolver saxEntityResolver;
+
+
+   /**
+    * Creates the resolver.
+    */
+   public SAXEntityResolver()
+   {
+      this.saxEntityResolver = null;
+   }
+
+
+   /**
+    * Cleans up the object when it's destroyed.
+    */
+   protected void finalize()
+      throws Throwable
+   {
+      this.saxEntityResolver = null;
+      super.finalize();
+   }
+
+
+   /**
+    * Sets the SAX EntityResolver listener.
+    *
+    * @param resolver the entity resolver
+    */
+   public void setEntityResolver(EntityResolver resolver)
+   {
+      this.saxEntityResolver = resolver;
+   }
+
+
+   /**
+    * Opens an external entity.
+    *
+    * @param xmlReader the current XML reader
+    * @param publicID the public ID, which may be null
+    * @param systemID the system ID
+    *
+    * @return the reader, or null if the reader could not be created/opened
+    */
+   protected Reader openExternalEntity(IXMLReader xmlReader,
+                                       String     publicID,
+                                       String     systemID)
+   {
+      try {
+         URL url = new URL(xmlReader.getSystemID());
+         url = new URL(url, systemID);
+
+         if (this.saxEntityResolver != null) {
+            InputSource source
+            = this.saxEntityResolver
+            .resolveEntity(publicID, url.toString());
+
+            if (source != null) {
+               Reader reader = source.getCharacterStream();
+
+               if (reader != null) {
+                  return reader;
+               }
+
+               InputStream stream = source.getByteStream();
+
+               if (stream == null) {
+                  publicID = source.getPublicId();
+                  systemID = source.getSystemId();
+               } else {
+                  String encoding = source.getEncoding();
+
+                  if (encoding != null) {
+                     return new InputStreamReader(stream, encoding);
+                  } else { // if encoding == null
+                     return new InputStreamReader(stream);
+                  }
+               }
+            }
+         }
+
+         return super.openExternalEntity(xmlReader, publicID, systemID);
+      } catch (Exception e) {
+         return null;
+      }
+   }
+
+}
diff --git a/Sources/SAX/net/n3/nanoxml/sax/SAXParser.java b/Sources/SAX/net/n3/nanoxml/sax/SAXParser.java
new file mode 100644
index 0000000..2736c7d
--- /dev/null
+++ b/Sources/SAX/net/n3/nanoxml/sax/SAXParser.java
@@ -0,0 +1,263 @@
+/* SAXParser.java                                                   NanoXML/SAX
+ *
+ * $Revision: 1.5 $
+ * $Date: 2002/03/24 11:39:20 $
+ * $Name: RELEASE_2_2_1 $
+ *
+ * This file is part of the SAX adapter for NanoXML 2 for Java.
+ * Copyright (C) 2000-2002 Marc De Scheemaecker, All Rights Reserved.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from the
+ * use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *  1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software in
+ *     a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ *
+ *  2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ *
+ *  3. This notice may not be removed or altered from any source distribution.
+ */
+
+package net.n3.nanoxml.sax;
+
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.util.Locale;
+import net.n3.nanoxml.IXMLBuilder;
+import net.n3.nanoxml.IXMLParser;
+import net.n3.nanoxml.IXMLReader;
+import net.n3.nanoxml.StdXMLReader;
+import net.n3.nanoxml.XMLParserFactory;
+import org.xml.sax.Parser;
+import org.xml.sax.DocumentHandler;
+import org.xml.sax.DTDHandler;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.HandlerBase;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributeListImpl;
+import org.xml.sax.helpers.LocatorImpl;
+
+
+/**
+ * SAXParser implements the SAX Parser interface. It is the frontend to SAX
+ * for the NanoXML parser.
+ *
+ * @author Marc De Scheemaecker
+ * @version $Name: RELEASE_2_2_1 $, $Revision: 1.5 $
+ */
+public class SAXParser
+   implements Parser
+{
+
+   /**
+    * The SAX adapter.
+    */
+   private SAXAdapter adapter;
+    
+
+   /**
+    * The client error handler.
+    */
+   private ErrorHandler errorHandler;
+
+
+   /**
+    * The entity resolver.
+    */
+   private SAXEntityResolver entityResolver;
+
+
+   /**
+    * Creates the SAX parser.
+    */
+   public SAXParser()
+   {
+      this.adapter = new SAXAdapter();
+      this.errorHandler = new HandlerBase();
+      this.entityResolver = new SAXEntityResolver();
+   }
+
+
+   /**
+    * Cleans up the object when it's destroyed.
+    */
+   protected void finalize()
+      throws Throwable
+   {
+      this.adapter = null;
+      this.errorHandler = null;
+      this.entityResolver = null;
+      super.finalize();
+   }
+
+
+   /**
+    * Sets the locale. Only locales using the language english are accepted.
+    *
+    * @param locale the locale
+    *
+    * @exception org.xml.sax.SAXException
+    *    if <CODE>locale</CODE> is <CODE>null</CODE> or the associated
+    *    language is not english.
+    */
+   public void setLocale(Locale locale)
+      throws SAXException
+   {
+      if ((locale == null) || (! locale.getLanguage().equals("en"))) {
+         throw new SAXException("NanoXML/SAX doesn't support locale: "
+                                + locale);
+      }
+   }
+
+
+   /**
+    * Sets the entity resolver.
+    *
+    * @param resolver the entity resolver
+    */
+   public void setEntityResolver(EntityResolver resolver)
+   {
+      this.entityResolver.setEntityResolver(resolver);
+   }
+
+
+   /**
+    * Sets the DTD handler. As the parser is non-validating, this handler is
+    * never called.
+    *
+    * @param handler the DTD handler
+    */
+   public void setDTDHandler(DTDHandler handler)
+   {
+      // nothing to do
+   }
+
+
+   /**
+    * Allows an application to register a document event handler.
+    *
+    * @param handler the document handler
+    */
+   public void setDocumentHandler(DocumentHandler handler)
+   {
+      this.adapter.setDocumentHandler(handler);
+   }
+
+
+   /**
+    * Allow an application to register an error event handler.
+    *
+    * @param handler the error handler
+    */
+   public void setErrorHandler(ErrorHandler handler)
+   {
+      this.errorHandler = handler;
+   }
+
+
+   /**
+    * Creates the XML parser.
+    */
+   private IXMLParser createParser()
+      throws SAXException
+   {
+      try {
+         return XMLParserFactory.createDefaultXMLParser();
+      } catch (Exception exception) {
+         throw new SAXException(exception);
+      }
+   }
+
+
+   /**
+    * Parse an XML document.
+    *
+    * @param source the input source
+    */
+   public void parse(InputSource source)
+      throws SAXException,
+             IOException
+   {
+      IXMLParser parser = this.createParser();
+      parser.setBuilder(this.adapter);
+      parser.setResolver(this.entityResolver);
+      Reader reader = source.getCharacterStream();
+
+      if (reader != null) {
+         parser.setReader(new StdXMLReader(reader));
+      } else {
+         InputStream stream = source.getByteStream();
+
+         if (stream != null) {
+            String encoding = source.getEncoding();
+
+            if (encoding != null) {
+               try {
+                  reader = new InputStreamReader(stream, encoding);
+                  parser.setReader(new StdXMLReader(reader));
+               } catch (UnsupportedEncodingException exception) {
+                  throw new SAXException(exception);
+               }
+            } else { // if encoding == null
+               parser.setReader(new StdXMLReader(stream));
+            }
+         } else { // if stream == null
+            parser.setReader(new StdXMLReader(source.getPublicId(),
+                                              source.getSystemId()));
+         }
+      }
+
+      try {
+         parser.parse();
+         this.adapter.endDocument();
+      } catch (IOException exception) {
+         throw exception;
+      } catch (Exception exception) {
+         throw new SAXException(exception);
+      } finally {
+          reader.close();
+      }
+   }
+
+
+   /**
+    * Parse an XML document from a system identifier (URI).
+    *
+    * @param systemId the system ID
+    */
+   public void parse(String systemId)
+      throws SAXException,
+             IOException
+   {
+      IXMLParser parser = this.createParser();
+      parser.setBuilder(this.adapter);
+      parser.setReader(new StdXMLReader(null, systemId));
+
+      try {
+         parser.parse();
+         this.adapter.endDocument();
+      } catch (IOException exception) {
+         throw exception;
+      } catch (Exception exception) {
+         throw new SAXException(exception);
+      }
+   }
+
+}
diff --git a/Test/Java/DumpXML.java b/Test/Java/DumpXML.java
new file mode 100644
index 0000000..4f0a326
--- /dev/null
+++ b/Test/Java/DumpXML.java
@@ -0,0 +1,50 @@
+/* DumpXML.java                                                    NanoXML/Java
+ *
+ * $Revision: 1.2 $
+ * $Date: 2002/01/03 20:25:27 $
+ * $Name:  $
+ *
+ * This file is part of NanoXML 2 for Java.
+ * 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.
+ */
+
+import net.n3.nanoxml.*;
+
+
+public class DumpXML
+{
+
+    public static void main(String args[])
+        throws Exception
+    {
+        if (args.length == 0) {
+            System.err.println("Usage: java DumpXML file.xml");
+            Runtime.getRuntime().exit(1);
+        }
+        
+        IXMLParser parser = XMLParserFactory.createDefaultXMLParser();
+        IXMLReader reader = StdXMLReader.fileReader(args[0]);
+        parser.setReader(reader);
+        XMLElement xml = (XMLElement) parser.parse();
+        (new XMLWriter(System.out)).write(xml, false);
+    }
+
+}
diff --git a/Test/Java/attributes.xml b/Test/Java/attributes.xml
new file mode 100644
index 0000000..da04256
--- /dev/null
+++ b/Test/Java/attributes.xml
@@ -0,0 +1,26 @@
+<!DOCTYPE FOO [
+	<!ENTITY cr "
">
+	<!ENTITY lf "
">
+	<!ENTITY lf2 "
+">
+	<!ENTITY sp " ">
+	<!ENTITY sp2 " ">
+	<!ENTITY tab "	">
+	<!ENTITY bell "">
+]>
+
+<FOO cr="&cr;"
+     cr2="
"
+     lf="&lf;"
+     lf2="&lf2;"
+     lf3="
"
+     lf4="
+"
+     sp="&sp;"
+     sp2="&sp2;"
+     sp3=" "
+     sp4=" "
+     tab="&tab;"
+     bell="&bell;"
+     bell2=""
+     all="&sp;&lf;&lf2;&cr;&tab;&bell;"/>
diff --git a/Test/Java/attributes.xml.out b/Test/Java/attributes.xml.out
new file mode 100644
index 0000000..c9a346e
--- /dev/null
+++ b/Test/Java/attributes.xml.out
@@ -0,0 +1,4 @@
+<FOO cr="&#xd;" cr2="&#xd;" lf="
+" lf2=" " lf3="
+" lf4=" " sp=" " sp2=" " sp3=" " sp4=" " tab="&#x9;" bell="&#x7;" bell2="&#x7;" all=" 
+ &#xd;&#x9;&#x7;"/>
\ No newline at end of file
diff --git a/Test/Java/complex_pe.xml b/Test/Java/complex_pe.xml
new file mode 100644
index 0000000..a0c303d
--- /dev/null
+++ b/Test/Java/complex_pe.xml
@@ -0,0 +1,12 @@
+<?xml version='1.0'?>
+<!DOCTYPE test [
+	<!ELEMENT test (#PCDATA)>
+	<!ENTITY % xx '%zz;'>
+	<!ENTITY % zz '<!ENTITY tricky "error-prone" >' >
+	%xx;
+	<!ENTITY % Boolean '(True|False)'>
+	<!ATTLIST test foo %Boolean; 'True'>
+]>
+
+<test>This sample shows a &tricky; method.</test>
+
diff --git a/Test/Java/complex_pe.xml.out b/Test/Java/complex_pe.xml.out
new file mode 100644
index 0000000..8c0ea5e
--- /dev/null
+++ b/Test/Java/complex_pe.xml.out
@@ -0,0 +1 @@
+<test foo="True">This sample shows a error-prone method.</test>
\ No newline at end of file
diff --git a/Test/Java/double_dtd.xml b/Test/Java/double_dtd.xml
new file mode 100644
index 0000000..2da46ac
--- /dev/null
+++ b/Test/Java/double_dtd.xml
@@ -0,0 +1,7 @@
+
+<!DOCTYPE FOO SYSTEM "test.dtd" [
+    <!ENTITY % extParamEntity "blah">
+]>
+
+<FOO x='1'>&value;</FOO>
+
diff --git a/Test/Java/double_dtd.xml.out b/Test/Java/double_dtd.xml.out
new file mode 100644
index 0000000..64a7a93
--- /dev/null
+++ b/Test/Java/double_dtd.xml.out
@@ -0,0 +1 @@
+<FOO x="1" z="defaultValue" y="fixedValue">blah</FOO>
\ No newline at end of file
diff --git a/Test/Java/external_dtd.xml b/Test/Java/external_dtd.xml
new file mode 100644
index 0000000..91b8590
--- /dev/null
+++ b/Test/Java/external_dtd.xml
@@ -0,0 +1,4 @@
+<!DOCTYPE FOO SYSTEM "test.dtd">
+
+<FOO x='1'>&value;</FOO>
+
diff --git a/Test/Java/external_dtd.xml.out b/Test/Java/external_dtd.xml.out
new file mode 100644
index 0000000..fb26ff3
--- /dev/null
+++ b/Test/Java/external_dtd.xml.out
@@ -0,0 +1,2 @@
+<FOO x="1" z="defaultValue" y="fixedValue">INCLUDE
+</FOO>
\ No newline at end of file
diff --git a/Test/Java/include.ent b/Test/Java/include.ent
new file mode 100644
index 0000000..42ddeae
--- /dev/null
+++ b/Test/Java/include.ent
@@ -0,0 +1 @@
+INCLUDE
diff --git a/Test/Java/internal_dtd.xml b/Test/Java/internal_dtd.xml
new file mode 100644
index 0000000..fe17482
--- /dev/null
+++ b/Test/Java/internal_dtd.xml
@@ -0,0 +1,16 @@
+<!DOCTYPE FOO [
+    <!ENTITY % extParamEntity SYSTEM "include.ent">
+    <!ENTITY value "%extParamEntity;">
+    <!ENTITY % name "FOO">
+    <!ELEMENT %name; (#PCDATA)>
+    <!ENTITY % arg "x CDATA #REQUIRED y CDATA #FIXED 'fixedValue'">
+    <!ATTLIST %name;
+        %arg;
+        z CDATA "defaultValue">
+    <!ENTITY tab "	">
+    <!ENTITY lf "
">
+    <!ENTITY cr "
">
+]>
+
+<FOO x='1'>&value;&tab;&lf;
&cr;</FOO>
+
diff --git a/Test/Java/internal_dtd.xml.out b/Test/Java/internal_dtd.xml.out
new file mode 100644
index 0000000..aaaff5a
--- /dev/null
+++ b/Test/Java/internal_dtd.xml.out
@@ -0,0 +1,3 @@
+<FOO x="1" z="defaultValue" y="fixedValue">INCLUDE
+&#x9;
+&#xd;&#xd;</FOO>
\ No newline at end of file
diff --git a/Test/Java/namespaces.xml b/Test/Java/namespaces.xml
new file mode 100644
index 0000000..eec9a37
--- /dev/null
+++ b/Test/Java/namespaces.xml
@@ -0,0 +1,22 @@
+<!DOCTYPE Foo [
+    <!ELEMENT Foo (ns:Bar)>
+    <!ATTLIST Foo
+        xmlns CDATA #FIXED 'http://nanoxml.n3.net/foo'
+        a     CDATA #REQUIRED>
+        
+    <!ELEMENT ns:Bar (Blah)>
+    <!ATTLIST ns:Bar
+        xmlns:ns CDATA #FIXED 'http://nanoxml.n3.net/bar'>
+        
+    <!ELEMENT Blah EMPTY>
+    <!ATTLIST Blah
+        x    CDATA #REQUIRED
+        ns:x CDATA #REQUIRED>
+]>
+
+<Foo a='test'>
+    <ns:Bar>
+        <Blah x='1' ns:x='2'/>
+    </ns:Bar>
+</Foo>
+
diff --git a/Test/Java/namespaces.xml.out b/Test/Java/namespaces.xml.out
new file mode 100644
index 0000000..f18eb13
--- /dev/null
+++ b/Test/Java/namespaces.xml.out
@@ -0,0 +1 @@
+<Foo xmlns="http://nanoxml.n3.net/foo" a="test"><ns:Bar xmlns:ns="http://nanoxml.n3.net/bar"><Blah xmlns="http://nanoxml.n3.net/foo" xmlns:ns="http://nanoxml.n3.net/bar" x="1" ns:x="2"/></ns:Bar></Foo>
\ No newline at end of file
diff --git a/Test/Java/simple.xml b/Test/Java/simple.xml
new file mode 100644
index 0000000..ff4ec50
--- /dev/null
+++ b/Test/Java/simple.xml
@@ -0,0 +1,17 @@
+<FOO>
+    <BAR/>
+    <BAR x='1'/>
+    <BAR x='1' y='2'/>
+    <BAR>
+        <BAR/>
+        <BARBAR/>
+        <BA/>
+        <FOO/>
+    </BAR>
+    <BAR>blah</BAR>
+    <BAR x='1<'>blah</BAR>
+    <BAR><![CDATA[<&>'"]]></BAR>
+    <BAR>a<![CDATA[b]]>c</BAR>
+	<	

<&>"'!&#x3A;&#x2030;
+</FOO>
+
diff --git a/Test/Java/simple.xml.out b/Test/Java/simple.xml.out
new file mode 100644
index 0000000..f8a83c5
--- /dev/null
+++ b/Test/Java/simple.xml.out
@@ -0,0 +1,4 @@
+<FOO><BAR/><BAR x="1"/><BAR x="1" y="2"/><BAR><BAR/><BARBAR/><BA/><FOO/></BAR><BAR>blah</BAR><BAR x="1<">blah</BAR><BAR><&>'"</BAR><BAR>abc</BAR>
+ <&#x9;
+&#xd;<&>"'!:&#x2030;
+</FOO>
\ No newline at end of file
diff --git a/Test/Java/test.dtd b/Test/Java/test.dtd
new file mode 100644
index 0000000..5103389
--- /dev/null
+++ b/Test/Java/test.dtd
@@ -0,0 +1,7 @@
+<!ENTITY % extParamEntity SYSTEM "include.ent">
+<!ENTITY value "%extParamEntity;">
+<!ELEMENT FOO (#PCDATA)>
+<!ATTLIST FOO 
+        x CDATA #REQUIRED
+        y CDATA #FIXED "fixedValue"
+        z CDATA "defaultValue">
diff --git a/Test/Lite/DumpXML_Lite.java b/Test/Lite/DumpXML_Lite.java
new file mode 100644
index 0000000..40c15f5
--- /dev/null
+++ b/Test/Lite/DumpXML_Lite.java
@@ -0,0 +1,57 @@
+/* DumpXML_Lite.java                                               NanoXML/Lite
+ *
+ * $Revision: 1.2 $
+ * $Date: 2002/01/03 20:19:36 $
+ * $Name:  $
+ *
+ * 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.
+ */
+
+import nanoxml.*;
+import java.util.*;
+import java.io.*;
+
+
+public class DumpXML_Lite
+{
+
+    public static void main(String args[])
+        throws Exception
+    {
+        if (args.length == 0) {
+            System.err.println("Usage: java DumpXML_Lite file.xml");
+            Runtime.getRuntime().exit(1);
+        }
+        
+        Hashtable entities = new Hashtable();
+        entities.put("foo", "bar");
+        XMLElement xml = new XMLElement(entities, false, false);
+        FileReader reader = new FileReader(args[0]);
+        xml.parseFromReader(reader);
+        reader.close();
+        reader = new FileReader(args[0]);
+        xml.parseFromReader(reader);
+        reader.close();
+        System.out.println(xml.toString());
+    }
+
+}
diff --git a/Test/Lite/comments.xml b/Test/Lite/comments.xml
new file mode 100644
index 0000000..3d46ab2
--- /dev/null
+++ b/Test/Lite/comments.xml
@@ -0,0 +1,10 @@
+<!-- comment before FOO -->
+<FOO>
+    <!-- comment before BARs -->
+    <BAR/>
+    <!-- comment between BARs -->
+    <BAR/>
+    <!-- comment after BARs -->
+</FOO>
+<!-- comment after FOO -->
+
diff --git a/Test/Lite/comments.xml.out b/Test/Lite/comments.xml.out
new file mode 100644
index 0000000..4f0d99e
--- /dev/null
+++ b/Test/Lite/comments.xml.out
@@ -0,0 +1 @@
+<FOO><BAR/><BAR/></FOO>
diff --git a/Test/Lite/simple.xml b/Test/Lite/simple.xml
new file mode 100644
index 0000000..9609a1c
--- /dev/null
+++ b/Test/Lite/simple.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="US-ASCII"?>
+<FOO>
+    <BAR/>
+    <BAR x='1'/>
+    <BAR x='1' y='2'/>
+    <BAR>
+        <BAR/>
+        <BARBAR/>
+        <BA/>
+        <FOO/>
+    </BAR>
+    <BAR>blah</BAR>
+    <BAR> </BAR>
+    <BAR x='1'>blah</BAR>
+    <BAR><![CDATA[<&>'"]]></BAR>
+    <BAR>a<![CDATA[b]]>c</BAR>
+    <BAR><&>"'!&#x3A;&#x2030;</BAR>
+</FOO>
+
diff --git a/Test/Lite/simple.xml.out b/Test/Lite/simple.xml.out
new file mode 100644
index 0000000..e8708b8
--- /dev/null
+++ b/Test/Lite/simple.xml.out
@@ -0,0 +1 @@
+<FOO><BAR/><BAR x="1"/><BAR x="1" y="2"/><BAR><BAR/><BARBAR/><BA/><FOO/></BAR><BAR>blah</BAR><BAR> </BAR><BAR x="1">blah</BAR><BAR><&>'"</BAR><BAR>abc</BAR><BAR><&>"'!:&#x2030;</BAR></FOO>
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..2315bbe
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+CURRDIR="`pwd`"; export CURRDIR
+mkdir Output
+rm -rf "/tmp/nanoxml-build-$$"
+rm -rf "/tmp/nanoxml-sax-build-$$"
+rm -rf "/tmp/nanoxml-lite-build-$$"
+
+echo "Compiling NanoXML/Lite 2.2.3"
+mkdir "/tmp/nanoxml-lite-build-$$"
+(cd Sources/Lite; javac -deprecation -target 1.1 -g:none -d "/tmp/nanoxml-lite-build-$$" nanoxml/*.java) || exit 1
+(cd /tmp/nanoxml-lite-build-$$; jar cMf "$CURRDIR/Output/nanoxml-lite.jar" nanoxml/*.class) || exit 1
+(cd Test/Lite; javac -classpath ":$CURRDIR/Output/nanoxml-lite.jar:." DumpXML_Lite.java) || exit 1
+
+echo "Compiling NanoXML/Java 2.2.3"
+mkdir "/tmp/nanoxml-build-$$"
+(cd Sources/Java; javac -deprecation -g:none -d "/tmp/nanoxml-build-$$" net/n3/nanoxml/*.java) || exit 1
+(cd /tmp/nanoxml-build-$$; jar cMf "$CURRDIR/Output/nanoxml.jar" net/n3/nanoxml/*.class) || exit 1
+(cd Test/Java; javac -classpath ":$CURRDIR/Output/nanoxml.jar:." DumpXML.java) || exit 1
+
+echo "Compiling NanoXML/SAX 2.2.3"
+CLASSPATH=":$CURRDIR/ThirdParty/SAX/sax.jar:/tmp/nanoxml-build-$$:."; export CLASSPATH
+mkdir "/tmp/nanoxml-sax-build-$$"
+mkdir "/tmp/nanoxml-sax-build-$$/net"
+mkdir "/tmp/nanoxml-sax-build-$$/net/n3"
+mkdir "/tmp/nanoxml-sax-build-$$/net/n3/nanoxml"
+for a in ContentReader.class IXMLBuilder.class IXMLParser.class IXMLReader.class IXMLValidator.class NonValidator.class StdXMLParser.class StdXMLReader.class XMLEntityResolver.class XMLParseException.class XMLParserFactory.class XMLUtil.class XMLValidationException.class
+    do cp "/tmp/nanoxml-build-$$/net/n3/nanoxml/$a" "/tmp/nanoxml-sax-build-$$/net/n3/nanoxml/"
+  done
+(cd Sources/SAX; javac -g:none -d "/tmp/nanoxml-build-$$" net/n3/nanoxml/sax/*.java) || exit 1
+(cd /tmp/nanoxml-build-$$; jar cMf "$CURRDIR/Output/nanoxml-sax.jar" net/n3/nanoxml/sax/*.class)
+
+echo "Generating JavaDoc"
+mkdir "$CURRDIR/Documentation/JavaDoc"
+javadoc -protected -sourcepath "$CURRDIR/Sources/Lite:$CURRDIR/Sources/Java:$CURRDIR/Sources/SAX"         -classpath "/tmp/nanoxml-lite-build-$$:/tmp/nanoxml-build-$$:/tmp/nanoxml-sax-build-$$:$CURRDIR/Thirdparty/SAX/sax.jar"         -d "$CURRDIR/Documentation/JavaDoc" -version -author -windowtitle "NanoXML 2.2.3"         net.n3.nanoxml         net.n3.nanoxml.sax         nanoxml >"/tmp/javadoc.log" 2>&1 || (cat /tmp/javadoc.log; exit 1) || exit 1
+
+rm -rf "/tmp/nanoxml-lite-build-$$"
+rm -rf "/tmp/nanoxml-sax-build-$$"
+rm -rf "/tmp/nanoxml-build-$$"
+
+echo "Done"
+echo "Output is in $CURRDIR/Output/"
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/libnanoxml2-java.git



More information about the pkg-java-commits mailing list