bump product version to 4.1.6.2
[LibreOffice.git] / unoxml / source / dom / document.cxx
blob0c69dfb64cee54da4eed645146a2ce2d75452ded
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 <com/sun/star/xml/sax/FastToken.hpp>
46 #include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
48 namespace DOM
50 static xmlNodePtr lcl_getDocumentType(xmlDocPtr const i_pDocument)
52 // find the doc type
53 xmlNodePtr cur = i_pDocument->children;
54 while (cur != NULL)
56 if ((cur->type == XML_DOCUMENT_TYPE_NODE) ||
57 (cur->type == XML_DTD_NODE)) {
58 return cur;
61 return 0;
64 /// get the pointer to the root element node of the document
65 static xmlNodePtr lcl_getDocumentRootPtr(xmlDocPtr const i_pDocument)
67 // find the document element
68 xmlNodePtr cur = i_pDocument->children;
69 while (cur != NULL)
71 if (cur->type == XML_ELEMENT_NODE)
72 break;
73 cur = cur->next;
75 return cur;
78 CDocument::CDocument(xmlDocPtr const pDoc)
79 : CDocument_Base(*this, m_Mutex,
80 NodeType_DOCUMENT_NODE, reinterpret_cast<xmlNodePtr>(pDoc))
81 , m_aDocPtr(pDoc)
82 , m_streamListeners()
83 , m_pEventDispatcher(new events::CEventDispatcher())
87 ::rtl::Reference<CDocument> CDocument::CreateCDocument(xmlDocPtr const pDoc)
89 ::rtl::Reference<CDocument> const xDoc(new CDocument(pDoc));
90 // add the doc itself to its nodemap!
91 xDoc->m_NodeMap.insert(
92 nodemap_t::value_type(reinterpret_cast<xmlNodePtr>(pDoc),
93 ::std::make_pair(
94 WeakReference<XNode>(static_cast<XDocument*>(xDoc.get())),
95 xDoc.get())));
96 return xDoc;
99 CDocument::~CDocument()
101 ::osl::MutexGuard const g(m_Mutex);
102 #ifdef DBG_UTIL
103 // node map must be empty now, otherwise CDocument must not die!
104 for (nodemap_t::iterator i = m_NodeMap.begin();
105 i != m_NodeMap.end(); ++i)
107 Reference<XNode> const xNode(i->second.first);
108 OSL_ENSURE(!xNode.is(),
109 "CDocument::~CDocument(): ERROR: live node in document node map!");
111 #endif
112 xmlFreeDoc(m_aDocPtr);
116 events::CEventDispatcher & CDocument::GetEventDispatcher()
118 return *m_pEventDispatcher;
121 ::rtl::Reference< CElement > CDocument::GetDocumentElement()
123 xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr);
124 ::rtl::Reference< CElement > const xRet(
125 dynamic_cast<CElement*>(GetCNode(pNode).get()));
126 return xRet;
129 void
130 CDocument::RemoveCNode(xmlNodePtr const pNode, CNode const*const pCNode)
132 nodemap_t::iterator const i = m_NodeMap.find(pNode);
133 if (i != m_NodeMap.end()) {
134 // #i113681# consider this scenario:
135 // T1 calls ~CNode
136 // T2 calls getCNode: lookup will find i->second->first invalid
137 // so a new CNode is created and inserted
138 // T1 calls removeCNode: i->second->second now points to a
139 // different CNode instance!
141 // check that the CNode is the right one
142 CNode *const pCurrent = i->second.second;
143 if (pCurrent == pCNode) {
144 m_NodeMap.erase(i);
149 /** NB: this is the CNode factory.
150 it is the only place where CNodes may be instantiated.
151 all CNodes must be registered at the m_NodeMap.
153 ::rtl::Reference<CNode>
154 CDocument::GetCNode(xmlNodePtr const pNode, bool const bCreate)
156 if (0 == pNode) {
157 return 0;
159 //check whether there is already an instance for this node
160 nodemap_t::const_iterator const i = m_NodeMap.find(pNode);
161 if (i != m_NodeMap.end()) {
162 // #i113681# check that the CNode is still alive
163 uno::Reference<XNode> const xNode(i->second.first);
164 if (xNode.is())
166 ::rtl::Reference<CNode> ret(i->second.second);
167 OSL_ASSERT(ret.is());
168 return ret;
172 if (!bCreate) { return 0; }
174 // there is not yet an instance wrapping this node,
175 // create it and store it in the map
177 ::rtl::Reference<CNode> pCNode;
178 switch (pNode->type)
180 case XML_ELEMENT_NODE:
181 // m_aNodeType = NodeType::ELEMENT_NODE;
182 pCNode = static_cast< CNode* >(
183 new CElement(*this, m_Mutex, pNode));
184 break;
185 case XML_TEXT_NODE:
186 // m_aNodeType = NodeType::TEXT_NODE;
187 pCNode = static_cast< CNode* >(
188 new CText(*this, m_Mutex, pNode));
189 break;
190 case XML_CDATA_SECTION_NODE:
191 // m_aNodeType = NodeType::CDATA_SECTION_NODE;
192 pCNode = static_cast< CNode* >(
193 new CCDATASection(*this, m_Mutex, pNode));
194 break;
195 case XML_ENTITY_REF_NODE:
196 // m_aNodeType = NodeType::ENTITY_REFERENCE_NODE;
197 pCNode = static_cast< CNode* >(
198 new CEntityReference(*this, m_Mutex, pNode));
199 break;
200 case XML_ENTITY_NODE:
201 // m_aNodeType = NodeType::ENTITY_NODE;
202 pCNode = static_cast< CNode* >(new CEntity(*this, m_Mutex,
203 reinterpret_cast<xmlEntityPtr>(pNode)));
204 break;
205 case XML_PI_NODE:
206 // m_aNodeType = NodeType::PROCESSING_INSTRUCTION_NODE;
207 pCNode = static_cast< CNode* >(
208 new CProcessingInstruction(*this, m_Mutex, pNode));
209 break;
210 case XML_COMMENT_NODE:
211 // m_aNodeType = NodeType::COMMENT_NODE;
212 pCNode = static_cast< CNode* >(
213 new CComment(*this, m_Mutex, pNode));
214 break;
215 case XML_DOCUMENT_NODE:
216 // m_aNodeType = NodeType::DOCUMENT_NODE;
217 OSL_ENSURE(false, "CDocument::GetCNode is not supposed to"
218 " create a CDocument!!!");
219 pCNode = static_cast< CNode* >(new CDocument(
220 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 = static_cast< CNode* >(new CDocumentType(*this, m_Mutex,
226 reinterpret_cast<xmlDtdPtr>(pNode)));
227 break;
228 case XML_DOCUMENT_FRAG_NODE:
229 // m_aNodeType = NodeType::DOCUMENT_FRAGMENT_NODE;
230 pCNode = static_cast< CNode* >(
231 new CDocumentFragment(*this, m_Mutex, pNode));
232 break;
233 case XML_NOTATION_NODE:
234 // m_aNodeType = NodeType::NOTATION_NODE;
235 pCNode = static_cast< CNode* >(new CNotation(*this, m_Mutex,
236 reinterpret_cast<xmlNotationPtr>(pNode)));
237 break;
238 case XML_ATTRIBUTE_NODE:
239 // m_aNodeType = NodeType::ATTRIBUTE_NODE;
240 pCNode = static_cast< CNode* >(new CAttr(*this, m_Mutex,
241 reinterpret_cast<xmlAttrPtr>(pNode)));
242 break;
243 // unsupported node types
244 case XML_HTML_DOCUMENT_NODE:
245 case XML_ELEMENT_DECL:
246 case XML_ATTRIBUTE_DECL:
247 case XML_ENTITY_DECL:
248 case XML_NAMESPACE_DECL:
249 default:
250 break;
253 if (pCNode != 0) {
254 bool const bInserted = m_NodeMap.insert(
255 nodemap_t::value_type(pNode,
256 ::std::make_pair(WeakReference<XNode>(pCNode.get()),
257 pCNode.get()))
258 ).second;
259 OSL_ASSERT(bInserted);
260 if (!bInserted) {
261 // if insertion failed, delete new instance and return null
262 return 0;
266 OSL_ENSURE(pCNode.is(), "no node produced during CDocument::GetCNode!");
267 return pCNode;
271 CDocument & CDocument::GetOwnerDocument()
273 return *this;
276 void CDocument::saxify(const Reference< XDocumentHandler >& i_xHandler)
278 i_xHandler->startDocument();
279 for (xmlNodePtr pChild = m_aNodePtr->children;
280 pChild != 0; pChild = pChild->next) {
281 ::rtl::Reference<CNode> const pNode = GetCNode(pChild);
282 OSL_ENSURE(pNode != 0, "CNode::get returned 0");
283 pNode->saxify(i_xHandler);
285 i_xHandler->endDocument();
288 void CDocument::fastSaxify( Context& rContext )
290 rContext.mxDocHandler->startDocument();
291 for (xmlNodePtr pChild = m_aNodePtr->children;
292 pChild != 0; pChild = pChild->next) {
293 ::rtl::Reference<CNode> const pNode = GetCNode(pChild);
294 OSL_ENSURE(pNode != 0, "CNode::get returned 0");
295 pNode->fastSaxify(rContext);
297 rContext.mxDocHandler->endDocument();
300 bool CDocument::IsChildTypeAllowed(NodeType const nodeType)
302 switch (nodeType) {
303 case NodeType_PROCESSING_INSTRUCTION_NODE:
304 case NodeType_COMMENT_NODE:
305 return true;
306 case NodeType_ELEMENT_NODE:
307 // there may be only one!
308 return 0 == lcl_getDocumentRootPtr(m_aDocPtr);
309 case NodeType_DOCUMENT_TYPE_NODE:
310 // there may be only one!
311 return 0 == lcl_getDocumentType(m_aDocPtr);
312 default:
313 return false;
318 void SAL_CALL CDocument::addListener(const Reference< XStreamListener >& aListener )
319 throw (RuntimeException)
321 ::osl::MutexGuard const g(m_Mutex);
323 m_streamListeners.insert(aListener);
326 void SAL_CALL CDocument::removeListener(const Reference< XStreamListener >& aListener )
327 throw (RuntimeException)
329 ::osl::MutexGuard const g(m_Mutex);
331 m_streamListeners.erase(aListener);
334 // IO context functions for libxml2 interaction
335 typedef struct {
336 Reference< XOutputStream > stream;
337 bool allowClose;
338 } IOContext;
340 extern "C" {
341 // write callback
342 // int xmlOutputWriteCallback (void * context, const char * buffer, int len)
343 static int writeCallback(void *context, const char* buffer, int len){
344 // create a sequence and write it to the stream
345 IOContext *pContext = static_cast<IOContext*>(context);
346 Sequence<sal_Int8> bs(reinterpret_cast<const sal_Int8*>(buffer), len);
347 pContext->stream->writeBytes(bs);
348 return len;
351 // clsoe callback
352 //int xmlOutputCloseCallback (void * context)
353 static int closeCallback(void *context)
355 IOContext *pContext = static_cast<IOContext*>(context);
356 if (pContext->allowClose) {
357 pContext->stream->closeOutput();
359 return 0;
361 } // extern "C"
363 void SAL_CALL CDocument::start()
364 throw (RuntimeException)
366 listenerlist_t streamListeners;
368 ::osl::MutexGuard const g(m_Mutex);
370 if (! m_rOutputStream.is()) { throw RuntimeException(); }
371 streamListeners = m_streamListeners;
374 // notify listeners about start
375 listenerlist_t::const_iterator iter1 = streamListeners.begin();
376 while (iter1 != streamListeners.end()) {
377 Reference< XStreamListener > aListener = *iter1;
378 aListener->started();
379 ++iter1;
383 ::osl::MutexGuard const g(m_Mutex);
385 // check again! could have been reset...
386 if (! m_rOutputStream.is()) { throw RuntimeException(); }
388 // setup libxml IO and write data to output stream
389 IOContext ioctx = {m_rOutputStream, false};
390 xmlOutputBufferPtr pOut = xmlOutputBufferCreateIO(
391 writeCallback, closeCallback, &ioctx, NULL);
392 xmlSaveFileTo(pOut, m_aNodePtr->doc, NULL);
395 // call listeners
396 listenerlist_t::const_iterator iter2 = streamListeners.begin();
397 while (iter2 != streamListeners.end()) {
398 Reference< XStreamListener > aListener = *iter2;
399 aListener->closed();
400 ++iter2;
404 void SAL_CALL CDocument::terminate()
405 throw (RuntimeException)
407 // not supported
410 void SAL_CALL CDocument::setOutputStream( const Reference< XOutputStream >& aStream )
411 throw (RuntimeException)
413 ::osl::MutexGuard const g(m_Mutex);
415 m_rOutputStream = aStream;
418 Reference< XOutputStream > SAL_CALL CDocument::getOutputStream() throw (RuntimeException)
420 ::osl::MutexGuard const g(m_Mutex);
422 return m_rOutputStream;
425 // Creates an Attr of the given name.
426 Reference< XAttr > SAL_CALL CDocument::createAttribute(const OUString& name)
427 throw (RuntimeException, DOMException)
429 ::osl::MutexGuard const g(m_Mutex);
431 OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8);
432 xmlChar *xName = (xmlChar*)o1.getStr();
433 xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr, xName, NULL);
434 ::rtl::Reference< CAttr > const pCAttr(
435 dynamic_cast< CAttr* >(GetCNode(
436 reinterpret_cast<xmlNodePtr>(pAttr)).get()));
437 pCAttr->m_bUnlinked = true;
438 return pCAttr.get();
441 // Creates an attribute of the given qualified name and namespace URI.
442 Reference< XAttr > SAL_CALL CDocument::createAttributeNS(
443 const OUString& ns, const OUString& qname)
444 throw (RuntimeException, DOMException)
446 ::osl::MutexGuard const g(m_Mutex);
448 // libxml does not allow a NS definition to be attached to an
449 // attribute node - which is a good thing, since namespaces are
450 // only defined as parts of element nodes
451 // thus the namespace data is stored in CAttr::m_pNamespace
452 sal_Int32 i = qname.indexOf(':');
453 OString oPrefix, oName, oUri;
454 if (i != -1)
456 oPrefix = OUStringToOString(qname.copy(0, i), RTL_TEXTENCODING_UTF8);
457 oName = OUStringToOString(qname.copy(i+1, qname.getLength()-i-1), RTL_TEXTENCODING_UTF8);
459 else
461 oName = OUStringToOString(qname, RTL_TEXTENCODING_UTF8);
463 oUri = OUStringToOString(ns, RTL_TEXTENCODING_UTF8);
464 xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr,
465 reinterpret_cast<xmlChar const*>(oName.getStr()), 0);
466 ::rtl::Reference< CAttr > const pCAttr(
467 dynamic_cast< CAttr* >(GetCNode(
468 reinterpret_cast<xmlNodePtr>(pAttr)).get()));
469 if (!pCAttr.is()) { throw RuntimeException(); }
470 // store the namespace data!
471 pCAttr->m_pNamespace.reset( new stringpair_t(oUri, oPrefix) );
472 pCAttr->m_bUnlinked = true;
474 return pCAttr.get();
477 // Creates a CDATASection node whose value is the specified string.
478 Reference< XCDATASection > SAL_CALL CDocument::createCDATASection(const OUString& data)
479 throw (RuntimeException)
481 ::osl::MutexGuard const g(m_Mutex);
483 OString const oData(
484 OUStringToOString(data, RTL_TEXTENCODING_UTF8));
485 xmlChar const*const pData =
486 reinterpret_cast<xmlChar const*>(oData.getStr());
487 xmlNodePtr const pText =
488 xmlNewCDataBlock(m_aDocPtr, pData, strlen(oData.getStr()));
489 Reference< XCDATASection > const xRet(
490 static_cast< XNode* >(GetCNode(pText).get()),
491 UNO_QUERY_THROW);
492 return xRet;
495 // Creates a Comment node given the specified string.
496 Reference< XComment > SAL_CALL CDocument::createComment(const OUString& data)
497 throw (RuntimeException)
499 ::osl::MutexGuard const g(m_Mutex);
501 OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
502 xmlChar *xData = (xmlChar*)o1.getStr();
503 xmlNodePtr pComment = xmlNewDocComment(m_aDocPtr, xData);
504 Reference< XComment > const xRet(
505 static_cast< XNode* >(GetCNode(pComment).get()),
506 UNO_QUERY_THROW);
507 return xRet;
510 //Creates an empty DocumentFragment object.
511 Reference< XDocumentFragment > SAL_CALL CDocument::createDocumentFragment()
512 throw (RuntimeException)
514 ::osl::MutexGuard const g(m_Mutex);
516 xmlNodePtr pFrag = xmlNewDocFragment(m_aDocPtr);
517 Reference< XDocumentFragment > const xRet(
518 static_cast< XNode* >(GetCNode(pFrag).get()),
519 UNO_QUERY_THROW);
520 return xRet;
523 // Creates an element of the type specified.
524 Reference< XElement > SAL_CALL CDocument::createElement(const OUString& tagName)
525 throw (RuntimeException, DOMException)
527 ::osl::MutexGuard const g(m_Mutex);
529 OString o1 = OUStringToOString(tagName, RTL_TEXTENCODING_UTF8);
530 xmlChar *xName = (xmlChar*)o1.getStr();
531 xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, NULL, xName, NULL);
532 Reference< XElement > const xRet(
533 static_cast< XNode* >(GetCNode(pNode).get()),
534 UNO_QUERY_THROW);
535 return xRet;
538 // Creates an element of the given qualified name and namespace URI.
539 Reference< XElement > SAL_CALL CDocument::createElementNS(
540 const OUString& ns, const OUString& qname)
541 throw (RuntimeException, DOMException)
543 ::osl::MutexGuard const g(m_Mutex);
545 sal_Int32 i = qname.indexOf(':');
546 if (ns.isEmpty()) throw RuntimeException();
547 xmlChar *xPrefix;
548 xmlChar *xName;
549 OString o1, o2, o3;
550 if ( i != -1) {
551 o1 = OUStringToOString(qname.copy(0, i), RTL_TEXTENCODING_UTF8);
552 xPrefix = (xmlChar*)o1.getStr();
553 o2 = OUStringToOString(qname.copy(i+1, qname.getLength()-i-1), RTL_TEXTENCODING_UTF8);
554 xName = (xmlChar*)o2.getStr();
555 } else {
556 // default prefix
557 xPrefix = (xmlChar*)"";
558 o2 = OUStringToOString(qname, RTL_TEXTENCODING_UTF8);
559 xName = (xmlChar*)o2.getStr();
561 o3 = OUStringToOString(ns, RTL_TEXTENCODING_UTF8);
562 xmlChar *xUri = (xmlChar*)o3.getStr();
564 // xmlNsPtr aNsPtr = xmlNewReconciledNs?
565 // xmlNsPtr aNsPtr = xmlNewGlobalNs?
566 xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, NULL, xName, NULL);
567 xmlNsPtr const pNs = xmlNewNs(pNode, xUri, xPrefix);
568 xmlSetNs(pNode, pNs);
569 Reference< XElement > const xRet(
570 static_cast< XNode* >(GetCNode(pNode).get()),
571 UNO_QUERY_THROW);
572 return xRet;
575 //Creates an EntityReference object.
576 Reference< XEntityReference > SAL_CALL CDocument::createEntityReference(const OUString& name)
577 throw (RuntimeException, DOMException)
579 ::osl::MutexGuard const g(m_Mutex);
581 OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8);
582 xmlChar *xName = (xmlChar*)o1.getStr();
583 xmlNodePtr const pNode = xmlNewReference(m_aDocPtr, xName);
584 Reference< XEntityReference > const xRet(
585 static_cast< XNode* >(GetCNode(pNode).get()),
586 UNO_QUERY_THROW);
587 return xRet;
590 // Creates a ProcessingInstruction node given the specified name and
591 // data strings.
592 Reference< XProcessingInstruction > SAL_CALL CDocument::createProcessingInstruction(
593 const OUString& target, const OUString& data)
594 throw (RuntimeException, DOMException)
596 ::osl::MutexGuard const g(m_Mutex);
598 OString o1 = OUStringToOString(target, RTL_TEXTENCODING_UTF8);
599 xmlChar *xTarget = (xmlChar*)o1.getStr();
600 OString o2 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
601 xmlChar *xData = (xmlChar*)o2.getStr();
602 xmlNodePtr const pNode = xmlNewDocPI(m_aDocPtr, xTarget, xData);
603 pNode->doc = m_aDocPtr;
604 Reference< XProcessingInstruction > const xRet(
605 static_cast< XNode* >(GetCNode(pNode).get()),
606 UNO_QUERY_THROW);
607 return xRet;
610 // Creates a Text node given the specified string.
611 Reference< XText > SAL_CALL CDocument::createTextNode(const OUString& data)
612 throw (RuntimeException)
614 ::osl::MutexGuard const g(m_Mutex);
616 OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8);
617 xmlChar *xData = (xmlChar*)o1.getStr();
618 xmlNodePtr const pNode = xmlNewDocText(m_aDocPtr, xData);
619 Reference< XText > const xRet(
620 static_cast< XNode* >(GetCNode(pNode).get()),
621 UNO_QUERY_THROW);
622 return xRet;
625 // The Document Type Declaration (see DocumentType) associated with this
626 // document.
627 Reference< XDocumentType > SAL_CALL CDocument::getDoctype()
628 throw (RuntimeException)
630 ::osl::MutexGuard const g(m_Mutex);
632 xmlNodePtr const pDocType(lcl_getDocumentType(m_aDocPtr));
633 Reference< XDocumentType > const xRet(
634 static_cast< XNode* >(GetCNode(pDocType).get()),
635 UNO_QUERY);
636 return xRet;
639 // This is a convenience attribute that allows direct access to the child
640 // node that is the root element of the document.
641 Reference< XElement > SAL_CALL CDocument::getDocumentElement()
642 throw (RuntimeException)
644 ::osl::MutexGuard const g(m_Mutex);
646 xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr);
647 if (!pNode) { return 0; }
648 Reference< XElement > const xRet(
649 static_cast< XNode* >(GetCNode(pNode).get()),
650 UNO_QUERY);
651 return xRet;
654 static xmlNodePtr
655 lcl_search_element_by_id(const xmlNodePtr cur, const xmlChar* id)
657 if (cur == NULL)
658 return NULL;
659 // look in current node
660 if (cur->type == XML_ELEMENT_NODE)
662 xmlAttrPtr a = cur->properties;
663 while (a != NULL)
665 if (a->atype == XML_ATTRIBUTE_ID) {
666 if (strcmp((char*)a->children->content, (char*)id) == 0)
667 return cur;
669 a = a->next;
672 // look in children
673 xmlNodePtr result = lcl_search_element_by_id(cur->children, id);
674 if (result != NULL)
675 return result;
676 result = lcl_search_element_by_id(cur->next, id);
677 return result;
680 // Returns the Element whose ID is given by elementId.
681 Reference< XElement > SAL_CALL
682 CDocument::getElementById(const OUString& elementId)
683 throw (RuntimeException)
685 ::osl::MutexGuard const g(m_Mutex);
687 // search the tree for an element with the given ID
688 OString o1 = OUStringToOString(elementId, RTL_TEXTENCODING_UTF8);
689 xmlChar *xId = (xmlChar*)o1.getStr();
690 xmlNodePtr const pStart = lcl_getDocumentRootPtr(m_aDocPtr);
691 if (!pStart) { return 0; }
692 xmlNodePtr const pNode = lcl_search_element_by_id(pStart, xId);
693 Reference< XElement > const xRet(
694 static_cast< XNode* >(GetCNode(pNode).get()),
695 UNO_QUERY);
696 return xRet;
700 Reference< XNodeList > SAL_CALL
701 CDocument::getElementsByTagName(OUString const& rTagname)
702 throw (RuntimeException)
704 ::osl::MutexGuard const g(m_Mutex);
706 Reference< XNodeList > const xRet(
707 new CElementList(this->GetDocumentElement(), m_Mutex, rTagname));
708 return xRet;
711 Reference< XNodeList > SAL_CALL CDocument::getElementsByTagNameNS(
712 OUString const& rNamespaceURI, OUString const& rLocalName)
713 throw (RuntimeException)
715 ::osl::MutexGuard const g(m_Mutex);
717 Reference< XNodeList > const xRet(
718 new CElementList(this->GetDocumentElement(), m_Mutex,
719 rLocalName, &rNamespaceURI));
720 return xRet;
723 Reference< XDOMImplementation > SAL_CALL CDocument::getImplementation()
724 throw (RuntimeException)
726 // does not need mutex currently
727 return Reference< XDOMImplementation >(CDOMImplementation::get());
730 // helper function to recursively import siblings
731 static void lcl_ImportSiblings(
732 Reference< XDocument > const& xTargetDocument,
733 Reference< XNode > const& xTargetParent,
734 Reference< XNode > const& xChild)
736 Reference< XNode > xSibling = xChild;
737 while (xSibling.is())
739 Reference< XNode > const xTmp(
740 xTargetDocument->importNode(xSibling, sal_True));
741 xTargetParent->appendChild(xTmp);
742 xSibling = xSibling->getNextSibling();
746 static Reference< XNode >
747 lcl_ImportNode( Reference< XDocument > const& xDocument,
748 Reference< XNode > const& xImportedNode, sal_Bool deep)
750 Reference< XNode > xNode;
751 NodeType aNodeType = xImportedNode->getNodeType();
752 switch (aNodeType)
754 case NodeType_ATTRIBUTE_NODE:
756 Reference< XAttr > const xAttr(xImportedNode, UNO_QUERY_THROW);
757 Reference< XAttr > const xNew =
758 xDocument->createAttribute(xAttr->getName());
759 xNew->setValue(xAttr->getValue());
760 xNode.set(xNew, UNO_QUERY);
761 break;
763 case NodeType_CDATA_SECTION_NODE:
765 Reference< XCDATASection > const xCData(xImportedNode,
766 UNO_QUERY_THROW);
767 Reference< XCDATASection > const xNewCData =
768 xDocument->createCDATASection(xCData->getData());
769 xNode.set(xNewCData, UNO_QUERY);
770 break;
772 case NodeType_COMMENT_NODE:
774 Reference< XComment > const xComment(xImportedNode,
775 UNO_QUERY_THROW);
776 Reference< XComment > const xNewComment =
777 xDocument->createComment(xComment->getData());
778 xNode.set(xNewComment, UNO_QUERY);
779 break;
781 case NodeType_DOCUMENT_FRAGMENT_NODE:
783 Reference< XDocumentFragment > const xFrag(xImportedNode,
784 UNO_QUERY_THROW);
785 Reference< XDocumentFragment > const xNewFrag =
786 xDocument->createDocumentFragment();
787 xNode.set(xNewFrag, UNO_QUERY);
788 break;
790 case NodeType_ELEMENT_NODE:
792 Reference< XElement > const xElement(xImportedNode,
793 UNO_QUERY_THROW);
794 OUString const aNsUri = xImportedNode->getNamespaceURI();
795 OUString const aNsPrefix = xImportedNode->getPrefix();
796 OUString aQName = xElement->getTagName();
797 Reference< XElement > xNewElement;
798 if (!aNsUri.isEmpty())
800 if (!aNsPrefix.isEmpty()) {
801 aQName = aNsPrefix + ":" + aQName;
803 xNewElement = xDocument->createElementNS(aNsUri, aQName);
804 } else {
805 xNewElement = xDocument->createElement(aQName);
808 // get attributes
809 if (xElement->hasAttributes())
811 Reference< XNamedNodeMap > attribs = xElement->getAttributes();
812 for (sal_Int32 i = 0; i < attribs->getLength(); i++)
814 Reference< XAttr > const curAttr(attribs->item(i),
815 UNO_QUERY_THROW);
816 OUString const aAttrUri = curAttr->getNamespaceURI();
817 OUString const aAttrPrefix = curAttr->getPrefix();
818 OUString aAttrName = curAttr->getName();
819 OUString const sValue = curAttr->getValue();
820 if (!aAttrUri.isEmpty())
822 if (!aAttrPrefix.isEmpty()) {
823 aAttrName = aAttrPrefix + ":" + aAttrName;
825 xNewElement->setAttributeNS(
826 aAttrUri, aAttrName, sValue);
827 } else {
828 xNewElement->setAttribute(aAttrName, sValue);
832 xNode.set(xNewElement, UNO_QUERY);
833 break;
835 case NodeType_ENTITY_REFERENCE_NODE:
837 Reference< XEntityReference > const xRef(xImportedNode,
838 UNO_QUERY_THROW);
839 Reference< XEntityReference > const xNewRef(
840 xDocument->createEntityReference(xRef->getNodeName()));
841 xNode.set(xNewRef, UNO_QUERY);
842 break;
844 case NodeType_PROCESSING_INSTRUCTION_NODE:
846 Reference< XProcessingInstruction > const xPi(xImportedNode,
847 UNO_QUERY_THROW);
848 Reference< XProcessingInstruction > const xNewPi(
849 xDocument->createProcessingInstruction(
850 xPi->getTarget(), xPi->getData()));
851 xNode.set(xNewPi, UNO_QUERY);
852 break;
854 case NodeType_TEXT_NODE:
856 Reference< XText > const xText(xImportedNode, UNO_QUERY_THROW);
857 Reference< XText > const xNewText(
858 xDocument->createTextNode(xText->getData()));
859 xNode.set(xNewText, UNO_QUERY);
860 break;
862 case NodeType_ENTITY_NODE:
863 case NodeType_DOCUMENT_NODE:
864 case NodeType_DOCUMENT_TYPE_NODE:
865 case NodeType_NOTATION_NODE:
866 default:
867 // can't be imported
868 throw RuntimeException();
871 if (deep)
873 // get children and import them
874 Reference< XNode > const xChild = xImportedNode->getFirstChild();
875 if (xChild.is())
877 lcl_ImportSiblings(xDocument, xNode, xChild);
881 /* DOMNodeInsertedIntoDocument
882 * Fired when a node is being inserted into a document,
883 * either through direct insertion of the Node or insertion of a
884 * subtree in which it is contained. This event is dispatched after
885 * the insertion has taken place. The target of this event is the node
886 * being inserted. If the Node is being directly inserted the DOMNodeInserted
887 * event will fire before the DOMNodeInsertedIntoDocument event.
888 * Bubbles: No
889 * Cancelable: No
890 * Context Info: None
892 if (xNode.is())
894 Reference< XDocumentEvent > const xDocevent(xDocument, UNO_QUERY);
895 Reference< XMutationEvent > const event(xDocevent->createEvent(
896 "DOMNodeInsertedIntoDocument"), UNO_QUERY_THROW);
897 event->initMutationEvent(
898 "DOMNodeInsertedIntoDocument", sal_True, sal_False, Reference< XNode >(),
899 OUString(), OUString(), OUString(), (AttrChangeType)0 );
900 Reference< XEventTarget > const xDocET(xDocument, UNO_QUERY);
901 xDocET->dispatchEvent(Reference< XEvent >(event, UNO_QUERY));
904 return xNode;
907 Reference< XNode > SAL_CALL CDocument::importNode(
908 Reference< XNode > const& xImportedNode, sal_Bool deep)
909 throw (RuntimeException, DOMException)
911 if (!xImportedNode.is()) { throw RuntimeException(); }
913 // NB: this whole operation inherently accesses 2 distinct documents.
914 // The imported node could even be from a different DOM implementation,
915 // so this implementation cannot make any assumptions about the
916 // locking strategy of the imported node.
917 // So the import takes no lock on this document;
918 // it only calls UNO methods on this document that temporarily
919 // lock the document, and UNO methods on the imported node that
920 // may temporarily lock the other document.
921 // As a consequence, the import is not atomic with regard to
922 // concurrent modifications of either document, but it should not
923 // deadlock.
924 // To ensure that no members are accessed, the implementation is in
925 // static non-member functions.
927 Reference< XDocument > const xDocument(this);
928 // already in doc?
929 if (xImportedNode->getOwnerDocument() == xDocument) {
930 return xImportedNode;
933 Reference< XNode > const xNode(
934 lcl_ImportNode(xDocument, xImportedNode, deep) );
935 return xNode;
938 OUString SAL_CALL CDocument::getNodeName()throw (RuntimeException)
940 // does not need mutex currently
941 return OUString("#document");
944 OUString SAL_CALL CDocument::getNodeValue() throw (RuntimeException)
946 // does not need mutex currently
947 return OUString();
950 Reference< XNode > SAL_CALL CDocument::cloneNode(sal_Bool bDeep)
951 throw (RuntimeException)
953 ::osl::MutexGuard const g(m_rMutex);
955 OSL_ASSERT(0 != m_aNodePtr);
956 if (0 == m_aNodePtr) {
957 return 0;
959 xmlDocPtr const pClone(xmlCopyDoc(m_aDocPtr, (bDeep) ? 1 : 0));
960 if (0 == pClone) { return 0; }
961 Reference< XNode > const xRet(
962 static_cast<CNode*>(CDocument::CreateCDocument(pClone).get()));
963 return xRet;
966 Reference< XEvent > SAL_CALL CDocument::createEvent(const OUString& aType) throw (RuntimeException)
968 // does not need mutex currently
969 events::CEvent *pEvent = 0;
970 if ( aType == "DOMSubtreeModified" || aType == "DOMNodeInserted" || aType == "DOMNodeRemoved"
971 || aType == "DOMNodeRemovedFromDocument" || aType == "DOMNodeInsertedIntoDocument" || aType == "DOMAttrModified"
972 || aType == "DOMCharacterDataModified")
974 pEvent = new events::CMutationEvent;
976 } else if ( aType == "DOMFocusIn" || aType == "DOMFocusOut" || aType == "DOMActivate")
978 pEvent = new events::CUIEvent;
979 } else if ( aType == "click" || aType == "mousedown" || aType == "mouseup"
980 || aType == "mouseover" || aType == "mousemove" || aType == "mouseout" )
982 pEvent = new events::CMouseEvent;
984 else // generic event
986 pEvent = new events::CEvent;
988 return Reference< XEvent >(pEvent);
991 // ::com::sun::star::xml::sax::XSAXSerializable
992 void SAL_CALL CDocument::serialize(
993 const Reference< XDocumentHandler >& i_xHandler,
994 const Sequence< beans::StringPair >& i_rNamespaces)
995 throw (RuntimeException, SAXException)
997 ::osl::MutexGuard const g(m_Mutex);
999 // add new namespaces to root node
1000 xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr);
1001 if (0 != pRoot) {
1002 const beans::StringPair * pSeq = i_rNamespaces.getConstArray();
1003 for (const beans::StringPair *pNsDef = pSeq;
1004 pNsDef < pSeq + i_rNamespaces.getLength(); ++pNsDef) {
1005 OString prefix = OUStringToOString(pNsDef->First,
1006 RTL_TEXTENCODING_UTF8);
1007 OString href = OUStringToOString(pNsDef->Second,
1008 RTL_TEXTENCODING_UTF8);
1009 // this will only add the ns if it does not exist already
1010 xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()),
1011 reinterpret_cast<const xmlChar*>(prefix.getStr()));
1013 // eliminate duplicate namespace declarations
1014 nscleanup(pRoot->children, pRoot);
1016 saxify(i_xHandler);
1019 // ::com::sun::star::xml::sax::XFastSAXSerializable
1020 void SAL_CALL CDocument::fastSerialize( const Reference< XFastDocumentHandler >& i_xHandler,
1021 const Reference< XFastTokenHandler >& i_xTokenHandler,
1022 const Sequence< beans::StringPair >& i_rNamespaces,
1023 const Sequence< beans::Pair< OUString, sal_Int32 > >& i_rRegisterNamespaces )
1024 throw (SAXException, RuntimeException)
1026 ::osl::MutexGuard const g(m_Mutex);
1028 // add new namespaces to root node
1029 xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr);
1030 if (0 != pRoot) {
1031 const beans::StringPair * pSeq = i_rNamespaces.getConstArray();
1032 for (const beans::StringPair *pNsDef = pSeq;
1033 pNsDef < pSeq + i_rNamespaces.getLength(); ++pNsDef) {
1034 OString prefix = OUStringToOString(pNsDef->First,
1035 RTL_TEXTENCODING_UTF8);
1036 OString href = OUStringToOString(pNsDef->Second,
1037 RTL_TEXTENCODING_UTF8);
1038 // this will only add the ns if it does not exist already
1039 xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()),
1040 reinterpret_cast<const xmlChar*>(prefix.getStr()));
1042 // eliminate duplicate namespace declarations
1043 nscleanup(pRoot->children, pRoot);
1046 Context aContext(i_xHandler,
1047 i_xTokenHandler);
1049 // register namespace ids
1050 const beans::Pair<OUString,sal_Int32>* pSeq = i_rRegisterNamespaces.getConstArray();
1051 for (const beans::Pair<OUString,sal_Int32>* pNs = pSeq;
1052 pNs < pSeq + i_rRegisterNamespaces.getLength(); ++pNs)
1054 OSL_ENSURE(pNs->Second >= FastToken::NAMESPACE,
1055 "CDocument::fastSerialize(): invalid NS token id");
1056 aContext.maNamespaceMap[ pNs->First ] = pNs->Second;
1059 fastSaxify(aContext);
1063 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */