/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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$
 */

#if !defined(XERCESC_INCLUDE_GUARD_DOMDOCUMENTIMPL_HPP)
#define XERCESC_INCLUDE_GUARD_DOMDOCUMENTIMPL_HPP

//
//  This file is part of the internal implementation of the C++ XML DOM.
//  It should NOT be included or used directly by application programs.
//
//  Applications should include the file <xercesc/dom/DOM.hpp> for the entire
//  DOM API, or xercesc/dom/DOM*.hpp for individual DOM classes, where the class
//  name is substituded for the *.
//

#include <xercesc/util/RefArrayOf.hpp>
#include <xercesc/util/RefStackOf.hpp>
#include <xercesc/util/RefHash2KeysTableOf.hpp>
#include <xercesc/util/StringPool.hpp>
#include <xercesc/util/KeyRefPair.hpp>
#include <xercesc/util/XMLChar.hpp>
#include <xercesc/dom/DOMDocument.hpp>
#include <xercesc/dom/DOMUserDataHandler.hpp>
#include <xercesc/dom/DOMMemoryManager.hpp>
#include "DOMNodeBase.hpp"
#include "DOMNodeImpl.hpp"
#include "DOMStringPool.hpp"
#include "DOMParentNode.hpp"
#include "DOMDeepNodeListPool.hpp"

XERCES_CPP_NAMESPACE_BEGIN


class DOMAttrImpl;
class DOMCDATASectionImpl;
class DOMCommentImpl;
class DOMConfiguration;
class DOMDeepNodeListImpl;
class DOMDocumentFragmentImpl;
class DOMDocumentTypeImpl;
class DOMElementImpl;
class DOMEntityImpl;
class DOMEntityReferenceImpl;
class DOMNotationImpl;
class DOMProcessingInstructionImpl;
class DOMTextImpl;
class DOMNodeIteratorImpl;
class DOMNormalizer;
class DOMTreeWalkerImpl;
class DOMNodeFilter;
class DOMNodeFilterImpl;
class DOMImplementation;
class DOMNodeIDMap;
class DOMRangeImpl;
class DOMBuffer;
class MemoryManager;
class XPathNSResolver;
class XPathExpression;

typedef RefVectorOf<DOMRangeImpl>        Ranges;
typedef RefVectorOf<DOMNodeIteratorImpl>     NodeIterators;
typedef KeyRefPair<void, DOMUserDataHandler> DOMUserDataRecord;
typedef RefStackOf<DOMNode>               DOMNodePtr;

class CDOM_EXPORT DOMDocumentImpl: public XMemory, public DOMMemoryManager, public DOMDocument,
        public HasDOMNodeImpl, public HasDOMParentImpl {
public:
    // -----------------------------------------------------------------------
    //  data
    // -----------------------------------------------------------------------
    DOMNodeImpl           fNode;           // Implements common node functionality.
    DOMParentNode         fParent;         // Implements common parent node functionality
    DOMNodeIDMap*         fNodeIDMap;     // for use by GetElementsById().

public:
    DOMDocumentImpl(DOMImplementation* domImpl, MemoryManager* const manager = XMLPlatformUtils::fgMemoryManager);
    DOMDocumentImpl(const XMLCh*     namespaceURI,     //DOM Level 2
                    const XMLCh*     qualifiedName,
                    DOMDocumentType* doctype,
                    DOMImplementation* domImpl,
                    MemoryManager* const manager = XMLPlatformUtils::fgMemoryManager);
    virtual ~DOMDocumentImpl();

    void                         setDocumentType(DOMDocumentType *doctype);

public:
    // Add all functions that are pure virtual in DOMNODE
    DOMNODE_FUNCTIONS;

    // Add accessors for implementation bits.
    DOMNODEIMPL_DECL;
    DOMPARENTIMPL_DECL;

public:
    // Add all functions that are pure virtual in DOMDocument
    virtual DOMAttr*             createAttribute(const XMLCh *name);
    virtual DOMCDATASection*     createCDATASection(const XMLCh *data);
    virtual DOMComment*          createComment(const XMLCh *data);
    virtual DOMDocumentFragment* createDocumentFragment();
    virtual DOMDocumentType*     createDocumentType(const XMLCh *name);
    virtual DOMDocumentType*     createDocumentType(const XMLCh *qName,
                                                    const XMLCh *publicId,
                                                    const XMLCh *systemId);
    virtual DOMElement*          createElement(const XMLCh * tagName);
    virtual DOMElement*          createElementNoCheck(const XMLCh *tagName);
    virtual DOMEntity*           createEntity(const XMLCh * name);
    virtual DOMEntityReference*  createEntityReference(const XMLCh * name);
    virtual DOMNotation*         createNotation(const XMLCh * name);
    virtual DOMProcessingInstruction* createProcessingInstruction(const XMLCh * target, const XMLCh * data);
    virtual DOMText*             createTextNode(const XMLCh * data);
    virtual DOMDocumentType*     getDoctype() const;
    virtual DOMElement*          getDocumentElement() const;
    virtual DOMNodeList*         getElementsByTagName(const XMLCh * tagname) const;
    virtual DOMImplementation*   getImplementation() const;
    bool                         isXMLName(const XMLCh * s);
    virtual DOMNodeIterator*     createNodeIterator(DOMNode *root,
                                                    DOMNodeFilter::ShowType whatToShow,
                                                    DOMNodeFilter* filter,
                                                    bool entityReferenceExpansion);
    virtual DOMTreeWalker*       createTreeWalker(DOMNode *root,
                                                  DOMNodeFilter::ShowType whatToShow,
                                                  DOMNodeFilter* filter,
                                                  bool entityReferenceExpansion);


    virtual DOMRange*            createRange();
    virtual Ranges*              getRanges() const;  //non-standard api
    virtual NodeIterators*       getNodeIterators() const;  //non-standard api
    virtual void                 removeRange(DOMRangeImpl* range); //non-standard api
    virtual void                 removeNodeIterator(DOMNodeIteratorImpl* nodeIterator); //non-standard api

    virtual DOMXPathExpression* createExpression(const XMLCh *expression,
                                                 const DOMXPathNSResolver *resolver);
    virtual DOMXPathNSResolver* createNSResolver(const DOMNode *nodeResolver);
    virtual DOMXPathResult* evaluate(const XMLCh *expression,
                                     const DOMNode *contextNode,
                                     const DOMXPathNSResolver *resolver,
                                     DOMXPathResult::ResultType type,
                                     DOMXPathResult* result);


    // Extension to be called by the Parser
    DOMEntityReference*  createEntityReferenceByParser(const XMLCh * name);

    // Add all functions that are pure virtual in DOMMemoryManager
    virtual XMLSize_t getMemoryAllocationBlockSize() const;
    virtual void setMemoryAllocationBlockSize(XMLSize_t size);
    virtual void* allocate(XMLSize_t amount);
    virtual void* allocate(XMLSize_t amount, DOMMemoryManager::NodeObjectType type);
    // try to remove the block from the list of allocated memory
    virtual void release(void* oldBuffer);
    virtual void release(DOMNode* object, DOMMemoryManager::NodeObjectType type);
    virtual XMLCh* cloneString(const XMLCh *src);

    //
    // Functions to keep track of document mutations, so that node list chached
    //   information can be invalidated.  One global changes counter per document.
    //
    virtual void                 changed();
    virtual int                  changes() const;

    /**
     * Sets whether the DOM implementation performs error checking
     * upon operations. Turning off error checking only affects
     * the following DOM checks:
     * <ul>
     * <li>Checking strings to make sure that all characters are
     *     legal XML characters
     * <li>Hierarchy checking such as allowed children, checks for
     *     cycles, etc.
     * </ul>
     * <p>
     * Turning off error checking does <em>not</em> turn off the
     * following checks:
     * <ul>
     * <li>Read only checks
     * <li>Checks related to DOM events
     * </ul>
     */
    inline void setErrorChecking(bool check) {
        errorChecking = check;
    }

    /**
     * Returns true if the DOM implementation performs error checking.
     */
    inline bool getErrorChecking() const {
        return errorChecking;
    }

    //Introduced in DOM Level 2
    virtual DOMNode*             importNode(const DOMNode *source, bool deep);
    virtual DOMElement*          createElementNS(const XMLCh *namespaceURI,
                                                 const XMLCh *qualifiedName);
    virtual DOMElement*          createElementNS(const XMLCh *namespaceURI,
                                                 const XMLCh *qualifiedName,
                                                 const XMLFileLoc lineNo,
                                                 const XMLFileLoc columnNo);
    virtual DOMAttr*             createAttributeNS(const XMLCh *namespaceURI,
                                                   const XMLCh *qualifiedName);
    virtual DOMNodeList*         getElementsByTagNameNS(const XMLCh *namespaceURI,
                                                        const XMLCh *localName) const;
    virtual DOMElement*          getElementById(const XMLCh *elementId) const;

    //Introduced in DOM Level 3
    virtual const XMLCh*         getInputEncoding() const;
    virtual const XMLCh*         getXmlEncoding() const;
    virtual bool                 getXmlStandalone() const;
    virtual void                 setXmlStandalone(bool standalone);
    virtual const XMLCh*         getXmlVersion() const;
    virtual void                 setXmlVersion(const XMLCh* version);
    virtual const XMLCh*         getDocumentURI() const;
    virtual void                 setDocumentURI(const XMLCh* documentURI);
    virtual bool                 getStrictErrorChecking() const;
    virtual void                 setStrictErrorChecking(bool strictErrorChecking);
    virtual DOMNode*             adoptNode(DOMNode* source);
    virtual void                 normalizeDocument();
    virtual DOMConfiguration*    getDOMConfig() const;

    void                         setInputEncoding(const XMLCh* actualEncoding);
    void                         setXmlEncoding(const XMLCh* encoding);
    // helper functions to prevent storing userdata pointers on every node.
    void*                        setUserData(DOMNodeImpl* n,
                                            const XMLCh* key,
                                            void* data,
                                            DOMUserDataHandler* handler);
    void*                        getUserData(const DOMNodeImpl* n,
                                             const XMLCh* key) const;
    void                         callUserDataHandlers(const DOMNodeImpl* n,
                                                      DOMUserDataHandler::DOMOperationType operation,
                                                      const DOMNode* src,
                                                      DOMNode* dst) const;
    void                         transferUserData(DOMNodeImpl* n1, DOMNodeImpl* n2);

    DOMNode*                     renameNode(DOMNode* n,
                                            const XMLCh* namespaceURI,
                                            const XMLCh* name);

    //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".
    static  int                  indexofQualifiedName(const XMLCh * qName);
    static  bool                 isKidOK(const DOMNode *parent, const DOMNode *child);

    inline DOMNodeIDMap*         getNodeIDMap() {return fNodeIDMap;};


    //
    // Memory Management Functions.  All memory is allocated by and owned by
    //                               a document, and is not recovered until the
    //                               document itself is deleted.
    //
    const XMLCh*                 getPooledString(const XMLCh*);
    const XMLCh*                 getPooledNString(const XMLCh*, XMLSize_t);
    void                         deleteHeap();
    void                         releaseDocNotifyUserData(DOMNode* object);
    void                         releaseBuffer(DOMBuffer* buffer);
    DOMBuffer*                   popBuffer(XMLSize_t nMinSize);
    MemoryManager*               getMemoryManager() const;

    // Factory methods for getting/creating node lists.
    // Because nothing is ever deleted, the implementation caches and recycles
    //  previously used instances of DOMDeepNodeList
    //
    DOMNodeList*                 getDeepNodeList(const DOMNode *rootNode, const XMLCh *tagName);
    DOMNodeList*                 getDeepNodeList(const DOMNode *rootNode,    //DOM Level 2
                                                 const XMLCh *namespaceURI,
                                                 const XMLCh *localName);

protected:
    //Internal helper functions
    virtual DOMNode*             importNode(const DOMNode *source, bool deep, bool cloningNode);

private:
    // -----------------------------------------------------------------------
    // Unimplemented constructors and operators
    // -----------------------------------------------------------------------
    DOMDocumentImpl(const DOMDocumentImpl &);
    DOMDocumentImpl & operator = (const DOMDocumentImpl &);

protected:
    // -----------------------------------------------------------------------
    //  data
    // -----------------------------------------------------------------------
    // New data introduced in DOM Level 3
    const XMLCh*          fInputEncoding;
    const XMLCh*          fXmlEncoding;
    bool                  fXmlStandalone;
    const XMLCh*          fXmlVersion;
    const XMLCh*          fDocumentURI;
    DOMConfiguration*     fDOMConfiguration;

    XMLStringPool         fUserDataTableKeys;
    RefHash2KeysTableOf<DOMUserDataRecord, PtrHasher>* fUserDataTable;


    // Per-Document heap Variables.
    //   The heap consists of one or more biggish blocks which are
    //   sub-allocated for individual allocations of nodes, strings, etc.
    //   The big blocks form a linked list, allowing them to be located for deletion.
    //
    //   There is no provision for deleting suballocated blocks, other than
    //     deleting the entire heap when the document is deleted.
    //
    //   There is no header on individual sub-allocated blocks.
    //   The header on big blocks consists only of a single back pointer to
    //    the previously allocated big block (our linked list of big blocks)
    //
    //
    //   revisit - this heap should be encapsulated into its own
    //                  class, rather than hanging naked on Document.
    //
    void*                 fCurrentBlock;
    void*                 fCurrentSingletonBlock;
    char*                 fFreePtr;
    XMLSize_t             fFreeBytesRemaining,
                          fHeapAllocSize;

    // To recycle the DOMNode pointer
    RefArrayOf<DOMNodePtr>* fRecycleNodePtr;

    // To recycle DOMBuffer pointer
    RefStackOf<DOMBuffer>* fRecycleBufferPtr;

    // Pool of DOMNodeList for getElementsByTagName
    DOMDeepNodeListPool<DOMDeepNodeListImpl>* fNodeListPool;

    // Other data
    DOMDocumentType*      fDocType;
    DOMElement*           fDocElement;

    DOMStringPoolEntry**  fNameTable;
    XMLSize_t             fNameTableSize;

    DOMNormalizer*        fNormalizer;
    Ranges*               fRanges;
    NodeIterators*        fNodeIterators;
    MemoryManager*        fMemoryManager;   // configurable memory manager
    DOMImplementation*    fDOMImplementation;

    int                   fChanges;
    bool                  errorChecking;    // Bypass error checking.

};

inline MemoryManager* DOMDocumentImpl::getMemoryManager() const
{
    return fMemoryManager;
}

inline const XMLCh*  DOMDocumentImpl::getPooledString(const XMLCh *in)
{
  if (in == 0)
    return 0;
  XMLSize_t n = XMLString::stringLen(in);

  DOMStringPoolEntry    **pspe;
  DOMStringPoolEntry    *spe;

  XMLSize_t inHash = XMLString::hash(in, fNameTableSize);
  pspe = &fNameTable[inHash];
  while (*pspe != 0)
  {
    if ((*pspe)->fLength == n && XMLString::equals((*pspe)->fString, in))
      return (*pspe)->fString;
    pspe = &((*pspe)->fNext);
  }

  // This string hasn't been seen before.  Add it to the pool.
  //

  // Compute size to allocate.  Note that there's 1 char of string
  // declared in the struct, so we don't need to add one again to
  // account for the trailing null.
  //
  XMLSize_t sizeToAllocate = sizeof(DOMStringPoolEntry) + n*sizeof(XMLCh);
  *pspe = spe = (DOMStringPoolEntry *)allocate(sizeToAllocate);
  spe->fLength = n;
  spe->fNext = 0;
  XMLString::copyString((XMLCh*)spe->fString, in);

  return spe->fString;
}

inline const XMLCh* DOMDocumentImpl::getPooledNString(const XMLCh *in, XMLSize_t n)
{
  if (in == 0)
    return 0;

  DOMStringPoolEntry    **pspe;
  DOMStringPoolEntry    *spe;

  XMLSize_t inHash = XMLString::hashN(in, n, fNameTableSize);
  pspe = &fNameTable[inHash];
  while (*pspe != 0)
  {
    if ((*pspe)->fLength == n && XMLString::equalsN((*pspe)->fString, in, n))
      return (*pspe)->fString;
    pspe = &((*pspe)->fNext);
  }

  // This string hasn't been seen before.  Add it to the pool.
  //

  // Compute size to allocate.  Note that there's 1 char of string
  // declared in the struct, so we don't need to add one again to
  // account for the trailing null.
  //
  XMLSize_t sizeToAllocate = sizeof(DOMStringPoolEntry) + n*sizeof(XMLCh);
  *pspe = spe = (DOMStringPoolEntry *)allocate(sizeToAllocate);
  spe->fLength = n;
  spe->fNext = 0;
  XMLString::copyNString((XMLCh*)spe->fString, in, n);

  return spe->fString;
}

inline int DOMDocumentImpl::indexofQualifiedName(const XMLCh* name)
{
  int i = 0;
  int colon = -1;
  int colon_count = 0;
  for (; *name != 0; ++i, ++name)
  {
    if (*name == chColon)
    {
      ++colon_count;
      colon = i;
    }
  }

  if (i == 0 || colon == 0 || colon == (i - 1) || colon_count > 1)
    return -1;

  return colon != -1 ? colon : 0;
}

XERCES_CPP_NAMESPACE_END

// ---------------------------------------------------------------------------
//
//  Operator new.  Global overloaded version, lets any object be allocated on
//                 the heap owned by a document.
//
// ---------------------------------------------------------------------------
inline void * operator new(size_t amt, XERCES_CPP_NAMESPACE_QUALIFIER DOMDocumentImpl *doc, XERCES_CPP_NAMESPACE_QUALIFIER DOMMemoryManager::NodeObjectType type)
{
    void *p = doc->allocate(amt, type);
    return p;
}

inline void * operator new(size_t amt, XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *doc, XERCES_CPP_NAMESPACE_QUALIFIER DOMMemoryManager::NodeObjectType type)
{
    XERCES_CPP_NAMESPACE_QUALIFIER DOMMemoryManager* mgr=(XERCES_CPP_NAMESPACE_QUALIFIER DOMMemoryManager*)doc->getFeature(XERCES_CPP_NAMESPACE_QUALIFIER XMLUni::fgXercescInterfaceDOMMemoryManager,0);
    void* p=0;
    if(mgr)
        p = mgr->allocate(amt, type);
    return p;
}

inline void * operator new(size_t amt, XERCES_CPP_NAMESPACE_QUALIFIER DOMDocumentImpl *doc)
{
    void* p = doc->allocate(amt);
    return p;
}

inline void * operator new(size_t amt, XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *doc)
{
    XERCES_CPP_NAMESPACE_QUALIFIER DOMMemoryManager* mgr=(XERCES_CPP_NAMESPACE_QUALIFIER DOMMemoryManager*)doc->getFeature(XERCES_CPP_NAMESPACE_QUALIFIER XMLUni::fgXercescInterfaceDOMMemoryManager,0);
    void* p=0;
    if(mgr)
        p = mgr->allocate(amt);
    return p;
}

// ---------------------------------------------------------------------------
//  For DOM:
//  Bypass compiler warning:
//    no matching operator delete found; memory will not be freed if initialization throws an exception
// ---------------------------------------------------------------------------
#if !defined(XERCES_NO_MATCHING_DELETE_OPERATOR)
inline void operator delete(void* /*ptr*/, XERCES_CPP_NAMESPACE_QUALIFIER DOMDocumentImpl * /*doc*/, XERCES_CPP_NAMESPACE_QUALIFIER DOMMemoryManager::NodeObjectType /*type*/)
{
    return;
}
inline void operator delete(void* /*ptr*/, XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument * /*doc*/, XERCES_CPP_NAMESPACE_QUALIFIER DOMMemoryManager::NodeObjectType /*type*/)
{
    return;
}

inline void operator delete(void* /*ptr*/, XERCES_CPP_NAMESPACE_QUALIFIER DOMDocumentImpl * /*doc*/)
{
    return;
}
inline void operator delete(void* /*ptr*/, XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument * /*doc*/)
{
    return;
}
#endif

#endif
