/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001-2003 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) 2001, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * $Id: DOMDocumentImpl.cpp,v 1.49 2003/10/23 21:19:58 amassari Exp $
 */
#include "DOMDocumentImpl.hpp"
#include "DOMCasts.hpp"
#include "DOMConfigurationImpl.hpp"
#include "DOMDocumentTypeImpl.hpp"
#include "DOMAttrImpl.hpp"
#include "DOMAttrNSImpl.hpp"
#include "DOMCDATASectionImpl.hpp"
#include "DOMCommentImpl.hpp"
#include "DOMDeepNodeListImpl.hpp"
#include "DOMDocumentFragmentImpl.hpp"
#include "DOMElementImpl.hpp"
#include "XSDElementNSImpl.hpp"
#include "DOMEntityImpl.hpp"
#include "DOMEntityReferenceImpl.hpp"
#include "DOMNamedNodeMapImpl.hpp"
#include "DOMNormalizer.hpp"
#include "DOMNotationImpl.hpp"
#include "DOMProcessingInstructionImpl.hpp"
#include "DOMTextImpl.hpp"
#include "DOMStringPool.hpp"
#include "DOMTreeWalkerImpl.hpp"
#include "DOMNodeIteratorImpl.hpp"
#include "DOMNodeIDMap.hpp"
#include "DOMRangeImpl.hpp"

#include <xercesc/dom/DOMImplementation.hpp>
#include <xercesc/util/XMLChar.hpp>
#include <xercesc/framework/MemoryManager.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>

XERCES_CPP_NAMESPACE_BEGIN


//
//   Constructors.   Warning - be very careful with the ordering of initialization
//                             of the heap.  Ordering depends on the order of declaration
//                             in the .hpp file, not on the order of initializers here
//                             in the constructor.  The heap declaration can not be
//                             first - fNode and fParent must be first for the casting
//                             functions in DOMCasts to work correctly.  This means that
//                             fNode and fParent constructors used here can not
//                             allocate.
//
DOMDocumentImpl::DOMDocumentImpl(MemoryManager* const manager)
    : fNode(this),
      fParent(this),
      fCurrentBlock(0),
      fDOMConfiguration(0),
      fFreePtr(0),
      fFreeBytesRemaining(0),
      fDocType(0),
      fDocElement(0),
      fNamePool(0),
      fNormalizer(0),
      fNodeIDMap(0),
      fRanges(0),
      fNodeIterators(0),
      fChanges(0),
      fNodeListPool(0),
      fActualEncoding(0),
      fEncoding(0),
      fVersion(0),
      fStandalone(false),
      fDocumentURI(0),
      fUserDataTable(0),
      fRecycleNodePtr(0),
      fRecycleBufferPtr(0),
      fMemoryManager(manager),
      errorChecking(true)
{
    fNamePool    = new (this) DOMStringPool(257, this);
};


//DOM Level 2
DOMDocumentImpl::DOMDocumentImpl(const XMLCh *fNamespaceURI,
                               const XMLCh *qualifiedName,
                               DOMDocumentType *doctype,
                               MemoryManager* const manager)
    : fNode(this),
      fParent(this),
      fCurrentBlock(0),
      fDOMConfiguration(0),
      fFreePtr(0),
      fFreeBytesRemaining(0),
      fDocType(0),
      fDocElement(0),
      fNamePool(0),
      fNormalizer(0),
      fNodeIDMap(0),
      fRanges(0),
      fNodeIterators(0),
      fChanges(0),
      fNodeListPool(0),
      fActualEncoding(0),
      fEncoding(0),
      fVersion(0),
      fStandalone(false),
      fDocumentURI(0),
      fUserDataTable(0),
      fRecycleNodePtr(0),
      fRecycleBufferPtr(0),
      fMemoryManager(manager),
      errorChecking(true)
{
    fNamePool    = new (this) DOMStringPool(257, this);
    try {
        setDocumentType(doctype);

        if (qualifiedName)
            appendChild(createElementNS(fNamespaceURI, qualifiedName));  //root element
        else if (fNamespaceURI)
            throw DOMException(DOMException::NAMESPACE_ERR, 0);
    }
    catch(const OutOfMemoryException&)
    {
        throw;
    }
    catch (...) {
        this->deleteHeap();
        throw;
    }
}

void DOMDocumentImpl::setDocumentType(DOMDocumentType *doctype)
{
    if (!doctype)
        return;

    // New doctypes can be created either with the factory methods on DOMImplementation, in
    //   which case ownerDocument will be 0, or with methods on DocumentImpl, in which case
    //   ownerDocument will be set, but the DocType won't yet be a child of the document.
    if (doctype->getOwnerDocument() != 0 && doctype->getOwnerDocument() != this)
        throw DOMException(    //one doctype can belong to only one DOMDocumentImpl
        DOMException::WRONG_DOCUMENT_ERR, 0);

    DOMDocumentTypeImpl* doctypeImpl = (DOMDocumentTypeImpl*) doctype;
    doctypeImpl->setOwnerDocument(this);

    // The doctype can not have any Entities or Notations yet, because they can not
    //   be created except through factory methods on a document.

    // revisit.  What if this doctype is already a child of the document?
    appendChild(doctype);

}

DOMDocumentImpl::~DOMDocumentImpl()
{
    //  Clean up the fNodeListPool
    if (fNodeListPool)
        fNodeListPool->cleanup();

    if (fRanges)
        delete fRanges; //fRanges->cleanup();

    if (fNodeIterators)
        delete fNodeIterators;//fNodeIterators->cleanup();

    if (fUserDataTable)
        delete fUserDataTable;//fUserDataTable->cleanup();

    if (fRecycleNodePtr) {
        fRecycleNodePtr->deleteAllElements();
        delete fRecycleNodePtr;
    }

    if (fRecycleBufferPtr) {
        delete fRecycleBufferPtr;
    }

    delete fNormalizer;

    //  Delete the heap for this document.  This uncerimoniously yanks the storage
    //      out from under all of the nodes in the document.  Destructors are NOT called.
    this->deleteHeap();
};


DOMNode *DOMDocumentImpl::cloneNode(bool deep) const {

    // Note:  the cloned document node goes on the system heap.  All other
    //   nodes added to the new document will go on that document's heap,
    //   but we need to construct the document first, before its heap exists.
    DOMDocumentImpl *newdoc = new (fMemoryManager) DOMDocumentImpl(fMemoryManager);

    // then the children by _importing_ them
    if (deep)
        for (DOMNode *n = this->getFirstChild(); n != 0; n = n->getNextSibling()) {
            newdoc->appendChild(newdoc->importNode(n, true, true));
    }

    fNode.callUserDataHandlers(DOMUserDataHandler::NODE_CLONED, this, newdoc);
    return newdoc;
};


const XMLCh * DOMDocumentImpl::getNodeName() const {
    static const XMLCh nam[] =  // "#document"
        {chPound, chLatin_d, chLatin_o, chLatin_c, chLatin_u, chLatin_m, chLatin_e, chLatin_n, chLatin_t, 0};
    return nam;
}


short DOMDocumentImpl::getNodeType() const {
    return DOMNode::DOCUMENT_NODE;
};


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


DOMAttr *DOMDocumentImpl::createAttribute(const XMLCh *nam)
{
    if(!nam || !isXMLName(nam))
        throw DOMException(DOMException::INVALID_CHARACTER_ERR,0);
    return new (this, DOMDocumentImpl::ATTR_OBJECT) DOMAttrImpl(this,nam);
};



DOMCDATASection *DOMDocumentImpl::createCDATASection(const XMLCh *data) {
    return new (this, DOMDocumentImpl::CDATA_SECTION_OBJECT) DOMCDATASectionImpl(this,data);
};



DOMComment *DOMDocumentImpl::createComment(const XMLCh *data)
{
    return new (this, DOMDocumentImpl::COMMENT_OBJECT) DOMCommentImpl(this, data);
};



DOMDocumentFragment *DOMDocumentImpl::createDocumentFragment()
{
    return new (this, DOMDocumentImpl::DOCUMENT_FRAGMENT_OBJECT) DOMDocumentFragmentImpl(this);
};



DOMDocumentType *DOMDocumentImpl::createDocumentType(const XMLCh *nam)
{
    if (!nam || !isXMLName(nam))
        throw DOMException(
        DOMException::INVALID_CHARACTER_ERR, 0);

    return new (this, DOMDocumentImpl::DOCUMENT_TYPE_OBJECT) DOMDocumentTypeImpl(this, nam, false);
};



DOMDocumentType *
    DOMDocumentImpl::createDocumentType(const XMLCh *qualifiedName,
                                     const XMLCh *publicId,
                                     const XMLCh *systemId)
{
    if (!qualifiedName || !isXMLName(qualifiedName))
        throw DOMException(
        DOMException::INVALID_CHARACTER_ERR, 0);

    return new (this, DOMDocumentImpl::DOCUMENT_TYPE_OBJECT) DOMDocumentTypeImpl(this, qualifiedName, publicId, systemId, false);
};



DOMElement *DOMDocumentImpl::createElement(const XMLCh *tagName)
{
    if(!tagName || !isXMLName(tagName))
        throw DOMException(DOMException::INVALID_CHARACTER_ERR,0);

    return new (this, DOMDocumentImpl::ELEMENT_OBJECT) DOMElementImpl(this,tagName);
};


DOMElement *DOMDocumentImpl::createElementNoCheck(const XMLCh *tagName)
{
    return new (this, DOMDocumentImpl::ELEMENT_OBJECT) DOMElementImpl(this, tagName);
};




DOMEntity *DOMDocumentImpl::createEntity(const XMLCh *nam)
{
    if (!nam || !isXMLName(nam))
        throw DOMException(
        DOMException::INVALID_CHARACTER_ERR, 0);

    return new (this, DOMDocumentImpl::ENTITY_OBJECT) DOMEntityImpl(this, nam);
};



DOMEntityReference *DOMDocumentImpl::createEntityReference(const XMLCh *nam)
{
    if (!nam || !isXMLName(nam))
        throw DOMException(
        DOMException::INVALID_CHARACTER_ERR, 0);

    return new (this, DOMDocumentImpl::ENTITY_REFERENCE_OBJECT) DOMEntityReferenceImpl(this, nam);
};

DOMEntityReference *DOMDocumentImpl::createEntityReferenceByParser(const XMLCh *nam)
{
    if (!nam || !isXMLName(nam))
        throw DOMException(
        DOMException::INVALID_CHARACTER_ERR, 0);

    return new (this, DOMDocumentImpl::ENTITY_REFERENCE_OBJECT) DOMEntityReferenceImpl(this, nam, false);
};

DOMNotation *DOMDocumentImpl::createNotation(const XMLCh *nam)
{
    if (!nam || !isXMLName(nam))
        throw DOMException(
        DOMException::INVALID_CHARACTER_ERR, 0);

    return new (this, DOMDocumentImpl::NOTATION_OBJECT) DOMNotationImpl(this, nam);
};



DOMProcessingInstruction *DOMDocumentImpl::createProcessingInstruction(
                                          const XMLCh *target, const XMLCh *data)
{
    if(!target || !isXMLName(target))
        throw DOMException(DOMException::INVALID_CHARACTER_ERR,0);
    return new (this, DOMDocumentImpl::PROCESSING_INSTRUCTION_OBJECT) DOMProcessingInstructionImpl(this,target,data);
};




DOMText *DOMDocumentImpl::createTextNode(const XMLCh *data)
{
    return new (this, DOMDocumentImpl::TEXT_OBJECT) DOMTextImpl(this,data);
};


DOMNodeIterator* DOMDocumentImpl::createNodeIterator (
          DOMNode *root, unsigned long whatToShow, DOMNodeFilter* filter, bool entityReferenceExpansion)
{
    if (!root) {
        throw DOMException(DOMException::NOT_SUPPORTED_ERR, 0);
        return 0;
    }

    DOMNodeIteratorImpl* nodeIterator = new (this) DOMNodeIteratorImpl(this, root, whatToShow, filter, entityReferenceExpansion);

    if (fNodeIterators == 0L) {
        //fNodeIterators = new (this) NodeIterators(1, false);
        fNodeIterators = new (fMemoryManager) NodeIterators(1, false, fMemoryManager);
    }
    fNodeIterators->addElement(nodeIterator);

    return nodeIterator;
}


NodeIterators* DOMDocumentImpl::getNodeIterators() const
{
    return fNodeIterators;
}

void DOMDocumentImpl::removeNodeIterator(DOMNodeIteratorImpl* nodeIterator)
{
    if (fNodeIterators != 0) {
        XMLSize_t sz = fNodeIterators->size();
        if (sz !=0) {
            for (XMLSize_t i =0; i<sz; i++) {
                if (fNodeIterators->elementAt(i) == nodeIterator) {
                    fNodeIterators->removeElementAt(i);
                    break;
                }
            }
        }
    }
}


const DOMXPathExpression* DOMDocumentImpl::createExpression(const XMLCh *expression, const DOMXPathNSResolver *resolver)
{
    throw DOMException(DOMException::NOT_SUPPORTED_ERR, 0);
    return 0;
}

const DOMXPathNSResolver* DOMDocumentImpl::createNSResolver(DOMNode *nodeResolver)

{
    throw DOMException(DOMException::NOT_SUPPORTED_ERR, 0);
    return 0;
}

void* DOMDocumentImpl::evaluate(const XMLCh *expression, DOMNode *contextNode, const DOMXPathNSResolver *resolver, 
                           unsigned short type, void* result) 
{
    throw DOMException(DOMException::NOT_SUPPORTED_ERR, 0);
    return 0;
}



DOMTreeWalker* DOMDocumentImpl::createTreeWalker (DOMNode *root, unsigned long whatToShow, DOMNodeFilter* filter, bool entityReferenceExpansion)
{
    if (!root) {
        throw DOMException(DOMException::NOT_SUPPORTED_ERR, 0);
        return 0;
    }

    return new (this) DOMTreeWalkerImpl(root, whatToShow, filter, entityReferenceExpansion);
}




DOMDocumentType *DOMDocumentImpl::getDoctype() const
{
    return fDocType;
};



DOMElement *DOMDocumentImpl::getDocumentElement() const
{
    return fDocElement;
};



DOMNodeList *DOMDocumentImpl::getElementsByTagName(const XMLCh *tagname) const
{
    // cast off the const of this because we will update the fNodeListPool
    return ((DOMDocumentImpl*)this)->getDeepNodeList(this,tagname);
};


DOMImplementation   *DOMDocumentImpl::getImplementation() const {
    return DOMImplementation::getImplementation();
}


DOMNode *DOMDocumentImpl::insertBefore(DOMNode *newChild, DOMNode *refChild)
{
    // Only one such child permitted
    if(
        (newChild->getNodeType() == DOMNode::ELEMENT_NODE  && fDocElement!=0)
        ||
        (newChild->getNodeType() == DOMNode::DOCUMENT_TYPE_NODE  && fDocType!=0)
        )
        throw DOMException(DOMException::HIERARCHY_REQUEST_ERR,0);

    // if the newChild is a documenttype node created from domimplementation, set the ownerDoc first
    if ((newChild->getNodeType() == DOMNode::DOCUMENT_TYPE_NODE) && !newChild->getOwnerDocument())
        ((DOMDocumentTypeImpl*)newChild)->setOwnerDocument(this);

    fParent.insertBefore(newChild,refChild);

    // If insert succeeded, cache the kid appropriately
    if(newChild->getNodeType() == DOMNode::ELEMENT_NODE)
        fDocElement=(DOMElement *)newChild;
    else if(newChild->getNodeType() == DOMNode::DOCUMENT_TYPE_NODE)
        fDocType=(DOMDocumentType *)newChild;

    return newChild;
};


DOMNode* DOMDocumentImpl::replaceChild(DOMNode *newChild, DOMNode *oldChild) {
    DOMDocumentType* tempDocType = fDocType;
    DOMElement* tempDocElement = fDocElement;

    if(oldChild->getNodeType() == DOMNode::DOCUMENT_TYPE_NODE)
        fDocType=0;
    else if(oldChild->getNodeType() == DOMNode::ELEMENT_NODE)
        fDocElement=0;

    try {
        insertBefore(newChild, oldChild);
        // changed() already done.

        if((oldChild->getNodeType() == DOMNode::DOCUMENT_TYPE_NODE)
        || (oldChild->getNodeType() == DOMNode::ELEMENT_NODE))
            return fParent.removeChild(oldChild);
        else
            return removeChild(oldChild);
    }
    catch(const OutOfMemoryException&)
    {
        throw;
    }
    catch(...) {
        fDocType = tempDocType;
        fDocElement = tempDocElement;
        throw;
    }
}

bool DOMDocumentImpl::isXMLName(const XMLCh *s)
{
    if (XMLString::equals(fVersion, XMLUni::fgVersion1_1))
        return XMLChar1_1::isValidName(s, XMLString::stringLen(s));
    else
        return XMLChar1_0::isValidName(s, XMLString::stringLen(s));
};




DOMNode *DOMDocumentImpl::removeChild(DOMNode *oldChild)
{
    fParent.removeChild(oldChild);

    // If remove succeeded, un-cache the kid appropriately
    if(oldChild->getNodeType() == DOMNode::ELEMENT_NODE)
        fDocElement=0;
    else if(oldChild->getNodeType() == DOMNode::DOCUMENT_TYPE_NODE)
        fDocType=0;

    return oldChild;
};



void DOMDocumentImpl::setNodeValue(const XMLCh *x)
{
    fNode.setNodeValue(x);
};


//Introduced in DOM Level 2
DOMNode *DOMDocumentImpl::importNode(DOMNode *source, bool deep)
{
    return importNode(source, deep, false);
}


DOMElement *DOMDocumentImpl::createElementNS(const XMLCh *fNamespaceURI,
    const XMLCh *qualifiedName)
{
    if(!qualifiedName || !isXMLName(qualifiedName))
        throw DOMException(DOMException::INVALID_CHARACTER_ERR,0);
    //XMLCh * pooledTagName = this->fNamePool->getPooledString(qualifiedName);
    return new (this, DOMDocumentImpl::ELEMENT_NS_OBJECT) DOMElementNSImpl(this, fNamespaceURI, qualifiedName);
}

DOMElement *DOMDocumentImpl::createElementNS(const XMLCh *fNamespaceURI,
                                              const XMLCh *qualifiedName,
                                              const XMLSSize_t lineNo,
                                              const XMLSSize_t columnNo)
{
    if(!qualifiedName || !isXMLName(qualifiedName))
        throw DOMException(DOMException::INVALID_CHARACTER_ERR,0);

    return new (this) XSDElementNSImpl(this, fNamespaceURI, qualifiedName, lineNo, columnNo);
}


DOMAttr *DOMDocumentImpl::createAttributeNS(const XMLCh *fNamespaceURI,
    const XMLCh *qualifiedName)
{
    if(!qualifiedName || !isXMLName(qualifiedName))
        throw DOMException(DOMException::INVALID_CHARACTER_ERR,0);
    return new (this, DOMDocumentImpl::ATTR_NS_OBJECT) DOMAttrNSImpl(this, fNamespaceURI, qualifiedName);
}


DOMNodeList *DOMDocumentImpl::getElementsByTagNameNS(const XMLCh *fNamespaceURI,
    const XMLCh *fLocalName)  const
{
    // cast off the const of this because we will update the fNodeListPool
    return ((DOMDocumentImpl*)this)->getDeepNodeList(this, fNamespaceURI, fLocalName);
}


DOMElement *DOMDocumentImpl::getElementById(const XMLCh *elementId) const
{
    if (fNodeIDMap == 0)
        return 0;

    DOMAttr *theAttr = fNodeIDMap->find(elementId);
    if (theAttr == 0)
        return 0;

    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" or "abcd:".
int DOMDocumentImpl::indexofQualifiedName(const XMLCh * qName)
{
    int qNameLen = XMLString::stringLen(qName);
    int index = -1, count = 0;
    for (int i = 0; i < qNameLen; ++i) {
        if (qName[i] == chColon) {
            index = i;
            ++count;    //number of ':' found
        }
    }

    if (qNameLen == 0 || count > 1 || index == 0 || index == qNameLen-1)
        return -1;
    return count == 0 ? 0 : index;
}


const XMLCh*     DOMDocumentImpl::getBaseURI() const
{
	  return fDocumentURI;
};


DOMRange* DOMDocumentImpl::createRange()
{

    DOMRangeImpl* range = new (this) DOMRangeImpl(this);

    if (fRanges == 0L) {
        //fRanges = new (this) Ranges(1, false);
        fRanges = new (fMemoryManager) Ranges(1, false, fMemoryManager); // XMemory
    }
    fRanges->addElement(range);
    return range;
}

Ranges* DOMDocumentImpl::getRanges() const
{
    return fRanges;
}

void DOMDocumentImpl::removeRange(DOMRangeImpl* range)
{
    if (fRanges != 0) {
        XMLSize_t sz = fRanges->size();
        if (sz !=0) {
            for (XMLSize_t i =0; i<sz; i++) {
                if (fRanges->elementAt(i) == range) {
                    fRanges->removeElementAt(i);
                    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 DOMDocumentImpl::isKidOK(DOMNode *parent, DOMNode *child)
{
      static int kidOK[14];

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

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

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

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

void            DOMDocumentImpl::changed()
{
    fChanges++;
}


int             DOMDocumentImpl::changes() const{
    return fChanges;
};



//
//    Delegation for functions inherited from DOMNode
//
           DOMNode*         DOMDocumentImpl::appendChild(DOMNode *newChild)          {return insertBefore(newChild, 0); };
           DOMNamedNodeMap* DOMDocumentImpl::getAttributes() const                   {return fNode.getAttributes (); };
           DOMNodeList*     DOMDocumentImpl::getChildNodes() const                   {return fParent.getChildNodes (); };
           DOMNode*         DOMDocumentImpl::getFirstChild() const                   {return fParent.getFirstChild (); };
           DOMNode*         DOMDocumentImpl::getLastChild() const                    {return fParent.getLastChild (); };
     const XMLCh*           DOMDocumentImpl::getLocalName() const                    {return fNode.getLocalName (); };
     const XMLCh*           DOMDocumentImpl::getNamespaceURI() const                 {return fNode.getNamespaceURI (); };
           DOMNode*         DOMDocumentImpl::getNextSibling() const                  {return fNode.getNextSibling (); };
     const XMLCh*           DOMDocumentImpl::getNodeValue() const                    {return fNode.getNodeValue (); };
     const XMLCh*           DOMDocumentImpl::getPrefix() const                       {return fNode.getPrefix (); };
           DOMNode*         DOMDocumentImpl::getParentNode() const                   {return fNode.getParentNode (); };
           DOMNode*         DOMDocumentImpl::getPreviousSibling() const              {return fNode.getPreviousSibling (); };
           bool             DOMDocumentImpl::hasChildNodes() const                   {return fParent.hasChildNodes (); };
           void             DOMDocumentImpl::normalize()                             {fParent.normalize (); };
           bool             DOMDocumentImpl::isSupported(const XMLCh *feature, const XMLCh *version) const
                                                                                     {return fNode.isSupported (feature, version); };
           void             DOMDocumentImpl::setPrefix(const XMLCh  *prefix)         {fNode.setPrefix(prefix); };
           bool             DOMDocumentImpl::hasAttributes() const                   {return fNode.hasAttributes(); };
           bool             DOMDocumentImpl::isSameNode(const DOMNode* other) const  {return fNode.isSameNode(other);};
           bool             DOMDocumentImpl::isEqualNode(const DOMNode* arg) const   {return fParent.isEqualNode(arg);};
           void*            DOMDocumentImpl::setUserData(const XMLCh* key, void* data, DOMUserDataHandler* handler)
                                                                                     {return fNode.setUserData(key, data, handler); };
           void*            DOMDocumentImpl::getUserData(const XMLCh* key) const     {return fNode.getUserData(key); };
           short            DOMDocumentImpl::compareTreePosition(const DOMNode* other) const {return fNode.compareTreePosition(other); };
           const XMLCh*     DOMDocumentImpl::getTextContent() const                  {return fNode.getTextContent(); };
           void             DOMDocumentImpl::setTextContent(const XMLCh* textContent){fNode.setTextContent(textContent); };
           const XMLCh*     DOMDocumentImpl::lookupNamespacePrefix(const XMLCh* namespaceURI, bool useDefault) const  {return fNode.lookupNamespacePrefix(namespaceURI, useDefault); };
           bool             DOMDocumentImpl::isDefaultNamespace(const XMLCh* namespaceURI) const {return fNode.isDefaultNamespace(namespaceURI); };
           const XMLCh*     DOMDocumentImpl::lookupNamespaceURI(const XMLCh* prefix) const  {return fNode.lookupNamespaceURI(prefix); };
           DOMNode*         DOMDocumentImpl::getInterface(const XMLCh* feature)      {return fNode.getInterface(feature); };




//-----------------------------------------------------------------------
//
//  Per Document Heap and Heap Helper functions
//
//        revisit - this stuff should be a class of its own, rather than
//                       just lying around naked in DocumentImpl.
//
//-----------------------------------------------------------------------

XMLCh * DOMDocumentImpl::cloneString(const XMLCh *src)
{
    if (!src) return 0;
    size_t   len = XMLString::stringLen(src);
    len = (len + 1) * sizeof(XMLCh);
    len = (len % 4) + len;
    XMLCh *newStr = (XMLCh *)this->allocate(len);
    XMLString::copyString(newStr, src);
    return newStr;
}


const XMLCh *  DOMDocumentImpl::getPooledString(const XMLCh *src)
{
    if (!src) return 0;
    else return this->fNamePool->getPooledString(src);
}

static const int kHeapAllocSize = 0x10000;    // The chunk size to allocate from the
                                              //   system allocator.

static const int kMaxSubAllocationSize = 4096;   // Any request for more bytes
                                                 //  than this will be handled by
                                                 //  allocating directly with system.

void *         DOMDocumentImpl::allocate(size_t amount)
{	
	//	Align the request size so that suballocated blocks
	//	beyond this one will be maintained at the same alignment.
	amount = XMLPlatformUtils::alignPointerForNewBlockAllocation(amount);

    // If the request is for a largish block, hand it off to the system
    //   allocator.  The block still must be linked into the list of
    //   allocated blocks so that it will be deleted when the time comes.
    if (amount > kMaxSubAllocationSize)
    {
		//	The size of the header we add to our raw blocks
		size_t sizeOfHeader = XMLPlatformUtils::alignPointerForNewBlockAllocation(sizeof(void *));

		//	Try to allocate the block
        void* newBlock;
        newBlock = fMemoryManager->allocate((sizeOfHeader + amount) * sizeof(char)); //new char[amount + sizeOfHeader];
        
		//	Link it into the list beyond current block, as current block
		//	is still being subdivided. If there is no current block
		//	then track that we have no bytes to further divide.
        if (fCurrentBlock)
        {
            *(void **)newBlock = *(void **)fCurrentBlock;
            *(void **)fCurrentBlock = newBlock;
        }
        else
        {
            fCurrentBlock = newBlock;
            fFreePtr = 0;
            fFreeBytesRemaining = 0;
        }
		
        void *retPtr = (char *)newBlock + sizeOfHeader;
        return retPtr;
    }

    //	It's a normal (sub-allocatable) request.
	//	Are we out of room in our current block?
	if (amount > fFreeBytesRemaining)
    {
        // Request doesn't fit in the current block.

		// The size of the header we add to our raw blocks
		size_t sizeOfHeader = XMLPlatformUtils::alignPointerForNewBlockAllocation(sizeof(void *));

        // Get a new block from the system allocator.
        void* newBlock;
        newBlock = fMemoryManager->allocate(kHeapAllocSize * sizeof(char)); //new char[kHeapAllocSize];
        
        *(void **)newBlock = fCurrentBlock;
        fCurrentBlock = newBlock;
        fFreePtr = (char *)newBlock + sizeOfHeader;
        fFreeBytesRemaining = kHeapAllocSize - sizeOfHeader;
    }

	//	Subdivide the request off current block
    void *retPtr = fFreePtr;
    fFreePtr += amount;
    fFreeBytesRemaining -= amount;
	
    return retPtr;
}


void    DOMDocumentImpl::deleteHeap()
{
    while (fCurrentBlock != 0)
    {
        void *nextBlock = *(void **)fCurrentBlock;
        fMemoryManager->deallocate(fCurrentBlock); //delete [] (char*) fCurrentBlock;
        fCurrentBlock = nextBlock;
    }
}


DOMNodeList *DOMDocumentImpl::getDeepNodeList(const DOMNode *rootNode, const XMLCh *tagName)
{
    if(!fNodeListPool) {
        fNodeListPool = new (this) DOMDeepNodeListPool<DOMDeepNodeListImpl>(109, false);
    }

    DOMDeepNodeListImpl* retList = fNodeListPool->getByKey(rootNode, tagName, 0);
    if (!retList) {
        int id = fNodeListPool->put((void*) rootNode, (XMLCh*) tagName, 0, new (this) DOMDeepNodeListImpl(rootNode, tagName));
        retList = fNodeListPool->getById(id);
    }

    return retList;
}


DOMNodeList *DOMDocumentImpl::getDeepNodeList(const DOMNode *rootNode,     //DOM Level 2
                                                   const XMLCh *namespaceURI,
                                                   const XMLCh *localName)
{
    if(!fNodeListPool) {
        fNodeListPool = new (this) DOMDeepNodeListPool<DOMDeepNodeListImpl>(109, false);
    }

    DOMDeepNodeListImpl* retList = fNodeListPool->getByKey(rootNode, localName, namespaceURI);
    if (!retList) {
        // the pool will adopt the DOMDeepNodeListImpl
        int id = fNodeListPool->put((void*) rootNode, (XMLCh*) localName, (XMLCh*) namespaceURI, new (this) DOMDeepNodeListImpl(rootNode, namespaceURI, localName));
        retList = fNodeListPool->getById(id);
    }

    return retList;
}


//Introduced in DOM Level 3
const XMLCh* DOMDocumentImpl::getActualEncoding() const {
    return fActualEncoding;
}

void DOMDocumentImpl::setActualEncoding(const XMLCh* actualEncoding){
    fActualEncoding = cloneString(actualEncoding);
}

const XMLCh* DOMDocumentImpl::getEncoding() const {
    return fEncoding;
}

void DOMDocumentImpl::setEncoding(const XMLCh* encoding){
    fEncoding = cloneString(encoding);
}

bool DOMDocumentImpl::getStandalone() const{
    return fStandalone;
}

void DOMDocumentImpl::setStandalone(bool standalone){
    fStandalone = standalone;
}

const XMLCh* DOMDocumentImpl::getVersion() const {
    return fVersion;
}

void DOMDocumentImpl::setVersion(const XMLCh* version){
    if ((version && *version) &&
        !XMLString::equals(version, XMLUni::fgVersion1_0) &&
        !XMLString::equals(version, XMLUni::fgVersion1_1))
        throw DOMException(DOMException::NOT_SUPPORTED_ERR, 0);

    fVersion = cloneString(version);
}

const XMLCh* DOMDocumentImpl::getDocumentURI() const
{
    return fDocumentURI;
}

void DOMDocumentImpl::setDocumentURI(const XMLCh* documentURI){
    if (documentURI && *documentURI) {
        XMLCh* temp = (XMLCh*) this->allocate((XMLString::stringLen(documentURI) + 9)*sizeof(XMLCh));
        XMLString::fixURI(documentURI, temp);
        fDocumentURI = temp;
    }
    else
        fDocumentURI = 0;
}

bool DOMDocumentImpl::getStrictErrorChecking() const {
    return getErrorChecking();
}

void DOMDocumentImpl::setStrictErrorChecking(bool strictErrorChecking) {
    setErrorChecking(strictErrorChecking);
}

DOMNode* DOMDocumentImpl::adoptNode(DOMNode* source) {
    throw DOMException(DOMException::NOT_SUPPORTED_ERR, 0);
    return 0;
}

void DOMDocumentImpl::normalizeDocument() {

    if(!fNormalizer) 
        fNormalizer = new (fMemoryManager) DOMNormalizer(fMemoryManager);

    fNormalizer->normalizeDocument(this);
}

DOMConfiguration* DOMDocumentImpl::getDOMConfiguration() const {
    if(!fDOMConfiguration)
        ((DOMDocumentImpl*)this)->fDOMConfiguration = new ((DOMDocumentImpl*)this) DOMConfigurationImpl();

    return fDOMConfiguration;
}

void DOMDocumentImpl::setDOMConfiguration(DOMConfiguration *config) {
    fDOMConfiguration = config;
}

DOMNode *DOMDocumentImpl::importNode(DOMNode *source, bool deep, bool cloningDoc)
{
    DOMNode *newnode=0;
    bool oldErrorCheckingFlag = errorChecking;

    switch (source->getNodeType())
    {
    case DOMNode::ELEMENT_NODE :
        {
            DOMElement *newelement;
            if (source->getLocalName() == 0)
                newelement = createElement(source->getNodeName());
            else
                newelement = createElementNS(source->getNamespaceURI(),

            source->getNodeName());
            DOMNamedNodeMap *srcattr=source->getAttributes();
            if(srcattr!=0)
                for(XMLSize_t i=0;i<srcattr->getLength();++i)
                {
                    DOMAttr *attr = (DOMAttr *) srcattr->item(i);
                    if (attr -> getSpecified()) { // not a default attribute
                        DOMAttr *nattr = (DOMAttr *) importNode(attr, true, false);
                        if (attr -> getLocalName() == 0)
                            newelement->setAttributeNode(nattr);
                        else
                            newelement->setAttributeNodeNS(nattr);

                        // if the imported attribute is of ID type, register the new node in fNodeIDMap
                        if (castToNodeImpl(attr)->isIdAttr()) {
                            castToNodeImpl(nattr)->isIdAttr(true);
                            if (!fNodeIDMap)
                                 fNodeIDMap = new (this) DOMNodeIDMap(500, this);
                            fNodeIDMap->add((DOMAttr*)nattr);
                        }
                    }
                }
            newnode=newelement;

        }
        break;
    case DOMNode::ATTRIBUTE_NODE :
        if (source->getLocalName() == 0)
            newnode = createAttribute(source->getNodeName());
        else
            newnode = createAttributeNS(source->getNamespaceURI(),
            source->getNodeName());
        deep = true;
        // Kids carry value

        break;
    case DOMNode::TEXT_NODE :
        newnode = createTextNode(source->getNodeValue());
        break;
    case DOMNode::CDATA_SECTION_NODE :
        newnode = createCDATASection(source->getNodeValue());
        break;
    case DOMNode::ENTITY_REFERENCE_NODE :
        {
            DOMEntityReferenceImpl* newentityRef = (DOMEntityReferenceImpl*)createEntityReference(source->getNodeName());
            newnode=newentityRef;
            errorChecking = false;
            newentityRef->setReadOnly(false, true); //allow deep import temporarily
        }
        break;
    case DOMNode::ENTITY_NODE :
        {
            DOMEntity *srcentity=(DOMEntity *)source;
            DOMEntityImpl *newentity = (DOMEntityImpl *)createEntity(source->getNodeName());
            newentity->setPublicId(srcentity->getPublicId());
            newentity->setSystemId(srcentity->getSystemId());
            newentity->setNotationName(srcentity->getNotationName());
            // Kids carry additional value
            newnode=newentity;
            castToNodeImpl(newentity)->setReadOnly(false, true);// allow deep import temporarily
        }
        break;
    case DOMNode::PROCESSING_INSTRUCTION_NODE :
        newnode = createProcessingInstruction(source->getNodeName(),
            source->getNodeValue());
        break;
    case DOMNode::COMMENT_NODE :
        newnode = createComment(source->getNodeValue());
        break;
    case DOMNode::DOCUMENT_TYPE_NODE :
        {
            // unless this is used as part of cloning a Document
            // forbid it for the sake of being compliant to the DOM spec
            if (!cloningDoc)
                throw DOMException(DOMException::NOT_SUPPORTED_ERR, 0);

            DOMDocumentType *srcdoctype = (DOMDocumentType *)source;
            DOMDocumentType *newdoctype = (DOMDocumentType *)
                createDocumentType(srcdoctype->getNodeName(),
                srcdoctype->getPublicId(),
                srcdoctype->getSystemId());
            // Values are on NamedNodeMaps
            DOMNamedNodeMap *smap = srcdoctype->getEntities();
            DOMNamedNodeMap *tmap = newdoctype->getEntities();
            if(smap != 0) {
                for(XMLSize_t i = 0; i < smap->getLength(); i++) {
                    tmap->setNamedItem(importNode(smap->item(i), true, false));
                }
            }
            smap = srcdoctype->getNotations();
            tmap = newdoctype->getNotations();
            if (smap != 0) {
                for(XMLSize_t i = 0; i < smap->getLength(); i++) {
                    tmap->setNamedItem(importNode(smap->item(i), true, false));
                }
            }
            // 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 DOMNode::DOCUMENT_FRAGMENT_NODE :
        newnode = createDocumentFragment();
        // No name, kids carry value
        break;
    case DOMNode::NOTATION_NODE :
        {
            DOMNotation *srcnotation=(DOMNotation *)source;
            DOMNotationImpl *newnotation = (DOMNotationImpl *)createNotation(source->getNodeName());
            newnotation->setPublicId(srcnotation->getPublicId());
            newnotation->setSystemId(srcnotation->getSystemId());
            // Kids carry additional value
            newnode=newnotation;
            // No name, no value
            break;
        }

    case DOMNode::DOCUMENT_NODE : // Document can't be child of Document
    default:                       // Unknown node type
        throw DOMException(DOMException::NOT_SUPPORTED_ERR, 0);
    }

    // If deep, replicate and attach the kids.
    if (deep)
        for (DOMNode *srckid = source->getFirstChild();
             srckid != 0;
             srckid = srckid->getNextSibling())
        {
            newnode->appendChild(importNode(srckid, true, false));
        }

    if (newnode->getNodeType() == DOMNode::ENTITY_REFERENCE_NODE
        || newnode->getNodeType() == DOMNode::ENTITY_NODE) {
        castToNodeImpl(newnode)->setReadOnly(true, true);
        errorChecking = oldErrorCheckingFlag;
    }

    if (!cloningDoc)
        fNode.callUserDataHandlers(DOMUserDataHandler::NODE_IMPORTED, source, newnode);

    return newnode;
}

// user data utility
void* DOMDocumentImpl::setUserData(DOMNodeImpl* n, const XMLCh* key, void* data, DOMUserDataHandler* handler)
{
    void* oldData = 0;
    DOMNodeUserDataTable* node_userDataTable = 0;

    if (!fUserDataTable) {
        // create the table on heap so that it can be cleaned in destructor
        //fUserDataTable = new (this) RefHashTableOf<DOMNodeUserDataTable>(29, true, new HashPtr());
        fUserDataTable = new (fMemoryManager) RefHashTableOf<DOMNodeUserDataTable>
        (
            29
            , true
            , new (fMemoryManager) HashPtr()
            , fMemoryManager
        );
    }
    else {
        node_userDataTable = fUserDataTable->get((void*)n);
        DOMUserDataRecord* oldDataRecord = 0;

        if (node_userDataTable) {
            oldDataRecord = node_userDataTable->get((void*)key);

            if (oldDataRecord) {
                oldData = oldDataRecord->getKey();
                node_userDataTable->removeKey((void*)key);
            }
        }
    }

    if (data) {

        // create the DOMNodeUserDataTable if not exists
        // create on the heap and adopted by the hashtable which will delete it upon removal.
        if (!node_userDataTable) {
            node_userDataTable  = new (fMemoryManager) RefHashTableOf<DOMUserDataRecord>(29, true, fMemoryManager);
            fUserDataTable->put(n, node_userDataTable);
        }

        // clone the key first, and create the DOMUserDataRecord
        // create on the heap and adopted by the hashtable which will delete it upon removal.
        node_userDataTable->put((void*)getPooledString(key), new (fMemoryManager) DOMUserDataRecord(data, handler));
    }
    else {
        if (node_userDataTable->isEmpty())
            n->hasUserData(false);
    }

    return oldData;
}

void* DOMDocumentImpl::getUserData(const DOMNodeImpl* n, const XMLCh* key) const
{
    if (fUserDataTable) {
        DOMNodeUserDataTable*  node_userDataTable = fUserDataTable->get((void*)n);

        if (node_userDataTable) {
            DOMUserDataRecord* dataRecord = node_userDataTable->get((void*)key);
            if (dataRecord)
                return dataRecord->getKey();
        }
    }

    return 0;
}

void DOMDocumentImpl::callUserDataHandlers(const DOMNodeImpl* n, DOMUserDataHandler::DOMOperationType operation, const DOMNode* src, const DOMNode* dst) const
{
    if (fUserDataTable) {
        DOMNodeUserDataTable*  node_userDataTable = fUserDataTable->get((void*)n);

        if (node_userDataTable) {
            RefHashTableOfEnumerator<DOMUserDataRecord> userDataEnum(node_userDataTable);

            // walk through the entire node_userDataTable table
            while (userDataEnum.hasMoreElements()) {
                // get the key
                XMLCh* key = (XMLCh*) userDataEnum.nextElementKey();

                // get the DOMUserDataRecord
                DOMUserDataRecord* userDataRecord = node_userDataTable->get((void*)key);

                // get the handler
                DOMUserDataHandler* handler = userDataRecord->getValue();

                if (handler) {
                    // get the data
                    void* data = userDataRecord->getKey();
                    handler->handle(operation, key, data, src, dst);
                }

                // if the operation is deleted, we in fact should remove the data from the table
                if (operation == DOMUserDataHandler::NODE_DELETED)
                    node_userDataTable->removeKey((void*)key);
            }
        }
    }
}


void DOMDocumentImpl::transferUserData(DOMNodeImpl* n1, DOMNodeImpl* n2)
{
    if (fUserDataTable) {
        fUserDataTable->transferElement((void*)n1, (void*)n2);
        n1->hasUserData(false);
        n2->hasUserData(true);
    }
}


DOMNode* DOMDocumentImpl::renameNode(DOMNode* n, const XMLCh* namespaceURI, const XMLCh* name)
{
    if (n->getOwnerDocument() != this)
        if (n->getNodeType() == DOCUMENT_NODE)
            throw DOMException(DOMException::NOT_SUPPORTED_ERR, 0);
        else
            throw DOMException(DOMException::WRONG_DOCUMENT_ERR, 0);

    switch (n->getNodeType()) {
        case ELEMENT_NODE:
            return ((DOMElementImpl*)n)->rename(namespaceURI, name);
        case ATTRIBUTE_NODE:
            return ((DOMAttrImpl*)n)->rename(namespaceURI, name);
        default:
            throw DOMException(DOMException::NOT_SUPPORTED_ERR, 0);
    }

    return 0;
}

void DOMDocumentImpl::release()
{
    DOMDocument* doc = (DOMDocument*) this;
    fNode.callUserDataHandlers(DOMUserDataHandler::NODE_DELETED, 0, 0);

    // notify userdatahandler first, if we have some
    if (fUserDataTable)
        releaseDocNotifyUserData(this);

    // release the docType in case it was created from heap
    if (fDocType) {
        castToNodeImpl(fDocType)->isToBeReleased(true);
        fDocType->release();
    }

    // delete the document memory pool
    delete doc;
}

void DOMDocumentImpl::releaseDocNotifyUserData(DOMNode* object)
{
    DOMNode *child = object->getFirstChild();
    
    while( child != 0)
    {
            
         DOMNamedNodeMap *attrlist=child->getAttributes(); 
    
         if(attrlist!=0) 
             for(XMLSize_t i=0;i<attrlist->getLength();++i) 
                 releaseDocNotifyUserData(attrlist->item(i)); 
            
        releaseDocNotifyUserData(child);
        child = child->getNextSibling();
    }

    castToNodeImpl(object)->callUserDataHandlers(DOMUserDataHandler::NODE_DELETED, 0, 0);
}

void DOMDocumentImpl::release(DOMNode* object, NodeObjectType type)
{
    if (!fRecycleNodePtr)
        fRecycleNodePtr = new (fMemoryManager) RefArrayOf<DOMNodePtr> (15, fMemoryManager);

    if (!fRecycleNodePtr->operator[](type))
        fRecycleNodePtr->operator[](type) = new (fMemoryManager) RefStackOf<DOMNode> (15, false, fMemoryManager);

    fRecycleNodePtr->operator[](type)->push(object);
}

void DOMDocumentImpl::releaseBuffer(DOMBuffer* buffer)
{
    if (!fRecycleBufferPtr)
        fRecycleBufferPtr = new (fMemoryManager) RefStackOf<DOMBuffer> (15, false, fMemoryManager);

    fRecycleBufferPtr->push(buffer);
}

DOMBuffer* DOMDocumentImpl::popBuffer()
{
    if (!fRecycleBufferPtr || fRecycleBufferPtr->empty())
        return 0;

    return fRecycleBufferPtr->pop();
}


void * DOMDocumentImpl::allocate(size_t amount, NodeObjectType type)
{
    if (!fRecycleNodePtr)
        return allocate(amount);

    DOMNodePtr* ptr = fRecycleNodePtr->operator[](type);
    if (!ptr || ptr->empty())
        return allocate(amount);

    return (void*) ptr->pop();

}

XERCES_CPP_NAMESPACE_END
