1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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"
24 #include "element.hxx"
25 #include "cdatasection.hxx"
26 #include "documentfragment.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"
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"
45 #include <osl/diagnose.h>
47 #include <com/sun/star/xml/sax/FastToken.hpp>
48 #include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
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
;
59 static xmlNodePtr
lcl_getDocumentType(xmlDocPtr
const i_pDocument
)
62 xmlNodePtr cur
= i_pDocument
->children
;
63 while (cur
!= nullptr)
65 if ((cur
->type
== XML_DOCUMENT_TYPE_NODE
) ||
66 (cur
->type
== XML_DTD_NODE
)) {
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
)
87 CDocument::CDocument(xmlDocPtr
const pDoc
)
88 : CDocument_Base(*this, m_Mutex
,
89 NodeType_DOCUMENT_NODE
, reinterpret_cast<xmlNodePtr
>(pDoc
))
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
),
103 WeakReference
<XNode
>(static_cast<XDocument
*>(xDoc
.get())),
108 CDocument::~CDocument()
110 ::osl::MutexGuard
const g(m_Mutex
);
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!");
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()));
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:
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
) {
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
) {
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
);
175 ::rtl::Reference
<CNode
> ret(i
->second
.second
);
176 OSL_ASSERT(ret
.is());
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
;
189 case XML_ELEMENT_NODE
:
190 // m_aNodeType = NodeType::ELEMENT_NODE;
191 pCNode
= static_cast< CNode
* >(
192 new CElement(*this, m_Mutex
, pNode
));
195 // m_aNodeType = NodeType::TEXT_NODE;
196 pCNode
= static_cast< CNode
* >(
197 new CText(*this, m_Mutex
, pNode
));
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
));
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
));
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
)));
215 // m_aNodeType = NodeType::PROCESSING_INSTRUCTION_NODE;
216 pCNode
= static_cast< CNode
* >(
217 new CProcessingInstruction(*this, m_Mutex
, pNode
));
219 case XML_COMMENT_NODE
:
220 // m_aNodeType = NodeType::COMMENT_NODE;
221 pCNode
= static_cast< CNode
* >(
222 new CComment(*this, m_Mutex
, pNode
));
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
)));
231 case XML_DOCUMENT_TYPE_NODE
:
233 // m_aNodeType = NodeType::DOCUMENT_TYPE_NODE;
234 pCNode
= static_cast< CNode
* >(new CDocumentType(*this, m_Mutex
,
235 reinterpret_cast<xmlDtdPtr
>(pNode
)));
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
));
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
)));
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
)));
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
:
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()),
268 OSL_ASSERT(bInserted
);
270 // if insertion failed, delete new instance and return null
275 OSL_ENSURE(pCNode
.is(), "no node produced during CDocument::GetCNode!");
280 CDocument
& CDocument::GetOwnerDocument()
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
)
312 case NodeType_PROCESSING_INSTRUCTION_NODE
:
313 case NodeType_COMMENT_NODE
:
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
);
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
345 Reference
< XOutputStream
> stream
;
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
);
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();
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();
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);
405 listenerlist_t::const_iterator iter2
= streamListeners
.begin();
406 while (iter2
!= streamListeners
.end()) {
407 Reference
< XStreamListener
> aListener
= *iter2
;
413 void SAL_CALL
CDocument::terminate()
414 throw (RuntimeException
, std::exception
)
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;
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
;
466 oPrefix
= OUStringToOString(qname
.copy(0, i
), RTL_TEXTENCODING_UTF8
);
467 oName
= OUStringToOString(qname
.copy(i
+1, qname
.getLength()-i
-1), RTL_TEXTENCODING_UTF8
);
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;
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
);
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()),
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()),
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()),
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()),
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
;
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());
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()),
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()),
600 // Creates a ProcessingInstruction node given the specified name and
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()),
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()),
635 // The Document Type Declaration (see DocumentType) associated with this
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()),
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()),
665 lcl_search_element_by_id(const xmlNodePtr cur
, const xmlChar
* id
)
669 // look in current node
670 if (cur
->type
== XML_ELEMENT_NODE
)
672 xmlAttrPtr a
= cur
->properties
;
675 if (a
->atype
== XML_ATTRIBUTE_ID
) {
676 if (strcmp(reinterpret_cast<char*>(a
->children
->content
), reinterpret_cast<char const *>(id
)) == 0)
683 xmlNodePtr result
= lcl_search_element_by_id(cur
->children
, id
);
684 if (result
!= nullptr)
686 result
= lcl_search_element_by_id(cur
->next
, id
);
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()),
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
));
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
));
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();
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
);
773 case NodeType_CDATA_SECTION_NODE
:
775 Reference
< XCDATASection
> const xCData(xImportedNode
,
777 Reference
< XCDATASection
> const xNewCData
=
778 xDocument
->createCDATASection(xCData
->getData());
779 xNode
.set(xNewCData
, UNO_QUERY
);
782 case NodeType_COMMENT_NODE
:
784 Reference
< XComment
> const xComment(xImportedNode
,
786 Reference
< XComment
> const xNewComment
=
787 xDocument
->createComment(xComment
->getData());
788 xNode
.set(xNewComment
, UNO_QUERY
);
791 case NodeType_DOCUMENT_FRAGMENT_NODE
:
793 Reference
< XDocumentFragment
> const xFrag(xImportedNode
,
795 Reference
< XDocumentFragment
> const xNewFrag
=
796 xDocument
->createDocumentFragment();
797 xNode
.set(xNewFrag
, UNO_QUERY
);
800 case NodeType_ELEMENT_NODE
:
802 Reference
< XElement
> const xElement(xImportedNode
,
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
);
815 xNewElement
= xDocument
->createElement(aQName
);
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
),
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
);
838 xNewElement
->setAttribute(aAttrName
, sValue
);
842 xNode
.set(xNewElement
, UNO_QUERY
);
845 case NodeType_ENTITY_REFERENCE_NODE
:
847 Reference
< XEntityReference
> const xRef(xImportedNode
,
849 Reference
< XEntityReference
> const xNewRef(
850 xDocument
->createEntityReference(xRef
->getNodeName()));
851 xNode
.set(xNewRef
, UNO_QUERY
);
854 case NodeType_PROCESSING_INSTRUCTION_NODE
:
856 Reference
< XProcessingInstruction
> const xPi(xImportedNode
,
858 Reference
< XProcessingInstruction
> const xNewPi(
859 xDocument
->createProcessingInstruction(
860 xPi
->getTarget(), xPi
->getData()));
861 xNode
.set(xNewPi
, UNO_QUERY
);
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
);
872 case NodeType_ENTITY_NODE
:
873 case NodeType_DOCUMENT_NODE
:
874 case NodeType_DOCUMENT_TYPE_NODE
:
875 case NodeType_NOTATION_NODE
:
878 throw RuntimeException();
883 // get children and import them
884 Reference
< XNode
> const xChild
= xImportedNode
->getFirstChild();
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.
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
);
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
934 // To ensure that no members are accessed, the implementation is in
935 // static non-member functions.
937 Reference
< XDocument
> const xDocument(this);
939 if (xImportedNode
->getOwnerDocument() == xDocument
) {
940 return xImportedNode
;
943 Reference
< XNode
> const xNode(
944 lcl_ImportNode(xDocument
, xImportedNode
, deep
) );
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
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
) {
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()));
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
);
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
,
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: */