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