/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   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 .
 */
 
#include <com/sun/star/uno/Sequence.h>
 
#include "document.hxx"
#include "attr.hxx"
#include "element.hxx"
#include "cdatasection.hxx"
#include "documentfragment.hxx"
#include "text.hxx"
#include "comment.hxx"
#include "processinginstruction.hxx"
#include "entityreference.hxx"
#include "documenttype.hxx"
#include "elementlist.hxx"
#include "domimplementation.hxx"
#include "entity.hxx"
#include "notation.hxx"
 
#include <event.hxx>
#include <mutationevent.hxx>
#include <uievent.hxx>
#include <mouseevent.hxx>
#include <eventdispatcher.hxx>
 
#include <string.h>
 
#include <osl/diagnose.h>
 
#include <com/sun/star/xml/sax/FastToken.hpp>
#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
 
using namespace css;
using namespace css::io;
using namespace css::uno;
using namespace css::xml::dom;
using namespace css::xml::dom::events;
using namespace css::xml::sax;
 
namespace DOM
{
    static xmlNodePtr lcl_getDocumentType(xmlDocPtr const i_pDocument)
    {
        // find the doc type
        xmlNodePtr cur = i_pDocument->children;
        while (cur != nullptr)
        {
            if ((cur->type == XML_DOCUMENT_TYPE_NODE) ||
                (cur->type == XML_DTD_NODE)) {
                    return cur;
            }
        }
        return nullptr;
    }
 
    /// get the pointer to the root element node of the document
    static xmlNodePtr lcl_getDocumentRootPtr(xmlDocPtr const i_pDocument)
    {
        // find the document element
        xmlNodePtr cur = i_pDocument->children;
        while (cur != nullptr)
        {
            if (cur->type == XML_ELEMENT_NODE)
                break;
            cur = cur->next;
        }
        return cur;
    }
 
    CDocument::CDocument(xmlDocPtr const pDoc)
        : CDocument_Base(*this, m_Mutex,
                NodeType_DOCUMENT_NODE, reinterpret_cast<xmlNodePtr>(pDoc))
        , m_aDocPtr(pDoc)
        , m_streamListeners()
        , m_pEventDispatcher(new events::CEventDispatcher)
    {
    }
 
    ::rtl::Reference<CDocument> CDocument::CreateCDocument(xmlDocPtr const pDoc)
    {
        ::rtl::Reference<CDocument> const xDoc(new CDocument(pDoc));
        // add the doc itself to its nodemap!
        xDoc->m_NodeMap.emplace(
                reinterpret_cast<xmlNodePtr>(pDoc),
                ::std::make_pair(
                    WeakReference<XNode>(static_cast<XDocument*>(xDoc.get())),
                    xDoc.get()));
        return xDoc;
    }
 
    CDocument::~CDocument()
    {
        ::osl::MutexGuard const g(m_Mutex);
#ifdef DBG_UTIL
        // node map must be empty now, otherwise CDocument must not die!
        for (nodemap_t::iterator i = m_NodeMap.begin();
                i != m_NodeMap.end(); ++i)
        {
            Reference<XNode> const xNode(i->second.first);
            OSL_ENSURE(!xNode.is(),
            "CDocument::~CDocument(): ERROR: live node in document node map!");
        }
#endif
        xmlFreeDoc(m_aDocPtr);
    }
 
 
    events::CEventDispatcher & CDocument::GetEventDispatcher()
    {
        return *m_pEventDispatcher;
    }
 
    ::rtl::Reference< CElement > CDocument::GetDocumentElement()
    {
        xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr);
        ::rtl::Reference< CElement > const xRet(
            dynamic_cast<CElement*>(GetCNode(pNode).get()));
        return xRet;
    }
 
    void
    CDocument::RemoveCNode(xmlNodePtr const pNode, CNode const*const pCNode)
    {
        nodemap_t::iterator const i = m_NodeMap.find(pNode);
        if (i != m_NodeMap.end()) {
            // #i113681# consider this scenario:
            // T1 calls ~CNode
            // T2 calls getCNode:    lookup will find i->second->first invalid
            //                       so a new CNode is created and inserted
            // T1 calls removeCNode: i->second->second now points to a
            //                       different CNode instance!
 
            // check that the CNode is the right one
            CNode *const pCurrent = i->second.second;
            if (pCurrent == pCNode) {
                m_NodeMap.erase(i);
            }
        }
    }
 
    /** NB: this is the CNode factory.
        it is the only place where CNodes may be instantiated.
        all CNodes must be registered at the m_NodeMap.
     */
    ::rtl::Reference<CNode>
    CDocument::GetCNode(xmlNodePtr const pNode, bool const bCreate)
    {
        if (nullptr == pNode) {
            return nullptr;
        }
        //check whether there is already an instance for this node
        nodemap_t::const_iterator const i = m_NodeMap.find(pNode);
        if (i != m_NodeMap.end()) {
            // #i113681# check that the CNode is still alive
            uno::Reference<XNode> const xNode(i->second.first);
            if (xNode.is())
            {
                ::rtl::Reference<CNode> ret(i->second.second);
                OSL_ASSERT(ret.is());
                return ret;
            }
        }
 
        if (!bCreate) { return nullptr; }
 
        // there is not yet an instance wrapping this node,
        // create it and store it in the map
 
        ::rtl::Reference<CNode> pCNode;
        switch (pNode->type)
        {
            case XML_ELEMENT_NODE:
                // m_aNodeType = NodeType::ELEMENT_NODE;
                pCNode = static_cast< CNode* >(
                        new CElement(*this, m_Mutex, pNode));
            break;
            case XML_TEXT_NODE:
                // m_aNodeType = NodeType::TEXT_NODE;
                pCNode = static_cast< CNode* >(
                        new CText(*this, m_Mutex, pNode));
            break;
            case XML_CDATA_SECTION_NODE:
                // m_aNodeType = NodeType::CDATA_SECTION_NODE;
                pCNode = static_cast< CNode* >(
                        new CCDATASection(*this, m_Mutex, pNode));
            break;
            case XML_ENTITY_REF_NODE:
                // m_aNodeType = NodeType::ENTITY_REFERENCE_NODE;
                pCNode = static_cast< CNode* >(
                        new CEntityReference(*this, m_Mutex, pNode));
            break;
            case XML_ENTITY_NODE:
                // m_aNodeType = NodeType::ENTITY_NODE;
                pCNode = static_cast< CNode* >(new CEntity(*this, m_Mutex,
                            reinterpret_cast<xmlEntityPtr>(pNode)));
            break;
            case XML_PI_NODE:
                // m_aNodeType = NodeType::PROCESSING_INSTRUCTION_NODE;
                pCNode = static_cast< CNode* >(
                        new CProcessingInstruction(*this, m_Mutex, pNode));
            break;
            case XML_COMMENT_NODE:
                // m_aNodeType = NodeType::COMMENT_NODE;
                pCNode = static_cast< CNode* >(
                        new CComment(*this, m_Mutex, pNode));
            break;
            case XML_DOCUMENT_NODE:
                // m_aNodeType = NodeType::DOCUMENT_NODE;
                OSL_ENSURE(false, "CDocument::GetCNode is not supposed to"
                        " create a CDocument!!!");
                pCNode = static_cast< CNode* >(new CDocument(
                            reinterpret_cast<xmlDocPtr>(pNode)));
            break;
            case XML_DOCUMENT_TYPE_NODE:
            case XML_DTD_NODE:
                // m_aNodeType = NodeType::DOCUMENT_TYPE_NODE;
                pCNode = static_cast< CNode* >(new CDocumentType(*this, m_Mutex,
                            reinterpret_cast<xmlDtdPtr>(pNode)));
            break;
            case XML_DOCUMENT_FRAG_NODE:
                // m_aNodeType = NodeType::DOCUMENT_FRAGMENT_NODE;
                pCNode = static_cast< CNode* >(
                        new CDocumentFragment(*this, m_Mutex, pNode));
            break;
            case XML_NOTATION_NODE:
                // m_aNodeType = NodeType::NOTATION_NODE;
                pCNode = static_cast< CNode* >(new CNotation(*this, m_Mutex,
                            reinterpret_cast<xmlNotationPtr>(pNode)));
            break;
            case XML_ATTRIBUTE_NODE:
                // m_aNodeType = NodeType::ATTRIBUTE_NODE;
                pCNode = static_cast< CNode* >(new CAttr(*this, m_Mutex,
                            reinterpret_cast<xmlAttrPtr>(pNode)));
            break;
            // unsupported node types
            case XML_HTML_DOCUMENT_NODE:
            case XML_ELEMENT_DECL:
            case XML_ATTRIBUTE_DECL:
            case XML_ENTITY_DECL:
            case XML_NAMESPACE_DECL:
            default:
            break;
        }
 
        if (pCNode != nullptr) {
            bool const bInserted = m_NodeMap.emplace(
                        pNode,
                        ::std::make_pair(WeakReference<XNode>(pCNode.get()), pCNode.get())
                ).second;
            OSL_ASSERT(bInserted);
            if (!bInserted) {
                // if insertion failed, delete new instance and return null
                return nullptr;
            }
        }
 
        OSL_ENSURE(pCNode.is(), "no node produced during CDocument::GetCNode!");
        return pCNode;
    }
 
 
    CDocument & CDocument::GetOwnerDocument()
    {
        return *this;
    }
 
    void CDocument::saxify(const Reference< XDocumentHandler >& i_xHandler)
    {
        i_xHandler->startDocument();
        for (xmlNodePtr pChild = m_aNodePtr->children;
                        pChild != nullptr; pChild = pChild->next) {
            ::rtl::Reference<CNode> const pNode = GetCNode(pChild);
            OSL_ENSURE(pNode != nullptr, "CNode::get returned 0");
            pNode->saxify(i_xHandler);
        }
        i_xHandler->endDocument();
    }
 
    void CDocument::fastSaxify( Context& rContext )
    {
        rContext.mxDocHandler->startDocument();
        for (xmlNodePtr pChild = m_aNodePtr->children;
                        pChild != nullptr; pChild = pChild->next) {
            ::rtl::Reference<CNode> const pNode = GetCNode(pChild);
            OSL_ENSURE(pNode != nullptr, "CNode::get returned 0");
            pNode->fastSaxify(rContext);
        }
        rContext.mxDocHandler->endDocument();
    }
 
    bool CDocument::IsChildTypeAllowed(NodeType const nodeType)
    {
        switch (nodeType) {
            case NodeType_PROCESSING_INSTRUCTION_NODE:
            case NodeType_COMMENT_NODE:
                return true;
            case NodeType_ELEMENT_NODE:
                 // there may be only one!
                return nullptr == lcl_getDocumentRootPtr(m_aDocPtr);
            case NodeType_DOCUMENT_TYPE_NODE:
                 // there may be only one!
                return nullptr == lcl_getDocumentType(m_aDocPtr);
            default:
                return false;
        }
    }
 
 
    void SAL_CALL CDocument::addListener(const Reference< XStreamListener >& aListener )
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        m_streamListeners.insert(aListener);
    }
 
    void SAL_CALL CDocument::removeListener(const Reference< XStreamListener >& aListener )
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        m_streamListeners.erase(aListener);
    }
 
    // IO context functions for libxml2 interaction
    typedef struct {
        Reference< XOutputStream > stream;
        bool allowClose;
    } IOContext;
 
    extern "C" {
    // write callback
    // int xmlOutputWriteCallback (void * context, const char * buffer, int len)
    static int writeCallback(void *context, const char* buffer, int len){
        // create a sequence and write it to the stream
        IOContext *pContext = static_cast<IOContext*>(context);
        Sequence<sal_Int8> bs(reinterpret_cast<const sal_Int8*>(buffer), len);
        pContext->stream->writeBytes(bs);
        return len;
    }
 
    // close callback
    //int xmlOutputCloseCallback (void * context)
    static int closeCallback(void *context)
    {
        IOContext *pContext = static_cast<IOContext*>(context);
        if (pContext->allowClose) {
            pContext->stream->closeOutput();
        }
        return 0;
    }
    } // extern "C"
 
    void SAL_CALL CDocument::start()
    {
        listenerlist_t streamListeners;
        {
            ::osl::MutexGuard const g(m_Mutex);
 
            if (! m_rOutputStream.is()) { throw RuntimeException(); }
            streamListeners = m_streamListeners;
        }
 
        // notify listeners about start
        listenerlist_t::const_iterator iter1 = streamListeners.begin();
        while (iter1 != streamListeners.end()) {
            Reference< XStreamListener > aListener = *iter1;
            aListener->started();
            ++iter1;
        }
 
        {
            ::osl::MutexGuard const g(m_Mutex);
 
            // check again! could have been reset...
            if (! m_rOutputStream.is()) { throw RuntimeException(); }
 
            // setup libxml IO and write data to output stream
            IOContext ioctx = {m_rOutputStream, false};
            xmlOutputBufferPtr pOut = xmlOutputBufferCreateIO(
                writeCallback, closeCallback, &ioctx, nullptr);
            xmlSaveFileTo(pOut, m_aNodePtr->doc, nullptr);
        }
 
        // call listeners
        listenerlist_t::const_iterator iter2 = streamListeners.begin();
        while (iter2 != streamListeners.end()) {
            Reference< XStreamListener > aListener = *iter2;
            aListener->closed();
            ++iter2;
        }
    }
 
    void SAL_CALL CDocument::terminate()
    {
        // not supported
    }
 
    void SAL_CALL CDocument::setOutputStream( const Reference< XOutputStream >& aStream )
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        m_rOutputStream = aStream;
    }
 
    Reference< XOutputStream > SAL_CALL  CDocument::getOutputStream()
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        return m_rOutputStream;
    }
 
    // Creates an Attr of the given name.
    Reference< XAttr > SAL_CALL CDocument::createAttribute(const OUString& name)
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8);
        xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr());
        xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr, pName, nullptr);
        ::rtl::Reference< CAttr > const pCAttr(
            dynamic_cast< CAttr* >(GetCNode(
                    reinterpret_cast<xmlNodePtr>(pAttr)).get()));
        if (!pCAttr.is()) { throw RuntimeException(); }
        pCAttr->m_bUnlinked = true;
        return pCAttr.get();
    };
 
    // Creates an attribute of the given qualified name and namespace URI.
    Reference< XAttr > SAL_CALL CDocument::createAttributeNS(
            const OUString& ns, const OUString& qname)
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        // libxml does not allow a NS definition to be attached to an
        // attribute node - which is a good thing, since namespaces are
        // only defined as parts of element nodes
        // thus the namespace data is stored in CAttr::m_pNamespace
        sal_Int32 i = qname.indexOf(':');
        OString oPrefix, oName, oUri;
        if (i != -1)
        {
            oPrefix = OUStringToOString(qname.copy(0, i), RTL_TEXTENCODING_UTF8);
            oName = OUStringToOString(qname.copy(i+1), RTL_TEXTENCODING_UTF8);
        }
        else
        {
            oName = OUStringToOString(qname, RTL_TEXTENCODING_UTF8);
        }
        oUri = OUStringToOString(ns, RTL_TEXTENCODING_UTF8);
        xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr,
                reinterpret_cast<xmlChar const*>(oName.getStr()), nullptr);
        ::rtl::Reference< CAttr > const pCAttr(
            dynamic_cast< CAttr* >(GetCNode(
                    reinterpret_cast<xmlNodePtr>(pAttr)).get()));
        if (!pCAttr.is()) { throw RuntimeException(); }
        // store the namespace data!
        pCAttr->m_pNamespace.reset( new stringpair_t(oUri, oPrefix) );
        pCAttr->m_bUnlinked = true;
 
        return pCAttr.get();
    };
 
    // Creates a CDATASection node whose value is the specified string.
    Reference< XCDATASection > SAL_CALL CDocument::createCDATASection(const OUString& data)
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        OString const oData(
                OUStringToOString(data, RTL_TEXTENCODING_UTF8));
        xmlChar const*const pData =
            reinterpret_cast<xmlChar const*>(oData.getStr());
        xmlNodePtr const pText =
            xmlNewCDataBlock(m_aDocPtr, pData, oData.getLength());
        Reference< XCDATASection > const xRet(
            static_cast< XNode* >(GetCNode(pText).get()),
            UNO_QUERY_THROW);
        return xRet;
    }
 
    // Creates a Comment node given the specified string.
    Reference< XComment > SAL_CALL CDocument::createComment(const OUString& data)
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
        xmlChar const *pData = reinterpret_cast<xmlChar const *>(o1.getStr());
        xmlNodePtr pComment = xmlNewDocComment(m_aDocPtr, pData);
        Reference< XComment > const xRet(
            static_cast< XNode* >(GetCNode(pComment).get()),
            UNO_QUERY_THROW);
        return xRet;
    }
 
    //Creates an empty DocumentFragment object.
    Reference< XDocumentFragment > SAL_CALL CDocument::createDocumentFragment()
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        xmlNodePtr pFrag = xmlNewDocFragment(m_aDocPtr);
        Reference< XDocumentFragment > const xRet(
            static_cast< XNode* >(GetCNode(pFrag).get()),
            UNO_QUERY_THROW);
        return xRet;
    }
 
    // Creates an element of the type specified.
    Reference< XElement > SAL_CALL CDocument::createElement(const OUString& tagName)
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        OString o1 = OUStringToOString(tagName, RTL_TEXTENCODING_UTF8);
        xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr());
        xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, nullptr, pName, nullptr);
        Reference< XElement > const xRet(
            static_cast< XNode* >(GetCNode(pNode).get()),
            UNO_QUERY_THROW);
        return xRet;
    }
 
    // Creates an element of the given qualified name and namespace URI.
    Reference< XElement > SAL_CALL CDocument::createElementNS(
            const OUString& ns, const OUString& qname)
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        sal_Int32 i = qname.indexOf(':');
        if (ns.isEmpty()) throw RuntimeException();
        xmlChar const *pPrefix;
        xmlChar const *pName;
        OString o1, o2, o3;
        if ( i != -1) {
            o1 = OUStringToOString(qname.copy(0, i), RTL_TEXTENCODING_UTF8);
            pPrefix = reinterpret_cast<xmlChar const *>(o1.getStr());
            o2 = OUStringToOString(qname.copy(i+1), RTL_TEXTENCODING_UTF8);
            pName = reinterpret_cast<xmlChar const *>(o2.getStr());
        } else {
            // default prefix
            pPrefix = reinterpret_cast<xmlChar const *>("");
            o2 = OUStringToOString(qname, RTL_TEXTENCODING_UTF8);
            pName = reinterpret_cast<xmlChar const *>(o2.getStr());
        }
        o3 = OUStringToOString(ns, RTL_TEXTENCODING_UTF8);
        xmlChar const *pUri = reinterpret_cast<xmlChar const *>(o3.getStr());
 
        // xmlNsPtr aNsPtr = xmlNewReconciledNs?
        // xmlNsPtr aNsPtr = xmlNewGlobalNs?
        xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, nullptr, pName, nullptr);
        xmlNsPtr const pNs = xmlNewNs(pNode, pUri, pPrefix);
        xmlSetNs(pNode, pNs);
        Reference< XElement > const xRet(
            static_cast< XNode* >(GetCNode(pNode).get()),
            UNO_QUERY_THROW);
        return xRet;
    }
 
    //Creates an EntityReference object.
    Reference< XEntityReference > SAL_CALL CDocument::createEntityReference(const OUString& name)
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8);
        xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr());
        xmlNodePtr const pNode = xmlNewReference(m_aDocPtr, pName);
        Reference< XEntityReference > const xRet(
            static_cast< XNode* >(GetCNode(pNode).get()),
            UNO_QUERY_THROW);
        return xRet;
    }
 
    // Creates a ProcessingInstruction node given the specified name and
    // data strings.
    Reference< XProcessingInstruction > SAL_CALL CDocument::createProcessingInstruction(
            const OUString& target, const OUString& data)
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        OString o1 = OUStringToOString(target, RTL_TEXTENCODING_UTF8);
        xmlChar const *pTarget = reinterpret_cast<xmlChar const *>(o1.getStr());
        OString o2 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
        xmlChar const *pData = reinterpret_cast<xmlChar const *>(o2.getStr());
        xmlNodePtr const pNode = xmlNewDocPI(m_aDocPtr, pTarget, pData);
        pNode->doc = m_aDocPtr;
        Reference< XProcessingInstruction > const xRet(
            static_cast< XNode* >(GetCNode(pNode).get()),
            UNO_QUERY_THROW);
        return xRet;
    }
 
    // Creates a Text node given the specified string.
    Reference< XText > SAL_CALL CDocument::createTextNode(const OUString& data)
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
        xmlChar const *pData = reinterpret_cast<xmlChar const *>(o1.getStr());
        xmlNodePtr const pNode = xmlNewDocText(m_aDocPtr, pData);
        Reference< XText > const xRet(
            static_cast< XNode* >(GetCNode(pNode).get()),
            UNO_QUERY_THROW);
        return xRet;
    }
 
    // The Document Type Declaration (see DocumentType) associated with this
    // document.
    Reference< XDocumentType > SAL_CALL CDocument::getDoctype()
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        xmlNodePtr const pDocType(lcl_getDocumentType(m_aDocPtr));
        Reference< XDocumentType > const xRet(
            static_cast< XNode* >(GetCNode(pDocType).get()),
            UNO_QUERY);
        return xRet;
    }
 
    // This is a convenience attribute that allows direct access to the child
    // node that is the root element of the document.
    Reference< XElement > SAL_CALL CDocument::getDocumentElement()
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr);
        if (!pNode) { return nullptr; }
        Reference< XElement > const xRet(
            static_cast< XNode* >(GetCNode(pNode).get()),
            UNO_QUERY);
        return xRet;
    }
 
    static xmlNodePtr
    lcl_search_element_by_id(const xmlNodePtr cur, const xmlChar* id)
    {
        if (cur == nullptr)
            return nullptr;
        // look in current node
        if (cur->type == XML_ELEMENT_NODE)
        {
            xmlAttrPtr a = cur->properties;
            while (a != nullptr)
            {
                if (a->atype == XML_ATTRIBUTE_ID) {
                    if (strcmp(reinterpret_cast<char*>(a->children->content), reinterpret_cast<char const *>(id)) == 0)
                        return cur;
                }
                a = a->next;
            }
        }
        // look in children
        xmlNodePtr result = lcl_search_element_by_id(cur->children, id);
        if (result != nullptr)
            return result;
        result = lcl_search_element_by_id(cur->next, id);
            return result;
    }
 
    // Returns the Element whose ID is given by elementId.
    Reference< XElement > SAL_CALL
    CDocument::getElementById(const OUString& elementId)
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        // search the tree for an element with the given ID
        OString o1 = OUStringToOString(elementId, RTL_TEXTENCODING_UTF8);
        xmlChar const *pId = reinterpret_cast<xmlChar const *>(o1.getStr());
        xmlNodePtr const pStart = lcl_getDocumentRootPtr(m_aDocPtr);
        if (!pStart) { return nullptr; }
        xmlNodePtr const pNode = lcl_search_element_by_id(pStart, pId);
        Reference< XElement > const xRet(
            static_cast< XNode* >(GetCNode(pNode).get()),
            UNO_QUERY);
        return xRet;
    }
 
 
    Reference< XNodeList > SAL_CALL
    CDocument::getElementsByTagName(OUString const& rTagname)
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        Reference< XNodeList > const xRet(
            new CElementList(GetDocumentElement(), m_Mutex, rTagname));
        return xRet;
    }
 
    Reference< XNodeList > SAL_CALL CDocument::getElementsByTagNameNS(
            OUString const& rNamespaceURI, OUString const& rLocalName)
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        Reference< XNodeList > const xRet(
            new CElementList(GetDocumentElement(), m_Mutex,
                rLocalName, &rNamespaceURI));
        return xRet;
    }
 
    Reference< XDOMImplementation > SAL_CALL CDocument::getImplementation()
    {
        // does not need mutex currently
        return Reference< XDOMImplementation >(CDOMImplementation::get());
    }
 
    // helper function to recursively import siblings
    static void lcl_ImportSiblings(
        Reference< XDocument > const& xTargetDocument,
        Reference< XNode > const& xTargetParent,
        Reference< XNode > const& xChild)
    {
        Reference< XNode > xSibling = xChild;
        while (xSibling.is())
        {
            Reference< XNode > const xTmp(
                    xTargetDocument->importNode(xSibling, true));
            xTargetParent->appendChild(xTmp);
            xSibling = xSibling->getNextSibling();
        }
    }
 
    static Reference< XNode >
    lcl_ImportNode( Reference< XDocument > const& xDocument,
            Reference< XNode > const& xImportedNode, bool deep)
    {
        Reference< XNode > xNode;
        NodeType aNodeType = xImportedNode->getNodeType();
        switch (aNodeType)
        {
        case NodeType_ATTRIBUTE_NODE:
        {
            Reference< XAttr > const xAttr(xImportedNode, UNO_QUERY_THROW);
            Reference< XAttr > const xNew =
                xDocument->createAttribute(xAttr->getName());
            xNew->setValue(xAttr->getValue());
            xNode.set(xNew, UNO_QUERY);
            break;
        }
        case NodeType_CDATA_SECTION_NODE:
        {
            Reference< XCDATASection > const xCData(xImportedNode,
                    UNO_QUERY_THROW);
            Reference< XCDATASection > const xNewCData =
                xDocument->createCDATASection(xCData->getData());
            xNode.set(xNewCData, UNO_QUERY);
            break;
        }
        case NodeType_COMMENT_NODE:
        {
            Reference< XComment > const xComment(xImportedNode,
                    UNO_QUERY_THROW);
            Reference< XComment > const xNewComment =
                xDocument->createComment(xComment->getData());
            xNode.set(xNewComment, UNO_QUERY);
            break;
        }
        case NodeType_DOCUMENT_FRAGMENT_NODE:
        {
            Reference< XDocumentFragment > const xFrag(xImportedNode,
                    UNO_QUERY_THROW);
            Reference< XDocumentFragment > const xNewFrag =
                xDocument->createDocumentFragment();
            xNode.set(xNewFrag, UNO_QUERY);
            break;
        }
        case NodeType_ELEMENT_NODE:
        {
            Reference< XElement > const xElement(xImportedNode,
                    UNO_QUERY_THROW);
            OUString const aNsUri = xImportedNode->getNamespaceURI();
            OUString const aNsPrefix = xImportedNode->getPrefix();
            OUString aQName = xElement->getTagName();
            Reference< XElement > xNewElement;
            if (!aNsUri.isEmpty())
            {
                if (!aNsPrefix.isEmpty()) {
                    aQName = aNsPrefix + ":" + aQName;
                }
                xNewElement = xDocument->createElementNS(aNsUri, aQName);
            } else {
                xNewElement = xDocument->createElement(aQName);
            }
 
            // get attributes
            if (xElement->hasAttributes())
            {
                Reference< XNamedNodeMap > attribs = xElement->getAttributes();
                for (sal_Int32 i = 0; i < attribs->getLength(); i++)
                {
                    Reference< XAttr > const curAttr(attribs->item(i),
                            UNO_QUERY_THROW);
                    OUString const aAttrUri = curAttr->getNamespaceURI();
                    OUString const aAttrPrefix = curAttr->getPrefix();
                    OUString aAttrName = curAttr->getName();
                    OUString const sValue = curAttr->getValue();
                    if (!aAttrUri.isEmpty())
                    {
                        if (!aAttrPrefix.isEmpty()) {
                            aAttrName = aAttrPrefix + ":" + aAttrName;
                        }
                        xNewElement->setAttributeNS(
                                aAttrUri, aAttrName, sValue);
                    } else {
                        xNewElement->setAttribute(aAttrName, sValue);
                    }
                }
            }
            xNode.set(xNewElement, UNO_QUERY);
            break;
        }
        case NodeType_ENTITY_REFERENCE_NODE:
        {
            Reference< XEntityReference > const xRef(xImportedNode,
                    UNO_QUERY_THROW);
            Reference< XEntityReference > const xNewRef(
                xDocument->createEntityReference(xRef->getNodeName()));
            xNode.set(xNewRef, UNO_QUERY);
            break;
        }
        case NodeType_PROCESSING_INSTRUCTION_NODE:
        {
            Reference< XProcessingInstruction > const xPi(xImportedNode,
                    UNO_QUERY_THROW);
            Reference< XProcessingInstruction > const xNewPi(
                xDocument->createProcessingInstruction(
                    xPi->getTarget(), xPi->getData()));
            xNode.set(xNewPi, UNO_QUERY);
            break;
        }
        case NodeType_TEXT_NODE:
        {
            Reference< XText > const xText(xImportedNode, UNO_QUERY_THROW);
            Reference< XText > const xNewText(
                xDocument->createTextNode(xText->getData()));
            xNode.set(xNewText, UNO_QUERY);
            break;
        }
        case NodeType_ENTITY_NODE:
        case NodeType_DOCUMENT_NODE:
        case NodeType_DOCUMENT_TYPE_NODE:
        case NodeType_NOTATION_NODE:
        default:
            // can't be imported
            throw RuntimeException();
 
        }
        if (deep)
        {
            // get children and import them
            Reference< XNode > const xChild = xImportedNode->getFirstChild();
            if (xChild.is())
            {
                lcl_ImportSiblings(xDocument, xNode, xChild);
            }
        }
 
        /* DOMNodeInsertedIntoDocument
         * Fired when a node is being inserted into a document,
         * either through direct insertion of the Node or insertion of a
         * subtree in which it is contained. This event is dispatched after
         * the insertion has taken place. The target of this event is the node
         * being inserted. If the Node is being directly inserted the DOMNodeInserted
         * event will fire before the DOMNodeInsertedIntoDocument event.
         *   Bubbles: No
         *   Cancelable: No
         *   Context Info: None
         */
        if (xNode.is())
        {
            Reference< XDocumentEvent > const xDocevent(xDocument, UNO_QUERY);
            Reference< XMutationEvent > const event(xDocevent->createEvent(
                "DOMNodeInsertedIntoDocument"), UNO_QUERY_THROW);
            event->initMutationEvent(
                "DOMNodeInsertedIntoDocument", true, false, Reference< XNode >(),
                OUString(), OUString(), OUString(), AttrChangeType(0) );
            Reference< XEventTarget > const xDocET(xDocument, UNO_QUERY);
            xDocET->dispatchEvent(event);
        }
 
        return xNode;
    }
 
    Reference< XNode > SAL_CALL CDocument::importNode(
            Reference< XNode > const& xImportedNode, sal_Bool deep)
    {
        if (!xImportedNode.is()) { throw RuntimeException(); }
 
        // NB: this whole operation inherently accesses 2 distinct documents.
        // The imported node could even be from a different DOM implementation,
        // so this implementation cannot make any assumptions about the
        // locking strategy of the imported node.
        // So the import takes no lock on this document;
        // it only calls UNO methods on this document that temporarily
        // lock the document, and UNO methods on the imported node that
        // may temporarily lock the other document.
        // As a consequence, the import is not atomic with regard to
        // concurrent modifications of either document, but it should not
        // deadlock.
        // To ensure that no members are accessed, the implementation is in
        // static non-member functions.
 
        Reference< XDocument > const xDocument(this);
        // already in doc?
        if (xImportedNode->getOwnerDocument() == xDocument) {
            return xImportedNode;
        }
 
        Reference< XNode > const xNode(
            lcl_ImportNode(xDocument, xImportedNode, deep) );
        return xNode;
    }
 
    OUString SAL_CALL CDocument::getNodeName()
    {
        // does not need mutex currently
        return OUString("#document");
    }
 
    OUString SAL_CALL CDocument::getNodeValue()
    {
        // does not need mutex currently
        return OUString();
    }
 
    Reference< XNode > SAL_CALL CDocument::cloneNode(sal_Bool bDeep)
    {
        ::osl::MutexGuard const g(m_rMutex);
 
        OSL_ASSERT(nullptr != m_aNodePtr);
        if (nullptr == m_aNodePtr) {
            return nullptr;
        }
        xmlDocPtr const pClone(xmlCopyDoc(m_aDocPtr, bDeep ? 1 : 0));
        if (nullptr == pClone) { return nullptr; }
        Reference< XNode > const xRet(
            static_cast<CNode*>(CDocument::CreateCDocument(pClone).get()));
        return xRet;
    }
 
    Reference< XEvent > SAL_CALL CDocument::createEvent(const OUString& aType)
    {
        // does not need mutex currently
        events::CEvent *pEvent = nullptr;
        if ( aType == "DOMSubtreeModified" || aType == "DOMNodeInserted" || aType == "DOMNodeRemoved"
          || aType == "DOMNodeRemovedFromDocument" || aType == "DOMNodeInsertedIntoDocument" || aType == "DOMAttrModified"
          || aType == "DOMCharacterDataModified")
        {
            pEvent = new events::CMutationEvent;
 
        } else if ( aType == "DOMFocusIn" || aType == "DOMFocusOut" || aType == "DOMActivate")
        {
            pEvent = new events::CUIEvent;
        } else if ( aType == "click"     || aType == "mousedown" || aType == "mouseup"
                 || aType == "mouseover" || aType == "mousemove" || aType == "mouseout" )
        {
            pEvent = new events::CMouseEvent;
        }
        else // generic event
        {
            pEvent = new events::CEvent;
        }
        return Reference< XEvent >(pEvent);
    }
 
    // css::xml::sax::XSAXSerializable
    void SAL_CALL CDocument::serialize(
            const Reference< XDocumentHandler >& i_xHandler,
            const Sequence< beans::StringPair >& i_rNamespaces)
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        // add new namespaces to root node
        xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr);
        if (nullptr != pRoot) {
            const beans::StringPair * pSeq = i_rNamespaces.getConstArray();
            for (const beans::StringPair *pNsDef = pSeq;
                 pNsDef < pSeq + i_rNamespaces.getLength(); ++pNsDef) {
                OString prefix = OUStringToOString(pNsDef->First,
                                    RTL_TEXTENCODING_UTF8);
                OString href   = OUStringToOString(pNsDef->Second,
                                    RTL_TEXTENCODING_UTF8);
                // this will only add the ns if it does not exist already
                xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()),
                         reinterpret_cast<const xmlChar*>(prefix.getStr()));
            }
            // eliminate duplicate namespace declarations
            nscleanup(pRoot->children, pRoot);
        }
        saxify(i_xHandler);
    }
 
    // css::xml::sax::XFastSAXSerializable
    void SAL_CALL CDocument::fastSerialize( const Reference< XFastDocumentHandler >& i_xHandler,
                                            const Reference< XFastTokenHandler >& i_xTokenHandler,
                                            const Sequence< beans::StringPair >& i_rNamespaces,
                                            const Sequence< beans::Pair< OUString, sal_Int32 > >& i_rRegisterNamespaces )
    {
        ::osl::MutexGuard const g(m_Mutex);
 
        // add new namespaces to root node
        xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr);
        if (nullptr != pRoot) {
            const beans::StringPair * pSeq = i_rNamespaces.getConstArray();
            for (const beans::StringPair *pNsDef = pSeq;
                 pNsDef < pSeq + i_rNamespaces.getLength(); ++pNsDef) {
                OString prefix = OUStringToOString(pNsDef->First,
                                    RTL_TEXTENCODING_UTF8);
                OString href   = OUStringToOString(pNsDef->Second,
                                    RTL_TEXTENCODING_UTF8);
                // this will only add the ns if it does not exist already
                xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()),
                         reinterpret_cast<const xmlChar*>(prefix.getStr()));
            }
            // eliminate duplicate namespace declarations
            nscleanup(pRoot->children, pRoot);
        }
 
        Context aContext(i_xHandler,
                         i_xTokenHandler);
 
        // register namespace ids
        const beans::Pair<OUString,sal_Int32>* pSeq = i_rRegisterNamespaces.getConstArray();
        for (const beans::Pair<OUString,sal_Int32>* pNs = pSeq;
             pNs < pSeq + i_rRegisterNamespaces.getLength(); ++pNs)
        {
            OSL_ENSURE(pNs->Second >= FastToken::NAMESPACE,
                       "CDocument::fastSerialize(): invalid NS token id");
            aContext.maNamespaceMap[ pNs->First ] = pNs->Second;
        }
 
        fastSaxify(aContext);
    }
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.

V572 It is odd that the object which was created using 'new' operator is immediately cast to another type.