blob: 912020312bf3049ca9e9607a15cbe1e84cf9a36d [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 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 end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xerces" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache\@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``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 THE APACHE SOFTWARE FOUNDATION 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation, and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.ibm.com . For more information
* on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/**
* This file contains code to build the DOM tree. It registers a document
* handler with the scanner. In these handler methods, appropriate DOM nodes
* are created and added to the DOM tree.
*
* $Log$
* Revision 1.1 1999/11/09 01:07:49 twl
* Initial revision
*
* Revision 1.7 1999/11/08 20:44:52 rahul
* Swat for adding in Product name and CVS comment log variable.
*
*/
// ---------------------------------------------------------------------------
// Includes
// ---------------------------------------------------------------------------
#include <sax/EntityResolver.hpp>
#include <sax/ErrorHandler.hpp>
#include <sax/SAXParseException.hpp>
#include <framework/XMLNotationDecl.hpp>
#include <util/IOException.hpp>
#include <internal/URLInputSource.hpp>
#include <internal/XMLScanner.hpp>
#include <validators/DTD/DTDValidator.hpp>
#include <parsers/DOMParser.hpp>
// ---------------------------------------------------------------------------
// DOMParser: Constructors and Destructor
// ---------------------------------------------------------------------------
DOMParser::DOMParser(XMLValidator* const valToAdopt) :
fErrorHandler(0)
, fEntityResolver(0)
, fExpandEntityReferences(false)
, fNodeStack(0)
, fScanner(0)
, fValidator(valToAdopt)
{
// Create the validator if one was not provided
if (!fValidator)
fValidator = new DTDValidator;
//
// Create a scanner and tell it what validator to use. Then set us
// as the document event handler so we can fill the DOM document.
//
fScanner = new XMLScanner(fValidator);
fScanner->setDocHandler(this);
fNodeStack = new ValueStackOf<DOM_Node>(64);
this->reset();
}
DOMParser::~DOMParser()
{
delete fNodeStack;
delete fScanner;
delete fValidator;
}
void DOMParser::reset()
{
fDocument = DOM_Document::createDocument();
// Note: DOM Documents are reference counted. Doing this
// assignment will cause the old one to go away unless
// application code is also holding a reference to it.
fCurrentParent = 0;
fCurrentNode = 0;
fParseInProgress = false;
fWithinElement = false;
fNodeStack->removeAllElements();
};
// ---------------------------------------------------------------------------
// DOMParser: Getter methods
// ---------------------------------------------------------------------------
inline const XMLValidator& DOMParser::getValidator() const
{
return *fValidator;
}
// ---------------------------------------------------------------------------
// DOMParser: Setter methods
// ---------------------------------------------------------------------------
void DOMParser::setDoNamespaces(const bool newState)
{
fScanner->setDoNamespaces(newState);
}
void DOMParser::setDoValidation(const bool newState)
{
fScanner->setDoValidation(newState);
}
void DOMParser::setErrorHandler(ErrorHandler* const handler)
{
fErrorHandler = handler;
if (fErrorHandler)
{
fScanner->setErrorReporter(this);
fValidator->setErrorReporter(this);
}
else
{
fScanner->setErrorReporter(0);
fValidator->setErrorReporter(0);
}
}
void DOMParser::setEntityResolver(EntityResolver* const handler)
{
fEntityResolver = handler;
if (fEntityResolver)
fScanner->setEntityHandler(this);
else
fScanner->setEntityHandler(0);
}
void DOMParser::setExitOnFirstFatalError(const bool newState)
{
fScanner->setExitOnFirstFatal(newState);
}
// ---------------------------------------------------------------------------
// DOMParser: Parsing methods
// ---------------------------------------------------------------------------
void DOMParser::parse(const InputSource& source, const bool reuseValidator)
{
// Avoid multiple entrance
if (fParseInProgress)
ThrowXML(IOException, XML4CExcepts::Gen_ParseInProgress);
try
{
fParseInProgress = true;
fScanner->scanDocument(source, reuseValidator);
fParseInProgress = false;
}
catch(const SAXException&)
{
fParseInProgress = false;
throw;
}
catch(const XMLException&)
{
fParseInProgress = false;
throw;
}
}
void DOMParser::parse(const XMLCh* const systemId, const bool reuseValidator)
{
// Just call the URL input source version
parse(URLInputSource(systemId), reuseValidator);
}
void DOMParser::parse(const char* const systemId, const bool reuseValidator)
{
// Just call the URL input source version
parse(URLInputSource(systemId), reuseValidator);
}
// ---------------------------------------------------------------------------
// DOMParser: Progressive parse methods
// ---------------------------------------------------------------------------
bool DOMParser::parseFirst( const XMLCh* const systemId
, XMLPScanToken& toFill
, const bool reuseValidator)
{
// Call the other version with a URL input source
return parseFirst(URLInputSource(systemId), toFill, reuseValidator);
}
bool DOMParser::parseFirst( const char* const systemId
, XMLPScanToken& toFill
, const bool reuseValidator)
{
// Call the other version with a URL input source
return parseFirst(URLInputSource(systemId), toFill, reuseValidator);
}
bool DOMParser::parseFirst( const InputSource& source
, XMLPScanToken& toFill
, const bool reuseValidator)
{
//
// Avoid multiple entrance. We cannot enter here while a regular parse
// is in progress.
//
if (fParseInProgress)
ThrowXML(IOException, XML4CExcepts::Gen_ParseInProgress);
return fScanner->scanFirst(source, toFill, reuseValidator);
}
bool DOMParser::parseNext(XMLPScanToken& token)
{
return fScanner->scanNext(token);
}
// ---------------------------------------------------------------------------
// DOMParser: Implementation of the XMLErrorReporter interface
// ---------------------------------------------------------------------------
void DOMParser::error( const unsigned int code
, const XMLCh* const msgDomain
, const XMLErrorReporter::ErrTypes errType
, const XMLCh* const errorText
, const XMLCh* const systemId
, const XMLCh* const publicId
, const unsigned int lineNum
, const unsigned int colNum)
{
SAXParseException toThrow = SAXParseException
(
errorText
, publicId
, systemId
, lineNum
, colNum
);
//
// If there is an error handler registered, call it, otherwise ignore
// all but the fatal errors.
//
if (!fErrorHandler)
{
if (errType == XMLErrorReporter::ErrType_Fatal)
throw toThrow;
return;
}
if (errType == XMLErrorReporter::ErrType_Warning)
fErrorHandler->warning(toThrow);
else if (errType >= XMLErrorReporter::ErrType_Fatal)
fErrorHandler->fatalError(toThrow);
else
fErrorHandler->error(toThrow);
}
void DOMParser::resetErrors()
{
}
// ---------------------------------------------------------------------------
// DOMParser: Implementation of XMLEntityHandler interface
// ---------------------------------------------------------------------------
InputSource*
DOMParser::resolveEntity(const XMLCh* const publicId, const XMLCh* const systemId)
{
//
// Just map it to the SAX entity resolver. If there is not one installed,
// return a null pointer to cause the default resolution.
//
if (fEntityResolver)
return fEntityResolver->resolveEntity(publicId, systemId);
return 0;
}
// ---------------------------------------------------------------------------
// DOMParser: Implementation of XMLDocumentHandler interface
// ---------------------------------------------------------------------------
void DOMParser::docCharacters( const XMLCh* const chars
, const unsigned int length
, const bool cdataSection)
{
// Ignore chars outside of content
if (!fWithinElement)
return;
if (cdataSection == true)
{
DOM_CDATASection node = fDocument.createCDATASection
(
DOMString(chars, length)
);
fCurrentParent.appendChild(node);
fCurrentNode = node;
}
else
{
if (fCurrentNode.getNodeType() == DOM_Node::TEXT_NODE)
{
DOM_Text node = (DOM_Text&)fCurrentNode;
node.appendData(DOMString(chars, length));
}
else
{
DOM_Text node = fDocument.createTextNode(DOMString(chars, length));
fCurrentParent.appendChild(node);
fCurrentNode = node;
}
}
}
void DOMParser::docComment(const XMLCh* const comment)
{
DOM_Comment dcom = fDocument.createComment(comment);
fCurrentParent.appendChild(dcom);
fCurrentNode = dcom;
}
void DOMParser::docPI( const XMLCh* const target
, const XMLCh* const data)
{
DOM_ProcessingInstruction pi = fDocument.createProcessingInstruction
(
target
, data
);
fCurrentParent.appendChild(pi);
fCurrentNode = pi;
}
void DOMParser::endEntityReference(const XMLEntityDecl& entDecl)
{
if (fExpandEntityReferences == true)
{
fCurrentParent = fNodeStack->pop();
fCurrentNode = fCurrentParent;
}
}
void DOMParser::endElement( const XMLElementDecl& elemDecl
, const unsigned int urlId
, const bool isRoot)
{
fCurrentNode = fCurrentParent;
fCurrentParent = fNodeStack->pop();
// If we've hit the end of content, clear the flag
if (fNodeStack->empty())
fWithinElement = false;
}
void DOMParser::ignorableWhitespace(const XMLCh* const chars
, const unsigned int length
, const bool cdataSection)
{
// Ignore chars before the root element
if (!fWithinElement)
return;
if (fCurrentNode.getNodeType() == DOM_Node::TEXT_NODE)
{
DOM_Text node = (DOM_Text&)fCurrentNode;
node.appendData(DOMString(chars, length));
}
else
{
DOM_Text node = fDocument.createTextNode(DOMString(chars, length));
fCurrentParent.appendChild(node);
fCurrentNode = node;
}
}
void DOMParser::resetDocument()
{
//
// The reset methods are called before a new parse event occurs.
// Reset this parsers state to clear out anything that may be left
// from a previous use, in particular the DOM document itself.
//
this->reset();
}
void DOMParser::startDocument()
{
// Just set the document as the current parent and current node
fCurrentParent = fDocument;
fCurrentNode = fDocument;
}
void DOMParser::startElement(const XMLElementDecl& elemDecl
, const unsigned int urlId
, const XMLCh* const elemPrefix
, const RefVectorOf<XMLAttr>& attrList
, const unsigned int attrCount
, const bool isEmpty
, const bool isRoot)
{
DOM_Element elem;
if (fScanner -> getDoNamespaces()) { //DOM Level 2, doNamespaces on
unsigned int globalNSid = fValidator -> getGlobalNamespaceId();
XMLBuffer buf;
DOMString namespaceURI = 0;
if (urlId != globalNSid) { //TagName has prefix
fValidator -> getURIText(urlId, buf); //get namespaceURI
namespaceURI = DOMString(buf.getRawBuffer());
}
elem = fDocument.createElementNS(namespaceURI, elemDecl.getFullName());
for (unsigned int index = 0; index < attrCount; ++index) {
const XMLAttr* oneAttrib = attrList.elementAt(index);
unsigned int attrURIId = oneAttrib -> getURIId();
namespaceURI = 0;
if (attrURIId != globalNSid) { //Attribute Name has prefix
fValidator -> getURIText(attrURIId, buf); //get namespaceURI
namespaceURI = DOMString(buf.getRawBuffer());
}
elem.setAttributeNS(namespaceURI, oneAttrib -> getQName(),
oneAttrib -> getValue());
}
} else { //DOM Level 1
elem = fDocument.createElement(elemDecl.getFullName());
for (unsigned int index = 0; index < attrCount; ++index) {
const XMLAttr* oneAttrib = attrList.elementAt(index);
elem.setAttribute(oneAttrib->getName(), oneAttrib->getValue());
}
}
fCurrentParent.appendChild(elem);
fNodeStack->push(fCurrentParent);
fCurrentParent = elem;
fCurrentNode = elem;
fWithinElement = true;
// If an empty element, do end right now (no endElement() will be called)
if (isEmpty)
endElement(elemDecl, urlId, isRoot);
}
void DOMParser::startEntityReference(const XMLEntityDecl& entDecl)
{
if (fExpandEntityReferences == true)
{
DOM_EntityReference er = fDocument.createEntityReference
(
DOMString(entDecl.getName())
);
fCurrentParent.appendChild(er);
fNodeStack->push(fCurrentParent);
fCurrentParent = er;
fCurrentNode = er;
}
}
void DOMParser::XMLDecl(const XMLCh* const
, const XMLCh* const
, const XMLCh* const
, const XMLCh* const)
{
// This is not used by DOM at this time
}