1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 #include <com/sun/star/uno/Sequence.h> 29 30 #include "document.hxx" 31 #include "attr.hxx" 32 #include "element.hxx" 33 #include "cdatasection.hxx" 34 #include "documentfragment.hxx" 35 #include "text.hxx" 36 #include "cdatasection.hxx" 37 #include "comment.hxx" 38 #include "processinginstruction.hxx" 39 #include "entityreference.hxx" 40 #include "documenttype.hxx" 41 #include "elementlist.hxx" 42 #include "domimplementation.hxx" 43 #include <entity.hxx> 44 #include <notation.hxx> 45 46 #include "../events/event.hxx" 47 #include "../events/mutationevent.hxx" 48 #include "../events/uievent.hxx" 49 #include "../events/mouseevent.hxx" 50 #include "../events/eventdispatcher.hxx" 51 52 #include <string.h> 53 54 #include <com/sun/star/xml/sax/FastToken.hpp> 55 #include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp> 56 57 namespace DOM 58 { 59 static xmlNodePtr lcl_getDocumentType(xmlDocPtr const i_pDocument) 60 { 61 // find the doc type 62 xmlNodePtr cur = i_pDocument->children; 63 while (cur != NULL) 64 { 65 if ((cur->type == XML_DOCUMENT_TYPE_NODE) || 66 (cur->type == XML_DTD_NODE)) { 67 return cur; 68 } 69 } 70 return 0; 71 } 72 73 /// get the pointer to the root element node of the document 74 static xmlNodePtr lcl_getDocumentRootPtr(xmlDocPtr const i_pDocument) 75 { 76 // find the document element 77 xmlNodePtr cur = i_pDocument->children; 78 while (cur != NULL) 79 { 80 if (cur->type == XML_ELEMENT_NODE) 81 break; 82 cur = cur->next; 83 } 84 return cur; 85 } 86 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()) 93 { 94 } 95 96 ::rtl::Reference<CDocument> CDocument::CreateCDocument(xmlDocPtr const pDoc) 97 { 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; 106 } 107 108 CDocument::~CDocument() 109 { 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) 115 { 116 Reference<XNode> const xNode(i->second.first); 117 OSL_ENSURE(!xNode.is(), 118 "CDocument::~CDocument(): ERROR: live node in document node map!"); 119 } 120 #endif 121 xmlFreeDoc(m_aDocPtr); 122 } 123 124 125 events::CEventDispatcher & CDocument::GetEventDispatcher() 126 { 127 return *m_pEventDispatcher; 128 } 129 130 ::rtl::Reference< CElement > CDocument::GetDocumentElement() 131 { 132 xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr); 133 ::rtl::Reference< CElement > const xRet( 134 dynamic_cast<CElement*>(GetCNode(pNode).get())); 135 return xRet; 136 } 137 138 void 139 CDocument::RemoveCNode(xmlNodePtr const pNode, CNode const*const pCNode) 140 { 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! 149 // 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); 154 } 155 } 156 } 157 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. 161 */ 162 ::rtl::Reference<CNode> 163 CDocument::GetCNode(xmlNodePtr const pNode, bool const bCreate) 164 { 165 if (0 == pNode) { 166 return 0; 167 } 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()) 174 { 175 ::rtl::Reference<CNode> ret(i->second.second); 176 OSL_ASSERT(ret.is()); 177 return ret; 178 } 179 } 180 181 if (!bCreate) { return 0; } 182 183 // there is not yet an instance wrapping this node, 184 // create it and store it in the map 185 186 ::rtl::Reference<CNode> pCNode; 187 switch (pNode->type) 188 { 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; 260 } 261 262 if (pCNode != 0) { 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 0; 272 } 273 } 274 275 OSL_ENSURE(pCNode.is(), "no node produced during CDocument::GetCNode!"); 276 return pCNode; 277 } 278 279 280 CDocument & CDocument::GetOwnerDocument() 281 { 282 return *this; 283 } 284 285 void CDocument::saxify(const Reference< XDocumentHandler >& i_xHandler) 286 { 287 i_xHandler->startDocument(); 288 for (xmlNodePtr pChild = m_aNodePtr->children; 289 pChild != 0; pChild = pChild->next) { 290 ::rtl::Reference<CNode> const pNode = GetCNode(pChild); 291 OSL_ENSURE(pNode != 0, "CNode::get returned 0"); 292 pNode->saxify(i_xHandler); 293 } 294 i_xHandler->endDocument(); 295 } 296 297 void CDocument::fastSaxify( Context& rContext ) 298 { 299 rContext.mxDocHandler->startDocument(); 300 for (xmlNodePtr pChild = m_aNodePtr->children; 301 pChild != 0; pChild = pChild->next) { 302 ::rtl::Reference<CNode> const pNode = GetCNode(pChild); 303 OSL_ENSURE(pNode != 0, "CNode::get returned 0"); 304 pNode->fastSaxify(rContext); 305 } 306 rContext.mxDocHandler->endDocument(); 307 } 308 309 bool CDocument::IsChildTypeAllowed(NodeType const nodeType) 310 { 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 0 == lcl_getDocumentRootPtr(m_aDocPtr); 318 case NodeType_DOCUMENT_TYPE_NODE: 319 // there may be only one! 320 return 0 == lcl_getDocumentType(m_aDocPtr); 321 default: 322 return false; 323 } 324 } 325 326 327 void SAL_CALL CDocument::addListener(const Reference< XStreamListener >& aListener ) 328 throw (RuntimeException) 329 { 330 ::osl::MutexGuard const g(m_Mutex); 331 332 m_streamListeners.insert(aListener); 333 } 334 335 void SAL_CALL CDocument::removeListener(const Reference< XStreamListener >& aListener ) 336 throw (RuntimeException) 337 { 338 ::osl::MutexGuard const g(m_Mutex); 339 340 m_streamListeners.erase(aListener); 341 } 342 343 // IO context functions for libxml2 interaction 344 typedef struct { 345 Reference< XOutputStream > stream; 346 bool allowClose; 347 } IOContext; 348 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; 358 } 359 360 // clsoe callback 361 //int xmlOutputCloseCallback (void * context) 362 static int closeCallback(void *context) 363 { 364 IOContext *pContext = static_cast<IOContext*>(context); 365 if (pContext->allowClose) { 366 pContext->stream->closeOutput(); 367 } 368 return 0; 369 } 370 } // extern "C" 371 372 void SAL_CALL CDocument::start() 373 throw (RuntimeException) 374 { 375 listenerlist_t streamListeners; 376 { 377 ::osl::MutexGuard const g(m_Mutex); 378 379 if (! m_rOutputStream.is()) { throw RuntimeException(); } 380 streamListeners = m_streamListeners; 381 } 382 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++; 389 } 390 391 { 392 ::osl::MutexGuard const g(m_Mutex); 393 394 // check again! could have been reset... 395 if (! m_rOutputStream.is()) { throw RuntimeException(); } 396 397 // setup libxml IO and write data to output stream 398 IOContext ioctx = {m_rOutputStream, false}; 399 xmlOutputBufferPtr pOut = xmlOutputBufferCreateIO( 400 writeCallback, closeCallback, &ioctx, NULL); 401 xmlSaveFileTo(pOut, m_aNodePtr->doc, NULL); 402 } 403 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++; 410 } 411 } 412 413 void SAL_CALL CDocument::terminate() 414 throw (RuntimeException) 415 { 416 // not supported 417 } 418 419 void SAL_CALL CDocument::setOutputStream( const Reference< XOutputStream >& aStream ) 420 throw (RuntimeException) 421 { 422 ::osl::MutexGuard const g(m_Mutex); 423 424 m_rOutputStream = aStream; 425 } 426 427 Reference< XOutputStream > SAL_CALL CDocument::getOutputStream() throw (RuntimeException) 428 { 429 ::osl::MutexGuard const g(m_Mutex); 430 431 return m_rOutputStream; 432 } 433 434 // Creates an Attr of the given name. 435 Reference< XAttr > SAL_CALL CDocument::createAttribute(const OUString& name) 436 throw (RuntimeException, DOMException) 437 { 438 ::osl::MutexGuard const g(m_Mutex); 439 440 OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); 441 xmlChar *xName = (xmlChar*)o1.getStr(); 442 xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr, xName, NULL); 443 ::rtl::Reference< CAttr > const pCAttr( 444 dynamic_cast< CAttr* >(GetCNode( 445 reinterpret_cast<xmlNodePtr>(pAttr)).get())); 446 pCAttr->m_bUnlinked = true; 447 return pCAttr.get(); 448 }; 449 450 // Creates an attribute of the given qualified name and namespace URI. 451 Reference< XAttr > SAL_CALL CDocument::createAttributeNS( 452 const OUString& ns, const OUString& qname) 453 throw (RuntimeException, DOMException) 454 { 455 ::osl::MutexGuard const g(m_Mutex); 456 457 // libxml does not allow a NS definition to be attached to an 458 // attribute node - which is a good thing, since namespaces are 459 // only defined as parts of element nodes 460 // thus the namespace data is stored in CAttr::m_pNamespace 461 sal_Int32 i = qname.indexOf(':'); 462 OString oPrefix, oName, oUri; 463 if (i != -1) 464 { 465 oPrefix = OUStringToOString(qname.copy(0, i), RTL_TEXTENCODING_UTF8); 466 oName = OUStringToOString(qname.copy(i+1, qname.getLength()-i-1), RTL_TEXTENCODING_UTF8); 467 } 468 else 469 { 470 oName = OUStringToOString(qname, RTL_TEXTENCODING_UTF8); 471 } 472 oUri = OUStringToOString(ns, RTL_TEXTENCODING_UTF8); 473 xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr, 474 reinterpret_cast<xmlChar const*>(oName.getStr()), 0); 475 ::rtl::Reference< CAttr > const pCAttr( 476 dynamic_cast< CAttr* >(GetCNode( 477 reinterpret_cast<xmlNodePtr>(pAttr)).get())); 478 if (!pCAttr.is()) { throw RuntimeException(); } 479 // store the namespace data! 480 pCAttr->m_pNamespace.reset( new stringpair_t(oUri, oPrefix) ); 481 pCAttr->m_bUnlinked = true; 482 483 return pCAttr.get(); 484 }; 485 486 // Creates a CDATASection node whose value is the specified string. 487 Reference< XCDATASection > SAL_CALL CDocument::createCDATASection(const OUString& data) 488 throw (RuntimeException) 489 { 490 ::osl::MutexGuard const g(m_Mutex); 491 492 OString const oData( 493 ::rtl::OUStringToOString(data, RTL_TEXTENCODING_UTF8)); 494 xmlChar const*const pData = 495 reinterpret_cast<xmlChar const*>(oData.getStr()); 496 xmlNodePtr const pText = 497 xmlNewCDataBlock(m_aDocPtr, pData, strlen(oData.getStr())); 498 Reference< XCDATASection > const xRet( 499 static_cast< XNode* >(GetCNode(pText).get()), 500 UNO_QUERY_THROW); 501 return xRet; 502 } 503 504 // Creates a Comment node given the specified string. 505 Reference< XComment > SAL_CALL CDocument::createComment(const OUString& data) 506 throw (RuntimeException) 507 { 508 ::osl::MutexGuard const g(m_Mutex); 509 510 OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8); 511 xmlChar *xData = (xmlChar*)o1.getStr(); 512 xmlNodePtr pComment = xmlNewDocComment(m_aDocPtr, xData); 513 Reference< XComment > const xRet( 514 static_cast< XNode* >(GetCNode(pComment).get()), 515 UNO_QUERY_THROW); 516 return xRet; 517 } 518 519 //Creates an empty DocumentFragment object. 520 Reference< XDocumentFragment > SAL_CALL CDocument::createDocumentFragment() 521 throw (RuntimeException) 522 { 523 ::osl::MutexGuard const g(m_Mutex); 524 525 xmlNodePtr pFrag = xmlNewDocFragment(m_aDocPtr); 526 Reference< XDocumentFragment > const xRet( 527 static_cast< XNode* >(GetCNode(pFrag).get()), 528 UNO_QUERY_THROW); 529 return xRet; 530 } 531 532 // Creates an element of the type specified. 533 Reference< XElement > SAL_CALL CDocument::createElement(const OUString& tagName) 534 throw (RuntimeException, DOMException) 535 { 536 ::osl::MutexGuard const g(m_Mutex); 537 538 OString o1 = OUStringToOString(tagName, RTL_TEXTENCODING_UTF8); 539 xmlChar *xName = (xmlChar*)o1.getStr(); 540 xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, NULL, xName, NULL); 541 Reference< XElement > const xRet( 542 static_cast< XNode* >(GetCNode(pNode).get()), 543 UNO_QUERY_THROW); 544 return xRet; 545 } 546 547 // Creates an element of the given qualified name and namespace URI. 548 Reference< XElement > SAL_CALL CDocument::createElementNS( 549 const OUString& ns, const OUString& qname) 550 throw (RuntimeException, DOMException) 551 { 552 ::osl::MutexGuard const g(m_Mutex); 553 554 sal_Int32 i = qname.indexOf(':'); 555 if (ns.getLength() == 0) throw RuntimeException(); 556 xmlChar *xPrefix; 557 xmlChar *xName; 558 OString o1, o2, o3; 559 if ( i != -1) { 560 o1 = OUStringToOString(qname.copy(0, i), RTL_TEXTENCODING_UTF8); 561 xPrefix = (xmlChar*)o1.getStr(); 562 o2 = OUStringToOString(qname.copy(i+1, qname.getLength()-i-1), RTL_TEXTENCODING_UTF8); 563 xName = (xmlChar*)o2.getStr(); 564 } else { 565 // default prefix 566 xPrefix = (xmlChar*)""; 567 o2 = OUStringToOString(qname, RTL_TEXTENCODING_UTF8); 568 xName = (xmlChar*)o2.getStr(); 569 } 570 o3 = OUStringToOString(ns, RTL_TEXTENCODING_UTF8); 571 xmlChar *xUri = (xmlChar*)o3.getStr(); 572 573 // xmlNsPtr aNsPtr = xmlNewReconciledNs? 574 // xmlNsPtr aNsPtr = xmlNewGlobalNs? 575 xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, NULL, xName, NULL); 576 xmlNsPtr const pNs = xmlNewNs(pNode, xUri, xPrefix); 577 xmlSetNs(pNode, pNs); 578 Reference< XElement > const xRet( 579 static_cast< XNode* >(GetCNode(pNode).get()), 580 UNO_QUERY_THROW); 581 return xRet; 582 } 583 584 //Creates an EntityReference object. 585 Reference< XEntityReference > SAL_CALL CDocument::createEntityReference(const OUString& name) 586 throw (RuntimeException, DOMException) 587 { 588 ::osl::MutexGuard const g(m_Mutex); 589 590 OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); 591 xmlChar *xName = (xmlChar*)o1.getStr(); 592 xmlNodePtr const pNode = xmlNewReference(m_aDocPtr, xName); 593 Reference< XEntityReference > const xRet( 594 static_cast< XNode* >(GetCNode(pNode).get()), 595 UNO_QUERY_THROW); 596 return xRet; 597 } 598 599 // Creates a ProcessingInstruction node given the specified name and 600 // data strings. 601 Reference< XProcessingInstruction > SAL_CALL CDocument::createProcessingInstruction( 602 const OUString& target, const OUString& data) 603 throw (RuntimeException, DOMException) 604 { 605 ::osl::MutexGuard const g(m_Mutex); 606 607 OString o1 = OUStringToOString(target, RTL_TEXTENCODING_UTF8); 608 xmlChar *xTarget = (xmlChar*)o1.getStr(); 609 OString o2 = OUStringToOString(data, RTL_TEXTENCODING_UTF8); 610 xmlChar *xData = (xmlChar*)o2.getStr(); 611 xmlNodePtr const pNode = xmlNewDocPI(m_aDocPtr, xTarget, xData); 612 pNode->doc = m_aDocPtr; 613 Reference< XProcessingInstruction > const xRet( 614 static_cast< XNode* >(GetCNode(pNode).get()), 615 UNO_QUERY_THROW); 616 return xRet; 617 } 618 619 // Creates a Text node given the specified string. 620 Reference< XText > SAL_CALL CDocument::createTextNode(const OUString& data) 621 throw (RuntimeException) 622 { 623 ::osl::MutexGuard const g(m_Mutex); 624 625 OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8); 626 xmlChar *xData = (xmlChar*)o1.getStr(); 627 xmlNodePtr const pNode = xmlNewDocText(m_aDocPtr, xData); 628 Reference< XText > const xRet( 629 static_cast< XNode* >(GetCNode(pNode).get()), 630 UNO_QUERY_THROW); 631 return xRet; 632 } 633 634 // The Document Type Declaration (see DocumentType) associated with this 635 // document. 636 Reference< XDocumentType > SAL_CALL CDocument::getDoctype() 637 throw (RuntimeException) 638 { 639 ::osl::MutexGuard const g(m_Mutex); 640 641 xmlNodePtr const pDocType(lcl_getDocumentType(m_aDocPtr)); 642 Reference< XDocumentType > const xRet( 643 static_cast< XNode* >(GetCNode(pDocType).get()), 644 UNO_QUERY); 645 return xRet; 646 } 647 648 // This is a convenience attribute that allows direct access to the child 649 // node that is the root element of the document. 650 Reference< XElement > SAL_CALL CDocument::getDocumentElement() 651 throw (RuntimeException) 652 { 653 ::osl::MutexGuard const g(m_Mutex); 654 655 xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr); 656 if (!pNode) { return 0; } 657 Reference< XElement > const xRet( 658 static_cast< XNode* >(GetCNode(pNode).get()), 659 UNO_QUERY); 660 return xRet; 661 } 662 663 static xmlNodePtr 664 lcl_search_element_by_id(const xmlNodePtr cur, const xmlChar* id) 665 { 666 if (cur == NULL) 667 return NULL; 668 // look in current node 669 if (cur->type == XML_ELEMENT_NODE) 670 { 671 xmlAttrPtr a = cur->properties; 672 while (a != NULL) 673 { 674 if (a->atype == XML_ATTRIBUTE_ID) { 675 if (strcmp((char*)a->children->content, (char*)id) == 0) 676 return cur; 677 } 678 a = a->next; 679 } 680 } 681 // look in children 682 xmlNodePtr result = lcl_search_element_by_id(cur->children, id); 683 if (result != NULL) 684 return result; 685 result = lcl_search_element_by_id(cur->next, id); 686 return result; 687 } 688 689 // Returns the Element whose ID is given by elementId. 690 Reference< XElement > SAL_CALL 691 CDocument::getElementById(const OUString& elementId) 692 throw (RuntimeException) 693 { 694 ::osl::MutexGuard const g(m_Mutex); 695 696 // search the tree for an element with the given ID 697 OString o1 = OUStringToOString(elementId, RTL_TEXTENCODING_UTF8); 698 xmlChar *xId = (xmlChar*)o1.getStr(); 699 xmlNodePtr const pStart = lcl_getDocumentRootPtr(m_aDocPtr); 700 if (!pStart) { return 0; } 701 xmlNodePtr const pNode = lcl_search_element_by_id(pStart, xId); 702 Reference< XElement > const xRet( 703 static_cast< XNode* >(GetCNode(pNode).get()), 704 UNO_QUERY); 705 return xRet; 706 } 707 708 709 Reference< XNodeList > SAL_CALL 710 CDocument::getElementsByTagName(OUString const& rTagname) 711 throw (RuntimeException) 712 { 713 ::osl::MutexGuard const g(m_Mutex); 714 715 Reference< XNodeList > const xRet( 716 new CElementList(this->GetDocumentElement(), m_Mutex, rTagname)); 717 return xRet; 718 } 719 720 Reference< XNodeList > SAL_CALL CDocument::getElementsByTagNameNS( 721 OUString const& rNamespaceURI, OUString const& rLocalName) 722 throw (RuntimeException) 723 { 724 ::osl::MutexGuard const g(m_Mutex); 725 726 Reference< XNodeList > const xRet( 727 new CElementList(this->GetDocumentElement(), m_Mutex, 728 rLocalName, &rNamespaceURI)); 729 return xRet; 730 } 731 732 Reference< XDOMImplementation > SAL_CALL CDocument::getImplementation() 733 throw (RuntimeException) 734 { 735 // does not need mutex currently 736 return Reference< XDOMImplementation >(CDOMImplementation::get()); 737 } 738 739 // helper function to recursively import siblings 740 static void lcl_ImportSiblings( 741 Reference< XDocument > const& xTargetDocument, 742 Reference< XNode > const& xTargetParent, 743 Reference< XNode > const& xChild) 744 { 745 Reference< XNode > xSibling = xChild; 746 while (xSibling.is()) 747 { 748 Reference< XNode > const xTmp( 749 xTargetDocument->importNode(xSibling, sal_True)); 750 xTargetParent->appendChild(xTmp); 751 xSibling = xSibling->getNextSibling(); 752 } 753 } 754 755 static Reference< XNode > 756 lcl_ImportNode( Reference< XDocument > const& xDocument, 757 Reference< XNode > const& xImportedNode, sal_Bool deep) 758 { 759 Reference< XNode > xNode; 760 NodeType aNodeType = xImportedNode->getNodeType(); 761 switch (aNodeType) 762 { 763 case NodeType_ATTRIBUTE_NODE: 764 { 765 Reference< XAttr > const xAttr(xImportedNode, UNO_QUERY_THROW); 766 Reference< XAttr > const xNew = 767 xDocument->createAttribute(xAttr->getName()); 768 xNew->setValue(xAttr->getValue()); 769 xNode.set(xNew, UNO_QUERY); 770 break; 771 } 772 case NodeType_CDATA_SECTION_NODE: 773 { 774 Reference< XCDATASection > const xCData(xImportedNode, 775 UNO_QUERY_THROW); 776 Reference< XCDATASection > const xNewCData = 777 xDocument->createCDATASection(xCData->getData()); 778 xNode.set(xNewCData, UNO_QUERY); 779 break; 780 } 781 case NodeType_COMMENT_NODE: 782 { 783 Reference< XComment > const xComment(xImportedNode, 784 UNO_QUERY_THROW); 785 Reference< XComment > const xNewComment = 786 xDocument->createComment(xComment->getData()); 787 xNode.set(xNewComment, UNO_QUERY); 788 break; 789 } 790 case NodeType_DOCUMENT_FRAGMENT_NODE: 791 { 792 Reference< XDocumentFragment > const xFrag(xImportedNode, 793 UNO_QUERY_THROW); 794 Reference< XDocumentFragment > const xNewFrag = 795 xDocument->createDocumentFragment(); 796 xNode.set(xNewFrag, UNO_QUERY); 797 break; 798 } 799 case NodeType_ELEMENT_NODE: 800 { 801 Reference< XElement > const xElement(xImportedNode, 802 UNO_QUERY_THROW); 803 OUString const aNsUri = xImportedNode->getNamespaceURI(); 804 OUString const aNsPrefix = xImportedNode->getPrefix(); 805 OUString aQName = xElement->getTagName(); 806 Reference< XElement > xNewElement; 807 if (aNsUri.getLength() > 0) 808 { 809 if (aNsPrefix.getLength() > 0) { 810 aQName = aNsPrefix + OUString::createFromAscii(":") 811 + aQName; 812 } 813 xNewElement = xDocument->createElementNS(aNsUri, aQName); 814 } else { 815 xNewElement = xDocument->createElement(aQName); 816 } 817 818 // get attributes 819 if (xElement->hasAttributes()) 820 { 821 Reference< XNamedNodeMap > attribs = xElement->getAttributes(); 822 for (sal_Int32 i = 0; i < attribs->getLength(); i++) 823 { 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.getLength() > 0) 831 { 832 if (aAttrPrefix.getLength() > 0) { 833 aAttrName = aAttrPrefix + 834 OUString::createFromAscii(":") + aAttrName; 835 } 836 xNewElement->setAttributeNS( 837 aAttrUri, aAttrName, sValue); 838 } else { 839 xNewElement->setAttribute(aAttrName, sValue); 840 } 841 } 842 } 843 xNode.set(xNewElement, UNO_QUERY); 844 break; 845 } 846 case NodeType_ENTITY_REFERENCE_NODE: 847 { 848 Reference< XEntityReference > const xRef(xImportedNode, 849 UNO_QUERY_THROW); 850 Reference< XEntityReference > const xNewRef( 851 xDocument->createEntityReference(xRef->getNodeName())); 852 xNode.set(xNewRef, UNO_QUERY); 853 break; 854 } 855 case NodeType_PROCESSING_INSTRUCTION_NODE: 856 { 857 Reference< XProcessingInstruction > const xPi(xImportedNode, 858 UNO_QUERY_THROW); 859 Reference< XProcessingInstruction > const xNewPi( 860 xDocument->createProcessingInstruction( 861 xPi->getTarget(), xPi->getData())); 862 xNode.set(xNewPi, UNO_QUERY); 863 break; 864 } 865 case NodeType_TEXT_NODE: 866 { 867 Reference< XText > const xText(xImportedNode, UNO_QUERY_THROW); 868 Reference< XText > const xNewText( 869 xDocument->createTextNode(xText->getData())); 870 xNode.set(xNewText, UNO_QUERY); 871 break; 872 } 873 case NodeType_ENTITY_NODE: 874 case NodeType_DOCUMENT_NODE: 875 case NodeType_DOCUMENT_TYPE_NODE: 876 case NodeType_NOTATION_NODE: 877 default: 878 // can't be imported 879 throw RuntimeException(); 880 881 } 882 if (deep) 883 { 884 // get children and import them 885 Reference< XNode > const xChild = xImportedNode->getFirstChild(); 886 if (xChild.is()) 887 { 888 lcl_ImportSiblings(xDocument, xNode, xChild); 889 } 890 } 891 892 /* DOMNodeInsertedIntoDocument 893 * Fired when a node is being inserted into a document, 894 * either through direct insertion of the Node or insertion of a 895 * subtree in which it is contained. This event is dispatched after 896 * the insertion has taken place. The target of this event is the node 897 * being inserted. If the Node is being directly inserted the DOMNodeInserted 898 * event will fire before the DOMNodeInsertedIntoDocument event. 899 * Bubbles: No 900 * Cancelable: No 901 * Context Info: None 902 */ 903 if (xNode.is()) 904 { 905 Reference< XDocumentEvent > const xDocevent(xDocument, UNO_QUERY); 906 Reference< XMutationEvent > const event(xDocevent->createEvent( 907 OUString::createFromAscii("DOMNodeInsertedIntoDocument")), 908 UNO_QUERY_THROW); 909 event->initMutationEvent( 910 OUString::createFromAscii("DOMNodeInsertedIntoDocument") 911 , sal_True, sal_False, Reference< XNode >(), 912 OUString(), OUString(), OUString(), (AttrChangeType)0 ); 913 Reference< XEventTarget > const xDocET(xDocument, UNO_QUERY); 914 xDocET->dispatchEvent(Reference< XEvent >(event, UNO_QUERY)); 915 } 916 917 return xNode; 918 } 919 920 Reference< XNode > SAL_CALL CDocument::importNode( 921 Reference< XNode > const& xImportedNode, sal_Bool deep) 922 throw (RuntimeException, DOMException) 923 { 924 if (!xImportedNode.is()) { throw RuntimeException(); } 925 926 // NB: this whole operation inherently accesses 2 distinct documents. 927 // The imported node could even be from a different DOM implementation, 928 // so this implementation cannot make any assumptions about the 929 // locking strategy of the imported node. 930 // So the import takes no lock on this document; 931 // it only calls UNO methods on this document that temporarily 932 // lock the document, and UNO methods on the imported node that 933 // may temporarily lock the other document. 934 // As a consequence, the import is not atomic with regard to 935 // concurrent modifications of either document, but it should not 936 // deadlock. 937 // To ensure that no members are accessed, the implementation is in 938 // static non-member functions. 939 940 Reference< XDocument > const xDocument(this); 941 // already in doc? 942 if (xImportedNode->getOwnerDocument() == xDocument) { 943 return xImportedNode; 944 } 945 946 Reference< XNode > const xNode( 947 lcl_ImportNode(xDocument, xImportedNode, deep) ); 948 return xNode; 949 } 950 951 952 OUString SAL_CALL CDocument::getNodeName()throw (RuntimeException) 953 { 954 // does not need mutex currently 955 return OUString::createFromAscii("#document"); 956 } 957 958 OUString SAL_CALL CDocument::getNodeValue() throw (RuntimeException) 959 { 960 // does not need mutex currently 961 return OUString(); 962 } 963 964 Reference< XNode > SAL_CALL CDocument::cloneNode(sal_Bool bDeep) 965 throw (RuntimeException) 966 { 967 ::osl::MutexGuard const g(m_rMutex); 968 969 OSL_ASSERT(0 != m_aNodePtr); 970 if (0 == m_aNodePtr) { 971 return 0; 972 } 973 xmlDocPtr const pClone(xmlCopyDoc(m_aDocPtr, (bDeep) ? 1 : 0)); 974 if (0 == pClone) { return 0; } 975 Reference< XNode > const xRet( 976 static_cast<CNode*>(CDocument::CreateCDocument(pClone).get())); 977 return xRet; 978 } 979 980 Reference< XEvent > SAL_CALL CDocument::createEvent(const OUString& aType) throw (RuntimeException) 981 { 982 // does not need mutex currently 983 events::CEvent *pEvent = 0; 984 if ( 985 aType.compareToAscii("DOMSubtreeModified") == 0|| 986 aType.compareToAscii("DOMNodeInserted") == 0|| 987 aType.compareToAscii("DOMNodeRemoved") == 0|| 988 aType.compareToAscii("DOMNodeRemovedFromDocument") == 0|| 989 aType.compareToAscii("DOMNodeInsertedIntoDocument") == 0|| 990 aType.compareToAscii("DOMAttrModified") == 0|| 991 aType.compareToAscii("DOMCharacterDataModified") == 0) 992 { 993 pEvent = new events::CMutationEvent; 994 995 } else if ( 996 aType.compareToAscii("DOMFocusIn") == 0|| 997 aType.compareToAscii("DOMFocusOut") == 0|| 998 aType.compareToAscii("DOMActivate") == 0) 999 { 1000 pEvent = new events::CUIEvent; 1001 } else if ( 1002 aType.compareToAscii("click") == 0|| 1003 aType.compareToAscii("mousedown") == 0|| 1004 aType.compareToAscii("mouseup") == 0|| 1005 aType.compareToAscii("mouseover") == 0|| 1006 aType.compareToAscii("mousemove") == 0|| 1007 aType.compareToAscii("mouseout") == 0 ) 1008 { 1009 pEvent = new events::CMouseEvent; 1010 } 1011 else // generic event 1012 { 1013 pEvent = new events::CEvent; 1014 } 1015 return Reference< XEvent >(pEvent); 1016 } 1017 1018 // ::com::sun::star::xml::sax::XSAXSerializable 1019 void SAL_CALL CDocument::serialize( 1020 const Reference< XDocumentHandler >& i_xHandler, 1021 const Sequence< beans::StringPair >& i_rNamespaces) 1022 throw (RuntimeException, SAXException) 1023 { 1024 ::osl::MutexGuard const g(m_Mutex); 1025 1026 // add new namespaces to root node 1027 xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr); 1028 if (0 != pRoot) { 1029 const beans::StringPair * pSeq = i_rNamespaces.getConstArray(); 1030 for (const beans::StringPair *pNsDef = pSeq; 1031 pNsDef < pSeq + i_rNamespaces.getLength(); ++pNsDef) { 1032 OString prefix = OUStringToOString(pNsDef->First, 1033 RTL_TEXTENCODING_UTF8); 1034 OString href = OUStringToOString(pNsDef->Second, 1035 RTL_TEXTENCODING_UTF8); 1036 // this will only add the ns if it does not exist already 1037 xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()), 1038 reinterpret_cast<const xmlChar*>(prefix.getStr())); 1039 } 1040 // eliminate duplicate namespace declarations 1041 nscleanup(pRoot->children, pRoot); 1042 } 1043 saxify(i_xHandler); 1044 } 1045 1046 // ::com::sun::star::xml::sax::XFastSAXSerializable 1047 void SAL_CALL CDocument::fastSerialize( const Reference< XFastDocumentHandler >& i_xHandler, 1048 const Reference< XFastTokenHandler >& i_xTokenHandler, 1049 const Sequence< beans::StringPair >& i_rNamespaces, 1050 const Sequence< beans::Pair< rtl::OUString, sal_Int32 > >& i_rRegisterNamespaces ) 1051 throw (SAXException, RuntimeException) 1052 { 1053 ::osl::MutexGuard const g(m_Mutex); 1054 1055 // add new namespaces to root node 1056 xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr); 1057 if (0 != pRoot) { 1058 const beans::StringPair * pSeq = i_rNamespaces.getConstArray(); 1059 for (const beans::StringPair *pNsDef = pSeq; 1060 pNsDef < pSeq + i_rNamespaces.getLength(); ++pNsDef) { 1061 OString prefix = OUStringToOString(pNsDef->First, 1062 RTL_TEXTENCODING_UTF8); 1063 OString href = OUStringToOString(pNsDef->Second, 1064 RTL_TEXTENCODING_UTF8); 1065 // this will only add the ns if it does not exist already 1066 xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()), 1067 reinterpret_cast<const xmlChar*>(prefix.getStr())); 1068 } 1069 // eliminate duplicate namespace declarations 1070 nscleanup(pRoot->children, pRoot); 1071 } 1072 1073 Context aContext(i_xHandler, 1074 i_xTokenHandler); 1075 1076 // register namespace ids 1077 const beans::Pair<OUString,sal_Int32>* pSeq = i_rRegisterNamespaces.getConstArray(); 1078 for (const beans::Pair<OUString,sal_Int32>* pNs = pSeq; 1079 pNs < pSeq + i_rRegisterNamespaces.getLength(); ++pNs) 1080 { 1081 OSL_ENSURE(pNs->Second >= FastToken::NAMESPACE, 1082 "CDocument::fastSerialize(): invalid NS token id"); 1083 aContext.maNamespaceMap[ pNs->First ] = pNs->Second; 1084 } 1085 1086 fastSaxify(aContext); 1087 } 1088 } 1089