build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / unoxml / source / dom / document.cxx
blob8c0b25d2ffc57cf049ff81b9e714b06d2c9af871
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 "../events/event.hxx"
38 #include "../events/mutationevent.hxx"
39 #include "../events/uievent.hxx"
40 #include "../events/mouseevent.hxx"
41 #include "../events/eventdispatcher.hxx"
43 #include <string.h>
45 #include <osl/diagnose.h>
47 #include <com/sun/star/xml/sax/FastToken.hpp>
48 #include <com/sun/star/xml/sax/XExtendedDocumentHandler.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_streamListeners()
92 , m_pEventDispatcher(new events::CEventDispatcher())
96 ::rtl::Reference<CDocument> CDocument::CreateCDocument(xmlDocPtr const pDoc)
98 ::rtl::Reference<CDocument> const xDoc(new CDocument(pDoc));
99 // add the doc itself to its nodemap!
100 xDoc->m_NodeMap.insert(
101 nodemap_t::value_type(reinterpret_cast<xmlNodePtr>(pDoc),
102 ::std::make_pair(
103 WeakReference<XNode>(static_cast<XDocument*>(xDoc.get())),
104 xDoc.get())));
105 return xDoc;
108 CDocument::~CDocument()
110 ::osl::MutexGuard const g(m_Mutex);
111 #ifdef DBG_UTIL
112 // node map must be empty now, otherwise CDocument must not die!
113 for (nodemap_t::iterator i = m_NodeMap.begin();
114 i != m_NodeMap.end(); ++i)
116 Reference<XNode> const xNode(i->second.first);
117 OSL_ENSURE(!xNode.is(),
118 "CDocument::~CDocument(): ERROR: live node in document node map!");
120 #endif
121 xmlFreeDoc(m_aDocPtr);
125 events::CEventDispatcher & CDocument::GetEventDispatcher()
127 return *m_pEventDispatcher;
130 ::rtl::Reference< CElement > CDocument::GetDocumentElement()
132 xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr);
133 ::rtl::Reference< CElement > const xRet(
134 dynamic_cast<CElement*>(GetCNode(pNode).get()));
135 return xRet;
138 void
139 CDocument::RemoveCNode(xmlNodePtr const pNode, CNode const*const pCNode)
141 nodemap_t::iterator const i = m_NodeMap.find(pNode);
142 if (i != m_NodeMap.end()) {
143 // #i113681# consider this scenario:
144 // T1 calls ~CNode
145 // T2 calls getCNode: lookup will find i->second->first invalid
146 // so a new CNode is created and inserted
147 // T1 calls removeCNode: i->second->second now points to a
148 // different CNode instance!
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);
158 /** NB: this is the CNode factory.
159 it is the only place where CNodes may be instantiated.
160 all CNodes must be registered at the m_NodeMap.
162 ::rtl::Reference<CNode>
163 CDocument::GetCNode(xmlNodePtr const pNode, bool const bCreate)
165 if (nullptr == pNode) {
166 return nullptr;
168 //check whether there is already an instance for this node
169 nodemap_t::const_iterator const i = m_NodeMap.find(pNode);
170 if (i != m_NodeMap.end()) {
171 // #i113681# check that the CNode is still alive
172 uno::Reference<XNode> const xNode(i->second.first);
173 if (xNode.is())
175 ::rtl::Reference<CNode> ret(i->second.second);
176 OSL_ASSERT(ret.is());
177 return ret;
181 if (!bCreate) { return nullptr; }
183 // there is not yet an instance wrapping this node,
184 // create it and store it in the map
186 ::rtl::Reference<CNode> pCNode;
187 switch (pNode->type)
189 case XML_ELEMENT_NODE:
190 // m_aNodeType = NodeType::ELEMENT_NODE;
191 pCNode = static_cast< CNode* >(
192 new CElement(*this, m_Mutex, pNode));
193 break;
194 case XML_TEXT_NODE:
195 // m_aNodeType = NodeType::TEXT_NODE;
196 pCNode = static_cast< CNode* >(
197 new CText(*this, m_Mutex, pNode));
198 break;
199 case XML_CDATA_SECTION_NODE:
200 // m_aNodeType = NodeType::CDATA_SECTION_NODE;
201 pCNode = static_cast< CNode* >(
202 new CCDATASection(*this, m_Mutex, pNode));
203 break;
204 case XML_ENTITY_REF_NODE:
205 // m_aNodeType = NodeType::ENTITY_REFERENCE_NODE;
206 pCNode = static_cast< CNode* >(
207 new CEntityReference(*this, m_Mutex, pNode));
208 break;
209 case XML_ENTITY_NODE:
210 // m_aNodeType = NodeType::ENTITY_NODE;
211 pCNode = static_cast< CNode* >(new CEntity(*this, m_Mutex,
212 reinterpret_cast<xmlEntityPtr>(pNode)));
213 break;
214 case XML_PI_NODE:
215 // m_aNodeType = NodeType::PROCESSING_INSTRUCTION_NODE;
216 pCNode = static_cast< CNode* >(
217 new CProcessingInstruction(*this, m_Mutex, pNode));
218 break;
219 case XML_COMMENT_NODE:
220 // m_aNodeType = NodeType::COMMENT_NODE;
221 pCNode = static_cast< CNode* >(
222 new CComment(*this, m_Mutex, pNode));
223 break;
224 case XML_DOCUMENT_NODE:
225 // m_aNodeType = NodeType::DOCUMENT_NODE;
226 OSL_ENSURE(false, "CDocument::GetCNode is not supposed to"
227 " create a CDocument!!!");
228 pCNode = static_cast< CNode* >(new CDocument(
229 reinterpret_cast<xmlDocPtr>(pNode)));
230 break;
231 case XML_DOCUMENT_TYPE_NODE:
232 case XML_DTD_NODE:
233 // m_aNodeType = NodeType::DOCUMENT_TYPE_NODE;
234 pCNode = static_cast< CNode* >(new CDocumentType(*this, m_Mutex,
235 reinterpret_cast<xmlDtdPtr>(pNode)));
236 break;
237 case XML_DOCUMENT_FRAG_NODE:
238 // m_aNodeType = NodeType::DOCUMENT_FRAGMENT_NODE;
239 pCNode = static_cast< CNode* >(
240 new CDocumentFragment(*this, m_Mutex, pNode));
241 break;
242 case XML_NOTATION_NODE:
243 // m_aNodeType = NodeType::NOTATION_NODE;
244 pCNode = static_cast< CNode* >(new CNotation(*this, m_Mutex,
245 reinterpret_cast<xmlNotationPtr>(pNode)));
246 break;
247 case XML_ATTRIBUTE_NODE:
248 // m_aNodeType = NodeType::ATTRIBUTE_NODE;
249 pCNode = static_cast< CNode* >(new CAttr(*this, m_Mutex,
250 reinterpret_cast<xmlAttrPtr>(pNode)));
251 break;
252 // unsupported node types
253 case XML_HTML_DOCUMENT_NODE:
254 case XML_ELEMENT_DECL:
255 case XML_ATTRIBUTE_DECL:
256 case XML_ENTITY_DECL:
257 case XML_NAMESPACE_DECL:
258 default:
259 break;
262 if (pCNode != nullptr) {
263 bool const bInserted = m_NodeMap.insert(
264 nodemap_t::value_type(pNode,
265 ::std::make_pair(WeakReference<XNode>(pCNode.get()),
266 pCNode.get()))
267 ).second;
268 OSL_ASSERT(bInserted);
269 if (!bInserted) {
270 // if insertion failed, delete new instance and return null
271 return nullptr;
275 OSL_ENSURE(pCNode.is(), "no node produced during CDocument::GetCNode!");
276 return pCNode;
280 CDocument & CDocument::GetOwnerDocument()
282 return *this;
285 void CDocument::saxify(const Reference< XDocumentHandler >& i_xHandler)
287 i_xHandler->startDocument();
288 for (xmlNodePtr pChild = m_aNodePtr->children;
289 pChild != nullptr; pChild = pChild->next) {
290 ::rtl::Reference<CNode> const pNode = GetCNode(pChild);
291 OSL_ENSURE(pNode != nullptr, "CNode::get returned 0");
292 pNode->saxify(i_xHandler);
294 i_xHandler->endDocument();
297 void CDocument::fastSaxify( Context& rContext )
299 rContext.mxDocHandler->startDocument();
300 for (xmlNodePtr pChild = m_aNodePtr->children;
301 pChild != nullptr; pChild = pChild->next) {
302 ::rtl::Reference<CNode> const pNode = GetCNode(pChild);
303 OSL_ENSURE(pNode != nullptr, "CNode::get returned 0");
304 pNode->fastSaxify(rContext);
306 rContext.mxDocHandler->endDocument();
309 bool CDocument::IsChildTypeAllowed(NodeType const nodeType)
311 switch (nodeType) {
312 case NodeType_PROCESSING_INSTRUCTION_NODE:
313 case NodeType_COMMENT_NODE:
314 return true;
315 case NodeType_ELEMENT_NODE:
316 // there may be only one!
317 return nullptr == lcl_getDocumentRootPtr(m_aDocPtr);
318 case NodeType_DOCUMENT_TYPE_NODE:
319 // there may be only one!
320 return nullptr == lcl_getDocumentType(m_aDocPtr);
321 default:
322 return false;
327 void SAL_CALL CDocument::addListener(const Reference< XStreamListener >& aListener )
328 throw (RuntimeException, std::exception)
330 ::osl::MutexGuard const g(m_Mutex);
332 m_streamListeners.insert(aListener);
335 void SAL_CALL CDocument::removeListener(const Reference< XStreamListener >& aListener )
336 throw (RuntimeException, std::exception)
338 ::osl::MutexGuard const g(m_Mutex);
340 m_streamListeners.erase(aListener);
343 // IO context functions for libxml2 interaction
344 typedef struct {
345 Reference< XOutputStream > stream;
346 bool allowClose;
347 } IOContext;
349 extern "C" {
350 // write callback
351 // int xmlOutputWriteCallback (void * context, const char * buffer, int len)
352 static int writeCallback(void *context, const char* buffer, int len){
353 // create a sequence and write it to the stream
354 IOContext *pContext = static_cast<IOContext*>(context);
355 Sequence<sal_Int8> bs(reinterpret_cast<const sal_Int8*>(buffer), len);
356 pContext->stream->writeBytes(bs);
357 return len;
360 // close callback
361 //int xmlOutputCloseCallback (void * context)
362 static int closeCallback(void *context)
364 IOContext *pContext = static_cast<IOContext*>(context);
365 if (pContext->allowClose) {
366 pContext->stream->closeOutput();
368 return 0;
370 } // extern "C"
372 void SAL_CALL CDocument::start()
373 throw (RuntimeException, std::exception)
375 listenerlist_t streamListeners;
377 ::osl::MutexGuard const g(m_Mutex);
379 if (! m_rOutputStream.is()) { throw RuntimeException(); }
380 streamListeners = m_streamListeners;
383 // notify listeners about start
384 listenerlist_t::const_iterator iter1 = streamListeners.begin();
385 while (iter1 != streamListeners.end()) {
386 Reference< XStreamListener > aListener = *iter1;
387 aListener->started();
388 ++iter1;
392 ::osl::MutexGuard const g(m_Mutex);
394 // check again! could have been reset...
395 if (! m_rOutputStream.is()) { throw RuntimeException(); }
397 // setup libxml IO and write data to output stream
398 IOContext ioctx = {m_rOutputStream, false};
399 xmlOutputBufferPtr pOut = xmlOutputBufferCreateIO(
400 writeCallback, closeCallback, &ioctx, nullptr);
401 xmlSaveFileTo(pOut, m_aNodePtr->doc, nullptr);
404 // call listeners
405 listenerlist_t::const_iterator iter2 = streamListeners.begin();
406 while (iter2 != streamListeners.end()) {
407 Reference< XStreamListener > aListener = *iter2;
408 aListener->closed();
409 ++iter2;
413 void SAL_CALL CDocument::terminate()
414 throw (RuntimeException, std::exception)
416 // not supported
419 void SAL_CALL CDocument::setOutputStream( const Reference< XOutputStream >& aStream )
420 throw (RuntimeException, std::exception)
422 ::osl::MutexGuard const g(m_Mutex);
424 m_rOutputStream = aStream;
427 Reference< XOutputStream > SAL_CALL CDocument::getOutputStream() throw (RuntimeException, std::exception)
429 ::osl::MutexGuard const g(m_Mutex);
431 return m_rOutputStream;
434 // Creates an Attr of the given name.
435 Reference< XAttr > SAL_CALL CDocument::createAttribute(const OUString& name)
436 throw (RuntimeException, DOMException, std::exception)
438 ::osl::MutexGuard const g(m_Mutex);
440 OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8);
441 xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr());
442 xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr, pName, nullptr);
443 ::rtl::Reference< CAttr > const pCAttr(
444 dynamic_cast< CAttr* >(GetCNode(
445 reinterpret_cast<xmlNodePtr>(pAttr)).get()));
446 if (!pCAttr.is()) { throw RuntimeException(); }
447 pCAttr->m_bUnlinked = true;
448 return pCAttr.get();
451 // Creates an attribute of the given qualified name and namespace URI.
452 Reference< XAttr > SAL_CALL CDocument::createAttributeNS(
453 const OUString& ns, const OUString& qname)
454 throw (RuntimeException, DOMException, std::exception)
456 ::osl::MutexGuard const g(m_Mutex);
458 // libxml does not allow a NS definition to be attached to an
459 // attribute node - which is a good thing, since namespaces are
460 // only defined as parts of element nodes
461 // thus the namespace data is stored in CAttr::m_pNamespace
462 sal_Int32 i = qname.indexOf(':');
463 OString oPrefix, oName, oUri;
464 if (i != -1)
466 oPrefix = OUStringToOString(qname.copy(0, i), RTL_TEXTENCODING_UTF8);
467 oName = OUStringToOString(qname.copy(i+1, qname.getLength()-i-1), RTL_TEXTENCODING_UTF8);
469 else
471 oName = OUStringToOString(qname, RTL_TEXTENCODING_UTF8);
473 oUri = OUStringToOString(ns, RTL_TEXTENCODING_UTF8);
474 xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr,
475 reinterpret_cast<xmlChar const*>(oName.getStr()), nullptr);
476 ::rtl::Reference< CAttr > const pCAttr(
477 dynamic_cast< CAttr* >(GetCNode(
478 reinterpret_cast<xmlNodePtr>(pAttr)).get()));
479 if (!pCAttr.is()) { throw RuntimeException(); }
480 // store the namespace data!
481 pCAttr->m_pNamespace.reset( new stringpair_t(oUri, oPrefix) );
482 pCAttr->m_bUnlinked = true;
484 return pCAttr.get();
487 // Creates a CDATASection node whose value is the specified string.
488 Reference< XCDATASection > SAL_CALL CDocument::createCDATASection(const OUString& data)
489 throw (RuntimeException, std::exception)
491 ::osl::MutexGuard const g(m_Mutex);
493 OString const oData(
494 OUStringToOString(data, RTL_TEXTENCODING_UTF8));
495 xmlChar const*const pData =
496 reinterpret_cast<xmlChar const*>(oData.getStr());
497 xmlNodePtr const pText =
498 xmlNewCDataBlock(m_aDocPtr, pData, strlen(oData.getStr()));
499 Reference< XCDATASection > const xRet(
500 static_cast< XNode* >(GetCNode(pText).get()),
501 UNO_QUERY_THROW);
502 return xRet;
505 // Creates a Comment node given the specified string.
506 Reference< XComment > SAL_CALL CDocument::createComment(const OUString& data)
507 throw (RuntimeException, std::exception)
509 ::osl::MutexGuard const g(m_Mutex);
511 OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
512 xmlChar const *pData = reinterpret_cast<xmlChar const *>(o1.getStr());
513 xmlNodePtr pComment = xmlNewDocComment(m_aDocPtr, pData);
514 Reference< XComment > const xRet(
515 static_cast< XNode* >(GetCNode(pComment).get()),
516 UNO_QUERY_THROW);
517 return xRet;
520 //Creates an empty DocumentFragment object.
521 Reference< XDocumentFragment > SAL_CALL CDocument::createDocumentFragment()
522 throw (RuntimeException, std::exception)
524 ::osl::MutexGuard const g(m_Mutex);
526 xmlNodePtr pFrag = xmlNewDocFragment(m_aDocPtr);
527 Reference< XDocumentFragment > const xRet(
528 static_cast< XNode* >(GetCNode(pFrag).get()),
529 UNO_QUERY_THROW);
530 return xRet;
533 // Creates an element of the type specified.
534 Reference< XElement > SAL_CALL CDocument::createElement(const OUString& tagName)
535 throw (RuntimeException, DOMException, std::exception)
537 ::osl::MutexGuard const g(m_Mutex);
539 OString o1 = OUStringToOString(tagName, RTL_TEXTENCODING_UTF8);
540 xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr());
541 xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, nullptr, pName, nullptr);
542 Reference< XElement > const xRet(
543 static_cast< XNode* >(GetCNode(pNode).get()),
544 UNO_QUERY_THROW);
545 return xRet;
548 // Creates an element of the given qualified name and namespace URI.
549 Reference< XElement > SAL_CALL CDocument::createElementNS(
550 const OUString& ns, const OUString& qname)
551 throw (RuntimeException, DOMException, std::exception)
553 ::osl::MutexGuard const g(m_Mutex);
555 sal_Int32 i = qname.indexOf(':');
556 if (ns.isEmpty()) throw RuntimeException();
557 xmlChar const *pPrefix;
558 xmlChar const *pName;
559 OString o1, o2, o3;
560 if ( i != -1) {
561 o1 = OUStringToOString(qname.copy(0, i), RTL_TEXTENCODING_UTF8);
562 pPrefix = reinterpret_cast<xmlChar const *>(o1.getStr());
563 o2 = OUStringToOString(qname.copy(i+1, qname.getLength()-i-1), RTL_TEXTENCODING_UTF8);
564 pName = reinterpret_cast<xmlChar const *>(o2.getStr());
565 } else {
566 // default prefix
567 pPrefix = reinterpret_cast<xmlChar const *>("");
568 o2 = OUStringToOString(qname, RTL_TEXTENCODING_UTF8);
569 pName = reinterpret_cast<xmlChar const *>(o2.getStr());
571 o3 = OUStringToOString(ns, RTL_TEXTENCODING_UTF8);
572 xmlChar const *pUri = reinterpret_cast<xmlChar const *>(o3.getStr());
574 // xmlNsPtr aNsPtr = xmlNewReconciledNs?
575 // xmlNsPtr aNsPtr = xmlNewGlobalNs?
576 xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, nullptr, pName, nullptr);
577 xmlNsPtr const pNs = xmlNewNs(pNode, pUri, pPrefix);
578 xmlSetNs(pNode, pNs);
579 Reference< XElement > const xRet(
580 static_cast< XNode* >(GetCNode(pNode).get()),
581 UNO_QUERY_THROW);
582 return xRet;
585 //Creates an EntityReference object.
586 Reference< XEntityReference > SAL_CALL CDocument::createEntityReference(const OUString& name)
587 throw (RuntimeException, DOMException, std::exception)
589 ::osl::MutexGuard const g(m_Mutex);
591 OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8);
592 xmlChar const *pName = reinterpret_cast<xmlChar const *>(o1.getStr());
593 xmlNodePtr const pNode = xmlNewReference(m_aDocPtr, pName);
594 Reference< XEntityReference > const xRet(
595 static_cast< XNode* >(GetCNode(pNode).get()),
596 UNO_QUERY_THROW);
597 return xRet;
600 // Creates a ProcessingInstruction node given the specified name and
601 // data strings.
602 Reference< XProcessingInstruction > SAL_CALL CDocument::createProcessingInstruction(
603 const OUString& target, const OUString& data)
604 throw (RuntimeException, DOMException, std::exception)
606 ::osl::MutexGuard const g(m_Mutex);
608 OString o1 = OUStringToOString(target, RTL_TEXTENCODING_UTF8);
609 xmlChar const *pTarget = reinterpret_cast<xmlChar const *>(o1.getStr());
610 OString o2 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
611 xmlChar const *pData = reinterpret_cast<xmlChar const *>(o2.getStr());
612 xmlNodePtr const pNode = xmlNewDocPI(m_aDocPtr, pTarget, pData);
613 pNode->doc = m_aDocPtr;
614 Reference< XProcessingInstruction > const xRet(
615 static_cast< XNode* >(GetCNode(pNode).get()),
616 UNO_QUERY_THROW);
617 return xRet;
620 // Creates a Text node given the specified string.
621 Reference< XText > SAL_CALL CDocument::createTextNode(const OUString& data)
622 throw (RuntimeException, std::exception)
624 ::osl::MutexGuard const g(m_Mutex);
626 OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
627 xmlChar const *pData = reinterpret_cast<xmlChar const *>(o1.getStr());
628 xmlNodePtr const pNode = xmlNewDocText(m_aDocPtr, pData);
629 Reference< XText > const xRet(
630 static_cast< XNode* >(GetCNode(pNode).get()),
631 UNO_QUERY_THROW);
632 return xRet;
635 // The Document Type Declaration (see DocumentType) associated with this
636 // document.
637 Reference< XDocumentType > SAL_CALL CDocument::getDoctype()
638 throw (RuntimeException, std::exception)
640 ::osl::MutexGuard const g(m_Mutex);
642 xmlNodePtr const pDocType(lcl_getDocumentType(m_aDocPtr));
643 Reference< XDocumentType > const xRet(
644 static_cast< XNode* >(GetCNode(pDocType).get()),
645 UNO_QUERY);
646 return xRet;
649 // This is a convenience attribute that allows direct access to the child
650 // node that is the root element of the document.
651 Reference< XElement > SAL_CALL CDocument::getDocumentElement()
652 throw (RuntimeException, std::exception)
654 ::osl::MutexGuard const g(m_Mutex);
656 xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr);
657 if (!pNode) { return nullptr; }
658 Reference< XElement > const xRet(
659 static_cast< XNode* >(GetCNode(pNode).get()),
660 UNO_QUERY);
661 return xRet;
664 static xmlNodePtr
665 lcl_search_element_by_id(const xmlNodePtr cur, const xmlChar* id)
667 if (cur == nullptr)
668 return nullptr;
669 // look in current node
670 if (cur->type == XML_ELEMENT_NODE)
672 xmlAttrPtr a = cur->properties;
673 while (a != nullptr)
675 if (a->atype == XML_ATTRIBUTE_ID) {
676 if (strcmp(reinterpret_cast<char*>(a->children->content), reinterpret_cast<char const *>(id)) == 0)
677 return cur;
679 a = a->next;
682 // look in children
683 xmlNodePtr result = lcl_search_element_by_id(cur->children, id);
684 if (result != nullptr)
685 return result;
686 result = lcl_search_element_by_id(cur->next, id);
687 return result;
690 // Returns the Element whose ID is given by elementId.
691 Reference< XElement > SAL_CALL
692 CDocument::getElementById(const OUString& elementId)
693 throw (RuntimeException, std::exception)
695 ::osl::MutexGuard const g(m_Mutex);
697 // search the tree for an element with the given ID
698 OString o1 = OUStringToOString(elementId, RTL_TEXTENCODING_UTF8);
699 xmlChar const *pId = reinterpret_cast<xmlChar const *>(o1.getStr());
700 xmlNodePtr const pStart = lcl_getDocumentRootPtr(m_aDocPtr);
701 if (!pStart) { return nullptr; }
702 xmlNodePtr const pNode = lcl_search_element_by_id(pStart, pId);
703 Reference< XElement > const xRet(
704 static_cast< XNode* >(GetCNode(pNode).get()),
705 UNO_QUERY);
706 return xRet;
710 Reference< XNodeList > SAL_CALL
711 CDocument::getElementsByTagName(OUString const& rTagname)
712 throw (RuntimeException, std::exception)
714 ::osl::MutexGuard const g(m_Mutex);
716 Reference< XNodeList > const xRet(
717 new CElementList(this->GetDocumentElement(), m_Mutex, rTagname));
718 return xRet;
721 Reference< XNodeList > SAL_CALL CDocument::getElementsByTagNameNS(
722 OUString const& rNamespaceURI, OUString const& rLocalName)
723 throw (RuntimeException, std::exception)
725 ::osl::MutexGuard const g(m_Mutex);
727 Reference< XNodeList > const xRet(
728 new CElementList(this->GetDocumentElement(), m_Mutex,
729 rLocalName, &rNamespaceURI));
730 return xRet;
733 Reference< XDOMImplementation > SAL_CALL CDocument::getImplementation()
734 throw (RuntimeException, std::exception)
736 // does not need mutex currently
737 return Reference< XDOMImplementation >(CDOMImplementation::get());
740 // helper function to recursively import siblings
741 static void lcl_ImportSiblings(
742 Reference< XDocument > const& xTargetDocument,
743 Reference< XNode > const& xTargetParent,
744 Reference< XNode > const& xChild)
746 Reference< XNode > xSibling = xChild;
747 while (xSibling.is())
749 Reference< XNode > const xTmp(
750 xTargetDocument->importNode(xSibling, true));
751 xTargetParent->appendChild(xTmp);
752 xSibling = xSibling->getNextSibling();
756 static Reference< XNode >
757 lcl_ImportNode( Reference< XDocument > const& xDocument,
758 Reference< XNode > const& xImportedNode, bool deep)
760 Reference< XNode > xNode;
761 NodeType aNodeType = xImportedNode->getNodeType();
762 switch (aNodeType)
764 case NodeType_ATTRIBUTE_NODE:
766 Reference< XAttr > const xAttr(xImportedNode, UNO_QUERY_THROW);
767 Reference< XAttr > const xNew =
768 xDocument->createAttribute(xAttr->getName());
769 xNew->setValue(xAttr->getValue());
770 xNode.set(xNew, UNO_QUERY);
771 break;
773 case NodeType_CDATA_SECTION_NODE:
775 Reference< XCDATASection > const xCData(xImportedNode,
776 UNO_QUERY_THROW);
777 Reference< XCDATASection > const xNewCData =
778 xDocument->createCDATASection(xCData->getData());
779 xNode.set(xNewCData, UNO_QUERY);
780 break;
782 case NodeType_COMMENT_NODE:
784 Reference< XComment > const xComment(xImportedNode,
785 UNO_QUERY_THROW);
786 Reference< XComment > const xNewComment =
787 xDocument->createComment(xComment->getData());
788 xNode.set(xNewComment, UNO_QUERY);
789 break;
791 case NodeType_DOCUMENT_FRAGMENT_NODE:
793 Reference< XDocumentFragment > const xFrag(xImportedNode,
794 UNO_QUERY_THROW);
795 Reference< XDocumentFragment > const xNewFrag =
796 xDocument->createDocumentFragment();
797 xNode.set(xNewFrag, UNO_QUERY);
798 break;
800 case NodeType_ELEMENT_NODE:
802 Reference< XElement > const xElement(xImportedNode,
803 UNO_QUERY_THROW);
804 OUString const aNsUri = xImportedNode->getNamespaceURI();
805 OUString const aNsPrefix = xImportedNode->getPrefix();
806 OUString aQName = xElement->getTagName();
807 Reference< XElement > xNewElement;
808 if (!aNsUri.isEmpty())
810 if (!aNsPrefix.isEmpty()) {
811 aQName = aNsPrefix + ":" + aQName;
813 xNewElement = xDocument->createElementNS(aNsUri, aQName);
814 } else {
815 xNewElement = xDocument->createElement(aQName);
818 // get attributes
819 if (xElement->hasAttributes())
821 Reference< XNamedNodeMap > attribs = xElement->getAttributes();
822 for (sal_Int32 i = 0; i < attribs->getLength(); i++)
824 Reference< XAttr > const curAttr(attribs->item(i),
825 UNO_QUERY_THROW);
826 OUString const aAttrUri = curAttr->getNamespaceURI();
827 OUString const aAttrPrefix = curAttr->getPrefix();
828 OUString aAttrName = curAttr->getName();
829 OUString const sValue = curAttr->getValue();
830 if (!aAttrUri.isEmpty())
832 if (!aAttrPrefix.isEmpty()) {
833 aAttrName = aAttrPrefix + ":" + aAttrName;
835 xNewElement->setAttributeNS(
836 aAttrUri, aAttrName, sValue);
837 } else {
838 xNewElement->setAttribute(aAttrName, sValue);
842 xNode.set(xNewElement, UNO_QUERY);
843 break;
845 case NodeType_ENTITY_REFERENCE_NODE:
847 Reference< XEntityReference > const xRef(xImportedNode,
848 UNO_QUERY_THROW);
849 Reference< XEntityReference > const xNewRef(
850 xDocument->createEntityReference(xRef->getNodeName()));
851 xNode.set(xNewRef, UNO_QUERY);
852 break;
854 case NodeType_PROCESSING_INSTRUCTION_NODE:
856 Reference< XProcessingInstruction > const xPi(xImportedNode,
857 UNO_QUERY_THROW);
858 Reference< XProcessingInstruction > const xNewPi(
859 xDocument->createProcessingInstruction(
860 xPi->getTarget(), xPi->getData()));
861 xNode.set(xNewPi, UNO_QUERY);
862 break;
864 case NodeType_TEXT_NODE:
866 Reference< XText > const xText(xImportedNode, UNO_QUERY_THROW);
867 Reference< XText > const xNewText(
868 xDocument->createTextNode(xText->getData()));
869 xNode.set(xNewText, UNO_QUERY);
870 break;
872 case NodeType_ENTITY_NODE:
873 case NodeType_DOCUMENT_NODE:
874 case NodeType_DOCUMENT_TYPE_NODE:
875 case NodeType_NOTATION_NODE:
876 default:
877 // can't be imported
878 throw RuntimeException();
881 if (deep)
883 // get children and import them
884 Reference< XNode > const xChild = xImportedNode->getFirstChild();
885 if (xChild.is())
887 lcl_ImportSiblings(xDocument, xNode, xChild);
891 /* DOMNodeInsertedIntoDocument
892 * Fired when a node is being inserted into a document,
893 * either through direct insertion of the Node or insertion of a
894 * subtree in which it is contained. This event is dispatched after
895 * the insertion has taken place. The target of this event is the node
896 * being inserted. If the Node is being directly inserted the DOMNodeInserted
897 * event will fire before the DOMNodeInsertedIntoDocument event.
898 * Bubbles: No
899 * Cancelable: No
900 * Context Info: None
902 if (xNode.is())
904 Reference< XDocumentEvent > const xDocevent(xDocument, UNO_QUERY);
905 Reference< XMutationEvent > const event(xDocevent->createEvent(
906 "DOMNodeInsertedIntoDocument"), UNO_QUERY_THROW);
907 event->initMutationEvent(
908 "DOMNodeInsertedIntoDocument", true, false, Reference< XNode >(),
909 OUString(), OUString(), OUString(), (AttrChangeType)0 );
910 Reference< XEventTarget > const xDocET(xDocument, UNO_QUERY);
911 xDocET->dispatchEvent(event);
914 return xNode;
917 Reference< XNode > SAL_CALL CDocument::importNode(
918 Reference< XNode > const& xImportedNode, sal_Bool deep)
919 throw (RuntimeException, DOMException, std::exception)
921 if (!xImportedNode.is()) { throw RuntimeException(); }
923 // NB: this whole operation inherently accesses 2 distinct documents.
924 // The imported node could even be from a different DOM implementation,
925 // so this implementation cannot make any assumptions about the
926 // locking strategy of the imported node.
927 // So the import takes no lock on this document;
928 // it only calls UNO methods on this document that temporarily
929 // lock the document, and UNO methods on the imported node that
930 // may temporarily lock the other document.
931 // As a consequence, the import is not atomic with regard to
932 // concurrent modifications of either document, but it should not
933 // deadlock.
934 // To ensure that no members are accessed, the implementation is in
935 // static non-member functions.
937 Reference< XDocument > const xDocument(this);
938 // already in doc?
939 if (xImportedNode->getOwnerDocument() == xDocument) {
940 return xImportedNode;
943 Reference< XNode > const xNode(
944 lcl_ImportNode(xDocument, xImportedNode, deep) );
945 return xNode;
948 OUString SAL_CALL CDocument::getNodeName()throw (RuntimeException, std::exception)
950 // does not need mutex currently
951 return OUString("#document");
954 OUString SAL_CALL CDocument::getNodeValue() throw (RuntimeException, std::exception)
956 // does not need mutex currently
957 return OUString();
960 Reference< XNode > SAL_CALL CDocument::cloneNode(sal_Bool bDeep)
961 throw (RuntimeException, std::exception)
963 ::osl::MutexGuard const g(m_rMutex);
965 OSL_ASSERT(nullptr != m_aNodePtr);
966 if (nullptr == m_aNodePtr) {
967 return nullptr;
969 xmlDocPtr const pClone(xmlCopyDoc(m_aDocPtr, (bDeep) ? 1 : 0));
970 if (nullptr == pClone) { return nullptr; }
971 Reference< XNode > const xRet(
972 static_cast<CNode*>(CDocument::CreateCDocument(pClone).get()));
973 return xRet;
976 Reference< XEvent > SAL_CALL CDocument::createEvent(const OUString& aType) throw (RuntimeException, std::exception)
978 // does not need mutex currently
979 events::CEvent *pEvent = nullptr;
980 if ( aType == "DOMSubtreeModified" || aType == "DOMNodeInserted" || aType == "DOMNodeRemoved"
981 || aType == "DOMNodeRemovedFromDocument" || aType == "DOMNodeInsertedIntoDocument" || aType == "DOMAttrModified"
982 || aType == "DOMCharacterDataModified")
984 pEvent = new events::CMutationEvent;
986 } else if ( aType == "DOMFocusIn" || aType == "DOMFocusOut" || aType == "DOMActivate")
988 pEvent = new events::CUIEvent;
989 } else if ( aType == "click" || aType == "mousedown" || aType == "mouseup"
990 || aType == "mouseover" || aType == "mousemove" || aType == "mouseout" )
992 pEvent = new events::CMouseEvent;
994 else // generic event
996 pEvent = new events::CEvent;
998 return Reference< XEvent >(pEvent);
1001 // css::xml::sax::XSAXSerializable
1002 void SAL_CALL CDocument::serialize(
1003 const Reference< XDocumentHandler >& i_xHandler,
1004 const Sequence< beans::StringPair >& i_rNamespaces)
1005 throw (RuntimeException, SAXException, std::exception)
1007 ::osl::MutexGuard const g(m_Mutex);
1009 // add new namespaces to root node
1010 xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr);
1011 if (nullptr != pRoot) {
1012 const beans::StringPair * pSeq = i_rNamespaces.getConstArray();
1013 for (const beans::StringPair *pNsDef = pSeq;
1014 pNsDef < pSeq + i_rNamespaces.getLength(); ++pNsDef) {
1015 OString prefix = OUStringToOString(pNsDef->First,
1016 RTL_TEXTENCODING_UTF8);
1017 OString href = OUStringToOString(pNsDef->Second,
1018 RTL_TEXTENCODING_UTF8);
1019 // this will only add the ns if it does not exist already
1020 xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()),
1021 reinterpret_cast<const xmlChar*>(prefix.getStr()));
1023 // eliminate duplicate namespace declarations
1024 nscleanup(pRoot->children, pRoot);
1026 saxify(i_xHandler);
1029 // css::xml::sax::XFastSAXSerializable
1030 void SAL_CALL CDocument::fastSerialize( const Reference< XFastDocumentHandler >& i_xHandler,
1031 const Reference< XFastTokenHandler >& i_xTokenHandler,
1032 const Sequence< beans::StringPair >& i_rNamespaces,
1033 const Sequence< beans::Pair< OUString, sal_Int32 > >& i_rRegisterNamespaces )
1034 throw (SAXException, RuntimeException, std::exception)
1036 ::osl::MutexGuard const g(m_Mutex);
1038 // add new namespaces to root node
1039 xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr);
1040 if (nullptr != pRoot) {
1041 const beans::StringPair * pSeq = i_rNamespaces.getConstArray();
1042 for (const beans::StringPair *pNsDef = pSeq;
1043 pNsDef < pSeq + i_rNamespaces.getLength(); ++pNsDef) {
1044 OString prefix = OUStringToOString(pNsDef->First,
1045 RTL_TEXTENCODING_UTF8);
1046 OString href = OUStringToOString(pNsDef->Second,
1047 RTL_TEXTENCODING_UTF8);
1048 // this will only add the ns if it does not exist already
1049 xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()),
1050 reinterpret_cast<const xmlChar*>(prefix.getStr()));
1052 // eliminate duplicate namespace declarations
1053 nscleanup(pRoot->children, pRoot);
1056 Context aContext(i_xHandler,
1057 i_xTokenHandler);
1059 // register namespace ids
1060 const beans::Pair<OUString,sal_Int32>* pSeq = i_rRegisterNamespaces.getConstArray();
1061 for (const beans::Pair<OUString,sal_Int32>* pNs = pSeq;
1062 pNs < pSeq + i_rRegisterNamespaces.getLength(); ++pNs)
1064 OSL_ENSURE(pNs->Second >= FastToken::NAMESPACE,
1065 "CDocument::fastSerialize(): invalid NS token id");
1066 aContext.maNamespaceMap[ pNs->First ] = pNs->Second;
1069 fastSaxify(aContext);
1073 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */