/* -*- 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 #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 #include #include #include #include #include #include #include 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(pDoc)) , m_aDocPtr(pDoc) , m_streamListeners() , m_pEventDispatcher(new events::CEventDispatcher) { } ::rtl::Reference CDocument::CreateCDocument(xmlDocPtr const pDoc) { ::rtl::Reference const xDoc(new CDocument(pDoc)); // add the doc itself to its nodemap! xDoc->m_NodeMap.emplace( reinterpret_cast(pDoc), ::std::make_pair( WeakReference(static_cast(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 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(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 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 const xNode(i->second.first); if (xNode.is()) { ::rtl::Reference 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 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(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(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(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(pNode)); break; case XML_ATTRIBUTE_NODE: // m_aNodeType = NodeType::ATTRIBUTE_NODE; pCNode = new CAttr(*this, m_Mutex, reinterpret_cast(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(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 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 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(context); Sequence bs(reinterpret_cast(buffer), len); pContext->stream->writeBytes(bs); return len; } // close callback //int xmlOutputCloseCallback (void * context) static int closeCallback(void *context) { IOContext *pContext = static_cast(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(o1.getStr()); xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr, pName, nullptr); ::rtl::Reference< CAttr > const pCAttr( dynamic_cast< CAttr* >(GetCNode( reinterpret_cast(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(oName.getStr()), nullptr); ::rtl::Reference< CAttr > const pCAttr( dynamic_cast< CAttr* >(GetCNode( reinterpret_cast(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(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(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(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(o1.getStr()); o2 = OUStringToOString(qname.copy(i+1), RTL_TEXTENCODING_UTF8); pName = reinterpret_cast(o2.getStr()); } else { // default prefix pPrefix = reinterpret_cast(""); o2 = OUStringToOString(qname, RTL_TEXTENCODING_UTF8); pName = reinterpret_cast(o2.getStr()); } o3 = OUStringToOString(ns, RTL_TEXTENCODING_UTF8); xmlChar const *pUri = reinterpret_cast(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(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(o1.getStr()); OString o2 = OUStringToOString(data, RTL_TEXTENCODING_UTF8); xmlChar const *pData = reinterpret_cast(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(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(a->children->content), reinterpret_cast(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(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(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(href.getStr()), reinterpret_cast(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(href.getStr()), reinterpret_cast(prefix.getStr())); } // eliminate duplicate namespace declarations nscleanup(pRoot->children, pRoot); } Context aContext(i_xHandler, dynamic_cast(i_xTokenHandler.get())); // register namespace ids for (const beans::Pair& 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: */