Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / unoxml / source / dom / node.cxx
blobcdeab8e2d41934e11ad11553c6a8b435f4bbeae6
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 <stdio.h>
23 #include <string.h>
25 #include <libxml/xmlstring.h>
27 #include <algorithm>
29 #include <rtl/uuid.h>
30 #include <rtl/instance.hxx>
31 #include <osl/mutex.hxx>
32 #include <osl/diagnose.h>
33 #include <sal/log.hxx>
35 #include <com/sun/star/xml/sax/FastToken.hpp>
37 #include <comphelper/servicehelper.hxx>
39 #include "document.hxx"
40 #include "attr.hxx"
41 #include "childlist.hxx"
43 #include <eventdispatcher.hxx>
44 #include <mutationevent.hxx>
46 using namespace css;
47 using namespace css::uno;
48 using namespace css::xml::dom;
49 using namespace css::xml::dom::events;
50 using namespace css::xml::sax;
52 namespace
54 class theCNodeUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theCNodeUnoTunnelId > {};
57 namespace DOM
59 void pushContext(Context& io_rContext)
61 // Explicitly use a temp. variable.
62 // Windows/VC++ seems to mess up if .back() is directly passed as
63 // parameter. i.e. Don't use push_back( .back() );
64 Context::NamespaceVectorType::value_type aVal = io_rContext.maNamespaces.back();
65 io_rContext.maNamespaces.push_back( aVal );
68 void popContext(Context& io_rContext)
70 io_rContext.maNamespaces.pop_back();
73 void addNamespaces(Context& io_rContext, xmlNodePtr pNode)
75 // add node's namespaces to current context
76 for (xmlNsPtr pNs = pNode->nsDef; pNs != nullptr; pNs = pNs->next) {
77 const xmlChar *pPrefix = pNs->prefix;
78 // prefix can be NULL when xmlns attribute is empty (xmlns="")
79 OString prefix(reinterpret_cast<const sal_Char*>(pPrefix),
80 pPrefix ? strlen(reinterpret_cast<const char*>(pPrefix)) : 0);
81 const xmlChar *pHref = pNs->href;
82 OUString val(reinterpret_cast<const sal_Char*>(pHref),
83 strlen(reinterpret_cast<const char*>(pHref)),
84 RTL_TEXTENCODING_UTF8);
86 Context::NamespaceMapType::iterator aIter=
87 io_rContext.maNamespaceMap.find(val);
88 if( aIter != io_rContext.maNamespaceMap.end() )
90 Context::Namespace aNS;
91 aNS.maPrefix = prefix;
92 aNS.mnToken = aIter->second;
94 io_rContext.maNamespaces.back().push_back(aNS);
96 SAL_INFO("unoxml", "Added with token " << aIter->second);
101 sal_Int32 getToken( const Context& rContext, const sal_Char* pToken )
103 const Sequence<sal_Int8> aSeq( reinterpret_cast<sal_Int8 const *>(pToken), strlen( pToken ) );
104 return rContext.mxTokenHandler->getTokenFromUTF8( aSeq );
107 sal_Int32 getTokenWithPrefix( const Context& rContext, const sal_Char* pPrefix, const sal_Char* pName )
109 sal_Int32 nNamespaceToken = FastToken::DONTKNOW;
110 OString prefix(pPrefix,
111 strlen(reinterpret_cast<const char*>(pPrefix)));
113 SAL_INFO("unoxml", "getTokenWithPrefix(): prefix " << pPrefix << ", name " << pName);
115 Context::NamespaceVectorType::value_type::const_iterator aIter;
116 if( (aIter=std::find_if(rContext.maNamespaces.back().begin(),
117 rContext.maNamespaces.back().end(),
118 [&prefix](const Context::Namespace &aNamespace){ return aNamespace.getPrefix() == prefix; } )) !=
119 rContext.maNamespaces.back().end() )
121 nNamespaceToken = aIter->mnToken;
122 sal_Int32 nNameToken = getToken( rContext, pName );
123 if( nNameToken == FastToken::DONTKNOW )
124 nNamespaceToken = FastToken::DONTKNOW;
125 else
126 nNamespaceToken |= nNameToken;
129 return nNamespaceToken;
133 CNode::CNode(CDocument const& rDocument, ::osl::Mutex const& rMutex,
134 NodeType const& reNodeType, xmlNodePtr const& rpNode)
135 : m_bUnlinked(false)
136 , m_aNodeType(reNodeType)
137 , m_aNodePtr(rpNode)
138 // keep containing document alive
139 // (but not if this is a document; that would create a leak!)
140 , m_xDocument( (m_aNodePtr->type != XML_DOCUMENT_NODE)
141 ? &const_cast<CDocument&>(rDocument) : nullptr )
142 , m_rMutex(const_cast< ::osl::Mutex & >(rMutex))
144 OSL_ASSERT(m_aNodePtr);
147 void CNode::invalidate()
149 //remove from list if this wrapper goes away
150 if (m_aNodePtr != nullptr && m_xDocument.is()) {
151 m_xDocument->RemoveCNode(m_aNodePtr, this);
153 // #i113663#: unlinked nodes will not be freed by xmlFreeDoc
154 if (m_bUnlinked) {
155 xmlFreeNode(m_aNodePtr);
157 m_aNodePtr = nullptr;
160 CNode::~CNode()
162 // if this is the document itself, the mutex is already freed!
163 if (NodeType_DOCUMENT_NODE == m_aNodeType) {
164 invalidate();
165 } else {
166 ::osl::MutexGuard const g(m_rMutex);
167 invalidate(); // other nodes are still alive so must lock mutex
171 const css::uno::Sequence< sal_Int8 > & CNode::getUnoTunnelId() throw()
173 return theCNodeUnoTunnelId::get().getSeq();
176 CDocument & CNode::GetOwnerDocument()
178 OSL_ASSERT(m_xDocument.is());
179 return *m_xDocument; // needs overriding in CDocument!
183 static void lcl_nsexchange(
184 xmlNodePtr const aNode, xmlNsPtr const oldNs, xmlNsPtr const newNs)
186 // recursively exchange any references to oldNs with references to newNs
187 xmlNodePtr cur = aNode;
188 while (cur != nullptr)
190 if (cur->ns == oldNs)
191 cur->ns = newNs;
192 if (cur->type == XML_ELEMENT_NODE)
194 xmlAttrPtr curAttr = cur->properties;
195 while(curAttr != nullptr)
197 if (curAttr->ns == oldNs)
198 curAttr->ns = newNs;
199 curAttr = curAttr->next;
201 lcl_nsexchange(cur->children, oldNs, newNs);
203 cur = cur->next;
207 /*static*/ void nscleanup(const xmlNodePtr aNode, const xmlNodePtr aParent)
209 xmlNodePtr cur = aNode;
211 //handle attributes
212 if (cur != nullptr && cur->type == XML_ELEMENT_NODE)
214 xmlAttrPtr curAttr = cur->properties;
215 while(curAttr != nullptr)
217 if (curAttr->ns != nullptr)
219 xmlNsPtr ns = xmlSearchNs(cur->doc, aParent, curAttr->ns->prefix);
220 if (ns != nullptr)
221 curAttr->ns = ns;
223 curAttr = curAttr->next;
227 while (cur != nullptr)
229 nscleanup(cur->children, cur);
230 if (cur->ns != nullptr)
232 xmlNsPtr ns = xmlSearchNs(cur->doc, aParent, cur->ns->prefix);
233 if (ns != nullptr && ns != cur->ns && strcmp(reinterpret_cast<char const *>(ns->href), reinterpret_cast<char const *>(cur->ns->href))==0)
235 xmlNsPtr curDef = cur->nsDef;
236 xmlNsPtr *refp = &(cur->nsDef); // insert point
237 while (curDef != nullptr)
239 ns = xmlSearchNs(cur->doc, aParent, curDef->prefix);
240 if (ns != nullptr && ns != curDef && strcmp(reinterpret_cast<char const *>(ns->href), reinterpret_cast<char const *>(curDef->href))==0)
242 // reconnect ns pointers in sub-tree to newly found ns before
243 // removing redundant nsdecl to prevent dangling pointers.
244 lcl_nsexchange(cur, curDef, ns);
245 *refp = curDef->next;
246 xmlFreeNs(curDef);
247 curDef = *refp;
248 } else {
249 refp = &(curDef->next);
250 curDef = curDef->next;
255 cur = cur->next;
259 void CNode::saxify(const Reference< XDocumentHandler >& i_xHandler)
261 if (!i_xHandler.is()) throw RuntimeException();
262 // default: do nothing
265 void CNode::fastSaxify(Context& io_rContext)
267 if (!io_rContext.mxDocHandler.is()) throw RuntimeException();
268 // default: do nothing
271 bool CNode::IsChildTypeAllowed(NodeType const /*nodeType*/)
273 // default: no children allowed
274 return false;
278 Adds the node newChild to the end of the list of children of this node.
280 Reference< XNode > SAL_CALL CNode::appendChild(
281 Reference< XNode > const& xNewChild)
283 ::osl::ClearableMutexGuard guard(m_rMutex);
285 if (nullptr == m_aNodePtr) { return nullptr; }
287 CNode *const pNewChild(comphelper::getUnoTunnelImplementation<CNode>(xNewChild));
288 if (!pNewChild) { throw RuntimeException(); }
289 xmlNodePtr const cur = pNewChild->GetNodePtr();
290 if (!cur) { throw RuntimeException(); }
292 // error checks:
293 // from other document
294 if (cur->doc != m_aNodePtr->doc) {
295 DOMException e;
296 e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
297 throw e;
299 // same node
300 if (cur == m_aNodePtr) {
301 DOMException e;
302 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
303 throw e;
305 if (cur->parent != nullptr) {
306 DOMException e;
307 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
308 throw e;
310 if (!IsChildTypeAllowed(pNewChild->m_aNodeType)) {
311 DOMException e;
312 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
313 throw e;
316 // check whether this is an attribute node; it needs special handling
317 xmlNodePtr res = nullptr;
318 if (cur->type == XML_ATTRIBUTE_NODE)
320 xmlChar const*const pChildren((cur->children)
321 ? cur->children->content
322 : reinterpret_cast<xmlChar const*>(""));
323 CAttr *const pCAttr(dynamic_cast<CAttr *>(pNewChild));
324 if (!pCAttr) { throw RuntimeException(); }
325 xmlNsPtr const pNs( pCAttr->GetNamespace(m_aNodePtr) );
326 if (pNs) {
327 res = reinterpret_cast<xmlNodePtr>(
328 xmlNewNsProp(m_aNodePtr, pNs, cur->name, pChildren));
329 } else {
330 res = reinterpret_cast<xmlNodePtr>(
331 xmlNewProp(m_aNodePtr, cur->name, pChildren));
334 else
336 res = xmlAddChild(m_aNodePtr, cur);
338 // libxml can do optimization when appending nodes.
339 // if res != cur, something was optimized and the newchild-wrapper
340 // should be updated
341 if (res && (cur != res)) {
342 pNewChild->invalidate(); // cur has been freed
346 if (!res) { return nullptr; }
348 // use custom ns cleanup instead of
349 // xmlReconciliateNs(m_aNodePtr->doc, m_aNodePtr);
350 // because that will not remove unneeded ns decls
351 nscleanup(res, m_aNodePtr);
353 ::rtl::Reference<CNode> const pNode = GetOwnerDocument().GetCNode(res);
355 if (!pNode.is()) { return nullptr; }
357 // dispatch DOMNodeInserted event, target is the new node
358 // this node is the related node
359 // does bubble
360 pNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
361 Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
362 Reference< XMutationEvent > event(docevent->createEvent(
363 "DOMNodeInserted"), UNO_QUERY);
364 event->initMutationEvent("DOMNodeInserted", true, false, this,
365 OUString(), OUString(), OUString(), AttrChangeType(0) );
367 // the following dispatch functions use only UNO interfaces
368 // and call event listeners, so release mutex to prevent deadlocks.
369 guard.clear();
371 dispatchEvent(event);
372 // dispatch subtree modified for this node
373 dispatchSubtreeModified();
375 return pNode.get();
379 Returns a duplicate of this node, i.e., serves as a generic copy
380 constructor for nodes.
382 Reference< XNode > SAL_CALL CNode::cloneNode(sal_Bool bDeep)
384 ::osl::MutexGuard const g(m_rMutex);
386 if (nullptr == m_aNodePtr) {
387 return nullptr;
389 ::rtl::Reference<CNode> const pNode = GetOwnerDocument().GetCNode(
390 xmlCopyNode(m_aNodePtr, bDeep ? 1 : 0));
391 if (!pNode.is()) { return nullptr; }
392 pNode->m_bUnlinked = true; // not linked yet
393 return pNode.get();
397 A NamedNodeMap containing the attributes of this node (if it is an Element)
398 or null otherwise.
400 Reference< XNamedNodeMap > SAL_CALL CNode::getAttributes()
402 // return empty reference; only element node may override this impl
403 return Reference< XNamedNodeMap>();
407 A NodeList that contains all children of this node.
409 Reference< XNodeList > SAL_CALL CNode::getChildNodes()
411 ::osl::MutexGuard const g(m_rMutex);
413 if (nullptr == m_aNodePtr) {
414 return nullptr;
416 Reference< XNodeList > const xNodeList(new CChildList(this, m_rMutex));
417 return xNodeList;
421 The first child of this node.
423 Reference< XNode > SAL_CALL CNode::getFirstChild()
425 ::osl::MutexGuard const g(m_rMutex);
427 if (nullptr == m_aNodePtr) {
428 return nullptr;
430 Reference< XNode > const xNode(
431 GetOwnerDocument().GetCNode(m_aNodePtr->children).get());
432 return xNode;
436 The last child of this node.
438 Reference< XNode > SAL_CALL CNode::getLastChild()
440 ::osl::MutexGuard const g(m_rMutex);
442 if (nullptr == m_aNodePtr) {
443 return nullptr;
445 Reference< XNode > const xNode(
446 GetOwnerDocument().GetCNode(xmlGetLastChild(m_aNodePtr)).get());
447 return xNode;
451 Returns the local part of the qualified name of this node.
453 OUString SAL_CALL CNode::getLocalName()
455 // see CElement/CAttr
456 return OUString();
461 The namespace URI of this node, or null if it is unspecified.
463 OUString SAL_CALL CNode::getNamespaceURI()
465 ::osl::MutexGuard const g(m_rMutex);
467 OUString aURI;
468 if (m_aNodePtr != nullptr &&
469 (m_aNodePtr->type == XML_ELEMENT_NODE || m_aNodePtr->type == XML_ATTRIBUTE_NODE) &&
470 m_aNodePtr->ns != nullptr)
472 const xmlChar* pHref = m_aNodePtr->ns->href;
473 aURI = OUString(reinterpret_cast<char const *>(pHref), strlen(reinterpret_cast<char const *>(pHref)), RTL_TEXTENCODING_UTF8);
475 return aURI;
479 The node immediately following this node.
481 Reference< XNode > SAL_CALL CNode::getNextSibling()
483 ::osl::MutexGuard const g(m_rMutex);
485 if (nullptr == m_aNodePtr) {
486 return nullptr;
488 Reference< XNode > const xNode(
489 GetOwnerDocument().GetCNode(m_aNodePtr->next).get());
490 return xNode;
494 The name of this node, depending on its type; see the table above.
496 OUString SAL_CALL CNode::getNodeName()
499 Interface nodeName nodeValue attributes
500 --------------------------------------------------------------------------------------
501 Attr name of attribute value of attribute null
502 CDATASection "#cdata-section" content of the CDATA Section null
503 Comment "#comment" content of the comment null
504 Document "#document" null null
505 DocumentFragment "#document-fragment" null null
506 DocumentType document type name null null
507 Element tag name null NamedNodeMap
508 Entity entity name null null
509 EntityReference name of entity null null
510 referenced
511 Notation notation name null null
512 Processing\ target entire content excluding null
513 Instruction the target
514 Text "#text" content of the text node null
516 return OUString();
520 A code representing the type of the underlying object, as defined above.
522 NodeType SAL_CALL CNode::getNodeType()
524 ::osl::MutexGuard const g(m_rMutex);
526 return m_aNodeType;
530 The value of this node, depending on its type; see the table above.
532 OUString SAL_CALL CNode::getNodeValue()
534 return OUString();
538 The Document object associated with this node.
540 Reference< XDocument > SAL_CALL CNode::getOwnerDocument()
542 ::osl::MutexGuard const g(m_rMutex);
544 if (nullptr == m_aNodePtr) {
545 return nullptr;
547 Reference< XDocument > const xDoc(& GetOwnerDocument());
548 return xDoc;
552 The parent of this node.
554 Reference< XNode > SAL_CALL CNode::getParentNode()
556 ::osl::MutexGuard const g(m_rMutex);
558 if (nullptr == m_aNodePtr) {
559 return nullptr;
561 Reference< XNode > const xNode(
562 GetOwnerDocument().GetCNode(m_aNodePtr->parent).get());
563 return xNode;
567 The namespace prefix of this node, or null if it is unspecified.
569 OUString SAL_CALL CNode::getPrefix()
571 ::osl::MutexGuard const g(m_rMutex);
573 OUString aPrefix;
574 if (m_aNodePtr != nullptr &&
575 (m_aNodePtr->type == XML_ELEMENT_NODE || m_aNodePtr->type == XML_ATTRIBUTE_NODE) &&
576 m_aNodePtr->ns != nullptr)
578 const xmlChar* pPrefix = m_aNodePtr->ns->prefix;
579 if( pPrefix != nullptr )
580 aPrefix = OUString(reinterpret_cast<char const *>(pPrefix), strlen(reinterpret_cast<char const *>(pPrefix)), RTL_TEXTENCODING_UTF8);
582 return aPrefix;
587 The node immediately preceding this node.
589 Reference< XNode > SAL_CALL CNode::getPreviousSibling()
591 ::osl::MutexGuard const g(m_rMutex);
593 if (nullptr == m_aNodePtr) {
594 return nullptr;
596 Reference< XNode > const xNode(
597 GetOwnerDocument().GetCNode(m_aNodePtr->prev).get());
598 return xNode;
602 Returns whether this node (if it is an element) has any attributes.
604 sal_Bool SAL_CALL CNode::hasAttributes()
606 ::osl::MutexGuard const g(m_rMutex);
608 return (m_aNodePtr != nullptr && m_aNodePtr->properties != nullptr);
612 Returns whether this node has any children.
614 sal_Bool SAL_CALL CNode::hasChildNodes()
616 ::osl::MutexGuard const g(m_rMutex);
618 return (m_aNodePtr != nullptr && m_aNodePtr->children != nullptr);
622 Inserts the node newChild before the existing child node refChild.
624 Reference< XNode > SAL_CALL CNode::insertBefore(
625 const Reference< XNode >& newChild, const Reference< XNode >& refChild)
627 if (!newChild.is() || !refChild.is()) { throw RuntimeException(); }
629 if (newChild->getOwnerDocument() != getOwnerDocument()) {
630 DOMException e;
631 e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
632 throw e;
634 if (refChild->getParentNode() != Reference< XNode >(this)) {
635 DOMException e;
636 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
637 throw e;
640 ::osl::ClearableMutexGuard guard(m_rMutex);
642 CNode *const pNewNode(comphelper::getUnoTunnelImplementation<CNode>(newChild));
643 CNode *const pRefNode(comphelper::getUnoTunnelImplementation<CNode>(refChild));
644 if (!pNewNode || !pRefNode) { throw RuntimeException(); }
645 xmlNodePtr const pNewChild(pNewNode->GetNodePtr());
646 xmlNodePtr const pRefChild(pRefNode->GetNodePtr());
647 if (!pNewChild || !pRefChild) { throw RuntimeException(); }
649 if (pNewChild == m_aNodePtr) {
650 DOMException e;
651 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
652 throw e;
654 // already has parent
655 if (pNewChild->parent != nullptr)
657 DOMException e;
658 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
659 throw e;
661 if (!IsChildTypeAllowed(pNewNode->m_aNodeType)) {
662 DOMException e;
663 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
664 throw e;
667 // attributes are unordered anyway, so just do appendChild
668 if (XML_ATTRIBUTE_NODE == pNewChild->type) {
669 guard.clear();
670 return appendChild(newChild);
673 xmlNodePtr cur = m_aNodePtr->children;
675 //search child before which to insert
676 while (cur != nullptr)
678 if (cur == pRefChild) {
679 // insert before
680 pNewChild->next = cur;
681 pNewChild->prev = cur->prev;
682 cur->prev = pNewChild;
683 if (pNewChild->prev != nullptr) {
684 pNewChild->prev->next = pNewChild;
686 pNewChild->parent = cur->parent;
687 if (pNewChild->parent->children == cur) {
688 pNewChild->parent->children = pNewChild;
690 // do not update parent->last here!
691 pNewNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
692 break;
694 cur = cur->next;
696 return refChild;
700 Tests whether the DOM implementation implements a specific feature and
701 that feature is supported by this node.
703 sal_Bool SAL_CALL CNode::isSupported(const OUString& /*feature*/, const OUString& /*ver*/)
705 OSL_ENSURE(false, "CNode::isSupported: not implemented (#i113683#)");
706 return false;
710 Puts all Text nodes in the full depth of the sub-tree underneath this
711 Node, including attribute nodes, into a "normal" form where only structure
712 (e.g., elements, comments, processing instructions, CDATA sections, and
713 entity references) separates Text nodes, i.e., there are neither adjacent
714 Text nodes nor empty Text nodes.
716 void SAL_CALL CNode::normalize()
718 //XXX combine adjacent text nodes and remove empty ones
719 OSL_ENSURE(false, "CNode::normalize: not implemented (#i113683#)");
723 Removes the child node indicated by oldChild from the list of children,
724 and returns it.
726 Reference< XNode > SAL_CALL
727 CNode::removeChild(const Reference< XNode >& xOldChild)
729 if (!xOldChild.is()) {
730 throw RuntimeException();
733 if (xOldChild->getOwnerDocument() != getOwnerDocument()) {
734 DOMException e;
735 e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
736 throw e;
738 if (xOldChild->getParentNode() != Reference< XNode >(this)) {
739 DOMException e;
740 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
741 throw e;
744 ::osl::ClearableMutexGuard guard(m_rMutex);
746 if (!m_aNodePtr) { throw RuntimeException(); }
748 Reference<XNode> xReturn( xOldChild );
750 ::rtl::Reference<CNode> const pOld(comphelper::getUnoTunnelImplementation<CNode>(xOldChild));
751 if (!pOld.is()) { throw RuntimeException(); }
752 xmlNodePtr const old = pOld->GetNodePtr();
753 if (!old) { throw RuntimeException(); }
755 if( old->type == XML_ATTRIBUTE_NODE )
757 xmlAttrPtr pAttr = reinterpret_cast<xmlAttrPtr>(old);
758 xmlRemoveProp( pAttr );
759 pOld->invalidate(); // freed by xmlRemoveProp
760 xReturn.clear();
762 else
764 xmlUnlinkNode(old);
765 pOld->m_bUnlinked = true;
768 /*DOMNodeRemoved
769 * Fired when a node is being removed from its parent node.
770 * This event is dispatched before the node is removed from the tree.
771 * The target of this event is the node being removed.
772 * Bubbles: Yes
773 * Cancelable: No
774 * Context Info: relatedNode holds the parent node
776 Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
777 Reference< XMutationEvent > event(docevent->createEvent(
778 "DOMNodeRemoved"), UNO_QUERY);
779 event->initMutationEvent("DOMNodeRemoved",
780 true,
781 false,
782 this,
783 OUString(), OUString(), OUString(), AttrChangeType(0) );
785 // the following dispatch functions use only UNO interfaces
786 // and call event listeners, so release mutex to prevent deadlocks.
787 guard.clear();
789 dispatchEvent(event);
790 // subtree modified for this node
791 dispatchSubtreeModified();
793 return xReturn;
797 Replaces the child node oldChild with newChild in the list of children,
798 and returns the oldChild node.
800 Reference< XNode > SAL_CALL CNode::replaceChild(
801 Reference< XNode > const& xNewChild,
802 Reference< XNode > const& xOldChild)
804 if (!xOldChild.is() || !xNewChild.is()) {
805 throw RuntimeException();
808 if (xNewChild->getOwnerDocument() != getOwnerDocument()) {
809 DOMException e;
810 e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
811 throw e;
813 if (xOldChild->getParentNode() != Reference< XNode >(this)) {
814 DOMException e;
815 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
816 throw e;
819 ::osl::ClearableMutexGuard guard(m_rMutex);
821 ::rtl::Reference<CNode> const pOldNode(
822 comphelper::getUnoTunnelImplementation<CNode>(xOldChild));
823 ::rtl::Reference<CNode> const pNewNode(
824 comphelper::getUnoTunnelImplementation<CNode>(xNewChild));
825 if (!pOldNode.is() || !pNewNode.is()) { throw RuntimeException(); }
826 xmlNodePtr const pOld = pOldNode->GetNodePtr();
827 xmlNodePtr const pNew = pNewNode->GetNodePtr();
828 if (!pOld || !pNew) { throw RuntimeException(); }
830 if (pNew == m_aNodePtr) {
831 DOMException e;
832 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
833 throw e;
835 // already has parent
836 if (pNew->parent != nullptr) {
837 DOMException e;
838 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
839 throw e;
841 if (!IsChildTypeAllowed(pNewNode->m_aNodeType)) {
842 DOMException e;
843 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
844 throw e;
847 if( pOld->type == XML_ATTRIBUTE_NODE )
849 // can only replace attribute with attribute
850 if ( pOld->type != pNew->type )
852 DOMException e;
853 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
854 throw e;
857 xmlAttrPtr pAttr = reinterpret_cast<xmlAttrPtr>(pOld);
858 xmlRemoveProp( pAttr );
859 pOldNode->invalidate(); // freed by xmlRemoveProp
860 appendChild(xNewChild);
862 else
865 xmlNodePtr cur = m_aNodePtr->children;
866 //find old node in child list
867 while (cur != nullptr)
869 if(cur == pOld)
871 // exchange nodes
872 pNew->prev = pOld->prev;
873 if (pNew->prev != nullptr)
874 pNew->prev->next = pNew;
875 pNew->next = pOld->next;
876 if (pNew->next != nullptr)
877 pNew->next->prev = pNew;
878 pNew->parent = pOld->parent;
879 assert(pNew->parent && "coverity[var_deref_op] pNew->parent cannot be NULL here");
880 if(pNew->parent->children == pOld)
881 pNew->parent->children = pNew;
882 if(pNew->parent->last == pOld)
883 pNew->parent->last = pNew;
884 pOld->next = nullptr;
885 pOld->prev = nullptr;
886 pOld->parent = nullptr;
887 pOldNode->m_bUnlinked = true;
888 pNewNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
890 cur = cur->next;
894 guard.clear(); // release for calling event handlers
895 dispatchSubtreeModified();
897 return xOldChild;
900 void CNode::dispatchSubtreeModified()
902 // only uses UNO interfaces => needs no mutex
904 // dispatch DOMSubtreeModified
905 // target is _this_ node
906 Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
907 Reference< XMutationEvent > event(docevent->createEvent(
908 "DOMSubtreeModified"), UNO_QUERY);
909 event->initMutationEvent(
910 "DOMSubtreeModified", true,
911 false, Reference< XNode >(),
912 OUString(), OUString(), OUString(), AttrChangeType(0) );
913 dispatchEvent(event);
917 The value of this node, depending on its type; see the table above.
919 void SAL_CALL CNode::setNodeValue(const OUString& /*nodeValue*/)
921 // use specific node implementation
922 // if we end up down here, something went wrong
923 DOMException e;
924 e.Code = DOMExceptionType_NO_MODIFICATION_ALLOWED_ERR;
925 throw e;
929 The namespace prefix of this node, or null if it is unspecified.
931 void SAL_CALL CNode::setPrefix(const OUString& prefix)
933 ::osl::MutexGuard const g(m_rMutex);
935 if ((nullptr == m_aNodePtr) ||
936 ((m_aNodePtr->type != XML_ELEMENT_NODE) &&
937 (m_aNodePtr->type != XML_ATTRIBUTE_NODE)))
939 DOMException e;
940 e.Code = DOMExceptionType_NO_MODIFICATION_ALLOWED_ERR;
941 throw e;
943 OString o1 = OUStringToOString(prefix, RTL_TEXTENCODING_UTF8);
944 xmlChar const *pBuf = reinterpret_cast<xmlChar const *>(o1.getStr());
945 if (m_aNodePtr != nullptr && m_aNodePtr->ns != nullptr)
947 xmlFree(const_cast<xmlChar *>(m_aNodePtr->ns->prefix));
948 m_aNodePtr->ns->prefix = xmlStrdup(pBuf);
953 // --- XEventTarget
954 void SAL_CALL CNode::addEventListener(const OUString& eventType,
955 const Reference< css::xml::dom::events::XEventListener >& listener,
956 sal_Bool useCapture)
958 ::osl::MutexGuard const g(m_rMutex);
960 CDocument & rDocument(GetOwnerDocument());
961 events::CEventDispatcher & rDispatcher(rDocument.GetEventDispatcher());
962 rDispatcher.addListener(m_aNodePtr, eventType, listener, useCapture);
965 void SAL_CALL CNode::removeEventListener(const OUString& eventType,
966 const Reference< css::xml::dom::events::XEventListener >& listener,
967 sal_Bool useCapture)
969 ::osl::MutexGuard const g(m_rMutex);
971 CDocument & rDocument(GetOwnerDocument());
972 events::CEventDispatcher & rDispatcher(rDocument.GetEventDispatcher());
973 rDispatcher.removeListener(m_aNodePtr, eventType, listener, useCapture);
976 sal_Bool SAL_CALL CNode::dispatchEvent(const Reference< XEvent >& evt)
978 CDocument * pDocument;
979 events::CEventDispatcher * pDispatcher;
980 xmlNodePtr pNode;
982 ::osl::MutexGuard const g(m_rMutex);
984 pDocument = & GetOwnerDocument();
985 pDispatcher = & pDocument->GetEventDispatcher();
986 pNode = m_aNodePtr;
988 // this calls event listeners, do not call with locked mutex
989 pDispatcher->dispatchEvent(*pDocument, m_rMutex, pNode, this, evt);
990 return true;
993 ::sal_Int64 SAL_CALL
994 CNode::getSomething(Sequence< ::sal_Int8 > const& rId)
996 if (isUnoTunnelId<CNode>(rId))
998 return ::sal::static_int_cast< sal_Int64 >(
999 reinterpret_cast< sal_IntPtr >(this) );
1001 return 0;
1005 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */