blob: 162a54b550dd0af2b1ee4a8fedbe036fd0a11686 [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/>.
*/
/**
* $Log$
* Revision 1.1 1999/11/09 01:08:43 twl
* Initial revision
*
* Revision 1.5 1999/11/08 20:44:24 rahul
* Swat for adding in Product name and CVS comment log variable.
*
*/
//
// file DocumentImpl.cpp
//
#include "DocumentImpl.hpp"
#include "DOM_DOMException.hpp"
#include "DOM_Node.hpp"
#include "DocumentTypeImpl.hpp"
#include "ElementImpl.hpp"
#include "AttrImpl.hpp"
#include "CDATASectionImpl.hpp"
#include "CommentImpl.hpp"
#include "DocumentFragmentImpl.hpp"
#include "EntityImpl.hpp"
#include "EntityReferenceImpl.hpp"
#include "NotationImpl.hpp"
#include "ProcessingInstructionImpl.hpp"
#include "TextImpl.hpp"
#include "DOM_DOMImplementation.hpp"
#include "DeepNodeListImpl.hpp"
#include "NamedNodeMapImpl.hpp"
#include "DStringPool.hpp"
#include <internal/XMLReader.hpp>
#include "TreeWalkerImpl.hpp"
#include "NodeIteratorImpl.hpp"
#include "DOM_Document.hpp"
static DOMString *nam = 0; // Will be lazily initialized to "#document"
DocumentImpl::DocumentImpl(): NodeImpl(null,
DStringPool::getStaticString("#document", &nam),
DOM_Node::DOCUMENT_NODE,
false,
null)
{
docType=null;
docElement=null;
namePool = new DStringPool(257);
iterators = 0L;
treeWalkers = 0L;
};
//DOM Level 2
DocumentImpl::DocumentImpl(const DOMString &namespaceURI,
const DOMString &qualifiedName, DocumentTypeImpl *doctype)
: NodeImpl(null, namespaceURI, qualifiedName, DOM_Node::DOCUMENT_NODE, false, null)
{
if (doctype != null && doctype->getOwnerDocument() != null)
throw new DOM_DOMException(
DOM_DOMException::WRONG_DOCUMENT_ERR, null);
docType=doctype;
if (doctype != null)
doctype -> setOwnerDocument(this);
docElement=null;
namePool = new DStringPool(257);
}
DocumentImpl::~DocumentImpl()
{
if (iterators != 0L) {
// The data in the vector is pointers owned by smart pointers, and will be cleaned up when they go away.
delete iterators;
}
if (treeWalkers != 0L) {
// The data in the vector is pointers owned by smart pointers, and will be cleaned up when they go away.
delete treeWalkers;
}
delete namePool;
// Do not delete docType and docElement pointers here.
// These are also normal child nodes of the document,
// and refcounting will take them out in the usual way.
};
NodeImpl *DocumentImpl::cloneNode(bool deep) {
DocumentImpl *newdoc=new DocumentImpl();
if (deep)
for(NodeImpl *n=getFirstChild();n!=null;n=n->getNextSibling())
newdoc->appendChild(newdoc->importNode(n,true));
return newdoc;
};
bool DocumentImpl::isDocumentImpl() {
return true;
};
AttrImpl *DocumentImpl::createAttribute(const DOMString &nam)
{
if(!isXMLName(nam))
throw new DOM_DOMException(DOM_DOMException::INVALID_CHARACTER_ERR,null);
return new AttrImpl(this,nam);
};
CDATASectionImpl *DocumentImpl::createCDATASection(const DOMString &data) {
return new CDATASectionImpl(this,data);
};
CommentImpl *DocumentImpl::createComment(const DOMString &data)
{
return new CommentImpl(this,data);
};
DocumentFragmentImpl *DocumentImpl::createDocumentFragment()
{
return new DocumentFragmentImpl(this);
};
DocumentTypeImpl *DocumentImpl::createDocumentType(const DOMString &nam)
{
if (!isXMLName(nam))
throw new DOM_DOMException(
DOM_DOMException::INVALID_CHARACTER_ERR, null);
return new DocumentTypeImpl(this, nam);
};
ElementImpl *DocumentImpl::createElement(const DOMString &tagName)
{
if(!isXMLName(tagName))
throw new DOM_DOMException(DOM_DOMException::INVALID_CHARACTER_ERR,null);
DOMString pooledTagName = this->namePool->getPooledString(tagName);
return new ElementImpl(this,pooledTagName);
};
ElementImpl *DocumentImpl::createElement(const XMLCh *tagName)
{
DOMString pooledTagName = this->namePool->getPooledString(tagName);
return new ElementImpl(this,pooledTagName);
};
EntityImpl *DocumentImpl::createEntity(const DOMString &nam)
{
if (!isXMLName(nam))
throw new DOM_DOMException(
DOM_DOMException::INVALID_CHARACTER_ERR, null);
return new EntityImpl(this, nam);
};
EntityReferenceImpl *DocumentImpl::createEntityReference(const DOMString &nam)
{
if (!isXMLName(nam))
throw new DOM_DOMException(
DOM_DOMException::INVALID_CHARACTER_ERR, null);
return new EntityReferenceImpl(this, nam);
};
NotationImpl *DocumentImpl::createNotation(const DOMString &nam)
{
if (!isXMLName(nam))
throw new DOM_DOMException(
DOM_DOMException::INVALID_CHARACTER_ERR, null);
return new NotationImpl(this, nam);
};
ProcessingInstructionImpl *DocumentImpl::createProcessingInstruction(
const DOMString &target, const DOMString &data)
{
if(!isXMLName(target))
throw new DOM_DOMException(DOM_DOMException::INVALID_CHARACTER_ERR,null);
return new ProcessingInstructionImpl(this,target,data);
};
TextImpl *DocumentImpl::createTextNode(const DOMString &data)
{
return new TextImpl(this,data);
};
NodeIteratorImpl* DocumentImpl::createNodeIterator (DOM_Node root, short whatToShow, DOM_NodeFilter filter, NodeFilterImpl* fi)
{
// Create the node iterator implementation object.
// Add it to the vector of iterators that must be synchronized when a node is deleted.
// The vector of iterators is kept in the "owner document" if there is one. If there isn't one, I assume that root is the
// owner document.
NodeIteratorImpl* iter = new NodeIteratorImpl(root, whatToShow, filter, fi);
DOM_Document doc = root.getOwnerDocument();
DocumentImpl* impl;
if (! doc.isNull()) {
impl = (DocumentImpl *) doc.fImpl;
}
else
impl = (DocumentImpl *) root.fImpl;
if (impl->iterators == 0L) {
impl->iterators = new NodeIterators(1, false);
impl->iterators->addElement(iter);
}
return iter;
}
TreeWalkerImpl* DocumentImpl::createTreeWalker (DOM_Node root, short whatToShow, DOM_NodeFilter filter, NodeFilterImpl* fi)
{
// See notes for createNodeIterator...
TreeWalkerImpl* twi = new TreeWalkerImpl(root, whatToShow, filter, fi);
DOM_Document doc = root.getOwnerDocument();
DocumentImpl* impl;
if (! doc.isNull()) {
impl = (DocumentImpl *) doc.fImpl;
}
else
impl = (DocumentImpl *) root.fImpl;
if (impl->treeWalkers == 0L) {
impl->treeWalkers = new TreeWalkers(1, false);
impl->treeWalkers->addElement(twi);
}
return twi;
}
DocumentTypeImpl *DocumentImpl::getDoctype()
{
return docType;
};
ElementImpl *DocumentImpl::getDocumentElement()
{
return docElement;
};
DeepNodeListImpl *DocumentImpl::getElementsByTagName(const DOMString &tagname)
{
return new DeepNodeListImpl(this,tagname);
};
NodeImpl *DocumentImpl::importNode(NodeImpl *source, bool deep)
{
NodeImpl *newnode=null;
switch (source->getNodeType())
{
case DOM_Node::ELEMENT_NODE :
{
ElementImpl *newelement = createElement(source->getNodeName());
NamedNodeMapImpl *srcattr=source->getAttributes();
if(srcattr!=null)
for(int i=0;i<srcattr->getLength();++i)
newelement->setAttributeNode(
(AttrImpl *)importNode(srcattr->item(i),true));
newnode=newelement;
}
break;
case DOM_Node::ATTRIBUTE_NODE :
newnode = createAttribute(source->getNodeName());
// Kids carry value
break;
case DOM_Node::TEXT_NODE :
newnode = createTextNode(source->getNodeValue());
break;
case DOM_Node::CDATA_SECTION_NODE :
newnode = createCDATASection(source->getNodeValue());
break;
case DOM_Node::ENTITY_REFERENCE_NODE :
newnode = createEntityReference(source->getNodeName());
deep=false; // ????? Right Thing?
// Value implied by doctype, so we should not copy it
// -- instead, refer to local doctype, if any.
break;
case DOM_Node::ENTITY_NODE :
{
EntityImpl *srcentity=(EntityImpl *)source;
EntityImpl *newentity = createEntity(source->getNodeName());
newentity->setPublicId(srcentity->getPublicId());
newentity->setSystemId(srcentity->getSystemId());
newentity->setNotationName(srcentity->getNotationName());
// Kids carry additional value
newnode=newentity;
}
break;
case DOM_Node::PROCESSING_INSTRUCTION_NODE :
newnode = createProcessingInstruction(source->getNodeName(), source->getNodeValue());
break;
case DOM_Node::COMMENT_NODE :
newnode = createComment(source->getNodeValue());
break;
case DOM_Node::DOCUMENT_TYPE_NODE :
{
DocumentTypeImpl *srcdoctype = (DocumentTypeImpl *)source;
DocumentTypeImpl *newdoctype = createDocumentType(source->getNodeName());
// Values are on NamedNodeMaps
NamedNodeMapImpl *smap=srcdoctype->getEntities();
NamedNodeMapImpl *tmap=newdoctype->getEntities();
if(smap!=null)
for(int i=0;i<smap->getLength();++i)
tmap->setNamedItem(importNode(smap->item(i),true));
smap=srcdoctype->getNotations();
tmap=newdoctype->getNotations();
if(smap!=null)
for(int i=0;i<smap->getLength();++i)
tmap->setNamedItem(
importNode(smap->item(i),true));
// NOTE: At this time, the DOM definition of DocumentType
// doesn't cover Elements and their Attributes. domimpl's
// extentions in that area will not be preserved, even if
// copying from domimpl to domimpl. We could special-case
// that here. Arguably we should. Consider. ?????
newnode=newdoctype;
}
break;
case DOM_Node::DOCUMENT_FRAGMENT_NODE :
newnode = createDocumentFragment();
// No name, kids carry value
break;
case DOM_Node::NOTATION_NODE :
{
NotationImpl *srcnotation=(NotationImpl *)source;
NotationImpl *newnotation = createNotation(source->getNodeName());
newnotation->setPublicId(srcnotation->getPublicId());
newnotation->setSystemId(srcnotation->getSystemId());
// Kids carry additional value
newnode=newnotation;
// No name, no value
break;
}
case DOM_Node::DOCUMENT_NODE : // Document can't be child of Document
default: // Unknown node type
throw new DOM_DOMException(DOM_DOMException::HIERARCHY_REQUEST_ERR,null);
}
// If deep, replicate and attach the kids.
if (deep)
for (NodeImpl *srckid = source->getFirstChild(); srckid != null; srckid = srckid->getNextSibling())
{
newnode->appendChild(importNode(srckid, true));
}
return newnode;
};
NodeImpl *DocumentImpl::insertBefore(NodeImpl *newChild, NodeImpl *refChild)
{
// Only one such child permitted
if(
(newChild->isElementImpl() && docElement!=null)
||
(newChild->isDocumentTypeImpl() && docType!=null)
)
throw new DOM_DOMException(DOM_DOMException::HIERARCHY_REQUEST_ERR,null);
NodeImpl::insertBefore(newChild,refChild);
// If insert succeeded, cache the kid appropriately
if(newChild->isElementImpl())
docElement=(ElementImpl *)newChild;
else if(newChild->isDocumentTypeImpl())
docType=(DocumentTypeImpl *)newChild;
return newChild;
};
bool DocumentImpl::isXMLName(const DOMString &s)
{
XMLCh *nam;
int length;
int i;
length = s.length();
if (length == 0)
return false;
nam = s.rawBuffer();
if (!XMLReader::isFirstNameChar(nam[0]))
return false;
for (i=1; i<length; i++)
{
if (!XMLReader::isNameChar(nam[i]))
return false;
}
return true;
};
// referenced(). Override this function here in class DocumentImpl because
// we don't want the action taken in NodeImpl, which is
// to add a reference to the node's owning document.
//
void DocumentImpl::referenced()
{
// Intentionally empty.
};
NodeImpl *DocumentImpl::removeChild(NodeImpl *oldChild)
{
NodeImpl::removeChild(oldChild);
// If remove succeeded, un-cache the kid appropriately
if(oldChild->isElementImpl())
docElement=null;
else if(oldChild->isDocumentTypeImpl())
docType=null;
return oldChild;
};
void DocumentImpl::setNodeValue(const DOMString &x)
{
throw new DOM_DOMException(DOM_DOMException::NO_MODIFICATION_ALLOWED_ERR, null);
};
//
// unreferenced() will be called whenever the refernce count on
// this document goes from 1 to 0. In all cases, when this
// happens to a document node (which is the case here), it
// is time to actually delete the document.
//
// See also NodeImpl::referenced() and unreferenced(), which
// update document node ref counts based on references coming
// or going to nodes owned by the document.
//
void DocumentImpl::unreferenced()
{
deleteIf(this);
};
//Introduced in DOM Level 2
ElementImpl *DocumentImpl::createElementNS(const DOMString &namespaceURI,
const DOMString &qualifiedName)
{
if (namespaceURI == null || namespaceURI.length() == 0)
return createElement(qualifiedName);
if(!isXMLName(qualifiedName))
throw new DOM_DOMException(DOM_DOMException::INVALID_CHARACTER_ERR,null);
//DOMString pooledTagName = this->namePool->getPooledString(qualifiedName);
return new ElementImpl(this, namespaceURI, qualifiedName);
}
AttrImpl *DocumentImpl::createAttributeNS(const DOMString &namespaceURI,
const DOMString &qualifiedName)
{
if (namespaceURI == null || namespaceURI.length() == 0)
return createAttribute(qualifiedName);
if(!isXMLName(qualifiedName))
throw new DOM_DOMException(DOM_DOMException::INVALID_CHARACTER_ERR,null);
return new AttrImpl(this, namespaceURI, qualifiedName);
}
DeepNodeListImpl *DocumentImpl::getElementsByTagNameNS(const DOMString &namespaceURI,
const DOMString &localName)
{
if (namespaceURI == null || namespaceURI.length() == 0)
return getElementsByTagName(localName);
return new DeepNodeListImpl(this, namespaceURI, localName);
}
ElementImpl *DocumentImpl::getElementById(const DOMString &elementId)
{
return null;
}