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