xref: /trunk/main/unoxml/source/dom/document.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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     {
325         ::osl::MutexGuard const g(m_Mutex);
326 
327         m_streamListeners.insert(aListener);
328     }
329 
removeListener(const Reference<XStreamListener> & aListener)330     void SAL_CALL CDocument::removeListener(const Reference< XStreamListener >& aListener )
331     {
332         ::osl::MutexGuard const g(m_Mutex);
333 
334         m_streamListeners.erase(aListener);
335     }
336 
337     // IO context functions for libxml2 interaction
338     typedef struct {
339         Reference< XOutputStream > stream;
340         bool allowClose;
341     } IOContext;
342 
343     extern "C" {
344     // write callback
345     // int xmlOutputWriteCallback (void * context, const char * buffer, int len)
writeCallback(void * context,const char * buffer,int len)346     static int writeCallback(void *context, const char* buffer, int len){
347         // create a sequence and write it to the stream
348         IOContext *pContext = static_cast<IOContext*>(context);
349         Sequence<sal_Int8> bs(reinterpret_cast<const sal_Int8*>(buffer), len);
350         pContext->stream->writeBytes(bs);
351         return len;
352     }
353 
354     // clsoe callback
355     //int xmlOutputCloseCallback (void * context)
closeCallback(void * context)356     static int closeCallback(void *context)
357     {
358         IOContext *pContext = static_cast<IOContext*>(context);
359         if (pContext->allowClose) {
360             pContext->stream->closeOutput();
361         }
362         return 0;
363     }
364     } // extern "C"
365 
start()366     void SAL_CALL CDocument::start()
367     {
368         listenerlist_t streamListeners;
369         {
370             ::osl::MutexGuard const g(m_Mutex);
371 
372             if (! m_rOutputStream.is()) { throw RuntimeException(); }
373             streamListeners = m_streamListeners;
374         }
375 
376         // notify listeners about start
377         listenerlist_t::const_iterator iter1 = streamListeners.begin();
378         while (iter1 != streamListeners.end()) {
379             Reference< XStreamListener > aListener = *iter1;
380             aListener->started();
381             iter1++;
382         }
383 
384         {
385             ::osl::MutexGuard const g(m_Mutex);
386 
387             // check again! could have been reset...
388             if (! m_rOutputStream.is()) { throw RuntimeException(); }
389 
390             // setup libxml IO and write data to output stream
391             IOContext ioctx = {m_rOutputStream, false};
392             xmlOutputBufferPtr pOut = xmlOutputBufferCreateIO(
393                 writeCallback, closeCallback, &ioctx, NULL);
394             xmlSaveFileTo(pOut, m_aNodePtr->doc, NULL);
395         }
396 
397         // call listeners
398         listenerlist_t::const_iterator iter2 = streamListeners.begin();
399         while (iter2 != streamListeners.end()) {
400             Reference< XStreamListener > aListener = *iter2;
401             aListener->closed();
402             iter2++;
403         }
404     }
405 
terminate()406     void SAL_CALL CDocument::terminate()
407     {
408         // not supported
409     }
410 
setOutputStream(const Reference<XOutputStream> & aStream)411     void SAL_CALL CDocument::setOutputStream( const Reference< XOutputStream >& aStream )
412     {
413         ::osl::MutexGuard const g(m_Mutex);
414 
415         m_rOutputStream = aStream;
416     }
417 
getOutputStream()418     Reference< XOutputStream > SAL_CALL  CDocument::getOutputStream()
419     {
420         ::osl::MutexGuard const g(m_Mutex);
421 
422         return m_rOutputStream;
423     }
424 
425     // Creates an Attr of the given name.
createAttribute(const OUString & name)426     Reference< XAttr > SAL_CALL CDocument::createAttribute(const OUString& name)
427     {
428         ::osl::MutexGuard const g(m_Mutex);
429 
430         OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8);
431         xmlChar *xName = (xmlChar*)o1.getStr();
432         xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr, xName, NULL);
433         ::rtl::Reference< CAttr > const pCAttr(
434             dynamic_cast< CAttr* >(GetCNode(
435                     reinterpret_cast<xmlNodePtr>(pAttr)).get()));
436         pCAttr->m_bUnlinked = true;
437         return pCAttr.get();
438     };
439 
440     // Creates an attribute of the given qualified name and namespace URI.
createAttributeNS(const OUString & ns,const OUString & qname)441     Reference< XAttr > SAL_CALL CDocument::createAttributeNS(
442             const OUString& ns, const OUString& qname)
443     {
444         ::osl::MutexGuard const g(m_Mutex);
445 
446         // libxml does not allow a NS definition to be attached to an
447         // attribute node - which is a good thing, since namespaces are
448         // only defined as parts of element nodes
449         // thus the namespace data is stored in CAttr::m_pNamespace
450         sal_Int32 i = qname.indexOf(':');
451         OString oPrefix, oName, oUri;
452         if (i != -1)
453         {
454             oPrefix = OUStringToOString(qname.copy(0, i), RTL_TEXTENCODING_UTF8);
455             oName = OUStringToOString(qname.copy(i+1, qname.getLength()-i-1), RTL_TEXTENCODING_UTF8);
456         }
457         else
458         {
459             oName = OUStringToOString(qname, RTL_TEXTENCODING_UTF8);
460         }
461         oUri = OUStringToOString(ns, RTL_TEXTENCODING_UTF8);
462         xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr,
463                 reinterpret_cast<xmlChar const*>(oName.getStr()), 0);
464         ::rtl::Reference< CAttr > const pCAttr(
465             dynamic_cast< CAttr* >(GetCNode(
466                     reinterpret_cast<xmlNodePtr>(pAttr)).get()));
467         if (!pCAttr.is()) { throw RuntimeException(); }
468         // store the namespace data!
469         pCAttr->m_pNamespace.reset( new stringpair_t(oUri, oPrefix) );
470         pCAttr->m_bUnlinked = true;
471 
472         return pCAttr.get();
473     };
474 
475     // Creates a CDATASection node whose value is the specified string.
createCDATASection(const OUString & data)476     Reference< XCDATASection > SAL_CALL CDocument::createCDATASection(const OUString& data)
477     {
478         ::osl::MutexGuard const g(m_Mutex);
479 
480         OString const oData(
481                 ::rtl::OUStringToOString(data, RTL_TEXTENCODING_UTF8));
482         xmlChar const*const pData =
483             reinterpret_cast<xmlChar const*>(oData.getStr());
484         xmlNodePtr const pText =
485             xmlNewCDataBlock(m_aDocPtr, pData, strlen(oData.getStr()));
486         Reference< XCDATASection > const xRet(
487             static_cast< XNode* >(GetCNode(pText).get()),
488             UNO_QUERY_THROW);
489         return xRet;
490     }
491 
492     // Creates a Comment node given the specified string.
createComment(const OUString & data)493     Reference< XComment > SAL_CALL CDocument::createComment(const OUString& data)
494     {
495         ::osl::MutexGuard const g(m_Mutex);
496 
497         OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
498         xmlChar *xData = (xmlChar*)o1.getStr();
499         xmlNodePtr pComment = xmlNewDocComment(m_aDocPtr, xData);
500         Reference< XComment > const xRet(
501             static_cast< XNode* >(GetCNode(pComment).get()),
502             UNO_QUERY_THROW);
503         return xRet;
504     }
505 
506     //Creates an empty DocumentFragment object.
createDocumentFragment()507     Reference< XDocumentFragment > SAL_CALL CDocument::createDocumentFragment()
508     {
509         ::osl::MutexGuard const g(m_Mutex);
510 
511         xmlNodePtr pFrag = xmlNewDocFragment(m_aDocPtr);
512         Reference< XDocumentFragment > const xRet(
513             static_cast< XNode* >(GetCNode(pFrag).get()),
514             UNO_QUERY_THROW);
515         return xRet;
516     }
517 
518     // Creates an element of the type specified.
createElement(const OUString & tagName)519     Reference< XElement > SAL_CALL CDocument::createElement(const OUString& tagName)
520     {
521         ::osl::MutexGuard const g(m_Mutex);
522 
523         OString o1 = OUStringToOString(tagName, RTL_TEXTENCODING_UTF8);
524         xmlChar *xName = (xmlChar*)o1.getStr();
525         xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, NULL, xName, NULL);
526         Reference< XElement > const xRet(
527             static_cast< XNode* >(GetCNode(pNode).get()),
528             UNO_QUERY_THROW);
529         return xRet;
530     }
531 
532     // Creates an element of the given qualified name and namespace URI.
createElementNS(const OUString & ns,const OUString & qname)533     Reference< XElement > SAL_CALL CDocument::createElementNS(
534             const OUString& ns, const OUString& qname)
535     {
536         ::osl::MutexGuard const g(m_Mutex);
537 
538         sal_Int32 i = qname.indexOf(':');
539         if (ns.getLength() == 0) throw RuntimeException();
540         xmlChar *xPrefix;
541         xmlChar *xName;
542         OString o1, o2, o3;
543         if ( i != -1) {
544             o1 = OUStringToOString(qname.copy(0, i), RTL_TEXTENCODING_UTF8);
545             xPrefix = (xmlChar*)o1.getStr();
546             o2 = OUStringToOString(qname.copy(i+1, qname.getLength()-i-1), RTL_TEXTENCODING_UTF8);
547             xName = (xmlChar*)o2.getStr();
548         } else {
549             // default prefix
550             xPrefix = (xmlChar*)"";
551             o2 = OUStringToOString(qname, RTL_TEXTENCODING_UTF8);
552             xName = (xmlChar*)o2.getStr();
553         }
554         o3 = OUStringToOString(ns, RTL_TEXTENCODING_UTF8);
555         xmlChar *xUri = (xmlChar*)o3.getStr();
556 
557         // xmlNsPtr aNsPtr = xmlNewReconciledNs?
558         // xmlNsPtr aNsPtr = xmlNewGlobalNs?
559         xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, NULL, xName, NULL);
560         xmlNsPtr const pNs = xmlNewNs(pNode, xUri, xPrefix);
561         xmlSetNs(pNode, pNs);
562         Reference< XElement > const xRet(
563             static_cast< XNode* >(GetCNode(pNode).get()),
564             UNO_QUERY_THROW);
565         return xRet;
566     }
567 
568     //Creates an EntityReference object.
createEntityReference(const OUString & name)569     Reference< XEntityReference > SAL_CALL CDocument::createEntityReference(const OUString& name)
570     {
571         ::osl::MutexGuard const g(m_Mutex);
572 
573         OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8);
574         xmlChar *xName = (xmlChar*)o1.getStr();
575         xmlNodePtr const pNode = xmlNewReference(m_aDocPtr, xName);
576         Reference< XEntityReference > const xRet(
577             static_cast< XNode* >(GetCNode(pNode).get()),
578             UNO_QUERY_THROW);
579         return xRet;
580     }
581 
582     // Creates a ProcessingInstruction node given the specified name and
583     // data strings.
createProcessingInstruction(const OUString & target,const OUString & data)584     Reference< XProcessingInstruction > SAL_CALL CDocument::createProcessingInstruction(
585             const OUString& target, const OUString& data)
586     {
587         ::osl::MutexGuard const g(m_Mutex);
588 
589         OString o1 = OUStringToOString(target, RTL_TEXTENCODING_UTF8);
590         xmlChar *xTarget = (xmlChar*)o1.getStr();
591         OString o2 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
592         xmlChar *xData = (xmlChar*)o2.getStr();
593         xmlNodePtr const pNode = xmlNewDocPI(m_aDocPtr, xTarget, xData);
594         pNode->doc = m_aDocPtr;
595         Reference< XProcessingInstruction > const xRet(
596             static_cast< XNode* >(GetCNode(pNode).get()),
597             UNO_QUERY_THROW);
598         return xRet;
599     }
600 
601     // Creates a Text node given the specified string.
createTextNode(const OUString & data)602     Reference< XText > SAL_CALL CDocument::createTextNode(const OUString& data)
603     {
604         ::osl::MutexGuard const g(m_Mutex);
605 
606         OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
607         xmlChar *xData = (xmlChar*)o1.getStr();
608         xmlNodePtr const pNode = xmlNewDocText(m_aDocPtr, xData);
609         Reference< XText > const xRet(
610             static_cast< XNode* >(GetCNode(pNode).get()),
611             UNO_QUERY_THROW);
612         return xRet;
613     }
614 
615     // The Document Type Declaration (see DocumentType) associated with this
616     // document.
getDoctype()617     Reference< XDocumentType > SAL_CALL CDocument::getDoctype()
618     {
619         ::osl::MutexGuard const g(m_Mutex);
620 
621         xmlNodePtr const pDocType(lcl_getDocumentType(m_aDocPtr));
622         Reference< XDocumentType > const xRet(
623             static_cast< XNode* >(GetCNode(pDocType).get()),
624             UNO_QUERY);
625         return xRet;
626     }
627 
628     // This is a convenience attribute that allows direct access to the child
629     // node that is the root element of the document.
getDocumentElement()630     Reference< XElement > SAL_CALL CDocument::getDocumentElement()
631     {
632         ::osl::MutexGuard const g(m_Mutex);
633 
634         xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr);
635         if (!pNode) { return 0; }
636         Reference< XElement > const xRet(
637             static_cast< XNode* >(GetCNode(pNode).get()),
638             UNO_QUERY);
639         return xRet;
640     }
641 
642     static xmlNodePtr
lcl_search_element_by_id(const xmlNodePtr cur,const xmlChar * id)643     lcl_search_element_by_id(const xmlNodePtr cur, const xmlChar* id)
644     {
645         if (cur == NULL)
646             return NULL;
647         // look in current node
648         if (cur->type == XML_ELEMENT_NODE)
649         {
650             xmlAttrPtr a = cur->properties;
651             while (a != NULL)
652             {
653                 if (a->atype == XML_ATTRIBUTE_ID) {
654                     if (strcmp((char*)a->children->content, (char*)id) == 0)
655                         return cur;
656                 }
657                 a = a->next;
658             }
659         }
660         // look in children
661         xmlNodePtr result = lcl_search_element_by_id(cur->children, id);
662         if (result != NULL)
663             return result;
664         result = lcl_search_element_by_id(cur->next, id);
665             return result;
666     }
667 
668     // Returns the Element whose ID is given by elementId.
669     Reference< XElement > SAL_CALL
getElementById(const OUString & elementId)670     CDocument::getElementById(const OUString& elementId)
671     {
672         ::osl::MutexGuard const g(m_Mutex);
673 
674         // search the tree for an element with the given ID
675         OString o1 = OUStringToOString(elementId, RTL_TEXTENCODING_UTF8);
676         xmlChar *xId = (xmlChar*)o1.getStr();
677         xmlNodePtr const pStart = lcl_getDocumentRootPtr(m_aDocPtr);
678         if (!pStart) { return 0; }
679         xmlNodePtr const pNode = lcl_search_element_by_id(pStart, xId);
680         Reference< XElement > const xRet(
681             static_cast< XNode* >(GetCNode(pNode).get()),
682             UNO_QUERY);
683         return xRet;
684     }
685 
686 
687     Reference< XNodeList > SAL_CALL
getElementsByTagName(OUString const & rTagname)688     CDocument::getElementsByTagName(OUString const& rTagname)
689     {
690         ::osl::MutexGuard const g(m_Mutex);
691 
692         Reference< XNodeList > const xRet(
693             new CElementList(this->GetDocumentElement(), m_Mutex, rTagname));
694         return xRet;
695     }
696 
getElementsByTagNameNS(OUString const & rNamespaceURI,OUString const & rLocalName)697     Reference< XNodeList > SAL_CALL CDocument::getElementsByTagNameNS(
698             OUString const& rNamespaceURI, OUString const& rLocalName)
699     {
700         ::osl::MutexGuard const g(m_Mutex);
701 
702         Reference< XNodeList > const xRet(
703             new CElementList(this->GetDocumentElement(), m_Mutex,
704                 rLocalName, &rNamespaceURI));
705         return xRet;
706     }
707 
getImplementation()708     Reference< XDOMImplementation > SAL_CALL CDocument::getImplementation()
709     {
710         // does not need mutex currently
711         return Reference< XDOMImplementation >(CDOMImplementation::get());
712     }
713 
714     // helper function to recursively import siblings
lcl_ImportSiblings(Reference<XDocument> const & xTargetDocument,Reference<XNode> const & xTargetParent,Reference<XNode> const & xChild)715     static void lcl_ImportSiblings(
716         Reference< XDocument > const& xTargetDocument,
717         Reference< XNode > const& xTargetParent,
718         Reference< XNode > const& xChild)
719     {
720         Reference< XNode > xSibling = xChild;
721         while (xSibling.is())
722         {
723             Reference< XNode > const xTmp(
724                     xTargetDocument->importNode(xSibling, sal_True));
725             xTargetParent->appendChild(xTmp);
726             xSibling = xSibling->getNextSibling();
727         }
728     }
729 
730     static Reference< XNode >
lcl_ImportNode(Reference<XDocument> const & xDocument,Reference<XNode> const & xImportedNode,sal_Bool deep)731     lcl_ImportNode( Reference< XDocument > const& xDocument,
732             Reference< XNode > const& xImportedNode, sal_Bool deep)
733     {
734         Reference< XNode > xNode;
735         NodeType aNodeType = xImportedNode->getNodeType();
736         switch (aNodeType)
737         {
738         case NodeType_ATTRIBUTE_NODE:
739         {
740             Reference< XAttr > const xAttr(xImportedNode, UNO_QUERY_THROW);
741             Reference< XAttr > const xNew =
742                 xDocument->createAttribute(xAttr->getName());
743             xNew->setValue(xAttr->getValue());
744             xNode.set(xNew, UNO_QUERY);
745             break;
746         }
747         case NodeType_CDATA_SECTION_NODE:
748         {
749             Reference< XCDATASection > const xCData(xImportedNode,
750                     UNO_QUERY_THROW);
751             Reference< XCDATASection > const xNewCData =
752                 xDocument->createCDATASection(xCData->getData());
753             xNode.set(xNewCData, UNO_QUERY);
754             break;
755         }
756         case NodeType_COMMENT_NODE:
757         {
758             Reference< XComment > const xComment(xImportedNode,
759                     UNO_QUERY_THROW);
760             Reference< XComment > const xNewComment =
761                 xDocument->createComment(xComment->getData());
762             xNode.set(xNewComment, UNO_QUERY);
763             break;
764         }
765         case NodeType_DOCUMENT_FRAGMENT_NODE:
766         {
767             Reference< XDocumentFragment > const xFrag(xImportedNode,
768                     UNO_QUERY_THROW);
769             Reference< XDocumentFragment > const xNewFrag =
770                 xDocument->createDocumentFragment();
771             xNode.set(xNewFrag, UNO_QUERY);
772             break;
773         }
774         case NodeType_ELEMENT_NODE:
775         {
776             Reference< XElement > const xElement(xImportedNode,
777                     UNO_QUERY_THROW);
778             OUString const aNsUri = xImportedNode->getNamespaceURI();
779             OUString const aNsPrefix = xImportedNode->getPrefix();
780             OUString aQName = xElement->getTagName();
781             Reference< XElement > xNewElement;
782             if (aNsUri.getLength() > 0)
783             {
784                 if (aNsPrefix.getLength() > 0) {
785                     aQName = aNsPrefix + OUString::createFromAscii(":")
786                                 + aQName;
787                 }
788                 xNewElement = xDocument->createElementNS(aNsUri, aQName);
789             } else {
790                 xNewElement = xDocument->createElement(aQName);
791             }
792 
793             // get attributes
794             if (xElement->hasAttributes())
795             {
796                 Reference< XNamedNodeMap > attribs = xElement->getAttributes();
797                 for (sal_Int32 i = 0; i < attribs->getLength(); i++)
798                 {
799                     Reference< XAttr > const curAttr(attribs->item(i),
800                             UNO_QUERY_THROW);
801                     OUString const aAttrUri = curAttr->getNamespaceURI();
802                     OUString const aAttrPrefix = curAttr->getPrefix();
803                     OUString aAttrName = curAttr->getName();
804                     OUString const sValue = curAttr->getValue();
805                     if (aAttrUri.getLength() > 0)
806                     {
807                         if (aAttrPrefix.getLength() > 0) {
808                             aAttrName = aAttrPrefix +
809                                 OUString::createFromAscii(":") + aAttrName;
810                         }
811                         xNewElement->setAttributeNS(
812                                 aAttrUri, aAttrName, sValue);
813                     } else {
814                         xNewElement->setAttribute(aAttrName, sValue);
815                     }
816                 }
817             }
818             xNode.set(xNewElement, UNO_QUERY);
819             break;
820         }
821         case NodeType_ENTITY_REFERENCE_NODE:
822         {
823             Reference< XEntityReference > const xRef(xImportedNode,
824                     UNO_QUERY_THROW);
825             Reference< XEntityReference > const xNewRef(
826                 xDocument->createEntityReference(xRef->getNodeName()));
827             xNode.set(xNewRef, UNO_QUERY);
828             break;
829         }
830         case NodeType_PROCESSING_INSTRUCTION_NODE:
831         {
832             Reference< XProcessingInstruction > const xPi(xImportedNode,
833                     UNO_QUERY_THROW);
834             Reference< XProcessingInstruction > const xNewPi(
835                 xDocument->createProcessingInstruction(
836                     xPi->getTarget(), xPi->getData()));
837             xNode.set(xNewPi, UNO_QUERY);
838             break;
839         }
840         case NodeType_TEXT_NODE:
841         {
842             Reference< XText > const xText(xImportedNode, UNO_QUERY_THROW);
843             Reference< XText > const xNewText(
844                 xDocument->createTextNode(xText->getData()));
845             xNode.set(xNewText, UNO_QUERY);
846             break;
847         }
848         case NodeType_ENTITY_NODE:
849         case NodeType_DOCUMENT_NODE:
850         case NodeType_DOCUMENT_TYPE_NODE:
851         case NodeType_NOTATION_NODE:
852         default:
853             // can't be imported
854             throw RuntimeException();
855 
856         }
857         if (deep)
858         {
859             // get children and import them
860             Reference< XNode > const xChild = xImportedNode->getFirstChild();
861             if (xChild.is())
862             {
863                 lcl_ImportSiblings(xDocument, xNode, xChild);
864             }
865         }
866 
867         /* DOMNodeInsertedIntoDocument
868          * Fired when a node is being inserted into a document,
869          * either through direct insertion of the Node or insertion of a
870          * subtree in which it is contained. This event is dispatched after
871          * the insertion has taken place. The target of this event is the node
872          * being inserted. If the Node is being directly inserted the DOMNodeInserted
873          * event will fire before the DOMNodeInsertedIntoDocument event.
874          *   Bubbles: No
875          *   Cancelable: No
876          *   Context Info: None
877          */
878         if (xNode.is())
879         {
880             Reference< XDocumentEvent > const xDocevent(xDocument, UNO_QUERY);
881             Reference< XMutationEvent > const event(xDocevent->createEvent(
882                 OUString::createFromAscii("DOMNodeInsertedIntoDocument")),
883                 UNO_QUERY_THROW);
884             event->initMutationEvent(
885                 OUString::createFromAscii("DOMNodeInsertedIntoDocument")
886                 , sal_True, sal_False, Reference< XNode >(),
887                 OUString(), OUString(), OUString(), (AttrChangeType)0 );
888             Reference< XEventTarget > const xDocET(xDocument, UNO_QUERY);
889             xDocET->dispatchEvent(Reference< XEvent >(event, UNO_QUERY));
890         }
891 
892         return xNode;
893     }
894 
importNode(Reference<XNode> const & xImportedNode,sal_Bool deep)895     Reference< XNode > SAL_CALL CDocument::importNode(
896             Reference< XNode > const& xImportedNode, sal_Bool deep)
897     {
898         if (!xImportedNode.is()) { throw RuntimeException(); }
899 
900         // NB: this whole operation inherently accesses 2 distinct documents.
901         // The imported node could even be from a different DOM implementation,
902         // so this implementation cannot make any assumptions about the
903         // locking strategy of the imported node.
904         // So the import takes no lock on this document;
905         // it only calls UNO methods on this document that temporarily
906         // lock the document, and UNO methods on the imported node that
907         // may temporarily lock the other document.
908         // As a consequence, the import is not atomic with regard to
909         // concurrent modifications of either document, but it should not
910         // deadlock.
911         // To ensure that no members are accessed, the implementation is in
912         // static non-member functions.
913 
914         Reference< XDocument > const xDocument(this);
915         // already in doc?
916         if (xImportedNode->getOwnerDocument() == xDocument) {
917             return xImportedNode;
918         }
919 
920         Reference< XNode > const xNode(
921             lcl_ImportNode(xDocument, xImportedNode, deep) );
922         return xNode;
923     }
924 
925 
getNodeName()926     OUString SAL_CALL CDocument::getNodeName()
927     {
928         // does not need mutex currently
929         return OUString::createFromAscii("#document");
930     }
931 
getNodeValue()932     OUString SAL_CALL CDocument::getNodeValue()
933     {
934         // does not need mutex currently
935         return OUString();
936     }
937 
cloneNode(sal_Bool bDeep)938     Reference< XNode > SAL_CALL CDocument::cloneNode(sal_Bool bDeep)
939     {
940         ::osl::MutexGuard const g(m_rMutex);
941 
942         OSL_ASSERT(0 != m_aNodePtr);
943         if (0 == m_aNodePtr) {
944             return 0;
945         }
946         xmlDocPtr const pClone(xmlCopyDoc(m_aDocPtr, (bDeep) ? 1 : 0));
947         if (0 == pClone) { return 0; }
948         Reference< XNode > const xRet(
949             static_cast<CNode*>(CDocument::CreateCDocument(pClone).get()));
950         return xRet;
951     }
952 
createEvent(const OUString & aType)953     Reference< XEvent > SAL_CALL CDocument::createEvent(const OUString& aType)
954     {
955         // does not need mutex currently
956         events::CEvent *pEvent = 0;
957         if (
958             aType.compareToAscii("DOMSubtreeModified")          == 0||
959             aType.compareToAscii("DOMNodeInserted")             == 0||
960             aType.compareToAscii("DOMNodeRemoved")              == 0||
961             aType.compareToAscii("DOMNodeRemovedFromDocument")  == 0||
962             aType.compareToAscii("DOMNodeInsertedIntoDocument") == 0||
963             aType.compareToAscii("DOMAttrModified")             == 0||
964             aType.compareToAscii("DOMCharacterDataModified")    == 0)
965         {
966             pEvent = new events::CMutationEvent;
967 
968         } else if (
969             aType.compareToAscii("DOMFocusIn")  == 0||
970             aType.compareToAscii("DOMFocusOut") == 0||
971             aType.compareToAscii("DOMActivate") == 0)
972         {
973             pEvent = new events::CUIEvent;
974         } else if (
975             aType.compareToAscii("click")     == 0||
976             aType.compareToAscii("mousedown") == 0||
977             aType.compareToAscii("mouseup")   == 0||
978             aType.compareToAscii("mouseover") == 0||
979             aType.compareToAscii("mousemove") == 0||
980             aType.compareToAscii("mouseout")  == 0 )
981         {
982             pEvent = new events::CMouseEvent;
983         }
984         else // generic event
985         {
986             pEvent = new events::CEvent;
987         }
988         return Reference< XEvent >(pEvent);
989     }
990 
991     // ::com::sun::star::xml::sax::XSAXSerializable
serialize(const Reference<XDocumentHandler> & i_xHandler,const Sequence<beans::StringPair> & i_rNamespaces)992     void SAL_CALL CDocument::serialize(
993             const Reference< XDocumentHandler >& i_xHandler,
994             const Sequence< beans::StringPair >& i_rNamespaces)
995     {
996         ::osl::MutexGuard const g(m_Mutex);
997 
998         // add new namespaces to root node
999         xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr);
1000         if (0 != pRoot) {
1001             const beans::StringPair * pSeq = i_rNamespaces.getConstArray();
1002             for (const beans::StringPair *pNsDef = pSeq;
1003                  pNsDef < pSeq + i_rNamespaces.getLength(); ++pNsDef) {
1004                 OString prefix = OUStringToOString(pNsDef->First,
1005                                     RTL_TEXTENCODING_UTF8);
1006                 OString href   = OUStringToOString(pNsDef->Second,
1007                                     RTL_TEXTENCODING_UTF8);
1008                 // this will only add the ns if it does not exist already
1009                 xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()),
1010                          reinterpret_cast<const xmlChar*>(prefix.getStr()));
1011             }
1012             // eliminate duplicate namespace declarations
1013             nscleanup(pRoot->children, pRoot);
1014         }
1015         saxify(i_xHandler);
1016     }
1017 
1018     // ::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)1019     void SAL_CALL CDocument::fastSerialize( const Reference< XFastDocumentHandler >& i_xHandler,
1020                                             const Reference< XFastTokenHandler >& i_xTokenHandler,
1021                                             const Sequence< beans::StringPair >& i_rNamespaces,
1022                                             const Sequence< beans::Pair< rtl::OUString, sal_Int32 > >& i_rRegisterNamespaces )
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 
1044         Context aContext(i_xHandler,
1045                          i_xTokenHandler);
1046 
1047         // register namespace ids
1048         const beans::Pair<OUString,sal_Int32>* pSeq = i_rRegisterNamespaces.getConstArray();
1049         for (const beans::Pair<OUString,sal_Int32>* pNs = pSeq;
1050              pNs < pSeq + i_rRegisterNamespaces.getLength(); ++pNs)
1051         {
1052             OSL_ENSURE(pNs->Second >= FastToken::NAMESPACE,
1053                        "CDocument::fastSerialize(): invalid NS token id");
1054             aContext.maNamespaceMap[ pNs->First ] = pNs->Second;
1055         }
1056 
1057         fastSaxify(aContext);
1058     }
1059 }
1060