xref: /aoo42x/main/unoxml/source/dom/node.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include <node.hxx>
29 
30 #include <stdio.h>
31 #include <string.h>
32 
33 #include <libxml/xmlstring.h>
34 
35 #include <algorithm>
36 
37 #include <boost/bind.hpp>
38 
39 #include <rtl/uuid.h>
40 #include <rtl/instance.hxx>
41 #include <osl/mutex.hxx>
42 
43 #include <com/sun/star/xml/sax/FastToken.hpp>
44 
45 #include <document.hxx>
46 #include <attr.hxx>
47 #include <childlist.hxx>
48 
49 #include "../events/eventdispatcher.hxx"
50 #include "../events/mutationevent.hxx"
51 
52 
53 
54 using namespace ::com::sun::star;
55 
56 
57 namespace {
58     struct UnoTunnelId
59         : public ::rtl::StaticWithInit< Sequence<sal_Int8>, UnoTunnelId >
60     {
61         Sequence<sal_Int8> operator() ()
62         {
63             Sequence<sal_Int8> ret(16);
64             rtl_createUuid(
65                 reinterpret_cast<sal_uInt8*>(ret.getArray()), 0, sal_True);
66             return ret;
67         }
68     };
69 }
70 
71 namespace DOM
72 {
73     void pushContext(Context& io_rContext)
74     {
75         io_rContext.maNamespaces.push_back(
76             io_rContext.maNamespaces.back());
77     }
78 
79     void popContext(Context& io_rContext)
80     {
81         io_rContext.maNamespaces.pop_back();
82     }
83 
84     void addNamespaces(Context& io_rContext, xmlNodePtr pNode)
85     {
86         // add node's namespaces to current context
87         for (xmlNsPtr pNs = pNode->nsDef; pNs != 0; pNs = pNs->next) {
88             const xmlChar *pPrefix = pNs->prefix;
89             OString prefix(reinterpret_cast<const sal_Char*>(pPrefix),
90                            strlen(reinterpret_cast<const char*>(pPrefix)));
91             const xmlChar *pHref = pNs->href;
92             OUString val(reinterpret_cast<const sal_Char*>(pHref),
93                 strlen(reinterpret_cast<const char*>(pHref)),
94                 RTL_TEXTENCODING_UTF8);
95 
96             OSL_TRACE("Trying to add namespace %s (prefix %s)",
97                       (const char*)pHref, (const char*)pPrefix);
98 
99             Context::NamespaceMapType::iterator aIter=
100                 io_rContext.maNamespaceMap.find(val);
101             if( aIter != io_rContext.maNamespaceMap.end() )
102             {
103                 Context::Namespace aNS;
104                 aNS.maPrefix = prefix;
105                 aNS.mnToken = aIter->second;
106                 aNS.maNamespaceURL = val;
107 
108                 io_rContext.maNamespaces.back().push_back(aNS);
109 
110                 OSL_TRACE("Added with token 0x%x", aIter->second);
111             }
112         }
113     }
114 
115     sal_Int32 getToken( const Context& rContext, const sal_Char* pToken )
116     {
117         const Sequence<sal_Int8> aSeq( (sal_Int8*)pToken, strlen( pToken ) );
118         return rContext.mxTokenHandler->getTokenFromUTF8( aSeq );
119     }
120 
121     sal_Int32 getTokenWithPrefix( const Context& rContext, const sal_Char* pPrefix, const sal_Char* pName )
122     {
123         sal_Int32 nNamespaceToken = FastToken::DONTKNOW;
124         OString prefix(pPrefix,
125                        strlen(reinterpret_cast<const char*>(pPrefix)));
126 
127         OSL_TRACE("getTokenWithPrefix(): prefix %s, name %s",
128                   (const char*)pPrefix, (const char*)pName);
129 
130         Context::NamespaceVectorType::value_type::const_iterator aIter;
131         if( (aIter=std::find_if(rContext.maNamespaces.back().begin(),
132                                 rContext.maNamespaces.back().end(),
133                                 boost::bind(std::equal_to<OString>(),
134                                             boost::bind(&Context::Namespace::getPrefix,
135                                                         _1),
136                                             boost::cref(prefix)))) != rContext.maNamespaces.back().end() )
137         {
138             nNamespaceToken = aIter->mnToken;
139             sal_Int32 nNameToken = getToken( rContext, pName );
140             if( nNameToken != FastToken::DONTKNOW )
141                 nNamespaceToken |= nNameToken;
142         }
143 
144         return nNamespaceToken;
145     }
146 
147 
148     CNode::CNode(CDocument const& rDocument, ::osl::Mutex const& rMutex,
149                 NodeType const& reNodeType, xmlNodePtr const& rpNode)
150         :   m_bUnlinked(false)
151         ,   m_aNodeType(reNodeType)
152         ,   m_aNodePtr(rpNode)
153         // keep containing document alive
154         // (but not if this is a document; that would create a leak!)
155         ,   m_xDocument( (m_aNodePtr->type != XML_DOCUMENT_NODE)
156                 ? &const_cast<CDocument&>(rDocument) : 0 )
157         ,   m_rMutex(const_cast< ::osl::Mutex & >(rMutex))
158     {
159         OSL_ASSERT(m_aNodePtr);
160     }
161 
162     void CNode::invalidate()
163     {
164         //remove from list if this wrapper goes away
165         if (m_aNodePtr != 0 && m_xDocument.is()) {
166             m_xDocument->RemoveCNode(m_aNodePtr, this);
167         }
168         // #i113663#: unlinked nodes will not be freed by xmlFreeDoc
169         if (m_bUnlinked) {
170             xmlFreeNode(m_aNodePtr);
171         }
172         m_aNodePtr = 0;
173     }
174 
175     CNode::~CNode()
176     {
177         // if this is the document itself, the mutex is already freed!
178         if (NodeType_DOCUMENT_NODE == m_aNodeType) {
179             invalidate();
180         } else {
181             ::osl::MutexGuard const g(m_rMutex);
182             invalidate(); // other nodes are still alive so must lock mutex
183         }
184     }
185 
186     CNode *
187     CNode::GetImplementation(uno::Reference<uno::XInterface> const& xNode)
188     {
189         uno::Reference<lang::XUnoTunnel> const xUnoTunnel(xNode, UNO_QUERY);
190         if (!xUnoTunnel.is()) { return 0; }
191         CNode *const pCNode( reinterpret_cast< CNode* >(
192                         ::sal::static_int_cast< sal_IntPtr >(
193                             xUnoTunnel->getSomething(UnoTunnelId::get()))));
194         return pCNode;
195     }
196 
197     CDocument & CNode::GetOwnerDocument()
198     {
199         OSL_ASSERT(m_xDocument.is());
200         return *m_xDocument; // needs overriding in CDocument!
201     }
202 
203 
204     static void lcl_nsexchange(
205             xmlNodePtr const aNode, xmlNsPtr const oldNs, xmlNsPtr const newNs)
206     {
207         // recursively exchange any references to oldNs with references to newNs
208         xmlNodePtr cur = aNode;
209         while (cur != 0)
210         {
211             if (cur->ns == oldNs)
212                 cur->ns = newNs;
213             if (cur->type == XML_ELEMENT_NODE)
214             {
215                 xmlAttrPtr curAttr = cur->properties;
216                 while(curAttr != 0)
217                 {
218                     if (curAttr->ns == oldNs)
219                         curAttr->ns = newNs;
220                     curAttr = curAttr->next;
221                 }
222                 lcl_nsexchange(cur->children, oldNs, newNs);
223             }
224             cur = cur->next;
225         }
226     }
227 
228     /*static*/ void nscleanup(const xmlNodePtr aNode, const xmlNodePtr aParent)
229     {
230         xmlNodePtr cur = aNode;
231 
232         //handle attributes
233         if (cur != NULL && cur->type == XML_ELEMENT_NODE)
234         {
235             xmlAttrPtr curAttr = cur->properties;
236             while(curAttr != 0)
237             {
238                 if (curAttr->ns != NULL)
239                 {
240                     xmlNsPtr ns = xmlSearchNs(cur->doc, aParent, curAttr->ns->prefix);
241                     if (ns != NULL)
242                         curAttr->ns = ns;
243                 }
244                 curAttr = curAttr->next;
245             }
246         }
247 
248         while (cur != NULL)
249         {
250             nscleanup(cur->children, cur);
251             if (cur->ns != NULL)
252             {
253                 xmlNsPtr ns = xmlSearchNs(cur->doc, aParent, cur->ns->prefix);
254                 if (ns != NULL && ns != cur->ns && strcmp((char*)ns->href, (char*)cur->ns->href)==0)
255                 {
256                     xmlNsPtr curDef = cur->nsDef;
257                     xmlNsPtr *refp = &(cur->nsDef); // insert point
258                     while (curDef != NULL)
259                     {
260                         ns = xmlSearchNs(cur->doc, aParent, curDef->prefix);
261                         if (ns != NULL && ns != curDef && strcmp((char*)ns->href, (char*)curDef->href)==0)
262                         {
263                             // reconnect ns pointers in sub-tree to newly found ns before
264                             // removing redundant nsdecl to prevent dangling pointers.
265                             lcl_nsexchange(cur, curDef, ns);
266                             *refp = curDef->next;
267                             xmlFreeNs(curDef);
268                             curDef = *refp;
269                         } else {
270                             refp = &(curDef->next);
271                             curDef = curDef->next;
272                         }
273                     }
274                 }
275             }
276             cur = cur->next;
277         }
278     }
279 
280     void CNode::saxify(const Reference< XDocumentHandler >& i_xHandler)
281     {
282         if (!i_xHandler.is()) throw RuntimeException();
283         // default: do nothing
284     }
285 
286     void CNode::fastSaxify(Context& io_rContext)
287     {
288         if (!io_rContext.mxDocHandler.is()) throw RuntimeException();
289         // default: do nothing
290     }
291 
292     bool CNode::IsChildTypeAllowed(NodeType const /*nodeType*/)
293     {
294         // default: no children allowed
295         return false;
296     }
297 
298     /**
299     Adds the node newChild to the end of the list of children of this node.
300     */
301     Reference< XNode > SAL_CALL CNode::appendChild(
302             Reference< XNode > const& xNewChild)
303         throw (RuntimeException, DOMException)
304     {
305         ::osl::ClearableMutexGuard guard(m_rMutex);
306 
307         if (0 == m_aNodePtr) { return 0; }
308 
309         CNode *const pNewChild(CNode::GetImplementation(xNewChild));
310         if (!pNewChild) { throw RuntimeException(); }
311         xmlNodePtr const cur = pNewChild->GetNodePtr();
312         if (!cur) { throw RuntimeException(); }
313 
314         // error checks:
315         // from other document
316         if (cur->doc != m_aNodePtr->doc) {
317             DOMException e;
318             e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
319             throw e;
320         }
321         // same node
322         if (cur == m_aNodePtr) {
323             DOMException e;
324             e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
325             throw e;
326         }
327         if (cur->parent != NULL) {
328             DOMException e;
329             e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
330             throw e;
331         }
332         if (!IsChildTypeAllowed(pNewChild->m_aNodeType)) {
333             DOMException e;
334             e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
335             throw e;
336         }
337 
338         // check whether this is an attribute node; it needs special handling
339         xmlNodePtr res = NULL;
340         if (cur->type == XML_ATTRIBUTE_NODE)
341         {
342             xmlChar const*const pChildren((cur->children)
343                     ? cur->children->content
344                     : reinterpret_cast<xmlChar const*>(""));
345             CAttr *const pCAttr(dynamic_cast<CAttr *>(pNewChild));
346             if (!pCAttr) { throw RuntimeException(); }
347             xmlNsPtr const pNs( pCAttr->GetNamespace(m_aNodePtr) );
348             if (pNs) {
349                 res = reinterpret_cast<xmlNodePtr>(
350                         xmlNewNsProp(m_aNodePtr, pNs, cur->name, pChildren));
351             } else {
352                 res = reinterpret_cast<xmlNodePtr>(
353                         xmlNewProp(m_aNodePtr, cur->name, pChildren));
354             }
355         }
356         else
357         {
358             res = xmlAddChild(m_aNodePtr, cur);
359 
360             // libxml can do optimization when appending nodes.
361             // if res != cur, something was optimized and the newchild-wrapper
362             // should be updated
363             if (res && (cur != res)) {
364                 pNewChild->invalidate(); // cur has been freed
365             }
366         }
367 
368         if (!res) { return 0; }
369 
370         // use custom ns cleanup instead of
371         // xmlReconciliateNs(m_aNodePtr->doc, m_aNodePtr);
372         // because that will not remove unneeded ns decls
373         nscleanup(res, m_aNodePtr);
374 
375         ::rtl::Reference<CNode> const pNode = GetOwnerDocument().GetCNode(res);
376 
377         if (!pNode.is()) { return 0; }
378 
379         // dispatch DOMNodeInserted event, target is the new node
380         // this node is the related node
381         // does bubble
382         pNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
383         Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
384         Reference< XMutationEvent > event(docevent->createEvent(
385             OUString::createFromAscii("DOMNodeInserted")), UNO_QUERY);
386         event->initMutationEvent(OUString::createFromAscii("DOMNodeInserted")
387             , sal_True, sal_False,
388             this,
389             OUString(), OUString(), OUString(), (AttrChangeType)0 );
390 
391         // the following dispatch functions use only UNO interfaces
392         // and call event listeners, so release mutex to prevent deadlocks.
393         guard.clear();
394 
395         dispatchEvent(Reference< XEvent >(event, UNO_QUERY));
396         // dispatch subtree modified for this node
397         dispatchSubtreeModified();
398 
399         return pNode.get();
400     }
401 
402     /**
403     Returns a duplicate of this node, i.e., serves as a generic copy
404     constructor for nodes.
405     */
406     Reference< XNode > SAL_CALL CNode::cloneNode(sal_Bool bDeep)
407         throw (RuntimeException)
408     {
409         ::osl::MutexGuard const g(m_rMutex);
410 
411         if (0 == m_aNodePtr) {
412             return 0;
413         }
414         ::rtl::Reference<CNode> const pNode = GetOwnerDocument().GetCNode(
415             xmlCopyNode(m_aNodePtr, (bDeep) ? 1 : 0));
416         if (!pNode.is()) { return 0; }
417         pNode->m_bUnlinked = true; // not linked yet
418         return pNode.get();
419     }
420 
421     /**
422     A NamedNodeMap containing the attributes of this node (if it is an Element)
423     or null otherwise.
424     */
425     Reference< XNamedNodeMap > SAL_CALL CNode::getAttributes()
426         throw (RuntimeException)
427     {
428         // return empty reference; only element node may override this impl
429         return Reference< XNamedNodeMap>();
430     }
431 
432     /**
433     A NodeList that contains all children of this node.
434     */
435     Reference< XNodeList > SAL_CALL CNode::getChildNodes()
436         throw (RuntimeException)
437     {
438         ::osl::MutexGuard const g(m_rMutex);
439 
440         if (0 == m_aNodePtr) {
441             return 0;
442         }
443         Reference< XNodeList > const xNodeList(new CChildList(this, m_rMutex));
444         return xNodeList;
445     }
446 
447     /**
448     The first child of this node.
449     */
450     Reference< XNode > SAL_CALL CNode::getFirstChild()
451         throw (RuntimeException)
452     {
453         ::osl::MutexGuard const g(m_rMutex);
454 
455         if (0 == m_aNodePtr) {
456             return 0;
457         }
458         Reference< XNode > const xNode(
459                 GetOwnerDocument().GetCNode(m_aNodePtr->children).get());
460         return xNode;
461     }
462 
463     /**
464     The last child of this node.
465     */
466     Reference< XNode > SAL_CALL CNode::getLastChild()
467         throw (RuntimeException)
468     {
469         ::osl::MutexGuard const g(m_rMutex);
470 
471         if (0 == m_aNodePtr) {
472             return 0;
473         }
474         Reference< XNode > const xNode(
475             GetOwnerDocument().GetCNode(xmlGetLastChild(m_aNodePtr)).get());
476         return xNode;
477     }
478 
479     /**
480     Returns the local part of the qualified name of this node.
481     */
482     OUString SAL_CALL CNode::getLocalName()
483         throw (RuntimeException)
484     {
485         // see CElement/CAttr
486         return ::rtl::OUString();
487     }
488 
489 
490     /**
491     The namespace URI of this node, or null if it is unspecified.
492     */
493     OUString SAL_CALL CNode::getNamespaceURI()
494         throw (RuntimeException)
495     {
496         ::osl::MutexGuard const g(m_rMutex);
497 
498         OUString aURI;
499         if (m_aNodePtr != NULL &&
500             (m_aNodePtr->type == XML_ELEMENT_NODE || m_aNodePtr->type == XML_ATTRIBUTE_NODE) &&
501             m_aNodePtr->ns != NULL)
502         {
503             const xmlChar* xHref = m_aNodePtr->ns->href;
504             aURI = OUString((sal_Char*)xHref, strlen((char*)xHref), RTL_TEXTENCODING_UTF8);
505         }
506         return aURI;
507     }
508 
509     /**
510     The node immediately following this node.
511     */
512     Reference< XNode > SAL_CALL CNode::getNextSibling()
513         throw (RuntimeException)
514     {
515         ::osl::MutexGuard const g(m_rMutex);
516 
517         if (0 == m_aNodePtr) {
518             return 0;
519         }
520         Reference< XNode > const xNode(
521                 GetOwnerDocument().GetCNode(m_aNodePtr->next).get());
522         return xNode;
523     }
524 
525     /**
526     The name of this node, depending on its type; see the table above.
527     */
528     OUString SAL_CALL CNode::getNodeName()
529         throw (RuntimeException)
530     {
531         /*
532         Interface  	     nodeName  	            nodeValue  	                    attributes
533         --------------------------------------------------------------------------------------
534         Attr 	         name of attribute 	    value of attribute 	            null
535         CDATASection 	 "#cdata-section" 	    content of the CDATA Section 	null
536         Comment 	     "#comment" 	        content of the comment 	        null
537         Document 	     "#document" 	        null 	                        null
538         DocumentFragment "#document-fragment" 	null 	                        null
539         DocumentType 	 document type name 	null 	                        null
540         Element 	     tag name 	            null 	                        NamedNodeMap
541         Entity 	         entity name 	        null 	                        null
542         EntityReference  name of entity         null 	                        null
543                          referenced
544         Notation 	     notation name 	        null 	                        null
545         Processing\   	 target                 entire content excluding        null
546         Instruction 	                        the target
547         Text 	         "#text" 	            content of the text node     	null
548         */
549         OUString aName;
550         return aName;
551     }
552 
553     /**
554     A code representing the type of the underlying object, as defined above.
555     */
556     NodeType SAL_CALL CNode::getNodeType()
557         throw (RuntimeException)
558     {
559         ::osl::MutexGuard const g(m_rMutex);
560 
561         return m_aNodeType;
562     }
563 
564     /**
565     The value of this node, depending on its type; see the table above.
566     */
567     OUString SAL_CALL CNode::getNodeValue()
568         throw (RuntimeException)
569     {
570         OUString aValue;
571         return aValue;
572     }
573 
574     /**
575     The Document object associated with this node.
576     */
577     Reference< XDocument > SAL_CALL CNode::getOwnerDocument()
578         throw (RuntimeException)
579     {
580         ::osl::MutexGuard const g(m_rMutex);
581 
582         if (0 == m_aNodePtr) {
583             return 0;
584         }
585         Reference< XDocument > const xDoc(& GetOwnerDocument());
586         return xDoc;
587     }
588 
589     /**
590     The parent of this node.
591     */
592     Reference< XNode > SAL_CALL CNode::getParentNode()
593         throw (RuntimeException)
594     {
595         ::osl::MutexGuard const g(m_rMutex);
596 
597         if (0 == m_aNodePtr) {
598             return 0;
599         }
600         Reference< XNode > const xNode(
601                 GetOwnerDocument().GetCNode(m_aNodePtr->parent).get());
602         return xNode;
603     }
604 
605     /**
606     The namespace prefix of this node, or null if it is unspecified.
607     */
608     OUString SAL_CALL CNode::getPrefix()
609         throw (RuntimeException)
610     {
611         ::osl::MutexGuard const g(m_rMutex);
612 
613         OUString aPrefix;
614         if (m_aNodePtr != NULL &&
615             (m_aNodePtr->type == XML_ELEMENT_NODE || m_aNodePtr->type == XML_ATTRIBUTE_NODE) &&
616             m_aNodePtr->ns != NULL)
617         {
618             const xmlChar* xPrefix = m_aNodePtr->ns->prefix;
619             if( xPrefix != NULL )
620                 aPrefix = OUString((sal_Char*)xPrefix, strlen((char*)xPrefix), RTL_TEXTENCODING_UTF8);
621         }
622         return aPrefix;
623 
624     }
625 
626     /**
627     The node immediately preceding this node.
628     */
629     Reference< XNode > SAL_CALL CNode::getPreviousSibling()
630         throw (RuntimeException)
631     {
632         ::osl::MutexGuard const g(m_rMutex);
633 
634         if (0 == m_aNodePtr) {
635             return 0;
636         }
637         Reference< XNode > const xNode(
638                 GetOwnerDocument().GetCNode(m_aNodePtr->prev).get());
639         return xNode;
640     }
641 
642     /**
643     Returns whether this node (if it is an element) has any attributes.
644     */
645     sal_Bool SAL_CALL CNode::hasAttributes()
646         throw (RuntimeException)
647     {
648         ::osl::MutexGuard const g(m_rMutex);
649 
650         return (m_aNodePtr != NULL && m_aNodePtr->properties != NULL);
651     }
652 
653     /**
654     Returns whether this node has any children.
655     */
656     sal_Bool SAL_CALL CNode::hasChildNodes()
657         throw (RuntimeException)
658     {
659         ::osl::MutexGuard const g(m_rMutex);
660 
661         return (m_aNodePtr != NULL && m_aNodePtr->children != NULL);
662     }
663 
664     /**
665     Inserts the node newChild before the existing child node refChild.
666     */
667     Reference< XNode > SAL_CALL CNode::insertBefore(
668             const Reference< XNode >& newChild, const Reference< XNode >& refChild)
669         throw (RuntimeException, DOMException)
670     {
671         if (!newChild.is() || !refChild.is()) { throw RuntimeException(); }
672 
673         if (newChild->getOwnerDocument() != getOwnerDocument()) {
674             DOMException e;
675             e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
676             throw e;
677         }
678         if (refChild->getParentNode() != Reference< XNode >(this)) {
679             DOMException e;
680             e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
681             throw e;
682         }
683 
684         ::osl::ClearableMutexGuard guard(m_rMutex);
685 
686         CNode *const pNewNode(CNode::GetImplementation(newChild));
687         CNode *const pRefNode(CNode::GetImplementation(refChild));
688         if (!pNewNode || !pRefNode) { throw RuntimeException(); }
689         xmlNodePtr const pNewChild(pNewNode->GetNodePtr());
690         xmlNodePtr const pRefChild(pRefNode->GetNodePtr());
691         if (!pNewChild || !pRefChild) { throw RuntimeException(); }
692 
693         if (pNewChild == m_aNodePtr) {
694             DOMException e;
695             e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
696             throw e;
697         }
698         // already has parent
699         if (pNewChild->parent != NULL)
700         {
701             DOMException e;
702             e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
703             throw e;
704         }
705         if (!IsChildTypeAllowed(pNewNode->m_aNodeType)) {
706             DOMException e;
707             e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
708             throw e;
709         }
710 
711         // attributes are unordered anyway, so just do appendChild
712         if (XML_ATTRIBUTE_NODE == pNewChild->type) {
713             guard.clear();
714             return appendChild(newChild);
715         }
716 
717         xmlNodePtr cur = m_aNodePtr->children;
718 
719         //search child before which to insert
720         while (cur != NULL)
721         {
722             if (cur == pRefChild) {
723                 // insert before
724                 pNewChild->next = cur;
725                 pNewChild->prev = cur->prev;
726                 cur->prev = pNewChild;
727                 if (pNewChild->prev != NULL) {
728                     pNewChild->prev->next = pNewChild;
729                 }
730                 pNewChild->parent = cur->parent;
731                 if (pNewChild->parent->children == cur) {
732                     pNewChild->parent->children = pNewChild;
733                 }
734                 // do not update parent->last here!
735                 pNewNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
736                 break;
737             }
738             cur = cur->next;
739         }
740         return refChild;
741     }
742 
743     /**
744     Tests whether the DOM implementation implements a specific feature and
745     that feature is supported by this node.
746     */
747   sal_Bool SAL_CALL CNode::isSupported(const OUString& /*feature*/, const OUString& /*ver*/)
748         throw (RuntimeException)
749     {
750         OSL_ENSURE(false, "CNode::isSupported: not implemented (#i113683#)");
751         return sal_False;
752     }
753 
754     /**
755     Puts all Text nodes in the full depth of the sub-tree underneath this
756     Node, including attribute nodes, into a "normal" form where only structure
757     (e.g., elements, comments, processing instructions, CDATA sections, and
758     entity references) separates Text nodes, i.e., there are neither adjacent
759     Text nodes nor empty Text nodes.
760     */
761     void SAL_CALL CNode::normalize()
762         throw (RuntimeException)
763     {
764         //XXX combine adjacent text nodes and remove empty ones
765         OSL_ENSURE(false, "CNode::normalize: not implemented (#i113683#)");
766     }
767 
768     /**
769     Removes the child node indicated by oldChild from the list of children,
770     and returns it.
771     */
772     Reference< XNode > SAL_CALL
773     CNode::removeChild(const Reference< XNode >& xOldChild)
774         throw (RuntimeException, DOMException)
775     {
776         if (!xOldChild.is()) {
777             throw RuntimeException();
778         }
779 
780         if (xOldChild->getOwnerDocument() != getOwnerDocument()) {
781             DOMException e;
782             e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
783             throw e;
784         }
785         if (xOldChild->getParentNode() != Reference< XNode >(this)) {
786             DOMException e;
787             e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
788             throw e;
789         }
790 
791         ::osl::ClearableMutexGuard guard(m_rMutex);
792 
793         if (!m_aNodePtr) { throw RuntimeException(); }
794 
795         Reference<XNode> xReturn( xOldChild );
796 
797         ::rtl::Reference<CNode> const pOld(CNode::GetImplementation(xOldChild));
798         if (!pOld.is()) { throw RuntimeException(); }
799         xmlNodePtr const old = pOld->GetNodePtr();
800         if (!old) { throw RuntimeException(); }
801 
802         if( old->type == XML_ATTRIBUTE_NODE )
803         {
804             xmlAttrPtr pAttr = reinterpret_cast<xmlAttrPtr>(old);
805             xmlRemoveProp( pAttr );
806             pOld->invalidate(); // freed by xmlRemoveProp
807             xReturn.clear();
808         }
809         else
810         {
811             xmlUnlinkNode(old);
812             pOld->m_bUnlinked = true;
813         }
814 
815         /*DOMNodeRemoved
816          * Fired when a node is being removed from its parent node.
817          * This event is dispatched before the node is removed from the tree.
818          * The target of this event is the node being removed.
819          *   Bubbles: Yes
820          *   Cancelable: No
821          *   Context Info: relatedNode holds the parent node
822          */
823         Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
824         Reference< XMutationEvent > event(docevent->createEvent(
825             OUString::createFromAscii("DOMNodeRemoved")), UNO_QUERY);
826         event->initMutationEvent(OUString::createFromAscii("DOMNodeRemoved"),
827             sal_True,
828             sal_False,
829             this,
830             OUString(), OUString(), OUString(), (AttrChangeType)0 );
831 
832         // the following dispatch functions use only UNO interfaces
833         // and call event listeners, so release mutex to prevent deadlocks.
834         guard.clear();
835 
836         dispatchEvent(Reference< XEvent >(event, UNO_QUERY));
837         // subtree modified for this node
838         dispatchSubtreeModified();
839 
840         return xReturn;
841     }
842 
843     /**
844     Replaces the child node oldChild with newChild in the list of children,
845     and returns the oldChild node.
846     */
847     Reference< XNode > SAL_CALL CNode::replaceChild(
848             Reference< XNode > const& xNewChild,
849             Reference< XNode > const& xOldChild)
850         throw (RuntimeException, DOMException)
851     {
852         if (!xOldChild.is() || !xNewChild.is()) {
853             throw RuntimeException();
854         }
855 
856         if (xNewChild->getOwnerDocument() != getOwnerDocument()) {
857             DOMException e;
858             e.Code = DOMExceptionType_WRONG_DOCUMENT_ERR;
859             throw e;
860         }
861         if (xOldChild->getParentNode() != Reference< XNode >(this)) {
862             DOMException e;
863             e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
864             throw e;
865         }
866 
867         ::osl::ClearableMutexGuard guard(m_rMutex);
868 
869 /*
870         Reference< XNode > aNode = removeChild(oldChild);
871         appendChild(newChild);
872 */
873         ::rtl::Reference<CNode> const pOldNode(
874                 CNode::GetImplementation(xOldChild));
875         ::rtl::Reference<CNode> const pNewNode(
876                 CNode::GetImplementation(xNewChild));
877         if (!pOldNode.is() || !pNewNode.is()) { throw RuntimeException(); }
878         xmlNodePtr const pOld = pOldNode->GetNodePtr();
879         xmlNodePtr const pNew = pNewNode->GetNodePtr();
880         if (!pOld || !pNew) { throw RuntimeException(); }
881 
882         if (pNew == m_aNodePtr) {
883             DOMException e;
884             e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
885             throw e;
886         }
887         // already has parent
888         if (pNew->parent != NULL) {
889             DOMException e;
890             e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
891             throw e;
892         }
893         if (!IsChildTypeAllowed(pNewNode->m_aNodeType)) {
894             DOMException e;
895             e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
896             throw e;
897         }
898 
899         if( pOld->type == XML_ATTRIBUTE_NODE )
900         {
901             // can only replace attribute with attribute
902             if ( pOld->type != pNew->type )
903             {
904                 DOMException e;
905                 e.Code = DOMExceptionType_HIERARCHY_REQUEST_ERR;
906                 throw e;
907             }
908 
909             xmlAttrPtr pAttr = (xmlAttrPtr)pOld;
910             xmlRemoveProp( pAttr );
911             pOldNode->invalidate(); // freed by xmlRemoveProp
912             appendChild(xNewChild);
913         }
914         else
915         {
916 
917         xmlNodePtr cur = m_aNodePtr->children;
918         //find old node in child list
919         while (cur != NULL)
920         {
921             if(cur == pOld)
922             {
923                 // exchange nodes
924                 pNew->prev = pOld->prev;
925                 if (pNew->prev != NULL)
926                     pNew->prev->next = pNew;
927                 pNew->next = pOld->next;
928                 if (pNew->next != NULL)
929                     pNew->next->prev = pNew;
930                 pNew->parent = pOld->parent;
931                 if(pNew->parent->children == pOld)
932                     pNew->parent->children = pNew;
933                 if(pNew->parent->last == pOld)
934                     pNew->parent->last = pNew;
935                 pOld->next = NULL;
936                 pOld->prev = NULL;
937                 pOld->parent = NULL;
938                 pOldNode->m_bUnlinked = true;
939                 pNewNode->m_bUnlinked = false; // will be deleted by xmlFreeDoc
940             }
941             cur = cur->next;
942         }
943         }
944 
945         guard.clear(); // release for calling event handlers
946         dispatchSubtreeModified();
947 
948         return xOldChild;
949     }
950 
951     void CNode::dispatchSubtreeModified()
952     {
953         // only uses UNO interfaces => needs no mutex
954 
955         // dispatch DOMSubtreeModified
956         // target is _this_ node
957         Reference< XDocumentEvent > docevent(getOwnerDocument(), UNO_QUERY);
958         Reference< XMutationEvent > event(docevent->createEvent(
959             OUString::createFromAscii("DOMSubtreeModified")), UNO_QUERY);
960         event->initMutationEvent(
961             OUString::createFromAscii("DOMSubtreeModified"), sal_True,
962             sal_False, Reference< XNode >(),
963             OUString(), OUString(), OUString(), (AttrChangeType)0 );
964         dispatchEvent(Reference< XEvent >(event, UNO_QUERY));
965     }
966 
967     /**
968     The value of this node, depending on its type; see the table above.
969     */
970     void SAL_CALL CNode::setNodeValue(const OUString& /*nodeValue*/)
971         throw (RuntimeException, DOMException)
972     {
973         // use specific node implememntation
974         // if we end up down here, something went wrong
975         DOMException e;
976         e.Code = DOMExceptionType_NO_MODIFICATION_ALLOWED_ERR;
977         throw e;
978     }
979 
980     /**
981     The namespace prefix of this node, or null if it is unspecified.
982     */
983     void SAL_CALL CNode::setPrefix(const OUString& prefix)
984         throw (RuntimeException, DOMException)
985     {
986         ::osl::MutexGuard const g(m_rMutex);
987 
988         if ((0 == m_aNodePtr) ||
989             ((m_aNodePtr->type != XML_ELEMENT_NODE) &&
990              (m_aNodePtr->type != XML_ATTRIBUTE_NODE)))
991         {
992             DOMException e;
993             e.Code = DOMExceptionType_NO_MODIFICATION_ALLOWED_ERR;
994             throw e;
995         }
996         OString o1 = OUStringToOString(prefix, RTL_TEXTENCODING_UTF8);
997         xmlChar *pBuf = (xmlChar*)o1.getStr();
998         if (m_aNodePtr != NULL && m_aNodePtr->ns != NULL)
999         {
1000             xmlFree(const_cast<xmlChar *>(m_aNodePtr->ns->prefix));
1001             m_aNodePtr->ns->prefix = xmlStrdup(pBuf);
1002         }
1003 
1004     }
1005 
1006         // --- XEventTarget
1007     void SAL_CALL CNode::addEventListener(const OUString& eventType,
1008         const Reference< com::sun::star::xml::dom::events::XEventListener >& listener,
1009         sal_Bool useCapture)
1010         throw (RuntimeException)
1011     {
1012         ::osl::MutexGuard const g(m_rMutex);
1013 
1014         CDocument & rDocument(GetOwnerDocument());
1015         events::CEventDispatcher & rDispatcher(rDocument.GetEventDispatcher());
1016         rDispatcher.addListener(m_aNodePtr, eventType, listener, useCapture);
1017     }
1018 
1019     void SAL_CALL CNode::removeEventListener(const OUString& eventType,
1020         const Reference< com::sun::star::xml::dom::events::XEventListener >& listener,
1021         sal_Bool useCapture)
1022         throw (RuntimeException)
1023     {
1024         ::osl::MutexGuard const g(m_rMutex);
1025 
1026         CDocument & rDocument(GetOwnerDocument());
1027         events::CEventDispatcher & rDispatcher(rDocument.GetEventDispatcher());
1028         rDispatcher.removeListener(m_aNodePtr, eventType, listener, useCapture);
1029     }
1030 
1031     sal_Bool SAL_CALL CNode::dispatchEvent(const Reference< XEvent >& evt)
1032         throw(RuntimeException, EventException)
1033     {
1034         CDocument * pDocument;
1035         events::CEventDispatcher * pDispatcher;
1036         xmlNodePtr pNode;
1037         {
1038             ::osl::MutexGuard const g(m_rMutex);
1039 
1040             pDocument = & GetOwnerDocument();
1041             pDispatcher = & pDocument->GetEventDispatcher();
1042             pNode = m_aNodePtr;
1043         }
1044         // this calls event listeners, do not call with locked mutex
1045         pDispatcher->dispatchEvent(*pDocument, m_rMutex, pNode, this, evt);
1046         return sal_True;
1047     }
1048 
1049     ::sal_Int64 SAL_CALL
1050     CNode::getSomething(Sequence< ::sal_Int8 > const& rId)
1051         throw (RuntimeException)
1052     {
1053         if ((rId.getLength() == 16) &&
1054             (0 == rtl_compareMemory(UnoTunnelId::get().getConstArray(),
1055                                     rId.getConstArray(), 16)))
1056         {
1057             return ::sal::static_int_cast< sal_Int64 >(
1058                     reinterpret_cast< sal_IntPtr >(this) );
1059         }
1060         return 0;
1061     }
1062 }
1063 
1064