Bump version to 24.04.3.4
[LibreOffice.git] / unoxml / source / dom / node.cxx
blob2e3f56c68957769c93218bb8ead3837d9e0ddf7b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <node.hxx>
22 #include <string.h>
24 #include <libxml/xmlstring.h>
26 #include <algorithm>
28 #include <osl/mutex.hxx>
29 #include <osl/diagnose.h>
30 #include <sal/log.hxx>
32 #include <com/sun/star/xml/dom/DOMException.hpp>
33 #include <com/sun/star/xml/dom/events/XMutationEvent.hpp>
34 #include <com/sun/star/xml/sax/FastToken.hpp>
36 #include <comphelper/servicehelper.hxx>
38 #include "document.hxx"
39 #include "attr.hxx"
40 #include "childlist.hxx"
42 #include <eventdispatcher.hxx>
44 using namespace css;
45 using namespace css::uno;
46 using namespace css::xml::dom;
47 using namespace css::xml::dom::events;
48 using namespace css::xml::sax;
50 namespace DOM
52 void pushContext(Context& io_rContext)
54 // Explicitly use a temp. variable.
55 // Windows/VC++ seems to mess up if .back() is directly passed as
56 // parameter. i.e. Don't use push_back( .back() );
57 Context::NamespaceVectorType::value_type aVal = io_rContext.maNamespaces.back();
58 io_rContext.maNamespaces.push_back( aVal );
61 void popContext(Context& io_rContext)
63 io_rContext.maNamespaces.pop_back();
66 void addNamespaces(Context& io_rContext, xmlNodePtr pNode)
68 // add node's namespaces to current context
69 for (xmlNsPtr pNs = pNode->nsDef; pNs != nullptr; pNs = pNs->next) {
70 const xmlChar *pPrefix = pNs->prefix;
71 // prefix can be NULL when xmlns attribute is empty (xmlns="")
72 OString prefix(reinterpret_cast<const char*>(pPrefix),
73 pPrefix ? strlen(reinterpret_cast<const char*>(pPrefix)) : 0);
74 const xmlChar *pHref = pNs->href;
75 OUString val(reinterpret_cast<const char*>(pHref),
76 strlen(reinterpret_cast<const char*>(pHref)),
77 RTL_TEXTENCODING_UTF8);
79 Context::NamespaceMapType::iterator aIter=
80 io_rContext.maNamespaceMap.find(val);
81 if( aIter != io_rContext.maNamespaceMap.end() )
83 Context::Namespace aNS;
84 aNS.maPrefix = prefix;
85 aNS.mnToken = aIter->second;
87 io_rContext.maNamespaces.back().push_back(aNS);
89 SAL_INFO("unoxml", "Added with token " << aIter->second);
94 sal_Int32 getToken( const Context& rContext, const char* pToken )
96 const Sequence<sal_Int8> aSeq( reinterpret_cast<sal_Int8 const *>(pToken), strlen( pToken ) );
97 return rContext.mxTokenHandler->getTokenFromUTF8( aSeq );
100 sal_Int32 getTokenWithPrefix( const Context& rContext, const char* pPrefix, const char* pName )
102 sal_Int32 nNamespaceToken = FastToken::DONTKNOW;
103 OString prefix(pPrefix,
104 strlen(pPrefix));
106 SAL_INFO("unoxml", "getTokenWithPrefix(): prefix " << pPrefix << ", name " << pName);
108 Context::NamespaceVectorType::value_type::const_iterator aIter;
109 if( (aIter=std::find_if(rContext.maNamespaces.back().begin(),
110 rContext.maNamespaces.back().end(),
111 [&prefix](const Context::Namespace &aNamespace){ return aNamespace.getPrefix() == prefix; } )) !=
112 rContext.maNamespaces.back().end() )
114 nNamespaceToken = aIter->mnToken;
115 sal_Int32 nNameToken = getToken( rContext, pName );
116 if( nNameToken == FastToken::DONTKNOW )
117 nNamespaceToken = FastToken::DONTKNOW;
118 else
119 nNamespaceToken |= nNameToken;
122 return nNamespaceToken;
126 CNode::CNode(CDocument const& rDocument, ::osl::Mutex const& rMutex,
127 NodeType const& reNodeType, xmlNodePtr const& rpNode)
128 : m_bUnlinked(false)
129 , m_aNodeType(reNodeType)
130 , m_aNodePtr(rpNode)
131 // keep containing document alive
132 // (but not if this is a document; that would create a leak!)
133 , m_xDocument( (m_aNodePtr->type != XML_DOCUMENT_NODE)
134 ? &const_cast<CDocument&>(rDocument) : nullptr )
135 , m_rMutex(const_cast< ::osl::Mutex & >(rMutex))
137 OSL_ASSERT(m_aNodePtr);
140 void CNode::invalidate()
142 //remove from list if this wrapper goes away
143 if (m_aNodePtr != nullptr && m_xDocument.is()) {
144 m_xDocument->RemoveCNode(m_aNodePtr, this);
146 // #i113663#: unlinked nodes will not be freed by xmlFreeDoc
147 if (m_bUnlinked) {
148 xmlFreeNode(m_aNodePtr);
150 m_aNodePtr = nullptr;
153 CNode::~CNode()
155 // if this is the document itself, the mutex is already freed!
156 if (NodeType_DOCUMENT_NODE == m_aNodeType) {
157 invalidate();
158 } else {
159 ::osl::MutexGuard const g(m_rMutex);
160 invalidate(); // other nodes are still alive so must lock mutex
164 CDocument & CNode::GetOwnerDocument()
166 OSL_ASSERT(m_xDocument.is());
167 return *m_xDocument; // needs overriding in CDocument!
171 static void lcl_nsexchange(
172 xmlNodePtr const aNode, xmlNsPtr const oldNs, xmlNsPtr const newNs)
174 // recursively exchange any references to oldNs with references to newNs
175 xmlNodePtr cur = aNode;
176 while (cur != nullptr)
178 if (cur->ns == oldNs)
179 cur->ns = newNs;
180 if (cur->type == XML_ELEMENT_NODE)
182 xmlAttrPtr curAttr = cur->properties;
183 while(curAttr != nullptr)
185 if (curAttr->ns == oldNs)
186 curAttr->ns = newNs;
187 curAttr = curAttr->next;
189 lcl_nsexchange(cur->children, oldNs, newNs);
191 cur = cur->next;
195 /*static*/ void nscleanup(const xmlNodePtr aNode, const xmlNodePtr aParent)
197 xmlNodePtr cur = aNode;
199 //handle attributes
200 if (cur != nullptr && cur->type == XML_ELEMENT_NODE)
202 xmlAttrPtr curAttr = cur->properties;
203 while(curAttr != nullptr)
205 if (curAttr->ns != nullptr)
207 xmlNsPtr ns = xmlSearchNs(cur->doc, aParent, curAttr->ns->prefix);
208 if (ns != nullptr)
209 curAttr->ns = ns;
211 curAttr = curAttr->next;
215 while (cur != nullptr)
217 nscleanup(cur->children, cur);
218 if (cur->ns != nullptr)
220 xmlNsPtr ns = xmlSearchNs(cur->doc, aParent, cur->ns->prefix);
221 if (ns != nullptr && ns != cur->ns && strcmp(reinterpret_cast<char const *>(ns->href), reinterpret_cast<char const *>(cur->ns->href))==0)
223 xmlNsPtr curDef = cur->nsDef;
224 xmlNsPtr *refp = &(cur->nsDef); // insert point
225 while (curDef != nullptr)
227 ns = xmlSearchNs(cur->doc, aParent, curDef->prefix);
228 if (ns != nullptr && ns != curDef && strcmp(reinterpret_cast<char const *>(ns->href), reinterpret_cast<char const *>(curDef->href))==0)
230 // reconnect ns pointers in sub-tree to newly found ns before
231 // removing redundant nsdecl to prevent dangling pointers.
232 lcl_nsexchange(cur, curDef, ns);
233 *refp = curDef->next;
234 xmlFreeNs(curDef);
235 curDef = *refp;
236 } else {
237 refp = &(curDef->next);
238 curDef = curDef->next;
243 cur = cur->next;
247 void CNode::saxify(const Reference< XDocumentHandler >& i_xHandler)
249 if (!i_xHandler.is()) throw RuntimeException();
250 // default: do nothing
253 void CNode::fastSaxify(Context& io_rContext)
255 if (!io_rContext.mxDocHandler.is()) throw RuntimeException();
256 // default: do nothing
259 bool CNode::IsChildTypeAllowed(NodeType const /*nodeType*/, NodeType const*const)
261 // default: no children allowed
262 return false;
265 void CNode::checkNoParent(Reference<XNode>const& xNode){
266 if (xNode->getParentNode() != Reference<XNode>(this)){
267 DOMException e;
268 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
269 throw e;
272 void CNode::checkNoParent(const xmlNodePtr pNode){
273 if (pNode->parent != nullptr){
274 DOMException e;
275 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
276 throw e;
279 void CNode::checkSameOwner(Reference<XNode>const& xNode){
280 if (xNode->getOwnerDocument() != getOwnerDocument()) {
281 DOMException e;
282 e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
283 throw e;
288 Adds the node newChild to the end of the list of children of this node.
290 Reference< XNode > SAL_CALL CNode::appendChild(
291 Reference< XNode > const& xNewChild)
293 ::osl::ClearableMutexGuard guard(m_rMutex);
295 if (nullptr == m_aNodePtr) { return nullptr; }
297 CNode *const pNewChild(dynamic_cast<CNode*>(xNewChild.get()));
298 if (!pNewChild) { throw RuntimeException(); }
299 xmlNodePtr const cur = pNewChild->GetNodePtr();
300 if (!cur) { throw RuntimeException(); }
302 // error checks:
303 // from other document
304 if (cur->doc != m_aNodePtr->doc) {
305 DOMException e;
306 e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
307 throw e;
309 // same node
310 if (cur == m_aNodePtr) {
311 DOMException e;
312 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
313 throw e;
315 checkNoParent(cur);
317 if (!IsChildTypeAllowed(pNewChild->m_aNodeType, nullptr)) {
318 DOMException e;
319 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
320 throw e;
323 // check whether this is an attribute node; it needs special handling
324 xmlNodePtr res = nullptr;
325 if (cur->type == XML_ATTRIBUTE_NODE)
327 xmlChar const*const pChildren((cur->children)
328 ? cur->children->content
329 : reinterpret_cast<xmlChar const*>(""));
330 CAttr *const pCAttr(dynamic_cast<CAttr *>(pNewChild));
331 if (!pCAttr) { throw RuntimeException(); }
332 xmlNsPtr const pNs( pCAttr->GetNamespace(m_aNodePtr) );
333 if (pNs) {
334 res = reinterpret_cast<xmlNodePtr>(
335 xmlNewNsProp(m_aNodePtr, pNs, cur->name, pChildren));
336 } else {
337 res = reinterpret_cast<xmlNodePtr>(
338 xmlNewProp(m_aNodePtr, cur->name, pChildren));
341 else
343 res = xmlAddChild(m_aNodePtr, cur);
345 // libxml can do optimization when appending nodes.
346 // if res != cur, something was optimized and the newchild-wrapper
347 // should be updated
348 if (res && (cur != res)) {
349 pNewChild->invalidate(); // cur has been freed
353 if (!res) { return nullptr; }
355 // use custom ns cleanup instead of
356 // xmlReconciliateNs(m_aNodePtr->doc, m_aNodePtr);
357 // because that will not remove unneeded ns decls
358 nscleanup(res, m_aNodePtr);
360 ::rtl::Reference<CNode> const pNode = GetOwnerDocument().GetCNode(res);
362 if (!pNode.is()) { return nullptr; }
364 // dispatch DOMNodeInserted event, target is the new node
365 // this node is the related node
366 // does bubble
367 pNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
368 Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
369 Reference< XMutationEvent > event(docevent->createEvent(
370 "DOMNodeInserted"), UNO_QUERY);
371 event->initMutationEvent("DOMNodeInserted", true, false, this,
372 OUString(), OUString(), OUString(), AttrChangeType(0) );
374 // the following dispatch functions use only UNO interfaces
375 // and call event listeners, so release mutex to prevent deadlocks.
376 guard.clear();
378 dispatchEvent(event);
379 // dispatch subtree modified for this node
380 dispatchSubtreeModified();
382 return pNode;
386 Returns a duplicate of this node, i.e., serves as a generic copy
387 constructor for nodes.
389 Reference< XNode > SAL_CALL CNode::cloneNode(sal_Bool bDeep)
391 ::osl::MutexGuard const g(m_rMutex);
393 if (nullptr == m_aNodePtr) {
394 return nullptr;
396 ::rtl::Reference<CNode> const pNode = GetOwnerDocument().GetCNode(
397 xmlCopyNode(m_aNodePtr, bDeep ? 1 : 0));
398 if (!pNode.is()) { return nullptr; }
399 pNode->m_bUnlinked = true; // not linked yet
400 return pNode;
404 A NamedNodeMap containing the attributes of this node (if it is an Element)
405 or null otherwise.
407 Reference< XNamedNodeMap > SAL_CALL CNode::getAttributes()
409 // return empty reference; only element node may override this impl
410 return Reference< XNamedNodeMap>();
414 A NodeList that contains all children of this node.
416 Reference< XNodeList > SAL_CALL CNode::getChildNodes()
418 ::osl::MutexGuard const g(m_rMutex);
420 if (nullptr == m_aNodePtr) {
421 return nullptr;
423 Reference< XNodeList > const xNodeList(new CChildList(this, m_rMutex));
424 return xNodeList;
428 The first child of this node.
430 Reference< XNode > SAL_CALL CNode::getFirstChild()
432 ::osl::MutexGuard const g(m_rMutex);
434 if (nullptr == m_aNodePtr) {
435 return nullptr;
437 return GetOwnerDocument().GetCNode(m_aNodePtr->children);
441 The last child of this node.
443 Reference< XNode > SAL_CALL CNode::getLastChild()
445 ::osl::MutexGuard const g(m_rMutex);
447 if (nullptr == m_aNodePtr) {
448 return nullptr;
450 return GetOwnerDocument().GetCNode(xmlGetLastChild(m_aNodePtr));
454 Returns the local part of the qualified name of this node.
456 OUString SAL_CALL CNode::getLocalName()
458 // see CElement/CAttr
459 return OUString();
464 The namespace URI of this node, or null if it is unspecified.
466 OUString SAL_CALL CNode::getNamespaceURI()
468 ::osl::MutexGuard const g(m_rMutex);
470 OUString aURI;
471 if (m_aNodePtr != nullptr &&
472 (m_aNodePtr->type == XML_ELEMENT_NODE || m_aNodePtr->type == XML_ATTRIBUTE_NODE) &&
473 m_aNodePtr->ns != nullptr)
475 const xmlChar* pHref = m_aNodePtr->ns->href;
476 aURI = OUString(reinterpret_cast<char const *>(pHref), strlen(reinterpret_cast<char const *>(pHref)), RTL_TEXTENCODING_UTF8);
478 return aURI;
482 The node immediately following this node.
484 Reference< XNode > SAL_CALL CNode::getNextSibling()
486 ::osl::MutexGuard const g(m_rMutex);
488 if (nullptr == m_aNodePtr) {
489 return nullptr;
491 return GetOwnerDocument().GetCNode(m_aNodePtr->next);
495 The name of this node, depending on its type; see the table above.
497 OUString SAL_CALL CNode::getNodeName()
500 Interface nodeName nodeValue attributes
501 --------------------------------------------------------------------------------------
502 Attr name of attribute value of attribute null
503 CDATASection "#cdata-section" content of the CDATA Section null
504 Comment "#comment" content of the comment null
505 Document "#document" null null
506 DocumentFragment "#document-fragment" null null
507 DocumentType document type name null null
508 Element tag name null NamedNodeMap
509 Entity entity name null null
510 EntityReference name of entity null null
511 referenced
512 Notation notation name null null
513 Processing\ target entire content excluding null
514 Instruction the target
515 Text "#text" content of the text node null
517 return OUString();
521 A code representing the type of the underlying object, as defined above.
523 NodeType SAL_CALL CNode::getNodeType()
525 ::osl::MutexGuard const g(m_rMutex);
527 return m_aNodeType;
531 The value of this node, depending on its type; see the table above.
533 OUString SAL_CALL CNode::getNodeValue()
535 return OUString();
539 The Document object associated with this node.
541 Reference< XDocument > SAL_CALL CNode::getOwnerDocument()
543 ::osl::MutexGuard const g(m_rMutex);
545 if (nullptr == m_aNodePtr) {
546 return nullptr;
548 Reference< XDocument > const xDoc(& GetOwnerDocument());
549 return xDoc;
553 The parent of this node.
555 Reference< XNode > SAL_CALL CNode::getParentNode()
557 ::osl::MutexGuard const g(m_rMutex);
559 if (nullptr == m_aNodePtr) {
560 return nullptr;
562 return GetOwnerDocument().GetCNode(m_aNodePtr->parent);
566 The namespace prefix of this node, or null if it is unspecified.
568 OUString SAL_CALL CNode::getPrefix()
570 ::osl::MutexGuard const g(m_rMutex);
572 OUString aPrefix;
573 if (m_aNodePtr != nullptr &&
574 (m_aNodePtr->type == XML_ELEMENT_NODE || m_aNodePtr->type == XML_ATTRIBUTE_NODE) &&
575 m_aNodePtr->ns != nullptr)
577 const xmlChar* pPrefix = m_aNodePtr->ns->prefix;
578 if( pPrefix != nullptr )
579 aPrefix = OUString(reinterpret_cast<char const *>(pPrefix), strlen(reinterpret_cast<char const *>(pPrefix)), RTL_TEXTENCODING_UTF8);
581 return aPrefix;
586 The node immediately preceding this node.
588 Reference< XNode > SAL_CALL CNode::getPreviousSibling()
590 ::osl::MutexGuard const g(m_rMutex);
592 if (nullptr == m_aNodePtr) {
593 return nullptr;
595 return GetOwnerDocument().GetCNode(m_aNodePtr->prev);
599 Returns whether this node (if it is an element) has any attributes.
601 sal_Bool SAL_CALL CNode::hasAttributes()
603 ::osl::MutexGuard const g(m_rMutex);
605 return (m_aNodePtr != nullptr && m_aNodePtr->properties != nullptr);
609 Returns whether this node has any children.
611 sal_Bool SAL_CALL CNode::hasChildNodes()
613 ::osl::MutexGuard const g(m_rMutex);
615 return (m_aNodePtr != nullptr && m_aNodePtr->children != nullptr);
619 Inserts the node newChild before the existing child node refChild.
621 Reference< XNode > SAL_CALL CNode::insertBefore(
622 const Reference< XNode >& newChild, const Reference< XNode >& refChild)
624 if (!newChild.is() || !refChild.is()) { throw RuntimeException(); }
626 checkSameOwner(newChild);
628 if (refChild->getParentNode() != Reference< XNode >(this)) {
629 DOMException e;
630 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
631 throw e;
634 ::osl::ClearableMutexGuard guard(m_rMutex);
636 CNode *const pNewNode(dynamic_cast<CNode*>(newChild.get()));
637 CNode *const pRefNode(dynamic_cast<CNode*>(refChild.get()));
638 if (!pNewNode || !pRefNode) { throw RuntimeException(); }
639 xmlNodePtr const pNewChild(pNewNode->GetNodePtr());
640 xmlNodePtr const pRefChild(pRefNode->GetNodePtr());
641 if (!pNewChild || !pRefChild) { throw RuntimeException(); }
643 if (pNewChild == m_aNodePtr) {
644 DOMException e;
645 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
646 throw e;
648 // already has parent
649 checkNoParent(pNewChild);
651 if (!IsChildTypeAllowed(pNewNode->m_aNodeType, nullptr)) {
652 DOMException e;
653 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
654 throw e;
657 // attributes are unordered anyway, so just do appendChild
658 if (XML_ATTRIBUTE_NODE == pNewChild->type) {
659 guard.clear();
660 return appendChild(newChild);
663 xmlNodePtr cur = m_aNodePtr->children;
665 //search child before which to insert
666 while (cur != nullptr)
668 if (cur == pRefChild) {
669 // insert before
670 pNewChild->next = cur;
671 pNewChild->prev = cur->prev;
672 cur->prev = pNewChild;
673 if (pNewChild->prev != nullptr) {
674 pNewChild->prev->next = pNewChild;
676 pNewChild->parent = cur->parent;
677 if (pNewChild->parent->children == cur) {
678 pNewChild->parent->children = pNewChild;
680 // do not update parent->last here!
681 pNewNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
682 break;
684 cur = cur->next;
686 return refChild;
690 Tests whether the DOM implementation implements a specific feature and
691 that feature is supported by this node.
693 sal_Bool SAL_CALL CNode::isSupported(const OUString& /*feature*/, const OUString& /*ver*/)
695 OSL_ENSURE(false, "CNode::isSupported: not implemented (#i113683#)");
696 return false;
700 Puts all Text nodes in the full depth of the sub-tree underneath this
701 Node, including attribute nodes, into a "normal" form where only structure
702 (e.g., elements, comments, processing instructions, CDATA sections, and
703 entity references) separates Text nodes, i.e., there are neither adjacent
704 Text nodes nor empty Text nodes.
706 void SAL_CALL CNode::normalize()
708 //XXX combine adjacent text nodes and remove empty ones
709 OSL_ENSURE(false, "CNode::normalize: not implemented (#i113683#)");
713 Removes the child node indicated by oldChild from the list of children,
714 and returns it.
716 Reference< XNode > SAL_CALL
717 CNode::removeChild(const Reference< XNode >& xOldChild)
719 if (!xOldChild.is()) {
720 throw RuntimeException();
723 checkSameOwner(xOldChild);
725 if (xOldChild->getParentNode() != Reference< XNode >(this)) {
726 DOMException e;
727 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
728 throw e;
731 ::osl::ClearableMutexGuard guard(m_rMutex);
733 if (!m_aNodePtr) { throw RuntimeException(); }
735 Reference<XNode> xReturn( xOldChild );
737 ::rtl::Reference<CNode> const pOld(dynamic_cast<CNode*>(xOldChild.get()));
738 if (!pOld.is()) { throw RuntimeException(); }
739 xmlNodePtr const old = pOld->GetNodePtr();
740 if (!old) { throw RuntimeException(); }
742 if( old->type == XML_ATTRIBUTE_NODE )
744 xmlAttrPtr pAttr = reinterpret_cast<xmlAttrPtr>(old);
745 xmlRemoveProp( pAttr );
746 pOld->invalidate(); // freed by xmlRemoveProp
747 xReturn.clear();
749 else
751 xmlUnlinkNode(old);
752 pOld->m_bUnlinked = true;
755 /*DOMNodeRemoved
756 * Fired when a node is being removed from its parent node.
757 * This event is dispatched before the node is removed from the tree.
758 * The target of this event is the node being removed.
759 * Bubbles: Yes
760 * Cancelable: No
761 * Context Info: relatedNode holds the parent node
763 Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
764 Reference< XMutationEvent > event(docevent->createEvent(
765 "DOMNodeRemoved"), UNO_QUERY);
766 event->initMutationEvent("DOMNodeRemoved",
767 true,
768 false,
769 this,
770 OUString(), OUString(), OUString(), AttrChangeType(0) );
772 // the following dispatch functions use only UNO interfaces
773 // and call event listeners, so release mutex to prevent deadlocks.
774 guard.clear();
776 dispatchEvent(event);
777 // subtree modified for this node
778 dispatchSubtreeModified();
780 return xReturn;
784 Replaces the child node oldChild with newChild in the list of children,
785 and returns the oldChild node.
787 Reference< XNode > SAL_CALL CNode::replaceChild(
788 Reference< XNode > const& xNewChild,
789 Reference< XNode > const& xOldChild)
791 if (!xOldChild.is() || !xNewChild.is()) {
792 throw RuntimeException();
795 checkSameOwner(xNewChild);
797 if (xOldChild->getParentNode() != Reference< XNode >(this)) {
798 DOMException e;
799 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
800 throw e;
803 ::osl::ClearableMutexGuard guard(m_rMutex);
805 ::rtl::Reference<CNode> const pOldNode(dynamic_cast<CNode*>(xOldChild.get()));
806 ::rtl::Reference<CNode> const pNewNode(dynamic_cast<CNode*>(xNewChild.get()));
807 if (!pOldNode.is() || !pNewNode.is()) { throw RuntimeException(); }
808 xmlNodePtr const pOld = pOldNode->GetNodePtr();
809 xmlNodePtr const pNew = pNewNode->GetNodePtr();
810 if (!pOld || !pNew) { throw RuntimeException(); }
812 if (pNew == m_aNodePtr) {
813 DOMException e;
814 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
815 throw e;
817 // already has parent
818 checkNoParent(pNew);
820 if (!IsChildTypeAllowed(pNewNode->m_aNodeType, &pOldNode->m_aNodeType)) {
821 DOMException e;
822 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
823 throw e;
826 if( pOld->type == XML_ATTRIBUTE_NODE )
828 // can only replace attribute with attribute
829 if ( pOld->type != pNew->type )
831 DOMException e;
832 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
833 throw e;
836 xmlAttrPtr pAttr = reinterpret_cast<xmlAttrPtr>(pOld);
837 xmlRemoveProp( pAttr );
838 pOldNode->invalidate(); // freed by xmlRemoveProp
839 appendChild(xNewChild);
841 else
844 xmlNodePtr cur = m_aNodePtr->children;
845 //find old node in child list
846 while (cur != nullptr)
848 if(cur == pOld)
850 // exchange nodes
851 pNew->prev = pOld->prev;
852 if (pNew->prev != nullptr)
853 pNew->prev->next = pNew;
854 pNew->next = pOld->next;
855 if (pNew->next != nullptr)
856 pNew->next->prev = pNew;
857 pNew->parent = pOld->parent;
858 assert(pNew->parent && "coverity[var_deref_op] pNew->parent cannot be NULL here");
859 if(pNew->parent->children == pOld)
860 pNew->parent->children = pNew;
861 if(pNew->parent->last == pOld)
862 pNew->parent->last = pNew;
863 pOld->next = nullptr;
864 pOld->prev = nullptr;
865 pOld->parent = nullptr;
866 pOldNode->m_bUnlinked = true;
867 pNewNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
869 cur = cur->next;
873 guard.clear(); // release for calling event handlers
874 dispatchSubtreeModified();
876 return xOldChild;
879 void CNode::dispatchSubtreeModified()
881 // only uses UNO interfaces => needs no mutex
883 // dispatch DOMSubtreeModified
884 // target is _this_ node
885 Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
886 Reference< XMutationEvent > event(docevent->createEvent(
887 "DOMSubtreeModified"), UNO_QUERY);
888 event->initMutationEvent(
889 "DOMSubtreeModified", true,
890 false, Reference< XNode >(),
891 OUString(), OUString(), OUString(), AttrChangeType(0) );
892 dispatchEvent(event);
896 The value of this node, depending on its type; see the table above.
898 void SAL_CALL CNode::setNodeValue(const OUString& /*nodeValue*/)
900 // use specific node implementation
901 // if we end up down here, something went wrong
902 DOMException e;
903 e.Code = DOMExceptionType_NO_MODIFICATION_ALLOWED_ERR;
904 throw e;
908 The namespace prefix of this node, or null if it is unspecified.
910 void SAL_CALL CNode::setPrefix(const OUString& prefix)
912 ::osl::MutexGuard const g(m_rMutex);
914 if ((nullptr == m_aNodePtr) ||
915 ((m_aNodePtr->type != XML_ELEMENT_NODE) &&
916 (m_aNodePtr->type != XML_ATTRIBUTE_NODE)))
918 DOMException e;
919 e.Code = DOMExceptionType_NO_MODIFICATION_ALLOWED_ERR;
920 throw e;
922 OString o1 = OUStringToOString(prefix, RTL_TEXTENCODING_UTF8);
923 xmlChar const *pBuf = reinterpret_cast<xmlChar const *>(o1.getStr());
924 if (m_aNodePtr != nullptr && m_aNodePtr->ns != nullptr)
926 xmlFree(const_cast<xmlChar *>(m_aNodePtr->ns->prefix));
927 m_aNodePtr->ns->prefix = xmlStrdup(pBuf);
932 // --- XEventTarget
933 void SAL_CALL CNode::addEventListener(const OUString& eventType,
934 const Reference< css::xml::dom::events::XEventListener >& listener,
935 sal_Bool useCapture)
937 ::osl::MutexGuard const g(m_rMutex);
939 CDocument & rDocument(GetOwnerDocument());
940 events::CEventDispatcher & rDispatcher(rDocument.GetEventDispatcher());
941 rDispatcher.addListener(m_aNodePtr, eventType, listener, useCapture);
944 void SAL_CALL CNode::removeEventListener(const OUString& eventType,
945 const Reference< css::xml::dom::events::XEventListener >& listener,
946 sal_Bool useCapture)
948 ::osl::MutexGuard const g(m_rMutex);
950 CDocument & rDocument(GetOwnerDocument());
951 events::CEventDispatcher & rDispatcher(rDocument.GetEventDispatcher());
952 rDispatcher.removeListener(m_aNodePtr, eventType, listener, useCapture);
955 sal_Bool SAL_CALL CNode::dispatchEvent(const Reference< XEvent >& evt)
957 CDocument * pDocument;
958 events::CEventDispatcher * pDispatcher;
959 xmlNodePtr pNode;
961 ::osl::MutexGuard const g(m_rMutex);
963 pDocument = & GetOwnerDocument();
964 pDispatcher = & pDocument->GetEventDispatcher();
965 pNode = m_aNodePtr;
967 // this calls event listeners, do not call with locked mutex
968 pDispatcher->dispatchEvent(*pDocument, m_rMutex, pNode, this, evt);
969 return true;
973 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */