/*
* Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
*
* This software is open source.
* See the bottom of this file for the licence.
*/
package org.dom4j.io;
import java.lang.reflect.
Method;
import java.util.
ArrayList;
import java.util.
HashMap;
import java.util.
List;
import java.util.
Map;
import org.dom4j.
Branch;
import org.dom4j.
Document;
import org.dom4j.
DocumentFactory;
import org.dom4j.
DocumentType;
import org.dom4j.
Element;
import org.dom4j.
ElementHandler;
import org.dom4j.
Namespace;
import org.dom4j.
QName;
import org.dom4j.dtd.
AttributeDecl;
import org.dom4j.dtd.
ElementDecl;
import org.dom4j.dtd.
ExternalEntityDecl;
import org.dom4j.dtd.
InternalEntityDecl;
import org.dom4j.tree.
AbstractElement;
import org.dom4j.tree.
NamespaceStack;
import org.xml.sax.
Attributes;
import org.xml.sax.
DTDHandler;
import org.xml.sax.
EntityResolver;
import org.xml.sax.
InputSource;
import org.xml.sax.
Locator;
import org.xml.sax.
SAXException;
import org.xml.sax.
SAXParseException;
import org.xml.sax.ext.
DeclHandler;
import org.xml.sax.ext.
LexicalHandler;
import org.xml.sax.helpers.
DefaultHandler;
/**
* <p>
* <code>SAXContentHandler</code> builds a dom4j tree via SAX events.
* </p>
*
* @author <a href="mailto:jstrachan@apache.org">James Strachan </a>
* @version $Revision: 1.61 $
*/
public class
SAXContentHandler extends
DefaultHandler implements
LexicalHandler,
DeclHandler,
DTDHandler {
/** The factory used to create new <code>Document</code> instances */
private
DocumentFactory documentFactory;
/** The document that is being built */
private
Document document;
/** stack of <code>Element</code> objects */
private
ElementStack elementStack;
/** stack of <code>Namespace</code> and <code>QName</code> objects */
private
NamespaceStack namespaceStack;
/** the <code>ElementHandler</code> called as the elements are complete */
private
ElementHandler elementHandler;
/** the Locator */
private
Locator locator;
/** The name of the current entity */
private
String entity;
/** Flag used to indicate that we are inside a DTD section */
private boolean
insideDTDSection;
/** Flag used to indicate that we are inside a CDATA section */
private boolean
insideCDATASection;
/**
* buffer to hold contents of cdata section across multiple characters
* events
*/
private
StringBuffer cdataText;
/** namespaces that are available for use */
private
Map availableNamespaceMap = new
HashMap();
/** declared namespaces that are not yet available for use */
private
List declaredNamespaceList = new
ArrayList();
/** internal DTD declarations */
private
List internalDTDDeclarations;
/** external DTD declarations */
private
List externalDTDDeclarations;
/** The number of namespaces that are declared in the current scope */
private int
declaredNamespaceIndex;
/** The entity resolver */
private
EntityResolver entityResolver;
private
InputSource inputSource;
/** The current element we are on */
private
Element currentElement;
/** Should internal DTD declarations be expanded into a List in the DTD */
private boolean
includeInternalDTDDeclarations = false;
/** Should external DTD declarations be expanded into a List in the DTD */
private boolean
includeExternalDTDDeclarations = false;
/** The number of levels deep we are inside a startEntity/endEntity call */
private int
entityLevel;
/** Are we in an internal DTD subset? */
private boolean
internalDTDsubset = false;
/** Whether adjacent text nodes should be merged */
private boolean
mergeAdjacentText = false;
/** Have we added text to the buffer */
private boolean
textInTextBuffer = false;
/** Should we ignore comments */
private boolean
ignoreComments = false;
/** Buffer used to concatenate text together */
private
StringBuffer textBuffer;
/** Holds value of property stripWhitespaceText. */
private boolean
stripWhitespaceText = false;
public
SAXContentHandler() {
this(
DocumentFactory.
getInstance());
}
public
SAXContentHandler(
DocumentFactory documentFactory) {
this(
documentFactory, null);
}
public
SAXContentHandler(
DocumentFactory documentFactory,
ElementHandler elementHandler) {
this(
documentFactory,
elementHandler, null);
this.
elementStack =
createElementStack();
}
public
SAXContentHandler(
DocumentFactory documentFactory,
ElementHandler elementHandler,
ElementStack elementStack) {
this.
documentFactory =
documentFactory;
this.
elementHandler =
elementHandler;
this.
elementStack =
elementStack;
this.
namespaceStack = new
NamespaceStack(
documentFactory);
}
/**
* DOCUMENT ME!
*
* @return the document that has been or is being built
*/
public
Document getDocument() {
if (
document == null) {
document =
createDocument();
}
return
document;
}
// ContentHandler interface
// -------------------------------------------------------------------------
public void
setDocumentLocator(
Locator documentLocator) {
this.
locator =
documentLocator;
}
public void
processingInstruction(
String target,
String data)
throws
SAXException {
if (
mergeAdjacentText &&
textInTextBuffer) {
completeCurrentTextNode();
}
if (
currentElement != null) {
currentElement.
addProcessingInstruction(
target,
data);
} else {
getDocument().
addProcessingInstruction(
target,
data);
}
}
public void
startPrefixMapping(
String prefix,
String uri)
throws
SAXException {
namespaceStack.
push(
prefix,
uri);
}
public void
endPrefixMapping(
String prefix) throws
SAXException {
namespaceStack.
pop(
prefix);
declaredNamespaceIndex =
namespaceStack.
size();
}
public void
startDocument() throws
SAXException {
// document = createDocument();
document = null;
currentElement = null;
elementStack.
clear();
if ((
elementHandler != null)
&& (
elementHandler instanceof
DispatchHandler)) {
elementStack.
setDispatchHandler((
DispatchHandler)
elementHandler);
}
namespaceStack.
clear();
declaredNamespaceIndex = 0;
if (
mergeAdjacentText && (
textBuffer == null)) {
textBuffer = new
StringBuffer();
}
textInTextBuffer = false;
}
public void
endDocument() throws
SAXException {
namespaceStack.
clear();
elementStack.
clear();
currentElement = null;
textBuffer = null;
}
public void
startElement(
String namespaceURI,
String localName,
String qualifiedName,
Attributes attributes) throws
SAXException {
if (
mergeAdjacentText &&
textInTextBuffer) {
completeCurrentTextNode();
}
QName qName =
namespaceStack.
getQName(
namespaceURI,
localName,
qualifiedName);
Branch branch =
currentElement;
if (
branch == null) {
branch =
getDocument();
}
Element element =
branch.
addElement(
qName);
// add all declared namespaces
addDeclaredNamespaces(
element);
// now lets add all attribute values
addAttributes(
element,
attributes);
elementStack.
pushElement(
element);
currentElement =
element;
entity = null; // fixes bug527062
if (
elementHandler != null) {
elementHandler.
onStart(
elementStack);
}
}
public void
endElement(
String namespaceURI,
String localName,
String qName)
throws
SAXException {
if (
mergeAdjacentText &&
textInTextBuffer) {
completeCurrentTextNode();
}
if ((
elementHandler != null) && (
currentElement != null)) {
elementHandler.
onEnd(
elementStack);
}
elementStack.
popElement();
currentElement =
elementStack.
peekElement();
}
public void
characters(char[]
ch, int
start, int
end) throws
SAXException {
if (
end == 0) {
return;
}
if (
currentElement != null) {
if (
entity != null) {
if (
mergeAdjacentText &&
textInTextBuffer) {
completeCurrentTextNode();
}
currentElement.
addEntity(
entity, new
String(
ch,
start,
end));
entity = null;
} else if (
insideCDATASection) {
if (
mergeAdjacentText &&
textInTextBuffer) {
completeCurrentTextNode();
}
cdataText.
append(new
String(
ch,
start,
end));
} else {
if (
mergeAdjacentText) {
textBuffer.
append(
ch,
start,
end);
textInTextBuffer = true;
} else {
currentElement.
addText(new
String(
ch,
start,
end));
}
}
}
}
// ErrorHandler interface
// -------------------------------------------------------------------------
/**
* This method is called when a warning occurs during the parsing of the
* document. This method does nothing.
*
* @param exception
* DOCUMENT ME!
*
* @throws SAXException
* DOCUMENT ME!
*/
public void
warning(
SAXParseException exception) throws
SAXException {
// ignore warnings by default
}
/**
* This method is called when an error is detected during parsing such as a
* validation error. This method rethrows the exception
*
* @param exception
* DOCUMENT ME!
*
* @throws SAXException
* DOCUMENT ME!
*/
public void
error(
SAXParseException exception) throws
SAXException {
throw
exception;
}
/**
* This method is called when a fatal error occurs during parsing. This
* method rethrows the exception
*
* @param exception
* DOCUMENT ME!
*
* @throws SAXException
* DOCUMENT ME!
*/
public void
fatalError(
SAXParseException exception) throws
SAXException {
throw
exception;
}
// LexicalHandler interface
// -------------------------------------------------------------------------
public void
startDTD(
String name,
String publicId,
String systemId)
throws
SAXException {
getDocument().
addDocType(
name,
publicId,
systemId);
insideDTDSection = true;
internalDTDsubset = true;
}
public void
endDTD() throws
SAXException {
insideDTDSection = false;
DocumentType docType =
getDocument().
getDocType();
if (
docType != null) {
if (
internalDTDDeclarations != null) {
docType.
setInternalDeclarations(
internalDTDDeclarations);
}
if (
externalDTDDeclarations != null) {
docType.
setExternalDeclarations(
externalDTDDeclarations);
}
}
internalDTDDeclarations = null;
externalDTDDeclarations = null;
}
public void
startEntity(
String name) throws
SAXException {
++
entityLevel;
// Ignore DTD references
entity = null;
if (!
insideDTDSection) {
if (!
isIgnorableEntity(
name)) {
entity =
name;
}
}
// internal DTD subsets can only appear outside of a
// startEntity/endEntity block
// see the startDTD method in
// http://dom4j.org/javadoc/org/xml/sax/ext/LexicalHandler.html
internalDTDsubset = false;
}
public void
endEntity(
String name) throws
SAXException {
--
entityLevel;
entity = null;
if (
entityLevel == 0) {
internalDTDsubset = true;
}
}
public void
startCDATA() throws
SAXException {
insideCDATASection = true;
cdataText = new
StringBuffer();
}
public void
endCDATA() throws
SAXException {
insideCDATASection = false;
currentElement.
addCDATA(
cdataText.
toString());
}
public void
comment(char[]
ch, int
start, int
end) throws
SAXException {
if (!
ignoreComments) {
if (
mergeAdjacentText &&
textInTextBuffer) {
completeCurrentTextNode();
}
String text = new
String(
ch,
start,
end);
if (!
insideDTDSection && (
text.
length() > 0)) {
if (
currentElement != null) {
currentElement.
addComment(
text);
} else {
getDocument().
addComment(
text);
}
}
}
}
// DeclHandler interface
// -------------------------------------------------------------------------
/**
* Report an element type declaration.
*
* <p>
* The content model will consist of the string "EMPTY", the string "ANY",
* or a parenthesised group, optionally followed by an occurrence indicator.
* The model will be normalized so that all parameter entities are fully
* resolved and all whitespace is removed,and will include the enclosing
* parentheses. Other normalization (such as removing redundant parentheses
* or simplifying occurrence indicators) is at the discretion of the parser.
* </p>
*
* @param name
* The element type name.
* @param model
* The content model as a normalized string.
*
* @exception SAXException
* The application may raise an exception.
*/
public void
elementDecl(
String name,
String model) throws
SAXException {
if (
internalDTDsubset) {
if (
includeInternalDTDDeclarations) {
addDTDDeclaration(new
ElementDecl(
name,
model));
}
} else {
if (
includeExternalDTDDeclarations) {
addExternalDTDDeclaration(new
ElementDecl(
name,
model));
}
}
}
/**
* Report an attribute type declaration.
*
* <p>
* Only the effective (first) declaration for an attribute will be reported.
* The type will be one of the strings "CDATA", "ID", "IDREF", "IDREFS",
* "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES", a parenthesized token group
* with the separator "|" and all whitespace removed, or the word "NOTATION"
* followed by a space followed by a parenthesized token group with all
* whitespace removed.
* </p>
*
* <p>
* Any parameter entities in the attribute value will be expanded, but
* general entities will not.
* </p>
*
* @param eName
* The name of the associated element.
* @param aName
* The name of the attribute.
* @param type
* A string representing the attribute type.
* @param valueDefault
* A string representing the attribute default ("#IMPLIED",
* "#REQUIRED", or "#FIXED") or null if none of these applies.
* @param val
* A string representing the attribute's default value, or null
* if there is none.
*
* @exception SAXException
* The application may raise an exception.
*/
public void
attributeDecl(
String eName,
String aName,
String type,
String valueDefault,
String val) throws
SAXException {
if (
internalDTDsubset) {
if (
includeInternalDTDDeclarations) {
addDTDDeclaration(new
AttributeDecl(
eName,
aName,
type,
valueDefault,
val));
}
} else {
if (
includeExternalDTDDeclarations) {
addExternalDTDDeclaration(new
AttributeDecl(
eName,
aName,
type,
valueDefault,
val));
}
}
}
/**
* Report an internal entity declaration.
*
* <p>
* Only the effective (first) declaration for each entity will be reported.
* All parameter entities in the value will be expanded, but general
* entities will not.
* </p>
*
* @param name
* The name of the entity. If it is a parameter entity, the name
* will begin with '%'.
* @param value
* The replacement text of the entity.
*
* @exception SAXException
* The application may raise an exception.
*
* @see #externalEntityDecl
* @see org.xml.sax.DTDHandler#unparsedEntityDecl
*/
public void
internalEntityDecl(
String name,
String value)
throws
SAXException {
if (
internalDTDsubset) {
if (
includeInternalDTDDeclarations) {
addDTDDeclaration(new
InternalEntityDecl(
name,
value));
}
} else {
if (
includeExternalDTDDeclarations) {
addExternalDTDDeclaration(new
InternalEntityDecl(
name,
value));
}
}
}
/**
* Report a parsed external entity declaration.
*
* <p>
* Only the effective (first) declaration for each entity will be reported.
* </p>
*
* @param name
* The name of the entity. If it is a parameter entity, the name
* will begin with '%'.
* @param publicId
* The declared public identifier of the entity, or null if none
* was declared.
* @param sysId
* The declared system identifier of the entity.
*
* @exception SAXException
* The application may raise an exception.
*
* @see #internalEntityDecl
* @see org.xml.sax.DTDHandler#unparsedEntityDecl
*/
public void
externalEntityDecl(
String name,
String publicId,
String sysId)
throws
SAXException {
ExternalEntityDecl declaration = new
ExternalEntityDecl(
name,
publicId,
sysId);
if (
internalDTDsubset) {
if (
includeInternalDTDDeclarations) {
addDTDDeclaration(
declaration);
}
} else {
if (
includeExternalDTDDeclarations) {
addExternalDTDDeclaration(
declaration);
}
}
}
// DTDHandler interface
// -------------------------------------------------------------------------
/**
* Receive notification of a notation declaration event.
*
* <p>
* It is up to the application to record the notation for later reference,
* if necessary.
* </p>
*
* <p>
* At least one of publicId and systemId must be non-null. If a system
* identifier is present, and it is a URL, the SAX parser must resolve it
* fully before passing it to the application through this event.
* </p>
*
* <p>
* There is no guarantee that the notation declaration will be reported
* before any unparsed entities that use it.
* </p>
*
* @param name
* The notation name.
* @param publicId
* The notation's public identifier, or null if none was given.
* @param systemId
* The notation's system identifier, or null if none was given.
*
* @exception SAXException
* Any SAX exception, possibly wrapping another exception.
*
* @see #unparsedEntityDecl
* @see org.xml.sax.AttributeList
*/
public void
notationDecl(
String name,
String publicId,
String systemId)
throws
SAXException {
// #### not supported yet!
}
/**
* Receive notification of an unparsed entity declaration event.
*
* <p>
* Note that the notation name corresponds to a notation reported by the
* {@link #notationDecl notationDecl}event. It is up to the application to
* record the entity for later reference, if necessary.
* </p>
*
* <p>
* If the system identifier is a URL, the parser must resolve it fully
* before passing it to the application.
* </p>
*
* @param name
* The unparsed entity's name.
* @param publicId
* The entity's public identifier, or null if none was given.
* @param systemId
* The entity's system identifier.
* @param notationName
* The name of the associated notation.
*
* @exception SAXException
* Any SAX exception, possibly wrapping another exception.
*
* @see #notationDecl
* @see org.xml.sax.AttributeList
*/
public void
unparsedEntityDecl(
String name,
String publicId,
String systemId,
String notationName) throws
SAXException {
// #### not supported yet!
}
// Properties
// -------------------------------------------------------------------------
public
ElementStack getElementStack() {
return
elementStack;
}
public void
setElementStack(
ElementStack elementStack) {
this.
elementStack =
elementStack;
}
public
EntityResolver getEntityResolver() {
return
entityResolver;
}
public void
setEntityResolver(
EntityResolver entityResolver) {
this.
entityResolver =
entityResolver;
}
public
InputSource getInputSource() {
return
inputSource;
}
public void
setInputSource(
InputSource inputSource) {
this.
inputSource =
inputSource;
}
/**
* DOCUMENT ME!
*
* @return whether internal DTD declarations should be expanded into the
* DocumentType object or not.
*/
public boolean
isIncludeInternalDTDDeclarations() {
return
includeInternalDTDDeclarations;
}
/**
* Sets whether internal DTD declarations should be expanded into the
* DocumentType object or not.
*
* @param include
* whether or not DTD declarations should be expanded and
* included into the DocumentType object.
*/
public void
setIncludeInternalDTDDeclarations(boolean
include) {
this.
includeInternalDTDDeclarations =
include;
}
/**
* DOCUMENT ME!
*
* @return whether external DTD declarations should be expanded into the
* DocumentType object or not.
*/
public boolean
isIncludeExternalDTDDeclarations() {
return
includeExternalDTDDeclarations;
}
/**
* Sets whether DTD external declarations should be expanded into the
* DocumentType object or not.
*
* @param include
* whether or not DTD declarations should be expanded and
* included into the DocumentType object.
*/
public void
setIncludeExternalDTDDeclarations(boolean
include) {
this.
includeExternalDTDDeclarations =
include;
}
/**
* Returns whether adjacent text nodes should be merged together.
*
* @return Value of property mergeAdjacentText.
*/
public boolean
isMergeAdjacentText() {
return
mergeAdjacentText;
}
/**
* Sets whether or not adjacent text nodes should be merged together when
* parsing.
*
* @param mergeAdjacentText
* New value of property mergeAdjacentText.
*/
public void
setMergeAdjacentText(boolean
mergeAdjacentText) {
this.
mergeAdjacentText =
mergeAdjacentText;
}
/**
* Sets whether whitespace between element start and end tags should be
* ignored
*
* @return Value of property stripWhitespaceText.
*/
public boolean
isStripWhitespaceText() {
return
stripWhitespaceText;
}
/**
* Sets whether whitespace between element start and end tags should be
* ignored.
*
* @param stripWhitespaceText
* New value of property stripWhitespaceText.
*/
public void
setStripWhitespaceText(boolean
stripWhitespaceText) {
this.
stripWhitespaceText =
stripWhitespaceText;
}
/**
* Returns whether we should ignore comments or not.
*
* @return boolean
*/
public boolean
isIgnoreComments() {
return
ignoreComments;
}
/**
* Sets whether we should ignore comments or not.
*
* @param ignoreComments
* whether we should ignore comments or not.
*/
public void
setIgnoreComments(boolean
ignoreComments) {
this.
ignoreComments =
ignoreComments;
}
// Implementation methods
// -------------------------------------------------------------------------
/**
* If the current text buffer contains any text then create a new text node
* with it and add it to the current element
*/
protected void
completeCurrentTextNode() {
if (
stripWhitespaceText) {
boolean
whitespace = true;
for (int
i = 0,
size =
textBuffer.
length();
i <
size;
i++) {
if (!
Character.
isWhitespace(
textBuffer.
charAt(
i))) {
whitespace = false;
break;
}
}
if (!
whitespace) {
currentElement.
addText(
textBuffer.
toString());
}
} else {
currentElement.
addText(
textBuffer.
toString());
}
textBuffer.
setLength(0);
textInTextBuffer = false;
}
/**
* DOCUMENT ME!
*
* @return the current document
*/
protected
Document createDocument() {
String encoding =
getEncoding();
Document doc =
documentFactory.
createDocument(
encoding);
// set the EntityResolver
doc.
setEntityResolver(
entityResolver);
if (
inputSource != null) {
doc.
setName(
inputSource.
getSystemId());
}
return
doc;
}
private
String getEncoding() {
if (
locator == null) {
return null;
}
// use reflection to avoid dependency on Locator2
// or other locator implemenations.
try {
Method m =
locator.
getClass().
getMethod("getEncoding",
new
Class[] {});
if (
m != null) {
return (
String)
m.
invoke(
locator, null);
}
} catch (
Exception e) {
// do nothing
}
// couldn't determine encoding, returning null...
return null;
}
/**
* a Strategy Method to determine if a given entity name is ignorable
*
* @param name
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
protected boolean
isIgnorableEntity(
String name) {
return "amp".
equals(
name) || "apos".
equals(
name) || "gt".
equals(
name)
|| "lt".
equals(
name) || "quot".
equals(
name);
}
/**
* Add all namespaces declared before the startElement() SAX event to the
* current element so that they are available to child elements and
* attributes
*
* @param element
* DOCUMENT ME!
*/
protected void
addDeclaredNamespaces(
Element element) {
Namespace elementNamespace =
element.
getNamespace();
for (int
size =
namespaceStack.
size();
declaredNamespaceIndex <
size;
declaredNamespaceIndex++) {
Namespace namespace =
namespaceStack
.
getNamespace(
declaredNamespaceIndex);
// if ( namespace != elementNamespace ) {
element.
add(
namespace);
// }
}
}
/**
* Add all the attributes to the given elements
*
* @param element
* DOCUMENT ME!
* @param attributes
* DOCUMENT ME!
*/
protected void
addAttributes(
Element element,
Attributes attributes) {
// XXXX: as an optimisation, we could deduce this value from the current
// SAX parser settings, the SAX namespaces-prefixes feature
boolean
noNamespaceAttributes = false;
if (
element instanceof
AbstractElement) {
// optimised method
AbstractElement baseElement = (
AbstractElement)
element;
baseElement.
setAttributes(
attributes,
namespaceStack,
noNamespaceAttributes);
} else {
int
size =
attributes.
getLength();
for (int
i = 0;
i <
size;
i++) {
String attributeQName =
attributes.
getQName(
i);
if (
noNamespaceAttributes
|| !
attributeQName.
startsWith("xmlns")) {
String attributeURI =
attributes.
getURI(
i);
String attributeLocalName =
attributes.
getLocalName(
i);
String attributeValue =
attributes.
getValue(
i);
QName qName =
namespaceStack.
getAttributeQName(
attributeURI,
attributeLocalName,
attributeQName);
element.
addAttribute(
qName,
attributeValue);
}
}
}
}
/**
* Adds an internal DTD declaration to the list of declarations
*
* @param declaration
* DOCUMENT ME!
*/
protected void
addDTDDeclaration(
Object declaration) {
if (
internalDTDDeclarations == null) {
internalDTDDeclarations = new
ArrayList();
}
internalDTDDeclarations.
add(
declaration);
}
/**
* Adds an external DTD declaration to the list of declarations
*
* @param declaration
* DOCUMENT ME!
*/
protected void
addExternalDTDDeclaration(
Object declaration) {
if (
externalDTDDeclarations == null) {
externalDTDDeclarations = new
ArrayList();
}
externalDTDDeclarations.
add(
declaration);
}
protected
ElementStack createElementStack() {
return new
ElementStack();
}
}
/*
* Redistribution and use of this software and associated documentation
* ("Software"), with or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain copyright statements and
* notices. Redistributions must also contain a copy of this document.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name "DOM4J" must not be used to endorse or promote products derived
* from this Software without prior written permission of MetaStuff, Ltd. For
* written permission, please contact dom4j-info@metastuff.com.
*
* 4. Products derived from this Software may not be called "DOM4J" nor may
* "DOM4J" appear in their names without prior written permission of MetaStuff,
* Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
*
* 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
*
* THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
*/