xref: /aoo42x/main/unoxml/source/dom/document.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 <com/sun/star/uno/Sequence.h>
29 
30 #include "document.hxx"
31 #include "attr.hxx"
32 #include "element.hxx"
33 #include "cdatasection.hxx"
34 #include "documentfragment.hxx"
35 #include "text.hxx"
36 #include "cdatasection.hxx"
37 #include "comment.hxx"
38 #include "processinginstruction.hxx"
39 #include "entityreference.hxx"
40 #include "documenttype.hxx"
41 #include "elementlist.hxx"
42 #include "domimplementation.hxx"
43 #include <entity.hxx>
44 #include <notation.hxx>
45 
46 #include "../events/event.hxx"
47 #include "../events/mutationevent.hxx"
48 #include "../events/uievent.hxx"
49 #include "../events/mouseevent.hxx"
50 #include "../events/eventdispatcher.hxx"
51 
52 #include <string.h>
53 
54 #include <com/sun/star/xml/sax/FastToken.hpp>
55 #include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
56 
57 namespace DOM
58 {
59     static xmlNodePtr lcl_getDocumentType(xmlDocPtr const i_pDocument)
60     {
61         // find the doc type
62         xmlNodePtr cur = i_pDocument->children;
63         while (cur != NULL)
64         {
65             if ((cur->type == XML_DOCUMENT_TYPE_NODE) ||
66                 (cur->type == XML_DTD_NODE)) {
67                     return cur;
68             }
69         }
70         return 0;
71     }
72 
73     /// get the pointer to the root element node of the document
74     static xmlNodePtr lcl_getDocumentRootPtr(xmlDocPtr const i_pDocument)
75     {
76         // find the document element
77         xmlNodePtr cur = i_pDocument->children;
78         while (cur != NULL)
79         {
80             if (cur->type == XML_ELEMENT_NODE)
81                 break;
82             cur = cur->next;
83         }
84         return cur;
85     }
86 
87     CDocument::CDocument(xmlDocPtr const pDoc)
88         : CDocument_Base(*this, m_Mutex,
89                 NodeType_DOCUMENT_NODE, reinterpret_cast<xmlNodePtr>(pDoc))
90         , m_aDocPtr(pDoc)
91         , m_streamListeners()
92         , m_pEventDispatcher(new events::CEventDispatcher())
93     {
94     }
95 
96     ::rtl::Reference<CDocument> CDocument::CreateCDocument(xmlDocPtr const pDoc)
97     {
98         ::rtl::Reference<CDocument> const xDoc(new CDocument(pDoc));
99         // add the doc itself to its nodemap!
100         xDoc->m_NodeMap.insert(
101             nodemap_t::value_type(reinterpret_cast<xmlNodePtr>(pDoc),
102                 ::std::make_pair(
103                     WeakReference<XNode>(static_cast<XDocument*>(xDoc.get())),
104                     xDoc.get())));
105         return xDoc;
106     }
107 
108     CDocument::~CDocument()
109     {
110         ::osl::MutexGuard const g(m_Mutex);
111 #ifdef DBG_UTIL
112         // node map must be empty now, otherwise CDocument must not die!
113         for (nodemap_t::iterator i = m_NodeMap.begin();
114                 i != m_NodeMap.end(); ++i)
115         {
116             Reference<XNode> const xNode(i->second.first);
117             OSL_ENSURE(!xNode.is(),
118             "CDocument::~CDocument(): ERROR: live node in document node map!");
119         }
120 #endif
121         xmlFreeDoc(m_aDocPtr);
122     }
123 
124 
125     events::CEventDispatcher & CDocument::GetEventDispatcher()
126     {
127         return *m_pEventDispatcher;
128     }
129 
130     ::rtl::Reference< CElement > CDocument::GetDocumentElement()
131     {
132         xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr);
133         ::rtl::Reference< CElement > const xRet(
134             dynamic_cast<CElement*>(GetCNode(pNode).get()));
135         return xRet;
136     }
137 
138     void
139     CDocument::RemoveCNode(xmlNodePtr const pNode, CNode const*const pCNode)
140     {
141         nodemap_t::iterator const i = m_NodeMap.find(pNode);
142         if (i != m_NodeMap.end()) {
143             // #i113681# consider this scenario:
144             // T1 calls ~CNode
145             // T2 calls getCNode:    lookup will find i->second->first invalid
146             //                       so a new CNode is created and inserted
147             // T1 calls removeCNode: i->second->second now points to a
148             //                       different CNode instance!
149             //
150             // check that the CNode is the right one
151             CNode *const pCurrent = i->second.second;
152             if (pCurrent == pCNode) {
153                 m_NodeMap.erase(i);
154             }
155         }
156     }
157 
158     /** NB: this is the CNode factory.
159         it is the only place where CNodes may be instantiated.
160         all CNodes must be registered at the m_NodeMap.
161      */
162     ::rtl::Reference<CNode>
163     CDocument::GetCNode(xmlNodePtr const pNode, bool const bCreate)
164     {
165         if (0 == pNode) {
166             return 0;
167         }
168         //check whether there is already an instance for this node
169         nodemap_t::const_iterator const i = m_NodeMap.find(pNode);
170         if (i != m_NodeMap.end()) {
171             // #i113681# check that the CNode is still alive
172             uno::Reference<XNode> const xNode(i->second.first);
173             if (xNode.is())
174             {
175                 ::rtl::Reference<CNode> ret(i->second.second);
176                 OSL_ASSERT(ret.is());
177                 return ret;
178             }
179         }
180 
181         if (!bCreate) { return 0; }
182 
183         // there is not yet an instance wrapping this node,
184         // create it and store it in the map
185 
186         ::rtl::Reference<CNode> pCNode;
187         switch (pNode->type)
188         {
189             case XML_ELEMENT_NODE:
190                 // m_aNodeType = NodeType::ELEMENT_NODE;
191                 pCNode = static_cast< CNode* >(
192                         new CElement(*this, m_Mutex, pNode));
193             break;
194             case XML_TEXT_NODE:
195                 // m_aNodeType = NodeType::TEXT_NODE;
196                 pCNode = static_cast< CNode* >(
197                         new CText(*this, m_Mutex, pNode));
198             break;
199             case XML_CDATA_SECTION_NODE:
200                 // m_aNodeType = NodeType::CDATA_SECTION_NODE;
201                 pCNode = static_cast< CNode* >(
202                         new CCDATASection(*this, m_Mutex, pNode));
203             break;
204             case XML_ENTITY_REF_NODE:
205                 // m_aNodeType = NodeType::ENTITY_REFERENCE_NODE;
206                 pCNode = static_cast< CNode* >(
207                         new CEntityReference(*this, m_Mutex, pNode));
208             break;
209             case XML_ENTITY_NODE:
210                 // m_aNodeType = NodeType::ENTITY_NODE;
211                 pCNode = static_cast< CNode* >(new CEntity(*this, m_Mutex,
212                             reinterpret_cast<xmlEntityPtr>(pNode)));
213             break;
214             case XML_PI_NODE:
215                 // m_aNodeType = NodeType::PROCESSING_INSTRUCTION_NODE;
216                 pCNode = static_cast< CNode* >(
217                         new CProcessingInstruction(*this, m_Mutex, pNode));
218             break;
219             case XML_COMMENT_NODE:
220                 // m_aNodeType = NodeType::COMMENT_NODE;
221                 pCNode = static_cast< CNode* >(
222                         new CComment(*this, m_Mutex, pNode));
223             break;
224             case XML_DOCUMENT_NODE:
225                 // m_aNodeType = NodeType::DOCUMENT_NODE;
226                 OSL_ENSURE(false, "CDocument::GetCNode is not supposed to"
227                         " create a CDocument!!!");
228                 pCNode = static_cast< CNode* >(new CDocument(
229                             reinterpret_cast<xmlDocPtr>(pNode)));
230             break;
231             case XML_DOCUMENT_TYPE_NODE:
232             case XML_DTD_NODE:
233                 // m_aNodeType = NodeType::DOCUMENT_TYPE_NODE;
234                 pCNode = static_cast< CNode* >(new CDocumentType(*this, m_Mutex,
235                             reinterpret_cast<xmlDtdPtr>(pNode)));
236             break;
237             case XML_DOCUMENT_FRAG_NODE:
238                 // m_aNodeType = NodeType::DOCUMENT_FRAGMENT_NODE;
239                 pCNode = static_cast< CNode* >(
240                         new CDocumentFragment(*this, m_Mutex, pNode));
241             break;
242             case XML_NOTATION_NODE:
243                 // m_aNodeType = NodeType::NOTATION_NODE;
244                 pCNode = static_cast< CNode* >(new CNotation(*this, m_Mutex,
245                             reinterpret_cast<xmlNotationPtr>(pNode)));
246             break;
247             case XML_ATTRIBUTE_NODE:
248                 // m_aNodeType = NodeType::ATTRIBUTE_NODE;
249                 pCNode = static_cast< CNode* >(new CAttr(*this, m_Mutex,
250                             reinterpret_cast<xmlAttrPtr>(pNode)));
251             break;
252             // unsupported node types
253             case XML_HTML_DOCUMENT_NODE:
254             case XML_ELEMENT_DECL:
255             case XML_ATTRIBUTE_DECL:
256             case XML_ENTITY_DECL:
257             case XML_NAMESPACE_DECL:
258             default:
259             break;
260         }
261 
262         if (pCNode != 0) {
263             bool const bInserted = m_NodeMap.insert(
264                     nodemap_t::value_type(pNode,
265                         ::std::make_pair(WeakReference<XNode>(pCNode.get()),
266                         pCNode.get()))
267                 ).second;
268             OSL_ASSERT(bInserted);
269             if (!bInserted) {
270                 // if insertion failed, delete new instance and return null
271                 return 0;
272             }
273         }
274 
275         OSL_ENSURE(pCNode.is(), "no node produced during CDocument::GetCNode!");
276         return pCNode;
277     }
278 
279 
280     CDocument & CDocument::GetOwnerDocument()
281     {
282         return *this;
283     }
284 
285     void CDocument::saxify(const Reference< XDocumentHandler >& i_xHandler)
286     {
287         i_xHandler->startDocument();
288         for (xmlNodePtr pChild = m_aNodePtr->children;
289                         pChild != 0; pChild = pChild->next) {
290             ::rtl::Reference<CNode> const pNode = GetCNode(pChild);
291             OSL_ENSURE(pNode != 0, "CNode::get returned 0");
292             pNode->saxify(i_xHandler);
293         }
294         i_xHandler->endDocument();
295     }
296 
297     void CDocument::fastSaxify( Context& rContext )
298     {
299         rContext.mxDocHandler->startDocument();
300         for (xmlNodePtr pChild = m_aNodePtr->children;
301                         pChild != 0; pChild = pChild->next) {
302             ::rtl::Reference<CNode> const pNode = GetCNode(pChild);
303             OSL_ENSURE(pNode != 0, "CNode::get returned 0");
304             pNode->fastSaxify(rContext);
305         }
306         rContext.mxDocHandler->endDocument();
307     }
308 
309     bool CDocument::IsChildTypeAllowed(NodeType const nodeType)
310     {
311         switch (nodeType) {
312             case NodeType_PROCESSING_INSTRUCTION_NODE:
313             case NodeType_COMMENT_NODE:
314                 return true;
315             case NodeType_ELEMENT_NODE:
316                  // there may be only one!
317                 return 0 == lcl_getDocumentRootPtr(m_aDocPtr);
318             case NodeType_DOCUMENT_TYPE_NODE:
319                  // there may be only one!
320                 return 0 == lcl_getDocumentType(m_aDocPtr);
321             default:
322                 return false;
323         }
324     }
325 
326 
327 	void SAL_CALL CDocument::addListener(const Reference< XStreamListener >& aListener )
328 		throw (RuntimeException)
329     {
330         ::osl::MutexGuard const g(m_Mutex);
331 
332 		m_streamListeners.insert(aListener);
333 	}
334 
335 	void SAL_CALL CDocument::removeListener(const Reference< XStreamListener >& aListener )
336 		throw (RuntimeException)
337     {
338         ::osl::MutexGuard const g(m_Mutex);
339 
340 		m_streamListeners.erase(aListener);
341 	}
342 
343 	// IO context functions for libxml2 interaction
344 	typedef struct {
345 		Reference< XOutputStream > stream;
346 		bool allowClose;
347 	} IOContext;
348 
349     extern "C" {
350 	// write callback
351 	// int xmlOutputWriteCallback (void * context, const char * buffer, int len)
352     static int writeCallback(void *context, const char* buffer, int len){
353 		// create a sequence and write it to the stream
354 		IOContext *pContext = static_cast<IOContext*>(context);
355 		Sequence<sal_Int8> bs(reinterpret_cast<const sal_Int8*>(buffer), len);
356 		pContext->stream->writeBytes(bs);
357 		return len;
358 	}
359 
360 	// clsoe callback
361 	//int xmlOutputCloseCallback (void * context)
362 	static int closeCallback(void *context)
363 	{
364 		IOContext *pContext = static_cast<IOContext*>(context);
365 		if (pContext->allowClose) {
366 			pContext->stream->closeOutput();
367 		}
368 		return 0;
369 	}
370     } // extern "C"
371 
372 	void SAL_CALL CDocument::start()
373 		throw (RuntimeException)
374 	{
375         listenerlist_t streamListeners;
376         {
377             ::osl::MutexGuard const g(m_Mutex);
378 
379             if (! m_rOutputStream.is()) { throw RuntimeException(); }
380             streamListeners = m_streamListeners;
381         }
382 
383 		// notify listeners about start
384 		listenerlist_t::const_iterator iter1 = streamListeners.begin();
385         while (iter1 != streamListeners.end()) {
386 			Reference< XStreamListener > aListener = *iter1;
387 			aListener->started();
388 			iter1++;
389 		}
390 
391         {
392             ::osl::MutexGuard const g(m_Mutex);
393 
394             // check again! could have been reset...
395             if (! m_rOutputStream.is()) { throw RuntimeException(); }
396 
397             // setup libxml IO and write data to output stream
398             IOContext ioctx = {m_rOutputStream, false};
399             xmlOutputBufferPtr pOut = xmlOutputBufferCreateIO(
400                 writeCallback, closeCallback, &ioctx, NULL);
401             xmlSaveFileTo(pOut, m_aNodePtr->doc, NULL);
402         }
403 
404 		// call listeners
405         listenerlist_t::const_iterator iter2 = streamListeners.begin();
406         while (iter2 != streamListeners.end()) {
407 			Reference< XStreamListener > aListener = *iter2;
408 			aListener->closed();
409 			iter2++;
410 		}
411 	}
412 
413 	void SAL_CALL CDocument::terminate()
414 		throw (RuntimeException)
415 	{
416 		// not supported
417 	}
418 
419 	void SAL_CALL CDocument::setOutputStream( const Reference< XOutputStream >& aStream )
420 		throw (RuntimeException)
421 	{
422         ::osl::MutexGuard const g(m_Mutex);
423 
424 		m_rOutputStream = aStream;
425 	}
426 
427 	Reference< XOutputStream > SAL_CALL  CDocument::getOutputStream() throw (RuntimeException)
428 	{
429         ::osl::MutexGuard const g(m_Mutex);
430 
431 		return m_rOutputStream;
432 	}
433 
434     // Creates an Attr of the given name.
435     Reference< XAttr > SAL_CALL CDocument::createAttribute(const OUString& name)
436         throw (RuntimeException, DOMException)
437     {
438         ::osl::MutexGuard const g(m_Mutex);
439 
440         OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8);
441         xmlChar *xName = (xmlChar*)o1.getStr();
442         xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr, xName, NULL);
443         ::rtl::Reference< CAttr > const pCAttr(
444             dynamic_cast< CAttr* >(GetCNode(
445                     reinterpret_cast<xmlNodePtr>(pAttr)).get()));
446         pCAttr->m_bUnlinked = true;
447         return pCAttr.get();
448     };
449 
450     // Creates an attribute of the given qualified name and namespace URI.
451     Reference< XAttr > SAL_CALL CDocument::createAttributeNS(
452             const OUString& ns, const OUString& qname)
453         throw (RuntimeException, DOMException)
454     {
455         ::osl::MutexGuard const g(m_Mutex);
456 
457         // libxml does not allow a NS definition to be attached to an
458         // attribute node - which is a good thing, since namespaces are
459         // only defined as parts of element nodes
460         // thus the namespace data is stored in CAttr::m_pNamespace
461         sal_Int32 i = qname.indexOf(':');
462         OString oPrefix, oName, oUri;
463         if (i != -1)
464         {
465             oPrefix = OUStringToOString(qname.copy(0, i), RTL_TEXTENCODING_UTF8);
466             oName = OUStringToOString(qname.copy(i+1, qname.getLength()-i-1), RTL_TEXTENCODING_UTF8);
467         }
468         else
469         {
470             oName = OUStringToOString(qname, RTL_TEXTENCODING_UTF8);
471         }
472         oUri = OUStringToOString(ns, RTL_TEXTENCODING_UTF8);
473         xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr,
474                 reinterpret_cast<xmlChar const*>(oName.getStr()), 0);
475         ::rtl::Reference< CAttr > const pCAttr(
476             dynamic_cast< CAttr* >(GetCNode(
477                     reinterpret_cast<xmlNodePtr>(pAttr)).get()));
478         if (!pCAttr.is()) { throw RuntimeException(); }
479         // store the namespace data!
480         pCAttr->m_pNamespace.reset( new stringpair_t(oUri, oPrefix) );
481         pCAttr->m_bUnlinked = true;
482 
483         return pCAttr.get();
484     };
485 
486     // Creates a CDATASection node whose value is the specified string.
487     Reference< XCDATASection > SAL_CALL CDocument::createCDATASection(const OUString& data)
488         throw (RuntimeException)
489     {
490         ::osl::MutexGuard const g(m_Mutex);
491 
492         OString const oData(
493                 ::rtl::OUStringToOString(data, RTL_TEXTENCODING_UTF8));
494         xmlChar const*const pData =
495             reinterpret_cast<xmlChar const*>(oData.getStr());
496         xmlNodePtr const pText =
497             xmlNewCDataBlock(m_aDocPtr, pData, strlen(oData.getStr()));
498         Reference< XCDATASection > const xRet(
499             static_cast< XNode* >(GetCNode(pText).get()),
500             UNO_QUERY_THROW);
501         return xRet;
502     }
503 
504     // Creates a Comment node given the specified string.
505     Reference< XComment > SAL_CALL CDocument::createComment(const OUString& data)
506         throw (RuntimeException)
507     {
508         ::osl::MutexGuard const g(m_Mutex);
509 
510         OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
511         xmlChar *xData = (xmlChar*)o1.getStr();
512         xmlNodePtr pComment = xmlNewDocComment(m_aDocPtr, xData);
513         Reference< XComment > const xRet(
514             static_cast< XNode* >(GetCNode(pComment).get()),
515             UNO_QUERY_THROW);
516         return xRet;
517     }
518 
519     //Creates an empty DocumentFragment object.
520     Reference< XDocumentFragment > SAL_CALL CDocument::createDocumentFragment()
521         throw (RuntimeException)
522     {
523         ::osl::MutexGuard const g(m_Mutex);
524 
525         xmlNodePtr pFrag = xmlNewDocFragment(m_aDocPtr);
526         Reference< XDocumentFragment > const xRet(
527             static_cast< XNode* >(GetCNode(pFrag).get()),
528             UNO_QUERY_THROW);
529         return xRet;
530     }
531 
532     // Creates an element of the type specified.
533     Reference< XElement > SAL_CALL CDocument::createElement(const OUString& tagName)
534         throw (RuntimeException, DOMException)
535     {
536         ::osl::MutexGuard const g(m_Mutex);
537 
538         OString o1 = OUStringToOString(tagName, RTL_TEXTENCODING_UTF8);
539         xmlChar *xName = (xmlChar*)o1.getStr();
540         xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, NULL, xName, NULL);
541         Reference< XElement > const xRet(
542             static_cast< XNode* >(GetCNode(pNode).get()),
543             UNO_QUERY_THROW);
544         return xRet;
545     }
546 
547     // Creates an element of the given qualified name and namespace URI.
548     Reference< XElement > SAL_CALL CDocument::createElementNS(
549             const OUString& ns, const OUString& qname)
550         throw (RuntimeException, DOMException)
551     {
552         ::osl::MutexGuard const g(m_Mutex);
553 
554         sal_Int32 i = qname.indexOf(':');
555         if (ns.getLength() == 0) throw RuntimeException();
556         xmlChar *xPrefix;
557         xmlChar *xName;
558         OString o1, o2, o3;
559         if ( i != -1) {
560             o1 = OUStringToOString(qname.copy(0, i), RTL_TEXTENCODING_UTF8);
561             xPrefix = (xmlChar*)o1.getStr();
562             o2 = OUStringToOString(qname.copy(i+1, qname.getLength()-i-1), RTL_TEXTENCODING_UTF8);
563             xName = (xmlChar*)o2.getStr();
564         } else {
565             // default prefix
566             xPrefix = (xmlChar*)"";
567             o2 = OUStringToOString(qname, RTL_TEXTENCODING_UTF8);
568             xName = (xmlChar*)o2.getStr();
569         }
570         o3 = OUStringToOString(ns, RTL_TEXTENCODING_UTF8);
571         xmlChar *xUri = (xmlChar*)o3.getStr();
572 
573         // xmlNsPtr aNsPtr = xmlNewReconciledNs?
574         // xmlNsPtr aNsPtr = xmlNewGlobalNs?
575         xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, NULL, xName, NULL);
576         xmlNsPtr const pNs = xmlNewNs(pNode, xUri, xPrefix);
577         xmlSetNs(pNode, pNs);
578         Reference< XElement > const xRet(
579             static_cast< XNode* >(GetCNode(pNode).get()),
580             UNO_QUERY_THROW);
581         return xRet;
582     }
583 
584     //Creates an EntityReference object.
585     Reference< XEntityReference > SAL_CALL CDocument::createEntityReference(const OUString& name)
586         throw (RuntimeException, DOMException)
587     {
588         ::osl::MutexGuard const g(m_Mutex);
589 
590         OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8);
591         xmlChar *xName = (xmlChar*)o1.getStr();
592         xmlNodePtr const pNode = xmlNewReference(m_aDocPtr, xName);
593         Reference< XEntityReference > const xRet(
594             static_cast< XNode* >(GetCNode(pNode).get()),
595             UNO_QUERY_THROW);
596         return xRet;
597     }
598 
599     // Creates a ProcessingInstruction node given the specified name and
600     // data strings.
601     Reference< XProcessingInstruction > SAL_CALL CDocument::createProcessingInstruction(
602             const OUString& target, const OUString& data)
603         throw (RuntimeException, DOMException)
604     {
605         ::osl::MutexGuard const g(m_Mutex);
606 
607         OString o1 = OUStringToOString(target, RTL_TEXTENCODING_UTF8);
608         xmlChar *xTarget = (xmlChar*)o1.getStr();
609         OString o2 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
610         xmlChar *xData = (xmlChar*)o2.getStr();
611         xmlNodePtr const pNode = xmlNewDocPI(m_aDocPtr, xTarget, xData);
612         pNode->doc = m_aDocPtr;
613         Reference< XProcessingInstruction > const xRet(
614             static_cast< XNode* >(GetCNode(pNode).get()),
615             UNO_QUERY_THROW);
616         return xRet;
617     }
618 
619     // Creates a Text node given the specified string.
620     Reference< XText > SAL_CALL CDocument::createTextNode(const OUString& data)
621         throw (RuntimeException)
622     {
623         ::osl::MutexGuard const g(m_Mutex);
624 
625         OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
626         xmlChar *xData = (xmlChar*)o1.getStr();
627         xmlNodePtr const pNode = xmlNewDocText(m_aDocPtr, xData);
628         Reference< XText > const xRet(
629             static_cast< XNode* >(GetCNode(pNode).get()),
630             UNO_QUERY_THROW);
631         return xRet;
632     }
633 
634     // The Document Type Declaration (see DocumentType) associated with this
635     // document.
636     Reference< XDocumentType > SAL_CALL CDocument::getDoctype()
637         throw (RuntimeException)
638     {
639         ::osl::MutexGuard const g(m_Mutex);
640 
641         xmlNodePtr const pDocType(lcl_getDocumentType(m_aDocPtr));
642         Reference< XDocumentType > const xRet(
643             static_cast< XNode* >(GetCNode(pDocType).get()),
644             UNO_QUERY);
645         return xRet;
646     }
647 
648     // This is a convenience attribute that allows direct access to the child
649     // node that is the root element of the document.
650     Reference< XElement > SAL_CALL CDocument::getDocumentElement()
651         throw (RuntimeException)
652     {
653         ::osl::MutexGuard const g(m_Mutex);
654 
655         xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr);
656         if (!pNode) { return 0; }
657         Reference< XElement > const xRet(
658             static_cast< XNode* >(GetCNode(pNode).get()),
659             UNO_QUERY);
660         return xRet;
661     }
662 
663     static xmlNodePtr
664     lcl_search_element_by_id(const xmlNodePtr cur, const xmlChar* id)
665     {
666         if (cur == NULL)
667             return NULL;
668         // look in current node
669         if (cur->type == XML_ELEMENT_NODE)
670         {
671             xmlAttrPtr a = cur->properties;
672             while (a != NULL)
673             {
674                 if (a->atype == XML_ATTRIBUTE_ID) {
675                     if (strcmp((char*)a->children->content, (char*)id) == 0)
676                         return cur;
677                 }
678                 a = a->next;
679             }
680         }
681         // look in children
682         xmlNodePtr result = lcl_search_element_by_id(cur->children, id);
683         if (result != NULL)
684             return result;
685         result = lcl_search_element_by_id(cur->next, id);
686             return result;
687     }
688 
689     // Returns the Element whose ID is given by elementId.
690     Reference< XElement > SAL_CALL
691     CDocument::getElementById(const OUString& elementId)
692         throw (RuntimeException)
693     {
694         ::osl::MutexGuard const g(m_Mutex);
695 
696         // search the tree for an element with the given ID
697         OString o1 = OUStringToOString(elementId, RTL_TEXTENCODING_UTF8);
698         xmlChar *xId = (xmlChar*)o1.getStr();
699         xmlNodePtr const pStart = lcl_getDocumentRootPtr(m_aDocPtr);
700         if (!pStart) { return 0; }
701         xmlNodePtr const pNode = lcl_search_element_by_id(pStart, xId);
702         Reference< XElement > const xRet(
703             static_cast< XNode* >(GetCNode(pNode).get()),
704             UNO_QUERY);
705         return xRet;
706     }
707 
708 
709     Reference< XNodeList > SAL_CALL
710     CDocument::getElementsByTagName(OUString const& rTagname)
711             throw (RuntimeException)
712     {
713         ::osl::MutexGuard const g(m_Mutex);
714 
715         Reference< XNodeList > const xRet(
716             new CElementList(this->GetDocumentElement(), m_Mutex, rTagname));
717         return xRet;
718     }
719 
720     Reference< XNodeList > SAL_CALL CDocument::getElementsByTagNameNS(
721             OUString const& rNamespaceURI, OUString const& rLocalName)
722         throw (RuntimeException)
723     {
724         ::osl::MutexGuard const g(m_Mutex);
725 
726         Reference< XNodeList > const xRet(
727             new CElementList(this->GetDocumentElement(), m_Mutex,
728                 rLocalName, &rNamespaceURI));
729         return xRet;
730     }
731 
732     Reference< XDOMImplementation > SAL_CALL CDocument::getImplementation()
733         throw (RuntimeException)
734     {
735         // does not need mutex currently
736         return Reference< XDOMImplementation >(CDOMImplementation::get());
737     }
738 
739     // helper function to recursively import siblings
740     static void lcl_ImportSiblings(
741         Reference< XDocument > const& xTargetDocument,
742         Reference< XNode > const& xTargetParent,
743         Reference< XNode > const& xChild)
744     {
745         Reference< XNode > xSibling = xChild;
746         while (xSibling.is())
747         {
748             Reference< XNode > const xTmp(
749                     xTargetDocument->importNode(xSibling, sal_True));
750             xTargetParent->appendChild(xTmp);
751             xSibling = xSibling->getNextSibling();
752         }
753     }
754 
755     static Reference< XNode >
756     lcl_ImportNode( Reference< XDocument > const& xDocument,
757             Reference< XNode > const& xImportedNode, sal_Bool deep)
758     {
759         Reference< XNode > xNode;
760         NodeType aNodeType = xImportedNode->getNodeType();
761         switch (aNodeType)
762         {
763         case NodeType_ATTRIBUTE_NODE:
764         {
765             Reference< XAttr > const xAttr(xImportedNode, UNO_QUERY_THROW);
766             Reference< XAttr > const xNew =
767                 xDocument->createAttribute(xAttr->getName());
768             xNew->setValue(xAttr->getValue());
769             xNode.set(xNew, UNO_QUERY);
770             break;
771         }
772         case NodeType_CDATA_SECTION_NODE:
773         {
774             Reference< XCDATASection > const xCData(xImportedNode,
775                     UNO_QUERY_THROW);
776             Reference< XCDATASection > const xNewCData =
777                 xDocument->createCDATASection(xCData->getData());
778             xNode.set(xNewCData, UNO_QUERY);
779             break;
780         }
781         case NodeType_COMMENT_NODE:
782         {
783             Reference< XComment > const xComment(xImportedNode,
784                     UNO_QUERY_THROW);
785             Reference< XComment > const xNewComment =
786                 xDocument->createComment(xComment->getData());
787             xNode.set(xNewComment, UNO_QUERY);
788             break;
789         }
790         case NodeType_DOCUMENT_FRAGMENT_NODE:
791         {
792             Reference< XDocumentFragment > const xFrag(xImportedNode,
793                     UNO_QUERY_THROW);
794             Reference< XDocumentFragment > const xNewFrag =
795                 xDocument->createDocumentFragment();
796             xNode.set(xNewFrag, UNO_QUERY);
797             break;
798         }
799         case NodeType_ELEMENT_NODE:
800         {
801             Reference< XElement > const xElement(xImportedNode,
802                     UNO_QUERY_THROW);
803             OUString const aNsUri = xImportedNode->getNamespaceURI();
804             OUString const aNsPrefix = xImportedNode->getPrefix();
805             OUString aQName = xElement->getTagName();
806             Reference< XElement > xNewElement;
807             if (aNsUri.getLength() > 0)
808             {
809                 if (aNsPrefix.getLength() > 0) {
810                     aQName = aNsPrefix + OUString::createFromAscii(":")
811                                 + aQName;
812                 }
813                 xNewElement = xDocument->createElementNS(aNsUri, aQName);
814             } else {
815                 xNewElement = xDocument->createElement(aQName);
816             }
817 
818             // get attributes
819             if (xElement->hasAttributes())
820             {
821                 Reference< XNamedNodeMap > attribs = xElement->getAttributes();
822                 for (sal_Int32 i = 0; i < attribs->getLength(); i++)
823                 {
824                     Reference< XAttr > const curAttr(attribs->item(i),
825                             UNO_QUERY_THROW);
826                     OUString const aAttrUri = curAttr->getNamespaceURI();
827                     OUString const aAttrPrefix = curAttr->getPrefix();
828                     OUString aAttrName = curAttr->getName();
829                     OUString const sValue = curAttr->getValue();
830                     if (aAttrUri.getLength() > 0)
831                     {
832                         if (aAttrPrefix.getLength() > 0) {
833                             aAttrName = aAttrPrefix +
834                                 OUString::createFromAscii(":") + aAttrName;
835                         }
836                         xNewElement->setAttributeNS(
837                                 aAttrUri, aAttrName, sValue);
838                     } else {
839                         xNewElement->setAttribute(aAttrName, sValue);
840                     }
841                 }
842             }
843             xNode.set(xNewElement, UNO_QUERY);
844             break;
845         }
846         case NodeType_ENTITY_REFERENCE_NODE:
847         {
848             Reference< XEntityReference > const xRef(xImportedNode,
849                     UNO_QUERY_THROW);
850             Reference< XEntityReference > const xNewRef(
851                 xDocument->createEntityReference(xRef->getNodeName()));
852             xNode.set(xNewRef, UNO_QUERY);
853             break;
854         }
855         case NodeType_PROCESSING_INSTRUCTION_NODE:
856         {
857             Reference< XProcessingInstruction > const xPi(xImportedNode,
858                     UNO_QUERY_THROW);
859             Reference< XProcessingInstruction > const xNewPi(
860                 xDocument->createProcessingInstruction(
861                     xPi->getTarget(), xPi->getData()));
862             xNode.set(xNewPi, UNO_QUERY);
863             break;
864         }
865         case NodeType_TEXT_NODE:
866         {
867             Reference< XText > const xText(xImportedNode, UNO_QUERY_THROW);
868             Reference< XText > const xNewText(
869                 xDocument->createTextNode(xText->getData()));
870             xNode.set(xNewText, UNO_QUERY);
871             break;
872         }
873         case NodeType_ENTITY_NODE:
874         case NodeType_DOCUMENT_NODE:
875         case NodeType_DOCUMENT_TYPE_NODE:
876         case NodeType_NOTATION_NODE:
877         default:
878             // can't be imported
879             throw RuntimeException();
880 
881         }
882         if (deep)
883         {
884             // get children and import them
885             Reference< XNode > const xChild = xImportedNode->getFirstChild();
886             if (xChild.is())
887             {
888                 lcl_ImportSiblings(xDocument, xNode, xChild);
889             }
890         }
891 
892         /* DOMNodeInsertedIntoDocument
893          * Fired when a node is being inserted into a document,
894          * either through direct insertion of the Node or insertion of a
895          * subtree in which it is contained. This event is dispatched after
896          * the insertion has taken place. The target of this event is the node
897          * being inserted. If the Node is being directly inserted the DOMNodeInserted
898          * event will fire before the DOMNodeInsertedIntoDocument event.
899          *   Bubbles: No
900          *   Cancelable: No
901          *   Context Info: None
902          */
903         if (xNode.is())
904         {
905             Reference< XDocumentEvent > const xDocevent(xDocument, UNO_QUERY);
906             Reference< XMutationEvent > const event(xDocevent->createEvent(
907                 OUString::createFromAscii("DOMNodeInsertedIntoDocument")),
908                 UNO_QUERY_THROW);
909             event->initMutationEvent(
910                 OUString::createFromAscii("DOMNodeInsertedIntoDocument")
911                 , sal_True, sal_False, Reference< XNode >(),
912                 OUString(), OUString(), OUString(), (AttrChangeType)0 );
913             Reference< XEventTarget > const xDocET(xDocument, UNO_QUERY);
914             xDocET->dispatchEvent(Reference< XEvent >(event, UNO_QUERY));
915         }
916 
917         return xNode;
918     }
919 
920     Reference< XNode > SAL_CALL CDocument::importNode(
921             Reference< XNode > const& xImportedNode, sal_Bool deep)
922         throw (RuntimeException, DOMException)
923     {
924         if (!xImportedNode.is()) { throw RuntimeException(); }
925 
926         // NB: this whole operation inherently accesses 2 distinct documents.
927         // The imported node could even be from a different DOM implementation,
928         // so this implementation cannot make any assumptions about the
929         // locking strategy of the imported node.
930         // So the import takes no lock on this document;
931         // it only calls UNO methods on this document that temporarily
932         // lock the document, and UNO methods on the imported node that
933         // may temporarily lock the other document.
934         // As a consequence, the import is not atomic with regard to
935         // concurrent modifications of either document, but it should not
936         // deadlock.
937         // To ensure that no members are accessed, the implementation is in
938         // static non-member functions.
939 
940         Reference< XDocument > const xDocument(this);
941         // already in doc?
942         if (xImportedNode->getOwnerDocument() == xDocument) {
943             return xImportedNode;
944         }
945 
946         Reference< XNode > const xNode(
947             lcl_ImportNode(xDocument, xImportedNode, deep) );
948         return xNode;
949     }
950 
951 
952     OUString SAL_CALL CDocument::getNodeName()throw (RuntimeException)
953     {
954         // does not need mutex currently
955         return OUString::createFromAscii("#document");
956     }
957 
958     OUString SAL_CALL CDocument::getNodeValue() throw (RuntimeException)
959     {
960         // does not need mutex currently
961         return OUString();
962     }
963 
964     Reference< XNode > SAL_CALL CDocument::cloneNode(sal_Bool bDeep)
965         throw (RuntimeException)
966     {
967         ::osl::MutexGuard const g(m_rMutex);
968 
969         OSL_ASSERT(0 != m_aNodePtr);
970         if (0 == m_aNodePtr) {
971             return 0;
972         }
973         xmlDocPtr const pClone(xmlCopyDoc(m_aDocPtr, (bDeep) ? 1 : 0));
974         if (0 == pClone) { return 0; }
975         Reference< XNode > const xRet(
976             static_cast<CNode*>(CDocument::CreateCDocument(pClone).get()));
977         return xRet;
978     }
979 
980     Reference< XEvent > SAL_CALL CDocument::createEvent(const OUString& aType) throw (RuntimeException)
981     {
982         // does not need mutex currently
983         events::CEvent *pEvent = 0;
984         if (
985             aType.compareToAscii("DOMSubtreeModified")          == 0||
986             aType.compareToAscii("DOMNodeInserted")             == 0||
987             aType.compareToAscii("DOMNodeRemoved")              == 0||
988             aType.compareToAscii("DOMNodeRemovedFromDocument")  == 0||
989             aType.compareToAscii("DOMNodeInsertedIntoDocument") == 0||
990             aType.compareToAscii("DOMAttrModified")             == 0||
991             aType.compareToAscii("DOMCharacterDataModified")    == 0)
992         {
993             pEvent = new events::CMutationEvent;
994 
995         } else if (
996             aType.compareToAscii("DOMFocusIn")  == 0||
997             aType.compareToAscii("DOMFocusOut") == 0||
998             aType.compareToAscii("DOMActivate") == 0)
999         {
1000             pEvent = new events::CUIEvent;
1001         } else if (
1002             aType.compareToAscii("click")     == 0||
1003             aType.compareToAscii("mousedown") == 0||
1004             aType.compareToAscii("mouseup")   == 0||
1005             aType.compareToAscii("mouseover") == 0||
1006             aType.compareToAscii("mousemove") == 0||
1007             aType.compareToAscii("mouseout")  == 0 )
1008         {
1009             pEvent = new events::CMouseEvent;
1010         }
1011         else // generic event
1012         {
1013             pEvent = new events::CEvent;
1014         }
1015         return Reference< XEvent >(pEvent);
1016     }
1017 
1018     // ::com::sun::star::xml::sax::XSAXSerializable
1019     void SAL_CALL CDocument::serialize(
1020             const Reference< XDocumentHandler >& i_xHandler,
1021             const Sequence< beans::StringPair >& i_rNamespaces)
1022         throw (RuntimeException, SAXException)
1023     {
1024         ::osl::MutexGuard const g(m_Mutex);
1025 
1026         // add new namespaces to root node
1027         xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr);
1028         if (0 != pRoot) {
1029             const beans::StringPair * pSeq = i_rNamespaces.getConstArray();
1030             for (const beans::StringPair *pNsDef = pSeq;
1031                  pNsDef < pSeq + i_rNamespaces.getLength(); ++pNsDef) {
1032                 OString prefix = OUStringToOString(pNsDef->First,
1033                                     RTL_TEXTENCODING_UTF8);
1034                 OString href   = OUStringToOString(pNsDef->Second,
1035                                     RTL_TEXTENCODING_UTF8);
1036                 // this will only add the ns if it does not exist already
1037                 xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()),
1038                          reinterpret_cast<const xmlChar*>(prefix.getStr()));
1039             }
1040             // eliminate duplicate namespace declarations
1041             nscleanup(pRoot->children, pRoot);
1042         }
1043         saxify(i_xHandler);
1044     }
1045 
1046     // ::com::sun::star::xml::sax::XFastSAXSerializable
1047     void SAL_CALL CDocument::fastSerialize( const Reference< XFastDocumentHandler >& i_xHandler,
1048                                             const Reference< XFastTokenHandler >& i_xTokenHandler,
1049                                             const Sequence< beans::StringPair >& i_rNamespaces,
1050                                             const Sequence< beans::Pair< rtl::OUString, sal_Int32 > >& i_rRegisterNamespaces )
1051         throw (SAXException, RuntimeException)
1052     {
1053         ::osl::MutexGuard const g(m_Mutex);
1054 
1055         // add new namespaces to root node
1056         xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr);
1057         if (0 != pRoot) {
1058             const beans::StringPair * pSeq = i_rNamespaces.getConstArray();
1059             for (const beans::StringPair *pNsDef = pSeq;
1060                  pNsDef < pSeq + i_rNamespaces.getLength(); ++pNsDef) {
1061                 OString prefix = OUStringToOString(pNsDef->First,
1062                                     RTL_TEXTENCODING_UTF8);
1063                 OString href   = OUStringToOString(pNsDef->Second,
1064                                     RTL_TEXTENCODING_UTF8);
1065                 // this will only add the ns if it does not exist already
1066                 xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()),
1067                          reinterpret_cast<const xmlChar*>(prefix.getStr()));
1068             }
1069             // eliminate duplicate namespace declarations
1070             nscleanup(pRoot->children, pRoot);
1071         }
1072 
1073         Context aContext(i_xHandler,
1074                          i_xTokenHandler);
1075 
1076         // register namespace ids
1077         const beans::Pair<OUString,sal_Int32>* pSeq = i_rRegisterNamespaces.getConstArray();
1078         for (const beans::Pair<OUString,sal_Int32>* pNs = pSeq;
1079              pNs < pSeq + i_rRegisterNamespaces.getLength(); ++pNs)
1080         {
1081             OSL_ENSURE(pNs->Second >= FastToken::NAMESPACE,
1082                        "CDocument::fastSerialize(): invalid NS token id");
1083             aContext.maNamespaceMap[ pNs->First ] = pNs->Second;
1084         }
1085 
1086         fastSaxify(aContext);
1087     }
1088 }
1089