1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 #include <com/sun/star/uno/Sequence.h> 25 26 #include "document.hxx" 27 #include "attr.hxx" 28 #include "element.hxx" 29 #include "cdatasection.hxx" 30 #include "documentfragment.hxx" 31 #include "text.hxx" 32 #include "cdatasection.hxx" 33 #include "comment.hxx" 34 #include "processinginstruction.hxx" 35 #include "entityreference.hxx" 36 #include "documenttype.hxx" 37 #include "elementlist.hxx" 38 #include "domimplementation.hxx" 39 #include <entity.hxx> 40 #include <notation.hxx> 41 42 #include "../events/event.hxx" 43 #include "../events/mutationevent.hxx" 44 #include "../events/uievent.hxx" 45 #include "../events/mouseevent.hxx" 46 #include "../events/eventdispatcher.hxx" 47 48 #include <string.h> 49 50 #include <com/sun/star/xml/sax/FastToken.hpp> 51 #include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp> 52 53 namespace DOM 54 { lcl_getDocumentType(xmlDocPtr const i_pDocument)55 static xmlNodePtr lcl_getDocumentType(xmlDocPtr const i_pDocument) 56 { 57 // find the doc type 58 xmlNodePtr cur = i_pDocument->children; 59 while (cur != NULL) 60 { 61 if ((cur->type == XML_DOCUMENT_TYPE_NODE) || 62 (cur->type == XML_DTD_NODE)) { 63 return cur; 64 } 65 } 66 return 0; 67 } 68 69 /// get the pointer to the root element node of the document lcl_getDocumentRootPtr(xmlDocPtr const i_pDocument)70 static xmlNodePtr lcl_getDocumentRootPtr(xmlDocPtr const i_pDocument) 71 { 72 // find the document element 73 xmlNodePtr cur = i_pDocument->children; 74 while (cur != NULL) 75 { 76 if (cur->type == XML_ELEMENT_NODE) 77 break; 78 cur = cur->next; 79 } 80 return cur; 81 } 82 CDocument(xmlDocPtr const pDoc)83 CDocument::CDocument(xmlDocPtr const pDoc) 84 : CDocument_Base(*this, m_Mutex, 85 NodeType_DOCUMENT_NODE, reinterpret_cast<xmlNodePtr>(pDoc)) 86 , m_aDocPtr(pDoc) 87 , m_streamListeners() 88 , m_pEventDispatcher(new events::CEventDispatcher()) 89 { 90 } 91 CreateCDocument(xmlDocPtr const pDoc)92 ::rtl::Reference<CDocument> CDocument::CreateCDocument(xmlDocPtr const pDoc) 93 { 94 ::rtl::Reference<CDocument> const xDoc(new CDocument(pDoc)); 95 // add the doc itself to its nodemap! 96 xDoc->m_NodeMap.insert( 97 nodemap_t::value_type(reinterpret_cast<xmlNodePtr>(pDoc), 98 ::std::make_pair( 99 WeakReference<XNode>(static_cast<XDocument*>(xDoc.get())), 100 xDoc.get()))); 101 return xDoc; 102 } 103 ~CDocument()104 CDocument::~CDocument() 105 { 106 ::osl::MutexGuard const g(m_Mutex); 107 #ifdef DBG_UTIL 108 // node map must be empty now, otherwise CDocument must not die! 109 for (nodemap_t::iterator i = m_NodeMap.begin(); 110 i != m_NodeMap.end(); ++i) 111 { 112 Reference<XNode> const xNode(i->second.first); 113 OSL_ENSURE(!xNode.is(), 114 "CDocument::~CDocument(): ERROR: live node in document node map!"); 115 } 116 #endif 117 xmlFreeDoc(m_aDocPtr); 118 } 119 120 GetEventDispatcher()121 events::CEventDispatcher & CDocument::GetEventDispatcher() 122 { 123 return *m_pEventDispatcher; 124 } 125 GetDocumentElement()126 ::rtl::Reference< CElement > CDocument::GetDocumentElement() 127 { 128 xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr); 129 ::rtl::Reference< CElement > const xRet( 130 dynamic_cast<CElement*>(GetCNode(pNode).get())); 131 return xRet; 132 } 133 134 void RemoveCNode(xmlNodePtr const pNode,CNode const * const pCNode)135 CDocument::RemoveCNode(xmlNodePtr const pNode, CNode const*const pCNode) 136 { 137 nodemap_t::iterator const i = m_NodeMap.find(pNode); 138 if (i != m_NodeMap.end()) { 139 // #i113681# consider this scenario: 140 // T1 calls ~CNode 141 // T2 calls getCNode: lookup will find i->second->first invalid 142 // so a new CNode is created and inserted 143 // T1 calls removeCNode: i->second->second now points to a 144 // different CNode instance! 145 // 146 // check that the CNode is the right one 147 CNode *const pCurrent = i->second.second; 148 if (pCurrent == pCNode) { 149 m_NodeMap.erase(i); 150 } 151 } 152 } 153 154 /** NB: this is the CNode factory. 155 it is the only place where CNodes may be instantiated. 156 all CNodes must be registered at the m_NodeMap. 157 */ 158 ::rtl::Reference<CNode> GetCNode(xmlNodePtr const pNode,bool const bCreate)159 CDocument::GetCNode(xmlNodePtr const pNode, bool const bCreate) 160 { 161 if (0 == pNode) { 162 return 0; 163 } 164 //check whether there is already an instance for this node 165 nodemap_t::const_iterator const i = m_NodeMap.find(pNode); 166 if (i != m_NodeMap.end()) { 167 // #i113681# check that the CNode is still alive 168 uno::Reference<XNode> const xNode(i->second.first); 169 if (xNode.is()) 170 { 171 ::rtl::Reference<CNode> ret(i->second.second); 172 OSL_ASSERT(ret.is()); 173 return ret; 174 } 175 } 176 177 if (!bCreate) { return 0; } 178 179 // there is not yet an instance wrapping this node, 180 // create it and store it in the map 181 182 ::rtl::Reference<CNode> pCNode; 183 switch (pNode->type) 184 { 185 case XML_ELEMENT_NODE: 186 // m_aNodeType = NodeType::ELEMENT_NODE; 187 pCNode = static_cast< CNode* >( 188 new CElement(*this, m_Mutex, pNode)); 189 break; 190 case XML_TEXT_NODE: 191 // m_aNodeType = NodeType::TEXT_NODE; 192 pCNode = static_cast< CNode* >( 193 new CText(*this, m_Mutex, pNode)); 194 break; 195 case XML_CDATA_SECTION_NODE: 196 // m_aNodeType = NodeType::CDATA_SECTION_NODE; 197 pCNode = static_cast< CNode* >( 198 new CCDATASection(*this, m_Mutex, pNode)); 199 break; 200 case XML_ENTITY_REF_NODE: 201 // m_aNodeType = NodeType::ENTITY_REFERENCE_NODE; 202 pCNode = static_cast< CNode* >( 203 new CEntityReference(*this, m_Mutex, pNode)); 204 break; 205 case XML_ENTITY_NODE: 206 // m_aNodeType = NodeType::ENTITY_NODE; 207 pCNode = static_cast< CNode* >(new CEntity(*this, m_Mutex, 208 reinterpret_cast<xmlEntityPtr>(pNode))); 209 break; 210 case XML_PI_NODE: 211 // m_aNodeType = NodeType::PROCESSING_INSTRUCTION_NODE; 212 pCNode = static_cast< CNode* >( 213 new CProcessingInstruction(*this, m_Mutex, pNode)); 214 break; 215 case XML_COMMENT_NODE: 216 // m_aNodeType = NodeType::COMMENT_NODE; 217 pCNode = static_cast< CNode* >( 218 new CComment(*this, m_Mutex, pNode)); 219 break; 220 case XML_DOCUMENT_NODE: 221 // m_aNodeType = NodeType::DOCUMENT_NODE; 222 OSL_ENSURE(false, "CDocument::GetCNode is not supposed to" 223 " create a CDocument!!!"); 224 pCNode = static_cast< CNode* >(new CDocument( 225 reinterpret_cast<xmlDocPtr>(pNode))); 226 break; 227 case XML_DOCUMENT_TYPE_NODE: 228 case XML_DTD_NODE: 229 // m_aNodeType = NodeType::DOCUMENT_TYPE_NODE; 230 pCNode = static_cast< CNode* >(new CDocumentType(*this, m_Mutex, 231 reinterpret_cast<xmlDtdPtr>(pNode))); 232 break; 233 case XML_DOCUMENT_FRAG_NODE: 234 // m_aNodeType = NodeType::DOCUMENT_FRAGMENT_NODE; 235 pCNode = static_cast< CNode* >( 236 new CDocumentFragment(*this, m_Mutex, pNode)); 237 break; 238 case XML_NOTATION_NODE: 239 // m_aNodeType = NodeType::NOTATION_NODE; 240 pCNode = static_cast< CNode* >(new CNotation(*this, m_Mutex, 241 reinterpret_cast<xmlNotationPtr>(pNode))); 242 break; 243 case XML_ATTRIBUTE_NODE: 244 // m_aNodeType = NodeType::ATTRIBUTE_NODE; 245 pCNode = static_cast< CNode* >(new CAttr(*this, m_Mutex, 246 reinterpret_cast<xmlAttrPtr>(pNode))); 247 break; 248 // unsupported node types 249 case XML_HTML_DOCUMENT_NODE: 250 case XML_ELEMENT_DECL: 251 case XML_ATTRIBUTE_DECL: 252 case XML_ENTITY_DECL: 253 case XML_NAMESPACE_DECL: 254 default: 255 break; 256 } 257 258 if (pCNode != 0) { 259 bool const bInserted = m_NodeMap.insert( 260 nodemap_t::value_type(pNode, 261 ::std::make_pair(WeakReference<XNode>(pCNode.get()), 262 pCNode.get())) 263 ).second; 264 OSL_ASSERT(bInserted); 265 if (!bInserted) { 266 // if insertion failed, delete new instance and return null 267 return 0; 268 } 269 } 270 271 OSL_ENSURE(pCNode.is(), "no node produced during CDocument::GetCNode!"); 272 return pCNode; 273 } 274 275 GetOwnerDocument()276 CDocument & CDocument::GetOwnerDocument() 277 { 278 return *this; 279 } 280 saxify(const Reference<XDocumentHandler> & i_xHandler)281 void CDocument::saxify(const Reference< XDocumentHandler >& i_xHandler) 282 { 283 i_xHandler->startDocument(); 284 for (xmlNodePtr pChild = m_aNodePtr->children; 285 pChild != 0; pChild = pChild->next) { 286 ::rtl::Reference<CNode> const pNode = GetCNode(pChild); 287 OSL_ENSURE(pNode != 0, "CNode::get returned 0"); 288 pNode->saxify(i_xHandler); 289 } 290 i_xHandler->endDocument(); 291 } 292 fastSaxify(Context & rContext)293 void CDocument::fastSaxify( Context& rContext ) 294 { 295 rContext.mxDocHandler->startDocument(); 296 for (xmlNodePtr pChild = m_aNodePtr->children; 297 pChild != 0; pChild = pChild->next) { 298 ::rtl::Reference<CNode> const pNode = GetCNode(pChild); 299 OSL_ENSURE(pNode != 0, "CNode::get returned 0"); 300 pNode->fastSaxify(rContext); 301 } 302 rContext.mxDocHandler->endDocument(); 303 } 304 IsChildTypeAllowed(NodeType const nodeType)305 bool CDocument::IsChildTypeAllowed(NodeType const nodeType) 306 { 307 switch (nodeType) { 308 case NodeType_PROCESSING_INSTRUCTION_NODE: 309 case NodeType_COMMENT_NODE: 310 return true; 311 case NodeType_ELEMENT_NODE: 312 // there may be only one! 313 return 0 == lcl_getDocumentRootPtr(m_aDocPtr); 314 case NodeType_DOCUMENT_TYPE_NODE: 315 // there may be only one! 316 return 0 == lcl_getDocumentType(m_aDocPtr); 317 default: 318 return false; 319 } 320 } 321 322 addListener(const Reference<XStreamListener> & aListener)323 void SAL_CALL CDocument::addListener(const Reference< XStreamListener >& aListener ) 324 { 325 ::osl::MutexGuard const g(m_Mutex); 326 327 m_streamListeners.insert(aListener); 328 } 329 removeListener(const Reference<XStreamListener> & aListener)330 void SAL_CALL CDocument::removeListener(const Reference< XStreamListener >& aListener ) 331 { 332 ::osl::MutexGuard const g(m_Mutex); 333 334 m_streamListeners.erase(aListener); 335 } 336 337 // IO context functions for libxml2 interaction 338 typedef struct { 339 Reference< XOutputStream > stream; 340 bool allowClose; 341 } IOContext; 342 343 extern "C" { 344 // write callback 345 // int xmlOutputWriteCallback (void * context, const char * buffer, int len) writeCallback(void * context,const char * buffer,int len)346 static int writeCallback(void *context, const char* buffer, int len){ 347 // create a sequence and write it to the stream 348 IOContext *pContext = static_cast<IOContext*>(context); 349 Sequence<sal_Int8> bs(reinterpret_cast<const sal_Int8*>(buffer), len); 350 pContext->stream->writeBytes(bs); 351 return len; 352 } 353 354 // clsoe callback 355 //int xmlOutputCloseCallback (void * context) closeCallback(void * context)356 static int closeCallback(void *context) 357 { 358 IOContext *pContext = static_cast<IOContext*>(context); 359 if (pContext->allowClose) { 360 pContext->stream->closeOutput(); 361 } 362 return 0; 363 } 364 } // extern "C" 365 start()366 void SAL_CALL CDocument::start() 367 { 368 listenerlist_t streamListeners; 369 { 370 ::osl::MutexGuard const g(m_Mutex); 371 372 if (! m_rOutputStream.is()) { throw RuntimeException(); } 373 streamListeners = m_streamListeners; 374 } 375 376 // notify listeners about start 377 listenerlist_t::const_iterator iter1 = streamListeners.begin(); 378 while (iter1 != streamListeners.end()) { 379 Reference< XStreamListener > aListener = *iter1; 380 aListener->started(); 381 iter1++; 382 } 383 384 { 385 ::osl::MutexGuard const g(m_Mutex); 386 387 // check again! could have been reset... 388 if (! m_rOutputStream.is()) { throw RuntimeException(); } 389 390 // setup libxml IO and write data to output stream 391 IOContext ioctx = {m_rOutputStream, false}; 392 xmlOutputBufferPtr pOut = xmlOutputBufferCreateIO( 393 writeCallback, closeCallback, &ioctx, NULL); 394 xmlSaveFileTo(pOut, m_aNodePtr->doc, NULL); 395 } 396 397 // call listeners 398 listenerlist_t::const_iterator iter2 = streamListeners.begin(); 399 while (iter2 != streamListeners.end()) { 400 Reference< XStreamListener > aListener = *iter2; 401 aListener->closed(); 402 iter2++; 403 } 404 } 405 terminate()406 void SAL_CALL CDocument::terminate() 407 { 408 // not supported 409 } 410 setOutputStream(const Reference<XOutputStream> & aStream)411 void SAL_CALL CDocument::setOutputStream( const Reference< XOutputStream >& aStream ) 412 { 413 ::osl::MutexGuard const g(m_Mutex); 414 415 m_rOutputStream = aStream; 416 } 417 getOutputStream()418 Reference< XOutputStream > SAL_CALL CDocument::getOutputStream() 419 { 420 ::osl::MutexGuard const g(m_Mutex); 421 422 return m_rOutputStream; 423 } 424 425 // Creates an Attr of the given name. createAttribute(const OUString & name)426 Reference< XAttr > SAL_CALL CDocument::createAttribute(const OUString& name) 427 { 428 ::osl::MutexGuard const g(m_Mutex); 429 430 OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); 431 xmlChar *xName = (xmlChar*)o1.getStr(); 432 xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr, xName, NULL); 433 ::rtl::Reference< CAttr > const pCAttr( 434 dynamic_cast< CAttr* >(GetCNode( 435 reinterpret_cast<xmlNodePtr>(pAttr)).get())); 436 pCAttr->m_bUnlinked = true; 437 return pCAttr.get(); 438 }; 439 440 // Creates an attribute of the given qualified name and namespace URI. createAttributeNS(const OUString & ns,const OUString & qname)441 Reference< XAttr > SAL_CALL CDocument::createAttributeNS( 442 const OUString& ns, const OUString& qname) 443 { 444 ::osl::MutexGuard const g(m_Mutex); 445 446 // libxml does not allow a NS definition to be attached to an 447 // attribute node - which is a good thing, since namespaces are 448 // only defined as parts of element nodes 449 // thus the namespace data is stored in CAttr::m_pNamespace 450 sal_Int32 i = qname.indexOf(':'); 451 OString oPrefix, oName, oUri; 452 if (i != -1) 453 { 454 oPrefix = OUStringToOString(qname.copy(0, i), RTL_TEXTENCODING_UTF8); 455 oName = OUStringToOString(qname.copy(i+1, qname.getLength()-i-1), RTL_TEXTENCODING_UTF8); 456 } 457 else 458 { 459 oName = OUStringToOString(qname, RTL_TEXTENCODING_UTF8); 460 } 461 oUri = OUStringToOString(ns, RTL_TEXTENCODING_UTF8); 462 xmlAttrPtr const pAttr = xmlNewDocProp(m_aDocPtr, 463 reinterpret_cast<xmlChar const*>(oName.getStr()), 0); 464 ::rtl::Reference< CAttr > const pCAttr( 465 dynamic_cast< CAttr* >(GetCNode( 466 reinterpret_cast<xmlNodePtr>(pAttr)).get())); 467 if (!pCAttr.is()) { throw RuntimeException(); } 468 // store the namespace data! 469 pCAttr->m_pNamespace.reset( new stringpair_t(oUri, oPrefix) ); 470 pCAttr->m_bUnlinked = true; 471 472 return pCAttr.get(); 473 }; 474 475 // Creates a CDATASection node whose value is the specified string. createCDATASection(const OUString & data)476 Reference< XCDATASection > SAL_CALL CDocument::createCDATASection(const OUString& data) 477 { 478 ::osl::MutexGuard const g(m_Mutex); 479 480 OString const oData( 481 ::rtl::OUStringToOString(data, RTL_TEXTENCODING_UTF8)); 482 xmlChar const*const pData = 483 reinterpret_cast<xmlChar const*>(oData.getStr()); 484 xmlNodePtr const pText = 485 xmlNewCDataBlock(m_aDocPtr, pData, strlen(oData.getStr())); 486 Reference< XCDATASection > const xRet( 487 static_cast< XNode* >(GetCNode(pText).get()), 488 UNO_QUERY_THROW); 489 return xRet; 490 } 491 492 // Creates a Comment node given the specified string. createComment(const OUString & data)493 Reference< XComment > SAL_CALL CDocument::createComment(const OUString& data) 494 { 495 ::osl::MutexGuard const g(m_Mutex); 496 497 OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8); 498 xmlChar *xData = (xmlChar*)o1.getStr(); 499 xmlNodePtr pComment = xmlNewDocComment(m_aDocPtr, xData); 500 Reference< XComment > const xRet( 501 static_cast< XNode* >(GetCNode(pComment).get()), 502 UNO_QUERY_THROW); 503 return xRet; 504 } 505 506 //Creates an empty DocumentFragment object. createDocumentFragment()507 Reference< XDocumentFragment > SAL_CALL CDocument::createDocumentFragment() 508 { 509 ::osl::MutexGuard const g(m_Mutex); 510 511 xmlNodePtr pFrag = xmlNewDocFragment(m_aDocPtr); 512 Reference< XDocumentFragment > const xRet( 513 static_cast< XNode* >(GetCNode(pFrag).get()), 514 UNO_QUERY_THROW); 515 return xRet; 516 } 517 518 // Creates an element of the type specified. createElement(const OUString & tagName)519 Reference< XElement > SAL_CALL CDocument::createElement(const OUString& tagName) 520 { 521 ::osl::MutexGuard const g(m_Mutex); 522 523 OString o1 = OUStringToOString(tagName, RTL_TEXTENCODING_UTF8); 524 xmlChar *xName = (xmlChar*)o1.getStr(); 525 xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, NULL, xName, NULL); 526 Reference< XElement > const xRet( 527 static_cast< XNode* >(GetCNode(pNode).get()), 528 UNO_QUERY_THROW); 529 return xRet; 530 } 531 532 // Creates an element of the given qualified name and namespace URI. createElementNS(const OUString & ns,const OUString & qname)533 Reference< XElement > SAL_CALL CDocument::createElementNS( 534 const OUString& ns, const OUString& qname) 535 { 536 ::osl::MutexGuard const g(m_Mutex); 537 538 sal_Int32 i = qname.indexOf(':'); 539 if (ns.getLength() == 0) throw RuntimeException(); 540 xmlChar *xPrefix; 541 xmlChar *xName; 542 OString o1, o2, o3; 543 if ( i != -1) { 544 o1 = OUStringToOString(qname.copy(0, i), RTL_TEXTENCODING_UTF8); 545 xPrefix = (xmlChar*)o1.getStr(); 546 o2 = OUStringToOString(qname.copy(i+1, qname.getLength()-i-1), RTL_TEXTENCODING_UTF8); 547 xName = (xmlChar*)o2.getStr(); 548 } else { 549 // default prefix 550 xPrefix = (xmlChar*)""; 551 o2 = OUStringToOString(qname, RTL_TEXTENCODING_UTF8); 552 xName = (xmlChar*)o2.getStr(); 553 } 554 o3 = OUStringToOString(ns, RTL_TEXTENCODING_UTF8); 555 xmlChar *xUri = (xmlChar*)o3.getStr(); 556 557 // xmlNsPtr aNsPtr = xmlNewReconciledNs? 558 // xmlNsPtr aNsPtr = xmlNewGlobalNs? 559 xmlNodePtr const pNode = xmlNewDocNode(m_aDocPtr, NULL, xName, NULL); 560 xmlNsPtr const pNs = xmlNewNs(pNode, xUri, xPrefix); 561 xmlSetNs(pNode, pNs); 562 Reference< XElement > const xRet( 563 static_cast< XNode* >(GetCNode(pNode).get()), 564 UNO_QUERY_THROW); 565 return xRet; 566 } 567 568 //Creates an EntityReference object. createEntityReference(const OUString & name)569 Reference< XEntityReference > SAL_CALL CDocument::createEntityReference(const OUString& name) 570 { 571 ::osl::MutexGuard const g(m_Mutex); 572 573 OString o1 = OUStringToOString(name, RTL_TEXTENCODING_UTF8); 574 xmlChar *xName = (xmlChar*)o1.getStr(); 575 xmlNodePtr const pNode = xmlNewReference(m_aDocPtr, xName); 576 Reference< XEntityReference > const xRet( 577 static_cast< XNode* >(GetCNode(pNode).get()), 578 UNO_QUERY_THROW); 579 return xRet; 580 } 581 582 // Creates a ProcessingInstruction node given the specified name and 583 // data strings. createProcessingInstruction(const OUString & target,const OUString & data)584 Reference< XProcessingInstruction > SAL_CALL CDocument::createProcessingInstruction( 585 const OUString& target, const OUString& data) 586 { 587 ::osl::MutexGuard const g(m_Mutex); 588 589 OString o1 = OUStringToOString(target, RTL_TEXTENCODING_UTF8); 590 xmlChar *xTarget = (xmlChar*)o1.getStr(); 591 OString o2 = OUStringToOString(data, RTL_TEXTENCODING_UTF8); 592 xmlChar *xData = (xmlChar*)o2.getStr(); 593 xmlNodePtr const pNode = xmlNewDocPI(m_aDocPtr, xTarget, xData); 594 pNode->doc = m_aDocPtr; 595 Reference< XProcessingInstruction > const xRet( 596 static_cast< XNode* >(GetCNode(pNode).get()), 597 UNO_QUERY_THROW); 598 return xRet; 599 } 600 601 // Creates a Text node given the specified string. createTextNode(const OUString & data)602 Reference< XText > SAL_CALL CDocument::createTextNode(const OUString& data) 603 { 604 ::osl::MutexGuard const g(m_Mutex); 605 606 OString o1 = OUStringToOString(data, RTL_TEXTENCODING_UTF8); 607 xmlChar *xData = (xmlChar*)o1.getStr(); 608 xmlNodePtr const pNode = xmlNewDocText(m_aDocPtr, xData); 609 Reference< XText > const xRet( 610 static_cast< XNode* >(GetCNode(pNode).get()), 611 UNO_QUERY_THROW); 612 return xRet; 613 } 614 615 // The Document Type Declaration (see DocumentType) associated with this 616 // document. getDoctype()617 Reference< XDocumentType > SAL_CALL CDocument::getDoctype() 618 { 619 ::osl::MutexGuard const g(m_Mutex); 620 621 xmlNodePtr const pDocType(lcl_getDocumentType(m_aDocPtr)); 622 Reference< XDocumentType > const xRet( 623 static_cast< XNode* >(GetCNode(pDocType).get()), 624 UNO_QUERY); 625 return xRet; 626 } 627 628 // This is a convenience attribute that allows direct access to the child 629 // node that is the root element of the document. getDocumentElement()630 Reference< XElement > SAL_CALL CDocument::getDocumentElement() 631 { 632 ::osl::MutexGuard const g(m_Mutex); 633 634 xmlNodePtr const pNode = lcl_getDocumentRootPtr(m_aDocPtr); 635 if (!pNode) { return 0; } 636 Reference< XElement > const xRet( 637 static_cast< XNode* >(GetCNode(pNode).get()), 638 UNO_QUERY); 639 return xRet; 640 } 641 642 static xmlNodePtr lcl_search_element_by_id(const xmlNodePtr cur,const xmlChar * id)643 lcl_search_element_by_id(const xmlNodePtr cur, const xmlChar* id) 644 { 645 if (cur == NULL) 646 return NULL; 647 // look in current node 648 if (cur->type == XML_ELEMENT_NODE) 649 { 650 xmlAttrPtr a = cur->properties; 651 while (a != NULL) 652 { 653 if (a->atype == XML_ATTRIBUTE_ID) { 654 if (strcmp((char*)a->children->content, (char*)id) == 0) 655 return cur; 656 } 657 a = a->next; 658 } 659 } 660 // look in children 661 xmlNodePtr result = lcl_search_element_by_id(cur->children, id); 662 if (result != NULL) 663 return result; 664 result = lcl_search_element_by_id(cur->next, id); 665 return result; 666 } 667 668 // Returns the Element whose ID is given by elementId. 669 Reference< XElement > SAL_CALL getElementById(const OUString & elementId)670 CDocument::getElementById(const OUString& elementId) 671 { 672 ::osl::MutexGuard const g(m_Mutex); 673 674 // search the tree for an element with the given ID 675 OString o1 = OUStringToOString(elementId, RTL_TEXTENCODING_UTF8); 676 xmlChar *xId = (xmlChar*)o1.getStr(); 677 xmlNodePtr const pStart = lcl_getDocumentRootPtr(m_aDocPtr); 678 if (!pStart) { return 0; } 679 xmlNodePtr const pNode = lcl_search_element_by_id(pStart, xId); 680 Reference< XElement > const xRet( 681 static_cast< XNode* >(GetCNode(pNode).get()), 682 UNO_QUERY); 683 return xRet; 684 } 685 686 687 Reference< XNodeList > SAL_CALL getElementsByTagName(OUString const & rTagname)688 CDocument::getElementsByTagName(OUString const& rTagname) 689 { 690 ::osl::MutexGuard const g(m_Mutex); 691 692 Reference< XNodeList > const xRet( 693 new CElementList(this->GetDocumentElement(), m_Mutex, rTagname)); 694 return xRet; 695 } 696 getElementsByTagNameNS(OUString const & rNamespaceURI,OUString const & rLocalName)697 Reference< XNodeList > SAL_CALL CDocument::getElementsByTagNameNS( 698 OUString const& rNamespaceURI, OUString const& rLocalName) 699 { 700 ::osl::MutexGuard const g(m_Mutex); 701 702 Reference< XNodeList > const xRet( 703 new CElementList(this->GetDocumentElement(), m_Mutex, 704 rLocalName, &rNamespaceURI)); 705 return xRet; 706 } 707 getImplementation()708 Reference< XDOMImplementation > SAL_CALL CDocument::getImplementation() 709 { 710 // does not need mutex currently 711 return Reference< XDOMImplementation >(CDOMImplementation::get()); 712 } 713 714 // helper function to recursively import siblings lcl_ImportSiblings(Reference<XDocument> const & xTargetDocument,Reference<XNode> const & xTargetParent,Reference<XNode> const & xChild)715 static void lcl_ImportSiblings( 716 Reference< XDocument > const& xTargetDocument, 717 Reference< XNode > const& xTargetParent, 718 Reference< XNode > const& xChild) 719 { 720 Reference< XNode > xSibling = xChild; 721 while (xSibling.is()) 722 { 723 Reference< XNode > const xTmp( 724 xTargetDocument->importNode(xSibling, sal_True)); 725 xTargetParent->appendChild(xTmp); 726 xSibling = xSibling->getNextSibling(); 727 } 728 } 729 730 static Reference< XNode > lcl_ImportNode(Reference<XDocument> const & xDocument,Reference<XNode> const & xImportedNode,sal_Bool deep)731 lcl_ImportNode( Reference< XDocument > const& xDocument, 732 Reference< XNode > const& xImportedNode, sal_Bool deep) 733 { 734 Reference< XNode > xNode; 735 NodeType aNodeType = xImportedNode->getNodeType(); 736 switch (aNodeType) 737 { 738 case NodeType_ATTRIBUTE_NODE: 739 { 740 Reference< XAttr > const xAttr(xImportedNode, UNO_QUERY_THROW); 741 Reference< XAttr > const xNew = 742 xDocument->createAttribute(xAttr->getName()); 743 xNew->setValue(xAttr->getValue()); 744 xNode.set(xNew, UNO_QUERY); 745 break; 746 } 747 case NodeType_CDATA_SECTION_NODE: 748 { 749 Reference< XCDATASection > const xCData(xImportedNode, 750 UNO_QUERY_THROW); 751 Reference< XCDATASection > const xNewCData = 752 xDocument->createCDATASection(xCData->getData()); 753 xNode.set(xNewCData, UNO_QUERY); 754 break; 755 } 756 case NodeType_COMMENT_NODE: 757 { 758 Reference< XComment > const xComment(xImportedNode, 759 UNO_QUERY_THROW); 760 Reference< XComment > const xNewComment = 761 xDocument->createComment(xComment->getData()); 762 xNode.set(xNewComment, UNO_QUERY); 763 break; 764 } 765 case NodeType_DOCUMENT_FRAGMENT_NODE: 766 { 767 Reference< XDocumentFragment > const xFrag(xImportedNode, 768 UNO_QUERY_THROW); 769 Reference< XDocumentFragment > const xNewFrag = 770 xDocument->createDocumentFragment(); 771 xNode.set(xNewFrag, UNO_QUERY); 772 break; 773 } 774 case NodeType_ELEMENT_NODE: 775 { 776 Reference< XElement > const xElement(xImportedNode, 777 UNO_QUERY_THROW); 778 OUString const aNsUri = xImportedNode->getNamespaceURI(); 779 OUString const aNsPrefix = xImportedNode->getPrefix(); 780 OUString aQName = xElement->getTagName(); 781 Reference< XElement > xNewElement; 782 if (aNsUri.getLength() > 0) 783 { 784 if (aNsPrefix.getLength() > 0) { 785 aQName = aNsPrefix + OUString::createFromAscii(":") 786 + aQName; 787 } 788 xNewElement = xDocument->createElementNS(aNsUri, aQName); 789 } else { 790 xNewElement = xDocument->createElement(aQName); 791 } 792 793 // get attributes 794 if (xElement->hasAttributes()) 795 { 796 Reference< XNamedNodeMap > attribs = xElement->getAttributes(); 797 for (sal_Int32 i = 0; i < attribs->getLength(); i++) 798 { 799 Reference< XAttr > const curAttr(attribs->item(i), 800 UNO_QUERY_THROW); 801 OUString const aAttrUri = curAttr->getNamespaceURI(); 802 OUString const aAttrPrefix = curAttr->getPrefix(); 803 OUString aAttrName = curAttr->getName(); 804 OUString const sValue = curAttr->getValue(); 805 if (aAttrUri.getLength() > 0) 806 { 807 if (aAttrPrefix.getLength() > 0) { 808 aAttrName = aAttrPrefix + 809 OUString::createFromAscii(":") + aAttrName; 810 } 811 xNewElement->setAttributeNS( 812 aAttrUri, aAttrName, sValue); 813 } else { 814 xNewElement->setAttribute(aAttrName, sValue); 815 } 816 } 817 } 818 xNode.set(xNewElement, UNO_QUERY); 819 break; 820 } 821 case NodeType_ENTITY_REFERENCE_NODE: 822 { 823 Reference< XEntityReference > const xRef(xImportedNode, 824 UNO_QUERY_THROW); 825 Reference< XEntityReference > const xNewRef( 826 xDocument->createEntityReference(xRef->getNodeName())); 827 xNode.set(xNewRef, UNO_QUERY); 828 break; 829 } 830 case NodeType_PROCESSING_INSTRUCTION_NODE: 831 { 832 Reference< XProcessingInstruction > const xPi(xImportedNode, 833 UNO_QUERY_THROW); 834 Reference< XProcessingInstruction > const xNewPi( 835 xDocument->createProcessingInstruction( 836 xPi->getTarget(), xPi->getData())); 837 xNode.set(xNewPi, UNO_QUERY); 838 break; 839 } 840 case NodeType_TEXT_NODE: 841 { 842 Reference< XText > const xText(xImportedNode, UNO_QUERY_THROW); 843 Reference< XText > const xNewText( 844 xDocument->createTextNode(xText->getData())); 845 xNode.set(xNewText, UNO_QUERY); 846 break; 847 } 848 case NodeType_ENTITY_NODE: 849 case NodeType_DOCUMENT_NODE: 850 case NodeType_DOCUMENT_TYPE_NODE: 851 case NodeType_NOTATION_NODE: 852 default: 853 // can't be imported 854 throw RuntimeException(); 855 856 } 857 if (deep) 858 { 859 // get children and import them 860 Reference< XNode > const xChild = xImportedNode->getFirstChild(); 861 if (xChild.is()) 862 { 863 lcl_ImportSiblings(xDocument, xNode, xChild); 864 } 865 } 866 867 /* DOMNodeInsertedIntoDocument 868 * Fired when a node is being inserted into a document, 869 * either through direct insertion of the Node or insertion of a 870 * subtree in which it is contained. This event is dispatched after 871 * the insertion has taken place. The target of this event is the node 872 * being inserted. If the Node is being directly inserted the DOMNodeInserted 873 * event will fire before the DOMNodeInsertedIntoDocument event. 874 * Bubbles: No 875 * Cancelable: No 876 * Context Info: None 877 */ 878 if (xNode.is()) 879 { 880 Reference< XDocumentEvent > const xDocevent(xDocument, UNO_QUERY); 881 Reference< XMutationEvent > const event(xDocevent->createEvent( 882 OUString::createFromAscii("DOMNodeInsertedIntoDocument")), 883 UNO_QUERY_THROW); 884 event->initMutationEvent( 885 OUString::createFromAscii("DOMNodeInsertedIntoDocument") 886 , sal_True, sal_False, Reference< XNode >(), 887 OUString(), OUString(), OUString(), (AttrChangeType)0 ); 888 Reference< XEventTarget > const xDocET(xDocument, UNO_QUERY); 889 xDocET->dispatchEvent(Reference< XEvent >(event, UNO_QUERY)); 890 } 891 892 return xNode; 893 } 894 importNode(Reference<XNode> const & xImportedNode,sal_Bool deep)895 Reference< XNode > SAL_CALL CDocument::importNode( 896 Reference< XNode > const& xImportedNode, sal_Bool deep) 897 { 898 if (!xImportedNode.is()) { throw RuntimeException(); } 899 900 // NB: this whole operation inherently accesses 2 distinct documents. 901 // The imported node could even be from a different DOM implementation, 902 // so this implementation cannot make any assumptions about the 903 // locking strategy of the imported node. 904 // So the import takes no lock on this document; 905 // it only calls UNO methods on this document that temporarily 906 // lock the document, and UNO methods on the imported node that 907 // may temporarily lock the other document. 908 // As a consequence, the import is not atomic with regard to 909 // concurrent modifications of either document, but it should not 910 // deadlock. 911 // To ensure that no members are accessed, the implementation is in 912 // static non-member functions. 913 914 Reference< XDocument > const xDocument(this); 915 // already in doc? 916 if (xImportedNode->getOwnerDocument() == xDocument) { 917 return xImportedNode; 918 } 919 920 Reference< XNode > const xNode( 921 lcl_ImportNode(xDocument, xImportedNode, deep) ); 922 return xNode; 923 } 924 925 getNodeName()926 OUString SAL_CALL CDocument::getNodeName() 927 { 928 // does not need mutex currently 929 return OUString::createFromAscii("#document"); 930 } 931 getNodeValue()932 OUString SAL_CALL CDocument::getNodeValue() 933 { 934 // does not need mutex currently 935 return OUString(); 936 } 937 cloneNode(sal_Bool bDeep)938 Reference< XNode > SAL_CALL CDocument::cloneNode(sal_Bool bDeep) 939 { 940 ::osl::MutexGuard const g(m_rMutex); 941 942 OSL_ASSERT(0 != m_aNodePtr); 943 if (0 == m_aNodePtr) { 944 return 0; 945 } 946 xmlDocPtr const pClone(xmlCopyDoc(m_aDocPtr, (bDeep) ? 1 : 0)); 947 if (0 == pClone) { return 0; } 948 Reference< XNode > const xRet( 949 static_cast<CNode*>(CDocument::CreateCDocument(pClone).get())); 950 return xRet; 951 } 952 createEvent(const OUString & aType)953 Reference< XEvent > SAL_CALL CDocument::createEvent(const OUString& aType) 954 { 955 // does not need mutex currently 956 events::CEvent *pEvent = 0; 957 if ( 958 aType.compareToAscii("DOMSubtreeModified") == 0|| 959 aType.compareToAscii("DOMNodeInserted") == 0|| 960 aType.compareToAscii("DOMNodeRemoved") == 0|| 961 aType.compareToAscii("DOMNodeRemovedFromDocument") == 0|| 962 aType.compareToAscii("DOMNodeInsertedIntoDocument") == 0|| 963 aType.compareToAscii("DOMAttrModified") == 0|| 964 aType.compareToAscii("DOMCharacterDataModified") == 0) 965 { 966 pEvent = new events::CMutationEvent; 967 968 } else if ( 969 aType.compareToAscii("DOMFocusIn") == 0|| 970 aType.compareToAscii("DOMFocusOut") == 0|| 971 aType.compareToAscii("DOMActivate") == 0) 972 { 973 pEvent = new events::CUIEvent; 974 } else if ( 975 aType.compareToAscii("click") == 0|| 976 aType.compareToAscii("mousedown") == 0|| 977 aType.compareToAscii("mouseup") == 0|| 978 aType.compareToAscii("mouseover") == 0|| 979 aType.compareToAscii("mousemove") == 0|| 980 aType.compareToAscii("mouseout") == 0 ) 981 { 982 pEvent = new events::CMouseEvent; 983 } 984 else // generic event 985 { 986 pEvent = new events::CEvent; 987 } 988 return Reference< XEvent >(pEvent); 989 } 990 991 // ::com::sun::star::xml::sax::XSAXSerializable serialize(const Reference<XDocumentHandler> & i_xHandler,const Sequence<beans::StringPair> & i_rNamespaces)992 void SAL_CALL CDocument::serialize( 993 const Reference< XDocumentHandler >& i_xHandler, 994 const Sequence< beans::StringPair >& i_rNamespaces) 995 { 996 ::osl::MutexGuard const g(m_Mutex); 997 998 // add new namespaces to root node 999 xmlNodePtr const pRoot = lcl_getDocumentRootPtr(m_aDocPtr); 1000 if (0 != pRoot) { 1001 const beans::StringPair * pSeq = i_rNamespaces.getConstArray(); 1002 for (const beans::StringPair *pNsDef = pSeq; 1003 pNsDef < pSeq + i_rNamespaces.getLength(); ++pNsDef) { 1004 OString prefix = OUStringToOString(pNsDef->First, 1005 RTL_TEXTENCODING_UTF8); 1006 OString href = OUStringToOString(pNsDef->Second, 1007 RTL_TEXTENCODING_UTF8); 1008 // this will only add the ns if it does not exist already 1009 xmlNewNs(pRoot, reinterpret_cast<const xmlChar*>(href.getStr()), 1010 reinterpret_cast<const xmlChar*>(prefix.getStr())); 1011 } 1012 // eliminate duplicate namespace declarations 1013 nscleanup(pRoot->children, pRoot); 1014 } 1015 saxify(i_xHandler); 1016 } 1017 1018 // ::com::sun::star::xml::sax::XFastSAXSerializable fastSerialize(const Reference<XFastDocumentHandler> & i_xHandler,const Reference<XFastTokenHandler> & i_xTokenHandler,const Sequence<beans::StringPair> & i_rNamespaces,const Sequence<beans::Pair<rtl::OUString,sal_Int32>> & i_rRegisterNamespaces)1019 void SAL_CALL CDocument::fastSerialize( const Reference< XFastDocumentHandler >& i_xHandler, 1020 const Reference< XFastTokenHandler >& i_xTokenHandler, 1021 const Sequence< beans::StringPair >& i_rNamespaces, 1022 const Sequence< beans::Pair< rtl::OUString, sal_Int32 > >& i_rRegisterNamespaces ) 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 1044 Context aContext(i_xHandler, 1045 i_xTokenHandler); 1046 1047 // register namespace ids 1048 const beans::Pair<OUString,sal_Int32>* pSeq = i_rRegisterNamespaces.getConstArray(); 1049 for (const beans::Pair<OUString,sal_Int32>* pNs = pSeq; 1050 pNs < pSeq + i_rRegisterNamespaces.getLength(); ++pNs) 1051 { 1052 OSL_ENSURE(pNs->Second >= FastToken::NAMESPACE, 1053 "CDocument::fastSerialize(): invalid NS token id"); 1054 aContext.maNamespaceMap[ pNs->First ] = pNs->Second; 1055 } 1056 1057 fastSaxify(aContext); 1058 } 1059 } 1060