summaryrefslogtreecommitdiffstats
path: root/unoxml/source/dom/document.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'unoxml/source/dom/document.cxx')
-rw-r--r--unoxml/source/dom/document.cxx1025
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: */