diff options
Diffstat (limited to 'unoxml/source/dom/document.cxx')
-rw-r--r-- | unoxml/source/dom/document.cxx | 1025 |
1 files changed, 1025 insertions, 0 deletions
diff --git a/unoxml/source/dom/document.cxx b/unoxml/source/dom/document.cxx new file mode 100644 index 000000000..68f88442e --- /dev/null +++ b/unoxml/source/dom/document.cxx @@ -0,0 +1,1025 @@ +/* -*- 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> + +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 (const auto& rEntry : m_NodeMap) + { + Reference<XNode> const xNode(rEntry.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 = new CElement(*this, m_Mutex, pNode); + break; + case XML_TEXT_NODE: + // m_aNodeType = NodeType::TEXT_NODE; + pCNode = new CText(*this, m_Mutex, pNode); + break; + case XML_CDATA_SECTION_NODE: + // m_aNodeType = NodeType::CDATA_SECTION_NODE; + pCNode = new CCDATASection(*this, m_Mutex, pNode); + break; + case XML_ENTITY_REF_NODE: + // m_aNodeType = NodeType::ENTITY_REFERENCE_NODE; + pCNode = new CEntityReference(*this, m_Mutex, pNode); + break; + case XML_ENTITY_NODE: + // m_aNodeType = NodeType::ENTITY_NODE; + pCNode = new CEntity(*this, m_Mutex, reinterpret_cast<xmlEntityPtr>(pNode)); + break; + case XML_PI_NODE: + // m_aNodeType = NodeType::PROCESSING_INSTRUCTION_NODE; + pCNode = new CProcessingInstruction(*this, m_Mutex, pNode); + break; + case XML_COMMENT_NODE: + // m_aNodeType = NodeType::COMMENT_NODE; + pCNode = 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 = new CDocument(reinterpret_cast<xmlDocPtr>(pNode)); + break; + case XML_DOCUMENT_TYPE_NODE: + case XML_DTD_NODE: + // m_aNodeType = NodeType::DOCUMENT_TYPE_NODE; + pCNode = new CDocumentType(*this, m_Mutex, reinterpret_cast<xmlDtdPtr>(pNode)); + break; + case XML_DOCUMENT_FRAG_NODE: + // m_aNodeType = NodeType::DOCUMENT_FRAGMENT_NODE; + pCNode = new CDocumentFragment(*this, m_Mutex, pNode); + break; + case XML_NOTATION_NODE: + // m_aNodeType = NodeType::NOTATION_NODE; + pCNode = new CNotation(*this, m_Mutex, reinterpret_cast<xmlNotationPtr>(pNode)); + break; + case XML_ATTRIBUTE_NODE: + // m_aNodeType = NodeType::ATTRIBUTE_NODE; + pCNode = 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); + } + + namespace { + + // IO context functions for libxml2 interaction + struct IOContext { + Reference< XOutputStream > stream; + bool allowClose; + }; + + } + + 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 + for (const Reference< XStreamListener >& aListener : streamListeners) { + aListener->started(); + } + + { + ::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 + for (const Reference< XStreamListener >& aListener : streamListeners) { + aListener->closed(); + } + } + + 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 = xNew; + break; + } + case NodeType_CDATA_SECTION_NODE: + { + Reference< XCDATASection > const xCData(xImportedNode, + UNO_QUERY_THROW); + Reference< XCDATASection > const xNewCData = + xDocument->createCDATASection(xCData->getData()); + xNode = xNewCData; + break; + } + case NodeType_COMMENT_NODE: + { + Reference< XComment > const xComment(xImportedNode, + UNO_QUERY_THROW); + Reference< XComment > const xNewComment = + xDocument->createComment(xComment->getData()); + xNode = xNewComment; + break; + } + case NodeType_DOCUMENT_FRAGMENT_NODE: + { + Reference< XDocumentFragment > const xFrag(xImportedNode, + UNO_QUERY_THROW); + Reference< XDocumentFragment > const xNewFrag = + xDocument->createDocumentFragment(); + xNode = xNewFrag; + 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 = xNewElement; + break; + } + case NodeType_ENTITY_REFERENCE_NODE: + { + Reference< XEntityReference > const xRef(xImportedNode, + UNO_QUERY_THROW); + Reference< XEntityReference > const xNewRef( + xDocument->createEntityReference(xRef->getNodeName())); + xNode = xNewRef; + 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 = xNewPi; + break; + } + case NodeType_TEXT_NODE: + { + Reference< XText > const xText(xImportedNode, UNO_QUERY_THROW); + Reference< XText > const xNewText( + xDocument->createTextNode(xText->getData())); + xNode = xNewText; + 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 "#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) { + for (const beans::StringPair& rNsDef : i_rNamespaces) { + OString prefix = OUStringToOString(rNsDef.First, + RTL_TEXTENCODING_UTF8); + OString href = OUStringToOString(rNsDef.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) { + for (const beans::StringPair& rNsDef : i_rNamespaces) { + OString prefix = OUStringToOString(rNsDef.First, + RTL_TEXTENCODING_UTF8); + OString href = OUStringToOString(rNsDef.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, + dynamic_cast<sax_fastparser::FastTokenHandlerBase*>(i_xTokenHandler.get())); + + // register namespace ids + for (const beans::Pair<OUString,sal_Int32>& rNs : i_rRegisterNamespaces) + { + OSL_ENSURE(rNs.Second >= FastToken::NAMESPACE, + "CDocument::fastSerialize(): invalid NS token id"); + aContext.maNamespaceMap[ rNs.First ] = rNs.Second; + } + + fastSaxify(aContext); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |