Version 6.1.4.1, tag libreoffice-6.1.4.1
[LibreOffice.git] / unoxml / source / dom / node.cxx
blob0769c2e5b5ab72f5af12d3dc2add33ed62443834
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>
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>
43 #include <mutationevent.hxx>
45 using namespace css;
46 using namespace css::uno;
47 using namespace css::xml::dom;
48 using namespace css::xml::dom::events;
49 using namespace css::xml::sax;
51 namespace
53 class theCNodeUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theCNodeUnoTunnelId > {};
56 namespace DOM
58 void pushContext(Context& io_rContext)
60 // Explicitly use a temp. variable.
61 // Windows/VC++ seems to mess up if .back() is directly passed as
62 // parameter. i.e. Don't use push_back( .back() );
63 Context::NamespaceVectorType::value_type aVal = io_rContext.maNamespaces.back();
64 io_rContext.maNamespaces.push_back( aVal );
67 void popContext(Context& io_rContext)
69 io_rContext.maNamespaces.pop_back();
72 void addNamespaces(Context& io_rContext, xmlNodePtr pNode)
74 // add node's namespaces to current context
75 for (xmlNsPtr pNs = pNode->nsDef; pNs != nullptr; pNs = pNs->next) {
76 const xmlChar *pPrefix = pNs->prefix;
77 // prefix can be NULL when xmlns attribute is empty (xmlns="")
78 OString prefix(reinterpret_cast<const sal_Char*>(pPrefix),
79 pPrefix ? strlen(reinterpret_cast<const char*>(pPrefix)) : 0);
80 const xmlChar *pHref = pNs->href;
81 OUString val(reinterpret_cast<const sal_Char*>(pHref),
82 strlen(reinterpret_cast<const char*>(pHref)),
83 RTL_TEXTENCODING_UTF8);
85 Context::NamespaceMapType::iterator aIter=
86 io_rContext.maNamespaceMap.find(val);
87 if( aIter != io_rContext.maNamespaceMap.end() )
89 Context::Namespace aNS;
90 aNS.maPrefix = prefix;
91 aNS.mnToken = aIter->second;
92 aNS.maNamespaceURL = val;
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 CNode *
172 CNode::GetImplementation(uno::Reference<uno::XInterface> const& xNode)
174 uno::Reference<lang::XUnoTunnel> const xUnoTunnel(xNode, UNO_QUERY);
175 if (!xUnoTunnel.is()) { return nullptr; }
176 CNode *const pCNode( reinterpret_cast< CNode* >(
177 ::sal::static_int_cast< sal_IntPtr >(
178 xUnoTunnel->getSomething(theCNodeUnoTunnelId::get().getSeq()))));
179 return pCNode;
182 CDocument & CNode::GetOwnerDocument()
184 OSL_ASSERT(m_xDocument.is());
185 return *m_xDocument; // needs overriding in CDocument!
189 static void lcl_nsexchange(
190 xmlNodePtr const aNode, xmlNsPtr const oldNs, xmlNsPtr const newNs)
192 // recursively exchange any references to oldNs with references to newNs
193 xmlNodePtr cur = aNode;
194 while (cur != nullptr)
196 if (cur->ns == oldNs)
197 cur->ns = newNs;
198 if (cur->type == XML_ELEMENT_NODE)
200 xmlAttrPtr curAttr = cur->properties;
201 while(curAttr != nullptr)
203 if (curAttr->ns == oldNs)
204 curAttr->ns = newNs;
205 curAttr = curAttr->next;
207 lcl_nsexchange(cur->children, oldNs, newNs);
209 cur = cur->next;
213 /*static*/ void nscleanup(const xmlNodePtr aNode, const xmlNodePtr aParent)
215 xmlNodePtr cur = aNode;
217 //handle attributes
218 if (cur != nullptr && cur->type == XML_ELEMENT_NODE)
220 xmlAttrPtr curAttr = cur->properties;
221 while(curAttr != nullptr)
223 if (curAttr->ns != nullptr)
225 xmlNsPtr ns = xmlSearchNs(cur->doc, aParent, curAttr->ns->prefix);
226 if (ns != nullptr)
227 curAttr->ns = ns;
229 curAttr = curAttr->next;
233 while (cur != nullptr)
235 nscleanup(cur->children, cur);
236 if (cur->ns != nullptr)
238 xmlNsPtr ns = xmlSearchNs(cur->doc, aParent, cur->ns->prefix);
239 if (ns != nullptr && ns != cur->ns && strcmp(reinterpret_cast<char const *>(ns->href), reinterpret_cast<char const *>(cur->ns->href))==0)
241 xmlNsPtr curDef = cur->nsDef;
242 xmlNsPtr *refp = &(cur->nsDef); // insert point
243 while (curDef != nullptr)
245 ns = xmlSearchNs(cur->doc, aParent, curDef->prefix);
246 if (ns != nullptr && ns != curDef && strcmp(reinterpret_cast<char const *>(ns->href), reinterpret_cast<char const *>(curDef->href))==0)
248 // reconnect ns pointers in sub-tree to newly found ns before
249 // removing redundant nsdecl to prevent dangling pointers.
250 lcl_nsexchange(cur, curDef, ns);
251 *refp = curDef->next;
252 xmlFreeNs(curDef);
253 curDef = *refp;
254 } else {
255 refp = &(curDef->next);
256 curDef = curDef->next;
261 cur = cur->next;
265 void CNode::saxify(const Reference< XDocumentHandler >& i_xHandler)
267 if (!i_xHandler.is()) throw RuntimeException();
268 // default: do nothing
271 void CNode::fastSaxify(Context& io_rContext)
273 if (!io_rContext.mxDocHandler.is()) throw RuntimeException();
274 // default: do nothing
277 bool CNode::IsChildTypeAllowed(NodeType const /*nodeType*/)
279 // default: no children allowed
280 return false;
284 Adds the node newChild to the end of the list of children of this node.
286 Reference< XNode > SAL_CALL CNode::appendChild(
287 Reference< XNode > const& xNewChild)
289 ::osl::ClearableMutexGuard guard(m_rMutex);
291 if (nullptr == m_aNodePtr) { return nullptr; }
293 CNode *const pNewChild(CNode::GetImplementation(xNewChild));
294 if (!pNewChild) { throw RuntimeException(); }
295 xmlNodePtr const cur = pNewChild->GetNodePtr();
296 if (!cur) { throw RuntimeException(); }
298 // error checks:
299 // from other document
300 if (cur->doc != m_aNodePtr->doc) {
301 DOMException e;
302 e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
303 throw e;
305 // same node
306 if (cur == m_aNodePtr) {
307 DOMException e;
308 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
309 throw e;
311 if (cur->parent != nullptr) {
312 DOMException e;
313 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
314 throw e;
316 if (!IsChildTypeAllowed(pNewChild->m_aNodeType)) {
317 DOMException e;
318 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
319 throw e;
322 // check whether this is an attribute node; it needs special handling
323 xmlNodePtr res = nullptr;
324 if (cur->type == XML_ATTRIBUTE_NODE)
326 xmlChar const*const pChildren((cur->children)
327 ? cur->children->content
328 : reinterpret_cast<xmlChar const*>(""));
329 CAttr *const pCAttr(dynamic_cast<CAttr *>(pNewChild));
330 if (!pCAttr) { throw RuntimeException(); }
331 xmlNsPtr const pNs( pCAttr->GetNamespace(m_aNodePtr) );
332 if (pNs) {
333 res = reinterpret_cast<xmlNodePtr>(
334 xmlNewNsProp(m_aNodePtr, pNs, cur->name, pChildren));
335 } else {
336 res = reinterpret_cast<xmlNodePtr>(
337 xmlNewProp(m_aNodePtr, cur->name, pChildren));
340 else
342 res = xmlAddChild(m_aNodePtr, cur);
344 // libxml can do optimization when appending nodes.
345 // if res != cur, something was optimized and the newchild-wrapper
346 // should be updated
347 if (res && (cur != res)) {
348 pNewChild->invalidate(); // cur has been freed
352 if (!res) { return nullptr; }
354 // use custom ns cleanup instead of
355 // xmlReconciliateNs(m_aNodePtr->doc, m_aNodePtr);
356 // because that will not remove unneeded ns decls
357 nscleanup(res, m_aNodePtr);
359 ::rtl::Reference<CNode> const pNode = GetOwnerDocument().GetCNode(res);
361 if (!pNode.is()) { return nullptr; }
363 // dispatch DOMNodeInserted event, target is the new node
364 // this node is the related node
365 // does bubble
366 pNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
367 Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
368 Reference< XMutationEvent > event(docevent->createEvent(
369 "DOMNodeInserted"), UNO_QUERY);
370 event->initMutationEvent("DOMNodeInserted", true, false, this,
371 OUString(), OUString(), OUString(), AttrChangeType(0) );
373 // the following dispatch functions use only UNO interfaces
374 // and call event listeners, so release mutex to prevent deadlocks.
375 guard.clear();
377 dispatchEvent(event);
378 // dispatch subtree modified for this node
379 dispatchSubtreeModified();
381 return pNode.get();
385 Returns a duplicate of this node, i.e., serves as a generic copy
386 constructor for nodes.
388 Reference< XNode > SAL_CALL CNode::cloneNode(sal_Bool bDeep)
390 ::osl::MutexGuard const g(m_rMutex);
392 if (nullptr == m_aNodePtr) {
393 return nullptr;
395 ::rtl::Reference<CNode> const pNode = GetOwnerDocument().GetCNode(
396 xmlCopyNode(m_aNodePtr, bDeep ? 1 : 0));
397 if (!pNode.is()) { return nullptr; }
398 pNode->m_bUnlinked = true; // not linked yet
399 return pNode.get();
403 A NamedNodeMap containing the attributes of this node (if it is an Element)
404 or null otherwise.
406 Reference< XNamedNodeMap > SAL_CALL CNode::getAttributes()
408 // return empty reference; only element node may override this impl
409 return Reference< XNamedNodeMap>();
413 A NodeList that contains all children of this node.
415 Reference< XNodeList > SAL_CALL CNode::getChildNodes()
417 ::osl::MutexGuard const g(m_rMutex);
419 if (nullptr == m_aNodePtr) {
420 return nullptr;
422 Reference< XNodeList > const xNodeList(new CChildList(this, m_rMutex));
423 return xNodeList;
427 The first child of this node.
429 Reference< XNode > SAL_CALL CNode::getFirstChild()
431 ::osl::MutexGuard const g(m_rMutex);
433 if (nullptr == m_aNodePtr) {
434 return nullptr;
436 Reference< XNode > const xNode(
437 GetOwnerDocument().GetCNode(m_aNodePtr->children).get());
438 return xNode;
442 The last child of this node.
444 Reference< XNode > SAL_CALL CNode::getLastChild()
446 ::osl::MutexGuard const g(m_rMutex);
448 if (nullptr == m_aNodePtr) {
449 return nullptr;
451 Reference< XNode > const xNode(
452 GetOwnerDocument().GetCNode(xmlGetLastChild(m_aNodePtr)).get());
453 return xNode;
457 Returns the local part of the qualified name of this node.
459 OUString SAL_CALL CNode::getLocalName()
461 // see CElement/CAttr
462 return OUString();
467 The namespace URI of this node, or null if it is unspecified.
469 OUString SAL_CALL CNode::getNamespaceURI()
471 ::osl::MutexGuard const g(m_rMutex);
473 OUString aURI;
474 if (m_aNodePtr != nullptr &&
475 (m_aNodePtr->type == XML_ELEMENT_NODE || m_aNodePtr->type == XML_ATTRIBUTE_NODE) &&
476 m_aNodePtr->ns != nullptr)
478 const xmlChar* pHref = m_aNodePtr->ns->href;
479 aURI = OUString(reinterpret_cast<char const *>(pHref), strlen(reinterpret_cast<char const *>(pHref)), RTL_TEXTENCODING_UTF8);
481 return aURI;
485 The node immediately following this node.
487 Reference< XNode > SAL_CALL CNode::getNextSibling()
489 ::osl::MutexGuard const g(m_rMutex);
491 if (nullptr == m_aNodePtr) {
492 return nullptr;
494 Reference< XNode > const xNode(
495 GetOwnerDocument().GetCNode(m_aNodePtr->next).get());
496 return xNode;
500 The name of this node, depending on its type; see the table above.
502 OUString SAL_CALL CNode::getNodeName()
505 Interface nodeName nodeValue attributes
506 --------------------------------------------------------------------------------------
507 Attr name of attribute value of attribute null
508 CDATASection "#cdata-section" content of the CDATA Section null
509 Comment "#comment" content of the comment null
510 Document "#document" null null
511 DocumentFragment "#document-fragment" null null
512 DocumentType document type name null null
513 Element tag name null NamedNodeMap
514 Entity entity name null null
515 EntityReference name of entity null null
516 referenced
517 Notation notation name null null
518 Processing\ target entire content excluding null
519 Instruction the target
520 Text "#text" content of the text node null
522 return OUString();
526 A code representing the type of the underlying object, as defined above.
528 NodeType SAL_CALL CNode::getNodeType()
530 ::osl::MutexGuard const g(m_rMutex);
532 return m_aNodeType;
536 The value of this node, depending on its type; see the table above.
538 OUString SAL_CALL CNode::getNodeValue()
540 return OUString();
544 The Document object associated with this node.
546 Reference< XDocument > SAL_CALL CNode::getOwnerDocument()
548 ::osl::MutexGuard const g(m_rMutex);
550 if (nullptr == m_aNodePtr) {
551 return nullptr;
553 Reference< XDocument > const xDoc(& GetOwnerDocument());
554 return xDoc;
558 The parent of this node.
560 Reference< XNode > SAL_CALL CNode::getParentNode()
562 ::osl::MutexGuard const g(m_rMutex);
564 if (nullptr == m_aNodePtr) {
565 return nullptr;
567 Reference< XNode > const xNode(
568 GetOwnerDocument().GetCNode(m_aNodePtr->parent).get());
569 return xNode;
573 The namespace prefix of this node, or null if it is unspecified.
575 OUString SAL_CALL CNode::getPrefix()
577 ::osl::MutexGuard const g(m_rMutex);
579 OUString aPrefix;
580 if (m_aNodePtr != nullptr &&
581 (m_aNodePtr->type == XML_ELEMENT_NODE || m_aNodePtr->type == XML_ATTRIBUTE_NODE) &&
582 m_aNodePtr->ns != nullptr)
584 const xmlChar* pPrefix = m_aNodePtr->ns->prefix;
585 if( pPrefix != nullptr )
586 aPrefix = OUString(reinterpret_cast<char const *>(pPrefix), strlen(reinterpret_cast<char const *>(pPrefix)), RTL_TEXTENCODING_UTF8);
588 return aPrefix;
593 The node immediately preceding this node.
595 Reference< XNode > SAL_CALL CNode::getPreviousSibling()
597 ::osl::MutexGuard const g(m_rMutex);
599 if (nullptr == m_aNodePtr) {
600 return nullptr;
602 Reference< XNode > const xNode(
603 GetOwnerDocument().GetCNode(m_aNodePtr->prev).get());
604 return xNode;
608 Returns whether this node (if it is an element) has any attributes.
610 sal_Bool SAL_CALL CNode::hasAttributes()
612 ::osl::MutexGuard const g(m_rMutex);
614 return (m_aNodePtr != nullptr && m_aNodePtr->properties != nullptr);
618 Returns whether this node has any children.
620 sal_Bool SAL_CALL CNode::hasChildNodes()
622 ::osl::MutexGuard const g(m_rMutex);
624 return (m_aNodePtr != nullptr && m_aNodePtr->children != nullptr);
628 Inserts the node newChild before the existing child node refChild.
630 Reference< XNode > SAL_CALL CNode::insertBefore(
631 const Reference< XNode >& newChild, const Reference< XNode >& refChild)
633 if (!newChild.is() || !refChild.is()) { throw RuntimeException(); }
635 if (newChild->getOwnerDocument() != getOwnerDocument()) {
636 DOMException e;
637 e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
638 throw e;
640 if (refChild->getParentNode() != Reference< XNode >(this)) {
641 DOMException e;
642 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
643 throw e;
646 ::osl::ClearableMutexGuard guard(m_rMutex);
648 CNode *const pNewNode(CNode::GetImplementation(newChild));
649 CNode *const pRefNode(CNode::GetImplementation(refChild));
650 if (!pNewNode || !pRefNode) { throw RuntimeException(); }
651 xmlNodePtr const pNewChild(pNewNode->GetNodePtr());
652 xmlNodePtr const pRefChild(pRefNode->GetNodePtr());
653 if (!pNewChild || !pRefChild) { throw RuntimeException(); }
655 if (pNewChild == m_aNodePtr) {
656 DOMException e;
657 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
658 throw e;
660 // already has parent
661 if (pNewChild->parent != nullptr)
663 DOMException e;
664 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
665 throw e;
667 if (!IsChildTypeAllowed(pNewNode->m_aNodeType)) {
668 DOMException e;
669 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
670 throw e;
673 // attributes are unordered anyway, so just do appendChild
674 if (XML_ATTRIBUTE_NODE == pNewChild->type) {
675 guard.clear();
676 return appendChild(newChild);
679 xmlNodePtr cur = m_aNodePtr->children;
681 //search child before which to insert
682 while (cur != nullptr)
684 if (cur == pRefChild) {
685 // insert before
686 pNewChild->next = cur;
687 pNewChild->prev = cur->prev;
688 cur->prev = pNewChild;
689 if (pNewChild->prev != nullptr) {
690 pNewChild->prev->next = pNewChild;
692 pNewChild->parent = cur->parent;
693 if (pNewChild->parent->children == cur) {
694 pNewChild->parent->children = pNewChild;
696 // do not update parent->last here!
697 pNewNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
698 break;
700 cur = cur->next;
702 return refChild;
706 Tests whether the DOM implementation implements a specific feature and
707 that feature is supported by this node.
709 sal_Bool SAL_CALL CNode::isSupported(const OUString& /*feature*/, const OUString& /*ver*/)
711 OSL_ENSURE(false, "CNode::isSupported: not implemented (#i113683#)");
712 return false;
716 Puts all Text nodes in the full depth of the sub-tree underneath this
717 Node, including attribute nodes, into a "normal" form where only structure
718 (e.g., elements, comments, processing instructions, CDATA sections, and
719 entity references) separates Text nodes, i.e., there are neither adjacent
720 Text nodes nor empty Text nodes.
722 void SAL_CALL CNode::normalize()
724 //XXX combine adjacent text nodes and remove empty ones
725 OSL_ENSURE(false, "CNode::normalize: not implemented (#i113683#)");
729 Removes the child node indicated by oldChild from the list of children,
730 and returns it.
732 Reference< XNode > SAL_CALL
733 CNode::removeChild(const Reference< XNode >& xOldChild)
735 if (!xOldChild.is()) {
736 throw RuntimeException();
739 if (xOldChild->getOwnerDocument() != getOwnerDocument()) {
740 DOMException e;
741 e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
742 throw e;
744 if (xOldChild->getParentNode() != Reference< XNode >(this)) {
745 DOMException e;
746 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
747 throw e;
750 ::osl::ClearableMutexGuard guard(m_rMutex);
752 if (!m_aNodePtr) { throw RuntimeException(); }
754 Reference<XNode> xReturn( xOldChild );
756 ::rtl::Reference<CNode> const pOld(CNode::GetImplementation(xOldChild));
757 if (!pOld.is()) { throw RuntimeException(); }
758 xmlNodePtr const old = pOld->GetNodePtr();
759 if (!old) { throw RuntimeException(); }
761 if( old->type == XML_ATTRIBUTE_NODE )
763 xmlAttrPtr pAttr = reinterpret_cast<xmlAttrPtr>(old);
764 xmlRemoveProp( pAttr );
765 pOld->invalidate(); // freed by xmlRemoveProp
766 xReturn.clear();
768 else
770 xmlUnlinkNode(old);
771 pOld->m_bUnlinked = true;
774 /*DOMNodeRemoved
775 * Fired when a node is being removed from its parent node.
776 * This event is dispatched before the node is removed from the tree.
777 * The target of this event is the node being removed.
778 * Bubbles: Yes
779 * Cancelable: No
780 * Context Info: relatedNode holds the parent node
782 Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
783 Reference< XMutationEvent > event(docevent->createEvent(
784 "DOMNodeRemoved"), UNO_QUERY);
785 event->initMutationEvent("DOMNodeRemoved",
786 true,
787 false,
788 this,
789 OUString(), OUString(), OUString(), AttrChangeType(0) );
791 // the following dispatch functions use only UNO interfaces
792 // and call event listeners, so release mutex to prevent deadlocks.
793 guard.clear();
795 dispatchEvent(event);
796 // subtree modified for this node
797 dispatchSubtreeModified();
799 return xReturn;
803 Replaces the child node oldChild with newChild in the list of children,
804 and returns the oldChild node.
806 Reference< XNode > SAL_CALL CNode::replaceChild(
807 Reference< XNode > const& xNewChild,
808 Reference< XNode > const& xOldChild)
810 if (!xOldChild.is() || !xNewChild.is()) {
811 throw RuntimeException();
814 if (xNewChild->getOwnerDocument() != getOwnerDocument()) {
815 DOMException e;
816 e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
817 throw e;
819 if (xOldChild->getParentNode() != Reference< XNode >(this)) {
820 DOMException e;
821 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
822 throw e;
825 ::osl::ClearableMutexGuard guard(m_rMutex);
827 ::rtl::Reference<CNode> const pOldNode(
828 CNode::GetImplementation(xOldChild));
829 ::rtl::Reference<CNode> const pNewNode(
830 CNode::GetImplementation(xNewChild));
831 if (!pOldNode.is() || !pNewNode.is()) { throw RuntimeException(); }
832 xmlNodePtr const pOld = pOldNode->GetNodePtr();
833 xmlNodePtr const pNew = pNewNode->GetNodePtr();
834 if (!pOld || !pNew) { throw RuntimeException(); }
836 if (pNew == m_aNodePtr) {
837 DOMException e;
838 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
839 throw e;
841 // already has parent
842 if (pNew->parent != nullptr) {
843 DOMException e;
844 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
845 throw e;
847 if (!IsChildTypeAllowed(pNewNode->m_aNodeType)) {
848 DOMException e;
849 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
850 throw e;
853 if( pOld->type == XML_ATTRIBUTE_NODE )
855 // can only replace attribute with attribute
856 if ( pOld->type != pNew->type )
858 DOMException e;
859 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
860 throw e;
863 xmlAttrPtr pAttr = reinterpret_cast<xmlAttrPtr>(pOld);
864 xmlRemoveProp( pAttr );
865 pOldNode->invalidate(); // freed by xmlRemoveProp
866 appendChild(xNewChild);
868 else
871 xmlNodePtr cur = m_aNodePtr->children;
872 //find old node in child list
873 while (cur != nullptr)
875 if(cur == pOld)
877 // exchange nodes
878 pNew->prev = pOld->prev;
879 if (pNew->prev != nullptr)
880 pNew->prev->next = pNew;
881 pNew->next = pOld->next;
882 if (pNew->next != nullptr)
883 pNew->next->prev = pNew;
884 pNew->parent = pOld->parent;
885 assert(pNew->parent && "coverity[var_deref_op] pNew->parent cannot be NULL here");
886 if(pNew->parent->children == pOld)
887 pNew->parent->children = pNew;
888 if(pNew->parent->last == pOld)
889 pNew->parent->last = pNew;
890 pOld->next = nullptr;
891 pOld->prev = nullptr;
892 pOld->parent = nullptr;
893 pOldNode->m_bUnlinked = true;
894 pNewNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
896 cur = cur->next;
900 guard.clear(); // release for calling event handlers
901 dispatchSubtreeModified();
903 return xOldChild;
906 void CNode::dispatchSubtreeModified()
908 // only uses UNO interfaces => needs no mutex
910 // dispatch DOMSubtreeModified
911 // target is _this_ node
912 Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
913 Reference< XMutationEvent > event(docevent->createEvent(
914 "DOMSubtreeModified"), UNO_QUERY);
915 event->initMutationEvent(
916 "DOMSubtreeModified", true,
917 false, Reference< XNode >(),
918 OUString(), OUString(), OUString(), AttrChangeType(0) );
919 dispatchEvent(event);
923 The value of this node, depending on its type; see the table above.
925 void SAL_CALL CNode::setNodeValue(const OUString& /*nodeValue*/)
927 // use specific node implementation
928 // if we end up down here, something went wrong
929 DOMException e;
930 e.Code = DOMExceptionType_NO_MODIFICATION_ALLOWED_ERR;
931 throw e;
935 The namespace prefix of this node, or null if it is unspecified.
937 void SAL_CALL CNode::setPrefix(const OUString& prefix)
939 ::osl::MutexGuard const g(m_rMutex);
941 if ((nullptr == m_aNodePtr) ||
942 ((m_aNodePtr->type != XML_ELEMENT_NODE) &&
943 (m_aNodePtr->type != XML_ATTRIBUTE_NODE)))
945 DOMException e;
946 e.Code = DOMExceptionType_NO_MODIFICATION_ALLOWED_ERR;
947 throw e;
949 OString o1 = OUStringToOString(prefix, RTL_TEXTENCODING_UTF8);
950 xmlChar const *pBuf = reinterpret_cast<xmlChar const *>(o1.getStr());
951 if (m_aNodePtr != nullptr && m_aNodePtr->ns != nullptr)
953 xmlFree(const_cast<xmlChar *>(m_aNodePtr->ns->prefix));
954 m_aNodePtr->ns->prefix = xmlStrdup(pBuf);
959 // --- XEventTarget
960 void SAL_CALL CNode::addEventListener(const OUString& eventType,
961 const Reference< css::xml::dom::events::XEventListener >& listener,
962 sal_Bool useCapture)
964 ::osl::MutexGuard const g(m_rMutex);
966 CDocument & rDocument(GetOwnerDocument());
967 events::CEventDispatcher & rDispatcher(rDocument.GetEventDispatcher());
968 rDispatcher.addListener(m_aNodePtr, eventType, listener, useCapture);
971 void SAL_CALL CNode::removeEventListener(const OUString& eventType,
972 const Reference< css::xml::dom::events::XEventListener >& listener,
973 sal_Bool useCapture)
975 ::osl::MutexGuard const g(m_rMutex);
977 CDocument & rDocument(GetOwnerDocument());
978 events::CEventDispatcher & rDispatcher(rDocument.GetEventDispatcher());
979 rDispatcher.removeListener(m_aNodePtr, eventType, listener, useCapture);
982 sal_Bool SAL_CALL CNode::dispatchEvent(const Reference< XEvent >& evt)
984 CDocument * pDocument;
985 events::CEventDispatcher * pDispatcher;
986 xmlNodePtr pNode;
988 ::osl::MutexGuard const g(m_rMutex);
990 pDocument = & GetOwnerDocument();
991 pDispatcher = & pDocument->GetEventDispatcher();
992 pNode = m_aNodePtr;
994 // this calls event listeners, do not call with locked mutex
995 pDispatcher->dispatchEvent(*pDocument, m_rMutex, pNode, this, evt);
996 return true;
999 ::sal_Int64 SAL_CALL
1000 CNode::getSomething(Sequence< ::sal_Int8 > const& rId)
1002 if ((rId.getLength() == 16) &&
1003 (0 == memcmp(theCNodeUnoTunnelId::get().getSeq().getConstArray(),
1004 rId.getConstArray(), 16)))
1006 return ::sal::static_int_cast< sal_Int64 >(
1007 reinterpret_cast< sal_IntPtr >(this) );
1009 return 0;
1013 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */