1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
24 #include <libxml/xmlstring.h>
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"
40 #include "childlist.hxx"
42 #include <eventdispatcher.hxx>
45 using namespace css::uno
;
46 using namespace css::xml::dom
;
47 using namespace css::xml::dom::events
;
48 using namespace css::xml::sax
;
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
,
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
;
119 nNamespaceToken
|= nNameToken
;
122 return nNamespaceToken
;
126 CNode::CNode(CDocument
const& rDocument
, ::osl::Mutex
const& rMutex
,
127 NodeType
const& reNodeType
, xmlNodePtr
const& rpNode
)
129 , m_aNodeType(reNodeType
)
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
148 xmlFreeNode(m_aNodePtr
);
150 m_aNodePtr
= nullptr;
155 // if this is the document itself, the mutex is already freed!
156 if (NodeType_DOCUMENT_NODE
== m_aNodeType
) {
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
)
180 if (cur
->type
== XML_ELEMENT_NODE
)
182 xmlAttrPtr curAttr
= cur
->properties
;
183 while(curAttr
!= nullptr)
185 if (curAttr
->ns
== oldNs
)
187 curAttr
= curAttr
->next
;
189 lcl_nsexchange(cur
->children
, oldNs
, newNs
);
195 /*static*/ void nscleanup(const xmlNodePtr aNode
, const xmlNodePtr aParent
)
197 xmlNodePtr cur
= aNode
;
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
);
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
;
237 refp
= &(curDef
->next
);
238 curDef
= curDef
->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
265 void CNode::checkNoParent(Reference
<XNode
>const& xNode
){
266 if (xNode
->getParentNode() != Reference
<XNode
>(this)){
268 e
.Code
= DOMExceptionType_HIERARCHY_REQUEST_ERR
;
272 void CNode::checkNoParent(const xmlNodePtr pNode
){
273 if (pNode
->parent
!= nullptr){
275 e
.Code
= DOMExceptionType_HIERARCHY_REQUEST_ERR
;
279 void CNode::checkSameOwner(Reference
<XNode
>const& xNode
){
280 if (xNode
->getOwnerDocument() != getOwnerDocument()) {
282 e
.Code
= DOMExceptionType_WRONG_DOCUMENT_ERR
;
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(); }
303 // from other document
304 if (cur
->doc
!= m_aNodePtr
->doc
) {
306 e
.Code
= DOMExceptionType_WRONG_DOCUMENT_ERR
;
310 if (cur
== m_aNodePtr
) {
312 e
.Code
= DOMExceptionType_HIERARCHY_REQUEST_ERR
;
317 if (!IsChildTypeAllowed(pNewChild
->m_aNodeType
, nullptr)) {
319 e
.Code
= DOMExceptionType_HIERARCHY_REQUEST_ERR
;
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
) );
334 res
= reinterpret_cast<xmlNodePtr
>(
335 xmlNewNsProp(m_aNodePtr
, pNs
, cur
->name
, pChildren
));
337 res
= reinterpret_cast<xmlNodePtr
>(
338 xmlNewProp(m_aNodePtr
, cur
->name
, pChildren
));
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
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
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.
378 dispatchEvent(event
);
379 // dispatch subtree modified for this node
380 dispatchSubtreeModified();
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
) {
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
404 A NamedNodeMap containing the attributes of this node (if it is an Element)
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
) {
423 Reference
< XNodeList
> const xNodeList(new CChildList(this, m_rMutex
));
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
) {
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
) {
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
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
);
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
);
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
) {
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
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
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
);
531 The value of this node, depending on its type; see the table above.
533 OUString SAL_CALL
CNode::getNodeValue()
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
) {
548 Reference
< XDocument
> const xDoc(& GetOwnerDocument());
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
) {
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
);
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
);
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
) {
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)) {
630 e
.Code
= DOMExceptionType_HIERARCHY_REQUEST_ERR
;
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
) {
645 e
.Code
= DOMExceptionType_HIERARCHY_REQUEST_ERR
;
648 // already has parent
649 checkNoParent(pNewChild
);
651 if (!IsChildTypeAllowed(pNewNode
->m_aNodeType
, nullptr)) {
653 e
.Code
= DOMExceptionType_HIERARCHY_REQUEST_ERR
;
657 // attributes are unordered anyway, so just do appendChild
658 if (XML_ATTRIBUTE_NODE
== pNewChild
->type
) {
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
) {
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
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#)");
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,
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)) {
727 e
.Code
= DOMExceptionType_HIERARCHY_REQUEST_ERR
;
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
752 pOld
->m_bUnlinked
= true;
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.
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",
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.
776 dispatchEvent(event
);
777 // subtree modified for this node
778 dispatchSubtreeModified();
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)) {
799 e
.Code
= DOMExceptionType_HIERARCHY_REQUEST_ERR
;
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
) {
814 e
.Code
= DOMExceptionType_HIERARCHY_REQUEST_ERR
;
817 // already has parent
820 if (!IsChildTypeAllowed(pNewNode
->m_aNodeType
, &pOldNode
->m_aNodeType
)) {
822 e
.Code
= DOMExceptionType_HIERARCHY_REQUEST_ERR
;
826 if( pOld
->type
== XML_ATTRIBUTE_NODE
)
828 // can only replace attribute with attribute
829 if ( pOld
->type
!= pNew
->type
)
832 e
.Code
= DOMExceptionType_HIERARCHY_REQUEST_ERR
;
836 xmlAttrPtr pAttr
= reinterpret_cast<xmlAttrPtr
>(pOld
);
837 xmlRemoveProp( pAttr
);
838 pOldNode
->invalidate(); // freed by xmlRemoveProp
839 appendChild(xNewChild
);
844 xmlNodePtr cur
= m_aNodePtr
->children
;
845 //find old node in child list
846 while (cur
!= nullptr)
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
873 guard
.clear(); // release for calling event handlers
874 dispatchSubtreeModified();
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
903 e
.Code
= DOMExceptionType_NO_MODIFICATION_ALLOWED_ERR
;
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
)))
919 e
.Code
= DOMExceptionType_NO_MODIFICATION_ALLOWED_ERR
;
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
);
933 void SAL_CALL
CNode::addEventListener(const OUString
& eventType
,
934 const Reference
< css::xml::dom::events::XEventListener
>& listener
,
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
,
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
;
961 ::osl::MutexGuard
const g(m_rMutex
);
963 pDocument
= & GetOwnerDocument();
964 pDispatcher
= & pDocument
->GetEventDispatcher();
967 // this calls event listeners, do not call with locked mutex
968 pDispatcher
->dispatchEvent(*pDocument
, m_rMutex
, pNode
, this, evt
);
973 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */