Version 24.2.2.2, tag libreoffice-24.2.2.2
[LibreOffice.git] / unoxml / source / dom / document.cxx
blob0825fc2cd7edd0a34384fa04e0fe67adbf7b35b4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <com/sun/star/uno/Sequence.h>
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"
29 #include "processinginstruction.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"
37 #include <event.hxx>
38 #include <mutationevent.hxx>
39 #include <uievent.hxx>
40 #include <mouseevent.hxx>
41 #include <eventdispatcher.hxx>
43 #include <string.h>
44 #include <libxml/xmlIO.h>
46 #include <osl/diagnose.h>
48 #include <com/sun/star/xml/sax/FastToken.hpp>
50 using namespace css;
51 using namespace css::io;
52 using namespace css::uno;
53 using namespace css::xml::dom;
54 using namespace css::xml::dom::events;
55 using namespace css::xml::sax;
57 namespace DOM
59 static xmlNodePtr lcl_getDocumentType(xmlDocPtr const i_pDocument)
61 // find the doc type
62 xmlNodePtr cur = i_pDocument->children;
63 while (cur != nullptr)
65 if ((cur->type == XML_DOCUMENT_TYPE_NODE) ||
66 (cur->type == XML_DTD_NODE)) {
67 return cur;
70 return nullptr;
73 /// get the pointer to the root element node of the document
74 static xmlNodePtr lcl_getDocumentRootPtr(xmlDocPtr const i_pDocument)
76 // find the document element
77 xmlNodePtr cur = i_pDocument->children;
78 while (cur != nullptr)
80 if (cur->type == XML_ELEMENT_NODE)
81 break;
82 cur = cur->next;
84 return cur;
87 CDocument::CDocument(xmlDocPtr const pDoc)
88 : CDocument_Base(*this, m_Mutex,
89 NodeType_DOCUMENT_NODE, reinterpret_cast<xmlNodePtr>(pDoc))
90 , m_aDocPtr(pDoc)
91 , m_pEventDispatcher(new events::CEventDispatcher)
95 ::rtl::Reference<CDocument> CDocument::CreateCDocument(xmlDocPtr const pDoc)
97 ::rtl::Reference<CDocument> const xDoc(new CDocument(pDoc));
98 // add the doc itself to its nodemap!
99 xDoc->m_NodeMap.emplace(
100 reinterpret_cast<xmlNodePtr>(pDoc),
101 ::std::make_pair(
102 WeakReference<XNode>(static_cast<XDocument*>(xDoc.get())),
103 xDoc.get()));
104 return xDoc;
107 CDocument::~CDocument()
109 ::osl::MutexGuard const g(m_Mutex);
110 #ifdef DBG_UTIL
111 // node map must be empty now, otherwise CDocument must not die!
112 for (const auto& rEntry : m_NodeMap)
114 Reference<XNode> const xNode(rEntry.second.first);
115 OSL_ENSURE(!xNode.is(),
116 "CDocument::~CDocument(): ERROR: live node in document node map!");
118 #endif
119 xmlFreeDoc(m_aDocPtr);
123 events::CEventDispatcher & CDocument::GetEventDispatcher()
125 return *m_pEventDispatcher;
128 ::rtl::Reference< CElement > CDocument::GetDocumentElement()
130 xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr);
131 ::rtl::Reference< CElement > const xRet(
132 dynamic_cast<CElement*>(GetCNode(pNode).get()));
133 return xRet;
136 void
137 CDocument::RemoveCNode(xmlNodePtr const pNode, CNode const*const pCNode)
139 nodemap_t::iterator const i = m_NodeMap.find(pNode);
140 if (i == m_NodeMap.end())
141 return;
143 // #i113681# consider this scenario:
144 // T1 calls ~CNode
145 // T2 calls getCNode: lookup will find i->second->first invalid
146 // so a new CNode is created and inserted
147 // T1 calls removeCNode: i->second->second now points to a
148 // different CNode instance!
150 // check that the CNode is the right one
151 CNode *const pCurrent = i->second.second;
152 if (pCurrent == pCNode) {
153 m_NodeMap.erase(i);
157 /** NB: this is the CNode factory.
158 it is the only place where CNodes may be instantiated.
159 all CNodes must be registered at the m_NodeMap.
161 ::rtl::Reference<CNode>
162 CDocument::GetCNode(xmlNodePtr const pNode, bool const bCreate)
164 if (nullptr == pNode) {
165 return nullptr;
167 //check whether there is already an instance for this node
168 nodemap_t::const_iterator const i = m_NodeMap.find(pNode);
169 if (i != m_NodeMap.end()) {
170 // #i113681# check that the CNode is still alive
171 uno::Reference<XNode> const xNode(i->second.first);
172 if (xNode.is())
174 ::rtl::Reference<CNode> ret(i->second.second);
175 OSL_ASSERT(ret.is());
176 return ret;
180 if (!bCreate) { return nullptr; }
182 // there is not yet an instance wrapping this node,
183 // create it and store it in the map
185 ::rtl::Reference<CNode> pCNode;
186 switch (pNode->type)
188 case XML_ELEMENT_NODE:
189 // m_aNodeType = NodeType::ELEMENT_NODE;
190 pCNode = new CElement(*this, m_Mutex, pNode);
191 break;
192 case XML_TEXT_NODE:
193 // m_aNodeType = NodeType::TEXT_NODE;
194 pCNode = new CText(*this, m_Mutex, pNode);
195 break;
196 case XML_CDATA_SECTION_NODE:
197 // m_aNodeType = NodeType::CDATA_SECTION_NODE;
198 pCNode = new CCDATASection(*this, m_Mutex, pNode);
199 break;
200 case XML_ENTITY_REF_NODE:
201 // m_aNodeType = NodeType::ENTITY_REFERENCE_NODE;
202 pCNode = new CEntityReference(*this, m_Mutex, pNode);
203 break;
204 case XML_ENTITY_NODE:
205 // m_aNodeType = NodeType::ENTITY_NODE;
206 pCNode = new CEntity(*this, m_Mutex, reinterpret_cast<xmlEntityPtr>(pNode));
207 break;
208 case XML_PI_NODE:
209 // m_aNodeType = NodeType::PROCESSING_INSTRUCTION_NODE;
210 pCNode = new CProcessingInstruction(*this, m_Mutex, pNode);
211 break;
212 case XML_COMMENT_NODE:
213 // m_aNodeType = NodeType::COMMENT_NODE;
214 pCNode = new CComment(*this, m_Mutex, pNode);
215 break;
216 case XML_DOCUMENT_NODE:
217 // m_aNodeType = NodeType::DOCUMENT_NODE;
218 OSL_ENSURE(false, "CDocument::GetCNode is not supposed to"
219 " create a CDocument!!!");
220 pCNode = new CDocument(reinterpret_cast<xmlDocPtr>(pNode));
221 break;
222 case XML_DOCUMENT_TYPE_NODE:
223 case XML_DTD_NODE:
224 // m_aNodeType = NodeType::DOCUMENT_TYPE_NODE;
225 pCNode = new CDocumentType(*this, m_Mutex, reinterpret_cast<xmlDtdPtr>(pNode));
226 break;
227 case XML_DOCUMENT_FRAG_NODE:
228 // m_aNodeType = NodeType::DOCUMENT_FRAGMENT_NODE;
229 pCNode = new CDocumentFragment(*this, m_Mutex, pNode);
230 break;
231 case XML_NOTATION_NODE:
232 // m_aNodeType = NodeType::NOTATION_NODE;
233 pCNode = new CNotation(*this, m_Mutex, reinterpret_cast<xmlNotationPtr>(pNode));
234 break;
235 case XML_ATTRIBUTE_NODE:
236 // m_aNodeType = NodeType::ATTRIBUTE_NODE;
237 pCNode = new CAttr(*this, m_Mutex, reinterpret_cast<xmlAttrPtr>(pNode));
238 break;
239 // unsupported node types
240 case XML_HTML_DOCUMENT_NODE:
241 case XML_ELEMENT_DECL:
242 case XML_ATTRIBUTE_DECL:
243 case XML_ENTITY_DECL:
244 case XML_NAMESPACE_DECL:
245 default:
246 break;
249 if (pCNode != nullptr) {
250 bool const bInserted = m_NodeMap.emplace(
251 pNode,
252 ::std::make_pair(WeakReference<XNode>(pCNode), pCNode.get())
253 ).second;
254 OSL_ASSERT(bInserted);
255 if (!bInserted) {
256 // if insertion failed, delete new instance and return null
257 return nullptr;
261 OSL_ENSURE(pCNode.is(), "no node produced during CDocument::GetCNode!");
262 return pCNode;
266 CDocument & CDocument::GetOwnerDocument()
268 return *this;
271 void CDocument::saxify(const Reference< XDocumentHandler >& i_xHandler)
273 i_xHandler->startDocument();
274 for (xmlNodePtr pChild = m_aNodePtr->children;
275 pChild != nullptr; pChild = pChild->next) {
276 ::rtl::Reference<CNode> const pNode = GetCNode(pChild);
277 OSL_ENSURE(pNode != nullptr, "CNode::get returned 0");
278 pNode->saxify(i_xHandler);
280 i_xHandler->endDocument();
283 void CDocument::fastSaxify( Context& rContext )
285 rContext.mxDocHandler->startDocument();
286 for (xmlNodePtr pChild = m_aNodePtr->children;
287 pChild != nullptr; pChild = pChild->next) {
288 ::rtl::Reference<CNode> const pNode = GetCNode(pChild);
289 OSL_ENSURE(pNode != nullptr, "CNode::get returned 0");
290 pNode->fastSaxify(rContext);
292 rContext.mxDocHandler->endDocument();
295 bool CDocument::IsChildTypeAllowed(NodeType const nodeType, NodeType const*const pReplacedNodeType)
297 switch (nodeType) {
298 case NodeType_PROCESSING_INSTRUCTION_NODE:
299 case NodeType_COMMENT_NODE:
300 return true;
301 case NodeType_ELEMENT_NODE:
302 // there may be only one!
303 return (pReplacedNodeType && *pReplacedNodeType == nodeType)
304 || nullptr == lcl_getDocumentRootPtr(m_aDocPtr);
305 case NodeType_DOCUMENT_TYPE_NODE:
306 // there may be only one!
307 return (pReplacedNodeType && *pReplacedNodeType == nodeType)
308 || nullptr == lcl_getDocumentType(m_aDocPtr);
309 default:
310 return false;
315 void SAL_CALL CDocument::addListener(const Reference< XStreamListener >& aListener )
317 ::osl::MutexGuard const g(m_Mutex);
319 m_streamListeners.insert(aListener);
322 void SAL_CALL CDocument::removeListener(const Reference< XStreamListener >& aListener )
324 ::osl::MutexGuard const g(m_Mutex);
326 m_streamListeners.erase(aListener);
329 namespace {
331 // IO context functions for libxml2 interaction
332 struct IOContext {
333 Reference< XOutputStream > stream;
334 bool allowClose;
339 extern "C" {
340 // write callback
341 // int xmlOutputWriteCallback (void * context, const char * buffer, int len)
342 static int writeCallback(void *context, const char* buffer, int len){
343 // create a sequence and write it to the stream
344 IOContext *pContext = static_cast<IOContext*>(context);
345 Sequence<sal_Int8> bs(reinterpret_cast<const sal_Int8*>(buffer), len);
346 pContext->stream->writeBytes(bs);
347 return len;
350 // close callback
351 //int xmlOutputCloseCallback (void * context)
352 static int closeCallback(void *context)
354 IOContext *pContext = static_cast<IOContext*>(context);
355 if (pContext->allowClose) {
356 pContext->stream->closeOutput();
358 return 0;
360 } // extern "C"
362 void SAL_CALL CDocument::start()
364 listenerlist_t streamListeners;
366 ::osl::MutexGuard const g(m_Mutex);
368 if (! m_rOutputStream.is()) { throw RuntimeException(); }
369 streamListeners = m_streamListeners;
372 // notify listeners about start
373 for (const Reference< XStreamListener >& aListener : streamListeners) {
374 aListener->started();
378 ::osl::MutexGuard const g(m_Mutex);
380 // check again! could have been reset...
381 if (! m_rOutputStream.is()) { throw RuntimeException(); }
383 // setup libxml IO and write data to output stream
384 IOContext ioctx = {m_rOutputStream, false};
385 xmlOutputBufferPtr pOut = xmlOutputBufferCreateIO(
386 writeCallback, closeCallback, &ioctx, nullptr);
387 xmlSaveFileTo(pOut, m_aNodePtr->doc, nullptr);
390 // call listeners
391 for (const Reference< XStreamListener >& aListener : streamListeners) {
392 aListener->closed();
396 void SAL_CALL CDocument::terminate()
398 // not supported
401 void SAL_CALL CDocument::setOutputStream( const Reference< XOutputStream >& aStream )
403 ::osl::MutexGuard const g(m_Mutex);
405 m_rOutputStream = aStream;
408 Reference< XOutputStream > SAL_CALL CDocument::getOutputStream()
410 ::osl::MutexGuard const g(m_Mutex);
412 return m_rOutputStream;
415 // Creates an Attr of the given name.
416 Reference< XAttr > SAL_CALL CDocument::createAttribute(const OUString& name)
418 ::osl::MutexGuard const g(m_Mutex);
420 OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8);
421 xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr());
422 xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr, pName, nullptr);
423 ::rtl::Reference< CAttr > const pCAttr(
424 dynamic_cast< CAttr* >(GetCNode(
425 reinterpret_cast<xmlNodePtr>(pAttr)).get()));
426 if (!pCAttr.is()) { throw RuntimeException(); }
427 pCAttr->m_bUnlinked = true;
428 return pCAttr;
431 // Creates an attribute of the given qualified name and namespace URI.
432 Reference< XAttr > SAL_CALL CDocument::createAttributeNS(
433 const OUString& ns, const OUString& qname)
435 ::osl::MutexGuard const g(m_Mutex);
437 // libxml does not allow a NS definition to be attached to an
438 // attribute node - which is a good thing, since namespaces are
439 // only defined as parts of element nodes
440 // thus the namespace data is stored in CAttr::m_pNamespace
441 sal_Int32 i = qname.indexOf(':');
442 OString oPrefix, oName, oUri;
443 if (i != -1)
445 oPrefix = OUStringToOString(qname.subView(0, i), RTL_TEXTENCODING_UTF8);
446 oName = OUStringToOString(qname.subView(i+1), RTL_TEXTENCODING_UTF8);
448 else
450 oName = OUStringToOString(qname, RTL_TEXTENCODING_UTF8);
452 oUri = OUStringToOString(ns, RTL_TEXTENCODING_UTF8);
453 xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr,
454 reinterpret_cast<xmlChar const*>(oName.getStr()), nullptr);
455 ::rtl::Reference< CAttr > const pCAttr(
456 dynamic_cast< CAttr* >(GetCNode(
457 reinterpret_cast<xmlNodePtr>(pAttr)).get()));
458 if (!pCAttr.is()) { throw RuntimeException(); }
459 // store the namespace data!
460 pCAttr->m_oNamespace.emplace( oUri, oPrefix );
461 pCAttr->m_bUnlinked = true;
463 return pCAttr;
466 // Creates a CDATASection node whose value is the specified string.
467 Reference< XCDATASection > SAL_CALL CDocument::createCDATASection(const OUString& data)
469 ::osl::MutexGuard const g(m_Mutex);
471 OString const oData(
472 OUStringToOString(data, RTL_TEXTENCODING_UTF8));
473 xmlChar const*const pData =
474 reinterpret_cast<xmlChar const*>(oData.getStr());
475 xmlNodePtr const pText =
476 xmlNewCDataBlock(m_aDocPtr, pData, oData.getLength());
477 Reference< XCDATASection > const xRet(
478 static_cast< XNode* >(GetCNode(pText).get()),
479 UNO_QUERY_THROW);
480 return xRet;
483 // Creates a Comment node given the specified string.
484 Reference< XComment > SAL_CALL CDocument::createComment(const OUString& data)
486 ::osl::MutexGuard const g(m_Mutex);
488 OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
489 xmlChar const *pData = reinterpret_cast<xmlChar const *>(o1.getStr());
490 xmlNodePtr pComment = xmlNewDocComment(m_aDocPtr, pData);
491 Reference< XComment > const xRet(
492 static_cast< XNode* >(GetCNode(pComment).get()),
493 UNO_QUERY_THROW);
494 return xRet;
497 //Creates an empty DocumentFragment object.
498 Reference< XDocumentFragment > SAL_CALL CDocument::createDocumentFragment()
500 ::osl::MutexGuard const g(m_Mutex);
502 xmlNodePtr pFrag = xmlNewDocFragment(m_aDocPtr);
503 Reference< XDocumentFragment > const xRet(
504 static_cast< XNode* >(GetCNode(pFrag).get()),
505 UNO_QUERY_THROW);
506 return xRet;
509 // Creates an element of the type specified.
510 Reference< XElement > SAL_CALL CDocument::createElement(const OUString& tagName)
512 ::osl::MutexGuard const g(m_Mutex);
514 OString o1 = OUStringToOString(tagName, RTL_TEXTENCODING_UTF8);
515 xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr());
516 xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, nullptr, pName, nullptr);
517 Reference< XElement > const xRet(
518 static_cast< XNode* >(GetCNode(pNode).get()),
519 UNO_QUERY_THROW);
520 return xRet;
523 // Creates an element of the given qualified name and namespace URI.
524 Reference< XElement > SAL_CALL CDocument::createElementNS(
525 const OUString& ns, const OUString& qname)
527 ::osl::MutexGuard const g(m_Mutex);
529 sal_Int32 i = qname.indexOf(':');
530 if (ns.isEmpty()) throw RuntimeException();
531 xmlChar const *pPrefix;
532 xmlChar const *pName;
533 OString o1, o2, o3;
534 if ( i != -1) {
535 o1 = OUStringToOString(qname.subView(0, i), RTL_TEXTENCODING_UTF8);
536 pPrefix = reinterpret_cast<xmlChar const *>(o1.getStr());
537 o2 = OUStringToOString(qname.subView(i+1), RTL_TEXTENCODING_UTF8);
538 pName = reinterpret_cast<xmlChar const *>(o2.getStr());
539 } else {
540 // default prefix
541 pPrefix = reinterpret_cast<xmlChar const *>("");
542 o2 = OUStringToOString(qname, RTL_TEXTENCODING_UTF8);
543 pName = reinterpret_cast<xmlChar const *>(o2.getStr());
545 o3 = OUStringToOString(ns, RTL_TEXTENCODING_UTF8);
546 xmlChar const *pUri = reinterpret_cast<xmlChar const *>(o3.getStr());
548 // xmlNsPtr aNsPtr = xmlNewReconciledNs?
549 // xmlNsPtr aNsPtr = xmlNewGlobalNs?
550 xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, nullptr, pName, nullptr);
551 xmlNsPtr const pNs = xmlNewNs(pNode, pUri, pPrefix);
552 xmlSetNs(pNode, pNs);
553 Reference< XElement > const xRet(
554 static_cast< XNode* >(GetCNode(pNode).get()),
555 UNO_QUERY_THROW);
556 return xRet;
559 //Creates an EntityReference object.
560 Reference< XEntityReference > SAL_CALL CDocument::createEntityReference(const OUString& name)
562 ::osl::MutexGuard const g(m_Mutex);
564 OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8);
565 xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr());
566 xmlNodePtr const pNode = xmlNewReference(m_aDocPtr, pName);
567 Reference< XEntityReference > const xRet(
568 static_cast< XNode* >(GetCNode(pNode).get()),
569 UNO_QUERY_THROW);
570 return xRet;
573 // Creates a ProcessingInstruction node given the specified name and
574 // data strings.
575 Reference< XProcessingInstruction > SAL_CALL CDocument::createProcessingInstruction(
576 const OUString& target, const OUString& data)
578 ::osl::MutexGuard const g(m_Mutex);
580 OString o1 = OUStringToOString(target, RTL_TEXTENCODING_UTF8);
581 xmlChar const *pTarget = reinterpret_cast<xmlChar const *>(o1.getStr());
582 OString o2 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
583 xmlChar const *pData = reinterpret_cast<xmlChar const *>(o2.getStr());
584 xmlNodePtr const pNode = xmlNewDocPI(m_aDocPtr, pTarget, pData);
585 pNode->doc = m_aDocPtr;
586 Reference< XProcessingInstruction > const xRet(
587 static_cast< XNode* >(GetCNode(pNode).get()),
588 UNO_QUERY_THROW);
589 return xRet;
592 // Creates a Text node given the specified string.
593 Reference< XText > SAL_CALL CDocument::createTextNode(const OUString& data)
595 ::osl::MutexGuard const g(m_Mutex);
597 OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
598 xmlChar const *pData = reinterpret_cast<xmlChar const *>(o1.getStr());
599 xmlNodePtr const pNode = xmlNewDocText(m_aDocPtr, pData);
600 Reference< XText > const xRet(
601 static_cast< XNode* >(GetCNode(pNode).get()),
602 UNO_QUERY_THROW);
603 return xRet;
606 // The Document Type Declaration (see DocumentType) associated with this
607 // document.
608 Reference< XDocumentType > SAL_CALL CDocument::getDoctype()
610 ::osl::MutexGuard const g(m_Mutex);
612 xmlNodePtr const pDocType(lcl_getDocumentType(m_aDocPtr));
613 Reference< XDocumentType > const xRet(
614 static_cast< XNode* >(GetCNode(pDocType).get()),
615 UNO_QUERY);
616 return xRet;
619 // This is a convenience attribute that allows direct access to the child
620 // node that is the root element of the document.
621 Reference< XElement > SAL_CALL CDocument::getDocumentElement()
623 ::osl::MutexGuard const g(m_Mutex);
625 xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr);
626 if (!pNode) { return nullptr; }
627 Reference< XElement > const xRet(
628 static_cast< XNode* >(GetCNode(pNode).get()),
629 UNO_QUERY);
630 return xRet;
633 static xmlNodePtr
634 lcl_search_element_by_id(const xmlNodePtr cur, const xmlChar* id)
636 if (cur == nullptr)
637 return nullptr;
638 // look in current node
639 if (cur->type == XML_ELEMENT_NODE)
641 xmlAttrPtr a = cur->properties;
642 while (a != nullptr)
644 if (a->atype == XML_ATTRIBUTE_ID) {
645 if (strcmp(reinterpret_cast<char*>(a->children->content), reinterpret_cast<char const *>(id)) == 0)
646 return cur;
648 a = a->next;
651 // look in children
652 xmlNodePtr result = lcl_search_element_by_id(cur->children, id);
653 if (result != nullptr)
654 return result;
655 result = lcl_search_element_by_id(cur->next, id);
656 return result;
659 // Returns the Element whose ID is given by elementId.
660 Reference< XElement > SAL_CALL
661 CDocument::getElementById(const OUString& elementId)
663 ::osl::MutexGuard const g(m_Mutex);
665 // search the tree for an element with the given ID
666 OString o1 = OUStringToOString(elementId, RTL_TEXTENCODING_UTF8);
667 xmlChar const *pId = reinterpret_cast<xmlChar const *>(o1.getStr());
668 xmlNodePtr const pStart = lcl_getDocumentRootPtr(m_aDocPtr);
669 if (!pStart) { return nullptr; }
670 xmlNodePtr const pNode = lcl_search_element_by_id(pStart, pId);
671 Reference< XElement > const xRet(
672 static_cast< XNode* >(GetCNode(pNode).get()),
673 UNO_QUERY);
674 return xRet;
678 Reference< XNodeList > SAL_CALL
679 CDocument::getElementsByTagName(OUString const& rTagname)
681 ::osl::MutexGuard const g(m_Mutex);
683 Reference< XNodeList > const xRet(
684 new CElementList(GetDocumentElement(), m_Mutex, rTagname));
685 return xRet;
688 Reference< XNodeList > SAL_CALL CDocument::getElementsByTagNameNS(
689 OUString const& rNamespaceURI, OUString const& rLocalName)
691 ::osl::MutexGuard const g(m_Mutex);
693 Reference< XNodeList > const xRet(
694 new CElementList(GetDocumentElement(), m_Mutex,
695 rLocalName, &rNamespaceURI));
696 return xRet;
699 Reference< XDOMImplementation > SAL_CALL CDocument::getImplementation()
701 // does not need mutex currently
702 return Reference< XDOMImplementation >(CDOMImplementation::get());
705 // helper function to recursively import siblings
706 static void lcl_ImportSiblings(
707 Reference< XDocument > const& xTargetDocument,
708 Reference< XNode > const& xTargetParent,
709 Reference< XNode > const& xChild)
711 Reference< XNode > xSibling = xChild;
712 while (xSibling.is())
714 Reference< XNode > const xTmp(
715 xTargetDocument->importNode(xSibling, true));
716 xTargetParent->appendChild(xTmp);
717 xSibling = xSibling->getNextSibling();
721 static Reference< XNode >
722 lcl_ImportNode( Reference< XDocument > const& xDocument,
723 Reference< XNode > const& xImportedNode, bool deep)
725 Reference< XNode > xNode;
726 NodeType aNodeType = xImportedNode->getNodeType();
727 switch (aNodeType)
729 case NodeType_ATTRIBUTE_NODE:
731 Reference< XAttr > const xAttr(xImportedNode, UNO_QUERY_THROW);
732 Reference< XAttr > const xNew =
733 xDocument->createAttribute(xAttr->getName());
734 xNew->setValue(xAttr->getValue());
735 xNode = xNew;
736 break;
738 case NodeType_CDATA_SECTION_NODE:
740 Reference< XCDATASection > const xCData(xImportedNode,
741 UNO_QUERY_THROW);
742 Reference< XCDATASection > const xNewCData =
743 xDocument->createCDATASection(xCData->getData());
744 xNode = xNewCData;
745 break;
747 case NodeType_COMMENT_NODE:
749 Reference< XComment > const xComment(xImportedNode,
750 UNO_QUERY_THROW);
751 Reference< XComment > const xNewComment =
752 xDocument->createComment(xComment->getData());
753 xNode = xNewComment;
754 break;
756 case NodeType_DOCUMENT_FRAGMENT_NODE:
758 Reference< XDocumentFragment > const xFrag(xImportedNode,
759 UNO_QUERY_THROW);
760 Reference< XDocumentFragment > const xNewFrag =
761 xDocument->createDocumentFragment();
762 xNode = xNewFrag;
763 break;
765 case NodeType_ELEMENT_NODE:
767 Reference< XElement > const xElement(xImportedNode,
768 UNO_QUERY_THROW);
769 OUString const aNsUri = xImportedNode->getNamespaceURI();
770 OUString const aNsPrefix = xImportedNode->getPrefix();
771 OUString aQName = xElement->getTagName();
772 Reference< XElement > xNewElement;
773 if (!aNsUri.isEmpty())
775 if (!aNsPrefix.isEmpty()) {
776 aQName = aNsPrefix + ":" + aQName;
778 xNewElement = xDocument->createElementNS(aNsUri, aQName);
779 } else {
780 xNewElement = xDocument->createElement(aQName);
783 // get attributes
784 if (xElement->hasAttributes())
786 Reference< XNamedNodeMap > attribs = xElement->getAttributes();
787 for (sal_Int32 i = 0; i < attribs->getLength(); i++)
789 Reference< XAttr > const curAttr(attribs->item(i),
790 UNO_QUERY_THROW);
791 OUString const aAttrUri = curAttr->getNamespaceURI();
792 OUString const aAttrPrefix = curAttr->getPrefix();
793 OUString aAttrName = curAttr->getName();
794 OUString const sValue = curAttr->getValue();
795 if (!aAttrUri.isEmpty())
797 if (!aAttrPrefix.isEmpty()) {
798 aAttrName = aAttrPrefix + ":" + aAttrName;
800 xNewElement->setAttributeNS(
801 aAttrUri, aAttrName, sValue);
802 } else {
803 xNewElement->setAttribute(aAttrName, sValue);
807 xNode = xNewElement;
808 break;
810 case NodeType_ENTITY_REFERENCE_NODE:
812 Reference< XEntityReference > const xRef(xImportedNode,
813 UNO_QUERY_THROW);
814 Reference< XEntityReference > const xNewRef(
815 xDocument->createEntityReference(xRef->getNodeName()));
816 xNode = xNewRef;
817 break;
819 case NodeType_PROCESSING_INSTRUCTION_NODE:
821 Reference< XProcessingInstruction > const xPi(xImportedNode,
822 UNO_QUERY_THROW);
823 Reference< XProcessingInstruction > const xNewPi(
824 xDocument->createProcessingInstruction(
825 xPi->getTarget(), xPi->getData()));
826 xNode = xNewPi;
827 break;
829 case NodeType_TEXT_NODE:
831 Reference< XText > const xText(xImportedNode, UNO_QUERY_THROW);
832 Reference< XText > const xNewText(
833 xDocument->createTextNode(xText->getData()));
834 xNode = xNewText;
835 break;
837 case NodeType_ENTITY_NODE:
838 case NodeType_DOCUMENT_NODE:
839 case NodeType_DOCUMENT_TYPE_NODE:
840 case NodeType_NOTATION_NODE:
841 default:
842 // can't be imported
843 throw RuntimeException();
846 if (deep)
848 // get children and import them
849 Reference< XNode > const xChild = xImportedNode->getFirstChild();
850 if (xChild.is())
852 lcl_ImportSiblings(xDocument, xNode, xChild);
856 /* DOMNodeInsertedIntoDocument
857 * Fired when a node is being inserted into a document,
858 * either through direct insertion of the Node or insertion of a
859 * subtree in which it is contained. This event is dispatched after
860 * the insertion has taken place. The target of this event is the node
861 * being inserted. If the Node is being directly inserted the DOMNodeInserted
862 * event will fire before the DOMNodeInsertedIntoDocument event.
863 * Bubbles: No
864 * Cancelable: No
865 * Context Info: None
867 if (xNode.is())
869 Reference< XDocumentEvent > const xDocevent(xDocument, UNO_QUERY);
870 Reference< XMutationEvent > const event(xDocevent->createEvent(
871 "DOMNodeInsertedIntoDocument"), UNO_QUERY_THROW);
872 event->initMutationEvent(
873 "DOMNodeInsertedIntoDocument", true, false, Reference< XNode >(),
874 OUString(), OUString(), OUString(), AttrChangeType(0) );
875 Reference< XEventTarget > const xDocET(xDocument, UNO_QUERY);
876 xDocET->dispatchEvent(event);
879 return xNode;
882 Reference< XNode > SAL_CALL CDocument::importNode(
883 Reference< XNode > const& xImportedNode, sal_Bool deep)
885 if (!xImportedNode.is()) { throw RuntimeException(); }
887 // NB: this whole operation inherently accesses 2 distinct documents.
888 // The imported node could even be from a different DOM implementation,
889 // so this implementation cannot make any assumptions about the
890 // locking strategy of the imported node.
891 // So the import takes no lock on this document;
892 // it only calls UNO methods on this document that temporarily
893 // lock the document, and UNO methods on the imported node that
894 // may temporarily lock the other document.
895 // As a consequence, the import is not atomic with regard to
896 // concurrent modifications of either document, but it should not
897 // deadlock.
898 // To ensure that no members are accessed, the implementation is in
899 // static non-member functions.
901 Reference< XDocument > const xDocument(this);
902 // already in doc?
903 if (xImportedNode->getOwnerDocument() == xDocument) {
904 return xImportedNode;
907 Reference< XNode > const xNode(
908 lcl_ImportNode(xDocument, xImportedNode, deep) );
909 return xNode;
912 OUString SAL_CALL CDocument::getNodeName()
914 // does not need mutex currently
915 return "#document";
918 OUString SAL_CALL CDocument::getNodeValue()
920 // does not need mutex currently
921 return OUString();
924 Reference< XNode > SAL_CALL CDocument::cloneNode(sal_Bool bDeep)
926 ::osl::MutexGuard const g(m_rMutex);
928 OSL_ASSERT(nullptr != m_aNodePtr);
929 if (nullptr == m_aNodePtr) {
930 return nullptr;
932 xmlDocPtr const pClone(xmlCopyDoc(m_aDocPtr, bDeep ? 1 : 0));
933 if (nullptr == pClone) { return nullptr; }
934 Reference< XNode > const xRet(
935 static_cast<CNode*>(CDocument::CreateCDocument(pClone).get()));
936 return xRet;
939 Reference< XEvent > SAL_CALL CDocument::createEvent(const OUString& aType)
941 // does not need mutex currently
942 rtl::Reference<events::CEvent> pEvent;
943 if ( aType == "DOMSubtreeModified" || aType == "DOMNodeInserted" || aType == "DOMNodeRemoved"
944 || aType == "DOMNodeRemovedFromDocument" || aType == "DOMNodeInsertedIntoDocument" || aType == "DOMAttrModified"
945 || aType == "DOMCharacterDataModified")
947 pEvent = new events::CMutationEvent;
949 } else if ( aType == "DOMFocusIn" || aType == "DOMFocusOut" || aType == "DOMActivate")
951 pEvent = new events::CUIEvent;
952 } else if ( aType == "click" || aType == "mousedown" || aType == "mouseup"
953 || aType == "mouseover" || aType == "mousemove" || aType == "mouseout" )
955 pEvent = new events::CMouseEvent;
957 else // generic event
959 pEvent = new events::CEvent;
961 return pEvent;
964 // css::xml::sax::XSAXSerializable
965 void SAL_CALL CDocument::serialize(
966 const Reference< XDocumentHandler >& i_xHandler,
967 const Sequence< beans::StringPair >& i_rNamespaces)
969 ::osl::MutexGuard const g(m_Mutex);
971 // add new namespaces to root node
972 xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr);
973 if (nullptr != pRoot) {
974 for (const beans::StringPair& rNsDef : i_rNamespaces) {
975 OString prefix = OUStringToOString(rNsDef.First,
976 RTL_TEXTENCODING_UTF8);
977 OString href = OUStringToOString(rNsDef.Second,
978 RTL_TEXTENCODING_UTF8);
979 // this will only add the ns if it does not exist already
980 xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()),
981 reinterpret_cast<const xmlChar*>(prefix.getStr()));
983 // eliminate duplicate namespace declarations
984 nscleanup(pRoot->children, pRoot);
986 saxify(i_xHandler);
989 // css::xml::sax::XFastSAXSerializable
990 void SAL_CALL CDocument::fastSerialize( const Reference< XFastDocumentHandler >& i_xHandler,
991 const Reference< XFastTokenHandler >& i_xTokenHandler,
992 const Sequence< beans::StringPair >& i_rNamespaces,
993 const Sequence< beans::Pair< OUString, sal_Int32 > >& i_rRegisterNamespaces )
995 ::osl::MutexGuard const g(m_Mutex);
997 // add new namespaces to root node
998 xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr);
999 if (nullptr != pRoot) {
1000 for (const beans::StringPair& rNsDef : i_rNamespaces) {
1001 OString prefix = OUStringToOString(rNsDef.First,
1002 RTL_TEXTENCODING_UTF8);
1003 OString href = OUStringToOString(rNsDef.Second,
1004 RTL_TEXTENCODING_UTF8);
1005 // this will only add the ns if it does not exist already
1006 xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()),
1007 reinterpret_cast<const xmlChar*>(prefix.getStr()));
1009 // eliminate duplicate namespace declarations
1010 nscleanup(pRoot->children, pRoot);
1013 Context aContext(i_xHandler,
1014 dynamic_cast<sax_fastparser::FastTokenHandlerBase*>(i_xTokenHandler.get()));
1016 // register namespace ids
1017 for (const beans::Pair<OUString,sal_Int32>& rNs : i_rRegisterNamespaces)
1019 OSL_ENSURE(rNs.Second >= FastToken::NAMESPACE,
1020 "CDocument::fastSerialize(): invalid NS token id");
1021 aContext.maNamespaceMap[ rNs.First ] = rNs.Second;
1024 fastSaxify(aContext);
1028 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */