LibreOffice Module unoxml (master) 1
node.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <node.hxx>
21
22#include <string.h>
23
24#include <libxml/xmlstring.h>
25
26#include <algorithm>
27
28#include <osl/mutex.hxx>
29#include <osl/diagnose.h>
30#include <sal/log.hxx>
31
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>
35
37
38#include "document.hxx"
39#include "attr.hxx"
40#include "childlist.hxx"
41
42#include <eventdispatcher.hxx>
43
44using namespace css;
45using namespace css::uno;
46using namespace css::xml::dom;
47using namespace css::xml::dom::events;
48using namespace css::xml::sax;
49
50namespace DOM
51{
52 void pushContext(Context& io_rContext)
53 {
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 );
59 }
60
61 void popContext(Context& io_rContext)
62 {
63 io_rContext.maNamespaces.pop_back();
64 }
65
66 void addNamespaces(Context& io_rContext, xmlNodePtr pNode)
67 {
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);
78
79 Context::NamespaceMapType::iterator aIter=
80 io_rContext.maNamespaceMap.find(val);
81 if( aIter != io_rContext.maNamespaceMap.end() )
82 {
84 aNS.maPrefix = prefix;
85 aNS.mnToken = aIter->second;
86
87 io_rContext.maNamespaces.back().push_back(aNS);
88
89 SAL_INFO("unoxml", "Added with token " << aIter->second);
90 }
91 }
92 }
93
94 sal_Int32 getToken( const Context& rContext, const char* pToken )
95 {
96 const Sequence<sal_Int8> aSeq( reinterpret_cast<sal_Int8 const *>(pToken), strlen( pToken ) );
97 return rContext.mxTokenHandler->getTokenFromUTF8( aSeq );
98 }
99
100 sal_Int32 getTokenWithPrefix( const Context& rContext, const char* pPrefix, const char* pName )
101 {
102 sal_Int32 nNamespaceToken = FastToken::DONTKNOW;
103 OString prefix(pPrefix,
104 strlen(pPrefix));
105
106 SAL_INFO("unoxml", "getTokenWithPrefix(): prefix " << pPrefix << ", name " << pName);
107
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() )
113 {
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;
120 }
121
122 return nNamespaceToken;
123 }
124
125
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))
136 {
137 OSL_ASSERT(m_aNodePtr);
138 }
139
140 void CNode::invalidate()
141 {
142 //remove from list if this wrapper goes away
143 if (m_aNodePtr != nullptr && m_xDocument.is()) {
144 m_xDocument->RemoveCNode(m_aNodePtr, this);
145 }
146 // #i113663#: unlinked nodes will not be freed by xmlFreeDoc
147 if (m_bUnlinked) {
148 xmlFreeNode(m_aNodePtr);
149 }
150 m_aNodePtr = nullptr;
151 }
152
153 CNode::~CNode()
154 {
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
161 }
162 }
163
164 CDocument & CNode::GetOwnerDocument()
165 {
166 OSL_ASSERT(m_xDocument.is());
167 return *m_xDocument; // needs overriding in CDocument!
168 }
169
170
171 static void lcl_nsexchange(
172 xmlNodePtr const aNode, xmlNsPtr const oldNs, xmlNsPtr const newNs)
173 {
174 // recursively exchange any references to oldNs with references to newNs
175 xmlNodePtr cur = aNode;
176 while (cur != nullptr)
177 {
178 if (cur->ns == oldNs)
179 cur->ns = newNs;
180 if (cur->type == XML_ELEMENT_NODE)
181 {
182 xmlAttrPtr curAttr = cur->properties;
183 while(curAttr != nullptr)
184 {
185 if (curAttr->ns == oldNs)
186 curAttr->ns = newNs;
187 curAttr = curAttr->next;
188 }
189 lcl_nsexchange(cur->children, oldNs, newNs);
190 }
191 cur = cur->next;
192 }
193 }
194
195 /*static*/ void nscleanup(const xmlNodePtr aNode, const xmlNodePtr aParent)
196 {
197 xmlNodePtr cur = aNode;
198
199 //handle attributes
200 if (cur != nullptr && cur->type == XML_ELEMENT_NODE)
201 {
202 xmlAttrPtr curAttr = cur->properties;
203 while(curAttr != nullptr)
204 {
205 if (curAttr->ns != nullptr)
206 {
207 xmlNsPtr ns = xmlSearchNs(cur->doc, aParent, curAttr->ns->prefix);
208 if (ns != nullptr)
209 curAttr->ns = ns;
210 }
211 curAttr = curAttr->next;
212 }
213 }
214
215 while (cur != nullptr)
216 {
217 nscleanup(cur->children, cur);
218 if (cur->ns != nullptr)
219 {
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)
222 {
223 xmlNsPtr curDef = cur->nsDef;
224 xmlNsPtr *refp = &(cur->nsDef); // insert point
225 while (curDef != nullptr)
226 {
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)
229 {
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;
239 }
240 }
241 }
242 }
243 cur = cur->next;
244 }
245 }
246
247 void CNode::saxify(const Reference< XDocumentHandler >& i_xHandler)
248 {
249 if (!i_xHandler.is()) throw RuntimeException();
250 // default: do nothing
251 }
252
253 void CNode::fastSaxify(Context& io_rContext)
254 {
255 if (!io_rContext.mxDocHandler.is()) throw RuntimeException();
256 // default: do nothing
257 }
258
259 bool CNode::IsChildTypeAllowed(NodeType const /*nodeType*/, NodeType const*const)
260 {
261 // default: no children allowed
262 return false;
263 }
264
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;
270 }
271 }
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;
277 }
278 }
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;
284 }
285 }
286
290 Reference< XNode > SAL_CALL CNode::appendChild(
291 Reference< XNode > const& xNewChild)
292 {
293 ::osl::ClearableMutexGuard guard(m_rMutex);
294
295 if (nullptr == m_aNodePtr) { return nullptr; }
296
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(); }
301
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;
308 }
309 // same node
310 if (cur == m_aNodePtr) {
311 DOMException e;
312 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
313 throw e;
314 }
315 checkNoParent(cur);
316
317 if (!IsChildTypeAllowed(pNewChild->m_aNodeType, nullptr)) {
318 DOMException e;
319 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
320 throw e;
321 }
322
323 // check whether this is an attribute node; it needs special handling
324 xmlNodePtr res = nullptr;
325 if (cur->type == XML_ATTRIBUTE_NODE)
326 {
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));
339 }
340 }
341 else
342 {
343 res = xmlAddChild(m_aNodePtr, cur);
344
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
350 }
351 }
352
353 if (!res) { return nullptr; }
354
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);
359
360 ::rtl::Reference<CNode> const pNode = GetOwnerDocument().GetCNode(res);
361
362 if (!pNode.is()) { return nullptr; }
363
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) );
373
374 // the following dispatch functions use only UNO interfaces
375 // and call event listeners, so release mutex to prevent deadlocks.
376 guard.clear();
377
378 dispatchEvent(event);
379 // dispatch subtree modified for this node
380 dispatchSubtreeModified();
381
382 return pNode;
383 }
384
389 Reference< XNode > SAL_CALL CNode::cloneNode(sal_Bool bDeep)
390 {
391 ::osl::MutexGuard const g(m_rMutex);
392
393 if (nullptr == m_aNodePtr) {
394 return nullptr;
395 }
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;
401 }
402
407 Reference< XNamedNodeMap > SAL_CALL CNode::getAttributes()
408 {
409 // return empty reference; only element node may override this impl
410 return Reference< XNamedNodeMap>();
411 }
412
416 Reference< XNodeList > SAL_CALL CNode::getChildNodes()
417 {
418 ::osl::MutexGuard const g(m_rMutex);
419
420 if (nullptr == m_aNodePtr) {
421 return nullptr;
422 }
423 Reference< XNodeList > const xNodeList(new CChildList(this, m_rMutex));
424 return xNodeList;
425 }
426
430 Reference< XNode > SAL_CALL CNode::getFirstChild()
431 {
432 ::osl::MutexGuard const g(m_rMutex);
433
434 if (nullptr == m_aNodePtr) {
435 return nullptr;
436 }
437 return GetOwnerDocument().GetCNode(m_aNodePtr->children);
438 }
439
443 Reference< XNode > SAL_CALL CNode::getLastChild()
444 {
445 ::osl::MutexGuard const g(m_rMutex);
446
447 if (nullptr == m_aNodePtr) {
448 return nullptr;
449 }
450 return GetOwnerDocument().GetCNode(xmlGetLastChild(m_aNodePtr));
451 }
452
456 OUString SAL_CALL CNode::getLocalName()
457 {
458 // see CElement/CAttr
459 return OUString();
460 }
461
462
466 OUString SAL_CALL CNode::getNamespaceURI()
467 {
468 ::osl::MutexGuard const g(m_rMutex);
469
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)
474 {
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);
477 }
478 return aURI;
479 }
480
484 Reference< XNode > SAL_CALL CNode::getNextSibling()
485 {
486 ::osl::MutexGuard const g(m_rMutex);
487
488 if (nullptr == m_aNodePtr) {
489 return nullptr;
490 }
491 return GetOwnerDocument().GetCNode(m_aNodePtr->next);
492 }
493
497 OUString SAL_CALL CNode::getNodeName()
498 {
499 /*
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
516 */
517 return OUString();
518 }
519
523 NodeType SAL_CALL CNode::getNodeType()
524 {
525 ::osl::MutexGuard const g(m_rMutex);
526
527 return m_aNodeType;
528 }
529
533 OUString SAL_CALL CNode::getNodeValue()
534 {
535 return OUString();
536 }
537
541 Reference< XDocument > SAL_CALL CNode::getOwnerDocument()
542 {
543 ::osl::MutexGuard const g(m_rMutex);
544
545 if (nullptr == m_aNodePtr) {
546 return nullptr;
547 }
548 Reference< XDocument > const xDoc(& GetOwnerDocument());
549 return xDoc;
550 }
551
555 Reference< XNode > SAL_CALL CNode::getParentNode()
556 {
557 ::osl::MutexGuard const g(m_rMutex);
558
559 if (nullptr == m_aNodePtr) {
560 return nullptr;
561 }
562 return GetOwnerDocument().GetCNode(m_aNodePtr->parent);
563 }
564
568 OUString SAL_CALL CNode::getPrefix()
569 {
570 ::osl::MutexGuard const g(m_rMutex);
571
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)
576 {
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);
580 }
581 return aPrefix;
582
583 }
584
588 Reference< XNode > SAL_CALL CNode::getPreviousSibling()
589 {
590 ::osl::MutexGuard const g(m_rMutex);
591
592 if (nullptr == m_aNodePtr) {
593 return nullptr;
594 }
595 return GetOwnerDocument().GetCNode(m_aNodePtr->prev);
596 }
597
601 sal_Bool SAL_CALL CNode::hasAttributes()
602 {
603 ::osl::MutexGuard const g(m_rMutex);
604
605 return (m_aNodePtr != nullptr && m_aNodePtr->properties != nullptr);
606 }
607
611 sal_Bool SAL_CALL CNode::hasChildNodes()
612 {
613 ::osl::MutexGuard const g(m_rMutex);
614
615 return (m_aNodePtr != nullptr && m_aNodePtr->children != nullptr);
616 }
617
621 Reference< XNode > SAL_CALL CNode::insertBefore(
622 const Reference< XNode >& newChild, const Reference< XNode >& refChild)
623 {
624 if (!newChild.is() || !refChild.is()) { throw RuntimeException(); }
625
626 checkSameOwner(newChild);
627
628 if (refChild->getParentNode() != Reference< XNode >(this)) {
629 DOMException e;
630 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
631 throw e;
632 }
633
634 ::osl::ClearableMutexGuard guard(m_rMutex);
635
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(); }
642
643 if (pNewChild == m_aNodePtr) {
644 DOMException e;
645 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
646 throw e;
647 }
648 // already has parent
649 checkNoParent(pNewChild);
650
651 if (!IsChildTypeAllowed(pNewNode->m_aNodeType, nullptr)) {
652 DOMException e;
653 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
654 throw e;
655 }
656
657 // attributes are unordered anyway, so just do appendChild
658 if (XML_ATTRIBUTE_NODE == pNewChild->type) {
659 guard.clear();
660 return appendChild(newChild);
661 }
662
663 xmlNodePtr cur = m_aNodePtr->children;
664
665 //search child before which to insert
666 while (cur != nullptr)
667 {
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;
675 }
676 pNewChild->parent = cur->parent;
677 if (pNewChild->parent->children == cur) {
678 pNewChild->parent->children = pNewChild;
679 }
680 // do not update parent->last here!
681 pNewNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
682 break;
683 }
684 cur = cur->next;
685 }
686 return refChild;
687 }
688
693 sal_Bool SAL_CALL CNode::isSupported(const OUString& /*feature*/, const OUString& /*ver*/)
694 {
695 OSL_ENSURE(false, "CNode::isSupported: not implemented (#i113683#)");
696 return false;
697 }
698
706 void SAL_CALL CNode::normalize()
707 {
708 //XXX combine adjacent text nodes and remove empty ones
709 OSL_ENSURE(false, "CNode::normalize: not implemented (#i113683#)");
710 }
711
716 Reference< XNode > SAL_CALL
717 CNode::removeChild(const Reference< XNode >& xOldChild)
718 {
719 if (!xOldChild.is()) {
720 throw RuntimeException();
721 }
722
723 checkSameOwner(xOldChild);
724
725 if (xOldChild->getParentNode() != Reference< XNode >(this)) {
726 DOMException e;
727 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
728 throw e;
729 }
730
731 ::osl::ClearableMutexGuard guard(m_rMutex);
732
733 if (!m_aNodePtr) { throw RuntimeException(); }
734
735 Reference<XNode> xReturn( xOldChild );
736
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(); }
741
742 if( old->type == XML_ATTRIBUTE_NODE )
743 {
744 xmlAttrPtr pAttr = reinterpret_cast<xmlAttrPtr>(old);
745 xmlRemoveProp( pAttr );
746 pOld->invalidate(); // freed by xmlRemoveProp
747 xReturn.clear();
748 }
749 else
750 {
751 xmlUnlinkNode(old);
752 pOld->m_bUnlinked = true;
753 }
754
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
762 */
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) );
771
772 // the following dispatch functions use only UNO interfaces
773 // and call event listeners, so release mutex to prevent deadlocks.
774 guard.clear();
775
776 dispatchEvent(event);
777 // subtree modified for this node
778 dispatchSubtreeModified();
779
780 return xReturn;
781 }
782
787 Reference< XNode > SAL_CALL CNode::replaceChild(
788 Reference< XNode > const& xNewChild,
789 Reference< XNode > const& xOldChild)
790 {
791 if (!xOldChild.is() || !xNewChild.is()) {
792 throw RuntimeException();
793 }
794
795 checkSameOwner(xNewChild);
796
797 if (xOldChild->getParentNode() != Reference< XNode >(this)) {
798 DOMException e;
799 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
800 throw e;
801 }
802
803 ::osl::ClearableMutexGuard guard(m_rMutex);
804
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(); }
811
812 if (pNew == m_aNodePtr) {
813 DOMException e;
814 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
815 throw e;
816 }
817 // already has parent
818 checkNoParent(pNew);
819
820 if (!IsChildTypeAllowed(pNewNode->m_aNodeType, &pOldNode->m_aNodeType)) {
821 DOMException e;
822 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
823 throw e;
824 }
825
826 if( pOld->type == XML_ATTRIBUTE_NODE )
827 {
828 // can only replace attribute with attribute
829 if ( pOld->type != pNew->type )
830 {
831 DOMException e;
832 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
833 throw e;
834 }
835
836 xmlAttrPtr pAttr = reinterpret_cast<xmlAttrPtr>(pOld);
837 xmlRemoveProp( pAttr );
838 pOldNode->invalidate(); // freed by xmlRemoveProp
839 appendChild(xNewChild);
840 }
841 else
842 {
843
844 xmlNodePtr cur = m_aNodePtr->children;
845 //find old node in child list
846 while (cur != nullptr)
847 {
848 if(cur == pOld)
849 {
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
868 }
869 cur = cur->next;
870 }
871 }
872
873 guard.clear(); // release for calling event handlers
874 dispatchSubtreeModified();
875
876 return xOldChild;
877 }
878
879 void CNode::dispatchSubtreeModified()
880 {
881 // only uses UNO interfaces => needs no mutex
882
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);
893 }
894
898 void SAL_CALL CNode::setNodeValue(const OUString& /*nodeValue*/)
899 {
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;
905 }
906
910 void SAL_CALL CNode::setPrefix(const OUString& prefix)
911 {
912 ::osl::MutexGuard const g(m_rMutex);
913
914 if ((nullptr == m_aNodePtr) ||
915 ((m_aNodePtr->type != XML_ELEMENT_NODE) &&
916 (m_aNodePtr->type != XML_ATTRIBUTE_NODE)))
917 {
918 DOMException e;
919 e.Code = DOMExceptionType_NO_MODIFICATION_ALLOWED_ERR;
920 throw e;
921 }
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)
925 {
926 xmlFree(const_cast<xmlChar *>(m_aNodePtr->ns->prefix));
927 m_aNodePtr->ns->prefix = xmlStrdup(pBuf);
928 }
929
930 }
931
932 // --- XEventTarget
933 void SAL_CALL CNode::addEventListener(const OUString& eventType,
934 const Reference< css::xml::dom::events::XEventListener >& listener,
935 sal_Bool useCapture)
936 {
937 ::osl::MutexGuard const g(m_rMutex);
938
939 CDocument & rDocument(GetOwnerDocument());
940 events::CEventDispatcher & rDispatcher(rDocument.GetEventDispatcher());
941 rDispatcher.addListener(m_aNodePtr, eventType, listener, useCapture);
942 }
943
944 void SAL_CALL CNode::removeEventListener(const OUString& eventType,
945 const Reference< css::xml::dom::events::XEventListener >& listener,
946 sal_Bool useCapture)
947 {
948 ::osl::MutexGuard const g(m_rMutex);
949
950 CDocument & rDocument(GetOwnerDocument());
951 events::CEventDispatcher & rDispatcher(rDocument.GetEventDispatcher());
952 rDispatcher.removeListener(m_aNodePtr, eventType, listener, useCapture);
953 }
954
955 sal_Bool SAL_CALL CNode::dispatchEvent(const Reference< XEvent >& evt)
956 {
957 CDocument * pDocument;
958 events::CEventDispatcher * pDispatcher;
959 xmlNodePtr pNode;
960 {
961 ::osl::MutexGuard const g(m_rMutex);
962
963 pDocument = & GetOwnerDocument();
964 pDispatcher = & pDocument->GetEventDispatcher();
965 pNode = m_aNodePtr;
966 }
967 // this calls event listeners, do not call with locked mutex
968 pDispatcher->dispatchEvent(*pDocument, m_rMutex, pNode, this, evt);
969 return true;
970 }
971}
972
973/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const char * pName
::osl::Mutex & m_rMutex
Reference< XOfficeDatabaseDocument > m_xDocument
Sequence< sal_Int8 > aSeq
#define SAL_INFO(area, stream)
Definition: attr.cxx:38
void nscleanup(const xmlNodePtr aNode, const xmlNodePtr aParent)
eliminate redundant namespace declarations
Definition: node.cxx:195
static void lcl_nsexchange(xmlNodePtr const aNode, xmlNsPtr const oldNs, xmlNsPtr const newNs)
Definition: node.cxx:171
void popContext(Context &io_rContext)
Definition: node.cxx:61
void pushContext(Context &io_rContext)
Definition: node.cxx:52
sal_Int32 getTokenWithPrefix(const Context &rContext, const char *pPrefix, const char *pName)
Definition: node.cxx:100
sal_Int32 getToken(const Context &rContext, const char *pToken)
Definition: node.cxx:94
void addNamespaces(Context &io_rContext, xmlNodePtr pNode)
add namespaces on this node to context
Definition: node.cxx:66
ns
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
NodeType
sal_Int32 mnToken
Definition: node.hxx:63
rtl::Reference< sax_fastparser::FastTokenHandlerBase > mxTokenHandler
Definition: node.hxx:77
NamespaceVectorType maNamespaces
outer vector: xml context; inner vector: current NS
Definition: node.hxx:72
NamespaceMapType maNamespaceMap
Definition: node.hxx:73
unsigned char sal_Bool
signed char sal_Int8
ResultType type