/*
 * Copyright 1999-2002,2004 The Apache Software Foundation.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * $Id: DocumentImpl.cpp 176070 2004-09-23 00:50:49Z cargilld $
 */

//
// file DocumentImpl.cpp
//

#include "DocumentImpl.hpp"
#include "DOM_DOMException.hpp"
#include "DOM_Node.hpp"

#include "DocumentTypeImpl.hpp"
#include "ElementImpl.hpp"
#include "ElementNSImpl.hpp"
#include "AttrImpl.hpp"
#include "AttrNSImpl.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 <xercesc/internal/XMLReader.hpp>
#include "TreeWalkerImpl.hpp"
#include "NodeIteratorImpl.hpp"
#include "NodeIDMap.hpp"
#include "DOM_Document.hpp"
#include <xercesc/util/HashPtr.hpp>
#include "RangeImpl.hpp"
#include "DOM_Document.hpp"

XERCES_CPP_NAMESPACE_BEGIN


static DOMString *nam = 0;  // will be lazily initialized to "#document"
static XMLRegisterCleanup namCleanup;

DocumentImpl::DocumentImpl(MemoryManager* const manager)
    : ParentNode(this)
    , docType(0)
    , docElement(0)
    , namePool(0)
    , fNodeIDMap(0)
    , iterators(0)
    , treeWalkers(0)
    , userData(0)
    , ranges(0)
    , fChanges(0)
    , errorChecking(true)
    , fMemoryManager(manager)
{
    namePool    = new (fMemoryManager) DStringPool(257, fMemoryManager);
};


//DOM Level 2
DocumentImpl::DocumentImpl(const DOMString &fNamespaceURI,
                           const DOMString &qualifiedName,
                           DocumentTypeImpl *doctype,
                           MemoryManager* const manager)
    : ParentNode(this)
    , docType(0)
    , docElement(0)
    , namePool(0)
    , fNodeIDMap(0)
    , iterators(0)
    , treeWalkers(0)
    , userData(0)
    , ranges(0)
    , fChanges(0)
    , errorChecking(true)
    , fMemoryManager(manager)
{
	setDocumentType(doctype);
    namePool    = new (fMemoryManager) DStringPool(257, fMemoryManager);
    appendChild(createElementNS(fNamespaceURI, qualifiedName));  //root element
}

void DocumentImpl::setDocumentType(DocumentTypeImpl *doctype)
{
	if (!doctype)
		return;

	if (doctype->getOwnerDocument() != null)
        throw DOM_DOMException(	//one doctype can belong to only one DocumentImpl
        DOM_DOMException::WRONG_DOCUMENT_ERR, null);

    doctype->setOwnerDocument(this);
    doctype->getEntities()->ownerNode->setOwnerDocument(this);
    doctype->getNotations()->ownerNode->setOwnerDocument(this);
    doctype -> referenced();         // Warning, tricky!  An external (DOM_Node) reference
                                     //  to a node normally bumps the reference count to its
                                     //  document also.  But this could not happen when the
                                     //  user created the DOM_DocumentType because there was
                                     //  no document yet.  Now we have the document, and
                                     //  the docs ref count must be got back in sync.

				
	appendChild(doctype);

}

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;
    }

    if (ranges != 0L) {
        delete ranges;
        ranges = 0;
    }

    if (userData) {
        // make sure we won't access userData any further
        hasUserData(false);
        delete userData;
    }

    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.

    delete fNodeIDMap;
};


NodeImpl *DocumentImpl::cloneNode(bool deep) {

    // clone the node itself
    DocumentImpl *newdoc = new (fMemoryManager) DocumentImpl(fMemoryManager);

    // then the children by _importing_ them
    if (deep) {
        for (ChildNode *n = firstChild; n != null; n = n->nextSibling) {
            newdoc->appendChild(newdoc->importNode(n, true));
	}
    }
    newdoc->setErrorChecking(errorChecking);
    return newdoc;
};


DOMString DocumentImpl::getNodeName() {
    return DStringPool::getStaticString("#document"
                                       , &nam
                                       , reinitDocumentImpl
                                       , namCleanup);
}


short DocumentImpl::getNodeType() {
    return DOM_Node::DOCUMENT_NODE;
};


// even though ownerDocument refers to this in this implementation
// the DOM Level 2 spec says it must be null, so make it appear so
DocumentImpl * DocumentImpl::getOwnerDocument() {
    return null;
}


bool DocumentImpl::isDocumentImpl() {
    return true;
};


AttrImpl *DocumentImpl::createAttribute(const DOMString &nam)
{
    if (errorChecking && !isXMLName(nam)) {
        throw DOM_DOMException(DOM_DOMException::INVALID_CHARACTER_ERR,null);
    }
    return new (fMemoryManager) AttrImpl(this,nam);
};



CDATASectionImpl *DocumentImpl::createCDATASection(const DOMString &data) {
    return new (fMemoryManager) CDATASectionImpl(this,data);
};



CommentImpl *DocumentImpl::createComment(const DOMString &data)
{
    return new (fMemoryManager) CommentImpl(this,data);
};



DocumentFragmentImpl *DocumentImpl::createDocumentFragment()
{
    return new (fMemoryManager) DocumentFragmentImpl(this);
};



DocumentTypeImpl *DocumentImpl::createDocumentType(const DOMString &nam)
{
    if (errorChecking && !isXMLName(nam)) {
        throw DOM_DOMException(DOM_DOMException::INVALID_CHARACTER_ERR, null);
    }
    return new (fMemoryManager) DocumentTypeImpl(this, nam);
};



DocumentTypeImpl *
    DocumentImpl::createDocumentType(const DOMString &qualifiedName,
                                     const DOMString &publicId,
                                     const DOMString &systemId)
{
    if (errorChecking && !isXMLName(qualifiedName)) {
        throw DOM_DOMException(DOM_DOMException::INVALID_CHARACTER_ERR, null);
    }
    return new (fMemoryManager) DocumentTypeImpl(this, qualifiedName, publicId, systemId);
};



ElementImpl *DocumentImpl::createElement(const DOMString &tagName)
{
    if (errorChecking && !isXMLName(tagName)) {
        throw DOM_DOMException(DOM_DOMException::INVALID_CHARACTER_ERR,null);
    }
    DOMString pooledTagName = this->namePool->getPooledString(tagName);
    return new (fMemoryManager) ElementImpl(this,pooledTagName);
};


ElementImpl *DocumentImpl::createElement(const XMLCh *tagName)
{
    DOMString pooledTagName = this->namePool->getPooledString(tagName);
    return new (fMemoryManager) ElementImpl(this,pooledTagName);
};




EntityImpl *DocumentImpl::createEntity(const DOMString &nam)
{
    if (errorChecking && !isXMLName(nam)) {
        throw DOM_DOMException(DOM_DOMException::INVALID_CHARACTER_ERR, null);
    }
    return new (fMemoryManager) EntityImpl(this, nam);
};



EntityReferenceImpl *DocumentImpl::createEntityReference(const DOMString &nam)
{
    if (errorChecking && !isXMLName(nam)) {
        throw DOM_DOMException(DOM_DOMException::INVALID_CHARACTER_ERR, null);
    }
    return new (fMemoryManager) EntityReferenceImpl(this, nam);
};



NotationImpl *DocumentImpl::createNotation(const DOMString &nam)
{
    if (errorChecking && !isXMLName(nam)) {
        throw DOM_DOMException(DOM_DOMException::INVALID_CHARACTER_ERR, null);
    }
    return new (fMemoryManager) NotationImpl(this, nam);
};



ProcessingInstructionImpl *DocumentImpl::createProcessingInstruction(
                                          const DOMString &target, const DOMString &data)
{
    if (errorChecking && !isXMLName(target)) {
        throw DOM_DOMException(DOM_DOMException::INVALID_CHARACTER_ERR,null);
    }
    return new (fMemoryManager) ProcessingInstructionImpl(this,target,data);
};




TextImpl *DocumentImpl::createTextNode(const DOMString &data)
{
    return new (fMemoryManager) TextImpl(this,data);
};


NodeIteratorImpl* DocumentImpl::createNodeIterator (DOM_Node root,
                                                    unsigned long whatToShow,
                                                    DOM_NodeFilter* filter,
                                                    bool entityReferenceExpansion,
                                                    MemoryManager* const manager)
{
		// 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 (manager) NodeIteratorImpl(root, whatToShow, filter, entityReferenceExpansion);
    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 (manager) NodeIterators(1, false, manager);
        impl->iterators->addElement(iter);
    }

    return iter;
}


TreeWalkerImpl* DocumentImpl::createTreeWalker (DOM_Node root, unsigned long whatToShow,
                                                DOM_NodeFilter* filter,
                                                bool entityReferenceExpansion,
                                                MemoryManager* const manager)
{
		// See notes for createNodeIterator...

    TreeWalkerImpl* twi = new (manager) TreeWalkerImpl(root, whatToShow, filter, entityReferenceExpansion);
    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 (manager) TreeWalkers(1, false, manager);
        impl->treeWalkers->addElement(twi);
    }

    return twi;
}




DocumentTypeImpl *DocumentImpl::getDoctype()
{
    return docType;
};



ElementImpl *DocumentImpl::getDocumentElement()
{
    return docElement;
};



DeepNodeListImpl *DocumentImpl::getElementsByTagName(const DOMString &tagname)
{
    return new (fMemoryManager) DeepNodeListImpl(this,tagname);
};



NodeImpl *DocumentImpl::insertBefore(NodeImpl *newChild, NodeImpl *refChild)
{
    // Only one such child permitted
    if (errorChecking &&
        ((newChild->isElementImpl() && docElement!=null) ||
         (newChild->isDocumentTypeImpl() && docType!=null))) {
        throw DOM_DOMException(DOM_DOMException::HIERARCHY_REQUEST_ERR,null);
    }
    ParentNode::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)
{
    return XMLChar1_0::isValidName(s.rawBuffer(),s.length());
};


//  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)
{
    ParentNode::removeChild(oldChild);

    // If remove succeeded, un-cache the kid appropriately
    if(oldChild->isElementImpl())
        docElement=null;
    else if(oldChild->isDocumentTypeImpl())
        docType=null;

    return oldChild;
};


//
//    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

NodeImpl *DocumentImpl::importNode(NodeImpl *source, bool deep)
{
    NodeImpl *newnode=null;
    bool oldErrorCheckingFlag = errorChecking;

    switch (source->getNodeType())
    {
        case DOM_Node::ELEMENT_NODE :
        {
            ElementImpl *newelement;
            if (source->getLocalName() == null)
                newelement = createElement(source->getNodeName());
            else
                newelement = createElementNS(source->getNamespaceURI(),
                                             source->getNodeName());
            NamedNodeMapImpl *srcattr=source->getAttributes();
            if (srcattr!=null)
                for(unsigned int i=0;i<srcattr->getLength();++i)
                {
                    AttrImpl *attr = (AttrImpl *) srcattr->item(i);
                    AttrImpl * pOldAttr = null;
                    if (attr -> getSpecified())	
                    { // not a default attribute
                        AttrImpl *nattr = (AttrImpl *) importNode(attr, true);
                        if (attr -> getLocalName() == null)
                            pOldAttr = newelement->setAttributeNode(nattr);
                        else
                            pOldAttr = newelement->setAttributeNodeNS(nattr);

                        if (pOldAttr)
                        {
                            if (pOldAttr->nodeRefCount == 0)
                                NodeImpl::deleteIf(pOldAttr);
                        }
                    }
                }

            newnode=newelement;
        }
        break;
    case DOM_Node::ATTRIBUTE_NODE :
        {
            if (source->getLocalName() == null)
                newnode = createAttribute(source->getNodeName());
            else
                newnode = createAttributeNS(source->getNamespaceURI(),
                                        source->getNodeName());
        // if source is an AttrImpl from this very same implementation
        // avoid creating the child nodes if possible
//        if (source instanceof AttrImpl) {
            AttrImpl *attr = (AttrImpl *) source;
            if (attr->hasStringValue()) {
                AttrImpl *newattr = (AttrImpl *) newnode;
                newattr->setValue(attr->getValue());
                deep = false;
            }
            else {
                deep = true;
            }
//        }
//        else {
//            // Kids carry value
//            deep = true;
//        }
        }
        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 :
        {
            EntityReferenceImpl* newentityRef = createEntityReference(source->getNodeName());
            newnode=newentityRef;
            errorChecking = false;
            newentityRef->setReadOnly(false, true); //allow deep import temporarily
        }
        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;
            newentity->setReadOnly(false, true);// allow deep import temporarily
        }
        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 = (DocumentTypeImpl *)
                createDocumentType(srcdoctype->getNodeName(),
                                   srcdoctype->getPublicId(),
                                   srcdoctype->getSystemId());
            // Values are on NamedNodeMaps
            NamedNodeMapImpl *smap = srcdoctype->getEntities();
            NamedNodeMapImpl *tmap = newdoctype->getEntities();
            if(smap != null) {
                for(unsigned int i = 0; i < smap->getLength(); i++) {
                    tmap->setNamedItem(importNode(smap->item(i), true));
                }
            }
            smap = srcdoctype->getNotations();
            tmap = newdoctype->getNotations();
            if (smap != null) {
                for(unsigned 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 DOM_DOMException(DOM_DOMException::NOT_SUPPORTED_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));
        }
    if (newnode->getNodeType() == DOM_Node::ENTITY_REFERENCE_NODE
        || newnode->getNodeType() == DOM_Node::ENTITY_NODE) {
         ((EntityReferenceImpl*)newnode)->setReadOnly(true, true);
         errorChecking = oldErrorCheckingFlag;
    }

    return newnode;
};


ElementImpl *DocumentImpl::createElementNS(const DOMString &fNamespaceURI,
	const DOMString &qualifiedName)
{
    if (errorChecking && !isXMLName(qualifiedName)) {
        throw DOM_DOMException(DOM_DOMException::INVALID_CHARACTER_ERR,null);
    }
    //DOMString pooledTagName = this->namePool->getPooledString(qualifiedName);
    return new (fMemoryManager) ElementNSImpl(this, fNamespaceURI, qualifiedName);
}


AttrImpl *DocumentImpl::createAttributeNS(const DOMString &fNamespaceURI,
	const DOMString &qualifiedName)
{
    if (!isXMLName(qualifiedName)) {
        throw DOM_DOMException(DOM_DOMException::INVALID_CHARACTER_ERR,null);
    }
    return new (fMemoryManager) AttrNSImpl(this, fNamespaceURI, qualifiedName);
}


DeepNodeListImpl *DocumentImpl::getElementsByTagNameNS(const DOMString &fNamespaceURI,
	const DOMString &fLocalName)
{
    return new (fMemoryManager) DeepNodeListImpl(this, fNamespaceURI, fLocalName);
}


ElementImpl *DocumentImpl::getElementById(const DOMString &elementId)
{
    if (fNodeIDMap == 0)
        return null;

    AttrImpl *theAttr = fNodeIDMap->find(elementId);
    if (theAttr == null)
	    return null;

    return theAttr->getOwnerElement();
}


//Return the index > 0 of ':' in the given qualified name qName="prefix:localName".
//Return 0 if there is no ':', or -1 if qName is malformed such as ":abcd".
int DocumentImpl::indexofQualifiedName(const DOMString & qName)
{
    //Check if s = prefix:localName, name or malformed
    const XMLCh *qNameP = qName.rawBuffer();
    int qNameLen = qName.length();	//note: qName[qNameLen] may not be 0
    int index = -1, count = 0;
    for (int i = 0; i < qNameLen; ++i)
	if (*qNameP++ == chColon) {
	    index = i;
	    ++count;	//number of ':' found
	}
    if (qNameLen == 0 || count > 1 || index == 0 || index == qNameLen-1)
        return -1;
    return count == 0 ? 0 : index;
}


XMLDeclImpl* DocumentImpl::createXMLDecl(const DOMString& version, const DOMString& encoding, const DOMString& standalone)
{
    return new (fMemoryManager) XMLDeclImpl(this, version, encoding, standalone);
}

RangeImpl* DocumentImpl::createRange()
{

    RangeImpl* range = new (fMemoryManager) RangeImpl(DOM_Document(this));

    if (ranges == 0L) {
        ranges = new (fMemoryManager) RangeImpls(1, false, fMemoryManager);
    }
    ranges->addElement(range);
    return range;
}

RangeImpls* DocumentImpl::getRanges()
{
    return ranges;
}

void DocumentImpl::removeRange(RangeImpl* range)
{
    if (ranges != null) {
        unsigned int sz = ranges->size();
        if (sz !=0) {
            for (unsigned int i =0; i<sz; i++) {
                if (ranges->elementAt(i) == range) {
                    ranges->removeElementAt(i);
                    delete range;
                    break;
                }
            }
        }
    }
}

/** Uses the kidOK lookup table to check whether the proposed
    tree structure is legal.

    ????? It feels like there must be a more efficient solution,
    but for the life of me I can't think what it would be.
*/
bool DocumentImpl::isKidOK(NodeImpl *parent, NodeImpl *child)
{
      static int kidOK[14];

      if (kidOK[DOM_Node::DOCUMENT_NODE] == 0)
      {
          kidOK[DOM_Node::DOCUMENT_NODE] =
              1 << DOM_Node::ELEMENT_NODE |
              1 << DOM_Node::PROCESSING_INSTRUCTION_NODE |
              1 << DOM_Node::COMMENT_NODE |
              1 << DOM_Node::DOCUMENT_TYPE_NODE |
              1 << DOM_Node::XML_DECL_NODE;

          kidOK[DOM_Node::DOCUMENT_FRAGMENT_NODE] =
              kidOK[DOM_Node::ENTITY_NODE] =
              kidOK[DOM_Node::ENTITY_REFERENCE_NODE] =
              kidOK[DOM_Node::ELEMENT_NODE] =
              1 << DOM_Node::ELEMENT_NODE |
              1 << DOM_Node::PROCESSING_INSTRUCTION_NODE |
              1 << DOM_Node::COMMENT_NODE |
              1 << DOM_Node::TEXT_NODE |
              1 << DOM_Node::CDATA_SECTION_NODE |
              1 << DOM_Node::ENTITY_REFERENCE_NODE |
              1 << DOM_Node::XML_DECL_NODE;

          kidOK[DOM_Node::ATTRIBUTE_NODE] =
              1 << DOM_Node::TEXT_NODE |
              1 << DOM_Node::ENTITY_REFERENCE_NODE;

          kidOK[DOM_Node::PROCESSING_INSTRUCTION_NODE] =
              kidOK[DOM_Node::COMMENT_NODE] =
              kidOK[DOM_Node::TEXT_NODE] =
              kidOK[DOM_Node::CDATA_SECTION_NODE] =
              kidOK[DOM_Node::NOTATION_NODE] =
              0;
      };
      int p=parent->getNodeType();
      int ch = child->getNodeType();
      return (kidOK[p] & 1<<ch) != 0;
}

void DocumentImpl::setUserData(NodeImpl* n, void* data)
{
	if (!userData && data)
		userData = new (fMemoryManager) RefHashTableOf<void>
        (
            29
            , false
            , new (fMemoryManager) HashPtr()
            , fMemoryManager
        );
	if (userData)
    {
        if (!data)
		    userData->removeKey((void*)n);
	    else
		    userData->put((void*)n,data);
    }
}

void* DocumentImpl::getUserData(NodeImpl* n)
{
	if (userData)
		return userData->get((void*)n);
	else
		return null;
}

void* DocumentImpl::getUserData()
{
	return (hasUserData()) ? getUserData(this) : null;
}

void DocumentImpl::setUserData(void* val)
{
	setUserData(this, val);
	if (val)
		hasUserData(true);
	else
		hasUserData(false);
};

/**
 * Denotes that this node has changed.
 */
void DocumentImpl::changed() {
    fChanges++;
}

/**
 * Returns the number of changes to this node.
 */
int DocumentImpl::changes() {
    return fChanges;
}

// -----------------------------------------------------------------------
//  Notification that lazy data has been deleted
// -----------------------------------------------------------------------
void DocumentImpl::reinitDocumentImpl() {
	delete nam;
	nam = 0;
}

XERCES_CPP_NAMESPACE_END

