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 "precompiled_sfx2.hxx" 29 30 #include <sfx2/Metadatable.hxx> 31 #include <sfx2/XmlIdRegistry.hxx> 32 33 #include <vos/mutex.hxx> 34 #include <vcl/svapp.hxx> // solarmutex 35 36 #include <rtl/random.h> 37 38 #include <boost/bind.hpp> 39 40 #include <memory> 41 #include <hash_map> 42 #include <list> 43 #include <algorithm> 44 #if OSL_DEBUG_LEVEL > 0 45 #include <typeinfo> 46 #endif 47 48 49 /** XML ID handling. 50 51 There is an abstract base class <type>XmlIdRegistry</type>, with 52 2 subclasses <type>XmlIdRegistryDocument</type> for "normal" documents, 53 and <type>XmlIdRegistryClipboard</type> for clipboard documents. 54 These classes are responsible for managing XML IDs for all elements 55 of the model. Only the implementation of the <type>Metadatable</type> 56 base class needs to know the registries, so they are not in the header. 57 58 The handling of XML IDs differs between clipboard and non-clipboard 59 documents in several aspects. Most importantly, non-clipboard documents 60 can have several elements associated with one XML ID. 61 This is necessary because of the weird undo implementation: 62 deleting a text node moves the deleted node to the undo array, but 63 executing undo will then create a <em>copy</em> of that node in the 64 document array. These 2 nodes must have the same XML ID, because 65 we cannot know whether the user will do a redo next, or something else. 66 67 Because we need to have a mechanism for several objects per XML ID anyway, 68 we use that also to enable some usability features: 69 The document registry has a list of Metadatables per XML ID. 70 This list is sorted by priority, i.e., the first element has highest 71 priority. When inserting copies, care must be taken that they are inserted 72 at the right position: either before or after the source. 73 This is done by <method>Metadatable::RegisterAsCopyOf</method>. 74 When a text node is split, then both resulting text nodes are inserted 75 into the list. If the user then deletes one text node, the other one 76 will have the XML ID. 77 Also, when a Metadatable is copied to the clipboard and then pasted, 78 the copy is inserted into the list. If the user then deletes the source, 79 the XML ID is not lost. 80 The goal is that it should be hard to lose an XML ID by accident, which 81 is especially important as long as we do not have an UI that displays them. 82 83 There are two subclasses of <type>Metadatable</type>: 84 <ul><li><type>MetadatableClipboard</type>: for copies in the clipboard</li> 85 <li><type>MetadatableUndo</type>: for undo, because a Metadatable 86 may be destroyed on delete and a new one created on undo.</li></ul> 87 These serve only to track the position in an XML ID list in a document 88 registry, so that future actions can insert objects at the right position. 89 Unfortunately, inserting dummy objects seems to be necessary: 90 <ul><li>it is not sufficent to just remember the saved id, because then 91 the relative priorities might change when executing the undo</li> 92 <li>it is not sufficient to record the position as an integer, because 93 if we delete a text node and then undo, the node will be copied(!), 94 and we will have one more node in the list.<li> 95 <li>it is not sufficient to record the pointer of the previous/next 96 Metadatable, because if we delete a text node, undo, and then 97 do something to clear the redo array, the original text node is 98 destroyed, and is replaced by the copy created by undo</li></ul> 99 100 If content from a non-clipboard document is copied into a clipboard 101 document, a dummy <type>MetadatableClipboard</type> is inserted into the 102 non-clipboard document registry in order to track the position of the 103 source element. When the clipboard content is pasted back into the source 104 document, this dummy object is used to associate the pasted element with 105 that same XML ID. 106 107 If a <type>Metadatable</type> is deleted or merged, 108 <method>Metadatable::CreateUndo</method> is called, and returns a 109 <type>MetadatableUndo<type> instance, which can be used to undo the action 110 by passing it to <method>Metadatable::RestoreMetadata</method>. 111 112 @author mst 113 */ 114 115 116 using namespace ::com::sun::star; 117 118 using ::sfx2::isValidXmlId; 119 120 121 namespace sfx2 { 122 123 static const char s_content [] = "content.xml"; 124 static const char s_styles [] = "styles.xml"; 125 static const char s_prefix [] = "id"; // prefix for generated xml:id 126 127 static bool isContentFile(::rtl::OUString const & i_rPath) 128 { 129 return i_rPath.equalsAscii(s_content); 130 } 131 132 static bool isStylesFile (::rtl::OUString const & i_rPath) 133 { 134 return i_rPath.equalsAscii(s_styles); 135 } 136 137 138 //============================================================================= 139 // XML ID handling --------------------------------------------------- 140 141 /** handles registration of XMetadatable. 142 143 This class is responsible for guaranteeing that XMetadatable objects 144 always have XML IDs that are unique within a stream. 145 146 This is an abstract base class; see subclasses XmlIdRegistryDocument and 147 XmlIdRegistryClipboard. 148 149 @see SwDoc::GetXmlIdRegistry 150 @see SwDocShell::GetXmlIdRegistry 151 */ 152 class XmlIdRegistry : public sfx2::IXmlIdRegistry 153 { 154 155 public: 156 XmlIdRegistry(); 157 158 virtual ~XmlIdRegistry(); 159 160 /** get the ODF element with the given metadata reference. */ 161 virtual ::com::sun::star::uno::Reference< 162 ::com::sun::star::rdf::XMetadatable > SAL_CALL 163 GetElementByMetadataReference( 164 const ::com::sun::star::beans::StringPair & i_rReference) const; 165 166 /** register an ODF element at a newly generated, unique metadata reference. 167 168 <p> 169 Find a fresh XML ID, and register it for the element. 170 The generated ID does not occur in any stream of the document. 171 </p> 172 */ 173 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) = 0; 174 175 /** try to register an ODF element at a given XML ID, or update its 176 registation to a different XML ID. 177 178 <p> 179 If the given new metadata reference is not already occupied in the 180 document, unregister the element at its old metadata reference if 181 it has one, and register the new metadata reference for the element. 182 Note that this method only ensures that XML IDs are unique per stream, 183 so using the same XML ID in both content.xml and styles.xml is allowed. 184 </p> 185 186 @returns 187 true iff the element has successfully been registered 188 */ 189 virtual bool TryRegisterMetadatable(Metadatable& i_xObject, 190 ::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref) 191 = 0; 192 193 /** unregister an ODF element. 194 195 <p> 196 Unregister the element at its metadata reference. 197 Does not remove the metadata reference from the element. 198 </p> 199 200 @see RemoveXmlIdForElement 201 */ 202 virtual void UnregisterMetadatable(Metadatable const&) = 0; 203 204 /** get the metadata reference for the given element. */ 205 ::com::sun::star::beans::StringPair 206 GetXmlIdForElement(Metadatable const&) const; 207 208 /** remove the metadata reference for the given element. */ 209 virtual void RemoveXmlIdForElement(Metadatable const&) = 0; 210 211 protected: 212 213 virtual bool LookupXmlId(const Metadatable& i_xObject, 214 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const = 0; 215 216 virtual Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName, 217 const ::rtl::OUString & i_rIdref) const = 0; 218 }; 219 220 // XmlIdRegistryDocument --------------------------------------------- 221 222 /** non-clipboard documents */ 223 class XmlIdRegistryDocument : public XmlIdRegistry 224 { 225 226 public: 227 XmlIdRegistryDocument(); 228 229 virtual ~XmlIdRegistryDocument(); 230 231 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject); 232 233 virtual bool TryRegisterMetadatable(Metadatable& i_xObject, 234 ::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref); 235 236 virtual void UnregisterMetadatable(Metadatable const&); 237 238 virtual void RemoveXmlIdForElement(Metadatable const&); 239 240 /** register i_rCopy as a copy of i_rSource, 241 with precedence iff i_bCopyPrecedesSource is true */ 242 void RegisterCopy(Metadatable const& i_rSource, Metadatable & i_rCopy, 243 const bool i_bCopyPrecedesSource); 244 245 /** create a Undo Metadatable for i_rObject. */ 246 ::boost::shared_ptr<MetadatableUndo> CreateUndo( 247 Metadatable const& i_rObject); 248 249 /** merge i_rMerged and i_rOther into i_rMerged. */ 250 void JoinMetadatables(Metadatable & i_rMerged, Metadatable const& i_rOther); 251 252 // unfortunately public, Metadatable::RegisterAsCopyOf needs this 253 virtual bool LookupXmlId(const Metadatable& i_xObject, 254 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const; 255 256 private: 257 258 virtual Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName, 259 const ::rtl::OUString & i_rIdref) const; 260 261 struct XmlIdRegistry_Impl; 262 ::std::auto_ptr<XmlIdRegistry_Impl> m_pImpl; 263 }; 264 265 // MetadatableUndo --------------------------------------------------- 266 267 /** the horrible Undo Metadatable: is inserted into lists to track position */ 268 class MetadatableUndo : public Metadatable 269 { 270 /// as determined by the stream of the source in original document 271 const bool m_isInContent; 272 public: 273 MetadatableUndo(const bool i_isInContent) 274 : m_isInContent(i_isInContent) { } 275 virtual ::sfx2::XmlIdRegistry& GetRegistry() 276 { 277 // N.B. for Undo, m_pReg is initialized by registering this as copy in 278 // CreateUndo; it is never cleared 279 OSL_ENSURE(m_pReg, "no m_pReg in MetadatableUndo ?"); 280 return *m_pReg; 281 } 282 virtual bool IsInClipboard() const { return false; } 283 virtual bool IsInUndo() const { return true; } 284 virtual bool IsInContent() const { return m_isInContent; } 285 virtual ::com::sun::star::uno::Reference< 286 ::com::sun::star::rdf::XMetadatable > MakeUnoObject() 287 { OSL_ENSURE(false, "MetadatableUndo::MakeUnoObject"); throw; } 288 }; 289 290 // MetadatableClipboard ---------------------------------------------- 291 292 /** the horrible Clipboard Metadatable: inserted into lists to track position */ 293 class MetadatableClipboard : public Metadatable 294 { 295 /// as determined by the stream of the source in original document 296 const bool m_isInContent; 297 public: 298 MetadatableClipboard(const bool i_isInContent) 299 : m_isInContent(i_isInContent) { } 300 virtual ::sfx2::XmlIdRegistry& GetRegistry() 301 { 302 // N.B. for Clipboard, m_pReg is initialized by registering this as copy in 303 // RegisterAsCopyOf; it is only cleared by OriginNoLongerInBusinessAnymore 304 OSL_ENSURE(m_pReg, "no m_pReg in MetadatableClipboard ?"); 305 return *m_pReg; 306 } 307 virtual bool IsInClipboard() const { return true; } 308 virtual bool IsInUndo() const { return false; } 309 virtual bool IsInContent() const { return m_isInContent; } 310 virtual ::com::sun::star::uno::Reference< 311 ::com::sun::star::rdf::XMetadatable > MakeUnoObject() 312 { OSL_ENSURE(false, "MetadatableClipboard::MakeUnoObject"); throw; } 313 void OriginNoLongerInBusinessAnymore() { m_pReg = 0; } 314 }; 315 316 // XmlIdRegistryClipboard -------------------------------------------- 317 318 class XmlIdRegistryClipboard : public XmlIdRegistry 319 { 320 321 public: 322 XmlIdRegistryClipboard(); 323 virtual ~XmlIdRegistryClipboard(); 324 325 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject); 326 327 virtual bool TryRegisterMetadatable(Metadatable& i_xObject, 328 ::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref); 329 330 virtual void UnregisterMetadatable(Metadatable const&); 331 332 virtual void RemoveXmlIdForElement(Metadatable const&); 333 334 /** register i_rCopy as a copy of i_rSource */ 335 MetadatableClipboard & RegisterCopyClipboard(Metadatable & i_rCopy, 336 beans::StringPair const & i_rReference, 337 const bool i_isLatent); 338 339 /** get the Metadatable that links i_rObject to its origin registry */ 340 MetadatableClipboard const* SourceLink(Metadatable const& i_rObject); 341 342 private: 343 virtual bool LookupXmlId(const Metadatable& i_xObject, 344 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const; 345 346 virtual Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName, 347 const ::rtl::OUString & i_rIdref) const; 348 349 /** create a Clipboard Metadatable for i_rObject. */ 350 ::boost::shared_ptr<MetadatableClipboard> CreateClipboard( 351 const bool i_isInContent); 352 353 struct XmlIdRegistry_Impl; 354 ::std::auto_ptr<XmlIdRegistry_Impl> m_pImpl; 355 }; 356 357 358 //============================================================================= 359 // XmlIdRegistry 360 361 ::sfx2::IXmlIdRegistry * createXmlIdRegistry(const bool i_DocIsClipboard) 362 { 363 return i_DocIsClipboard 364 ? static_cast<XmlIdRegistry*>( new XmlIdRegistryClipboard ) 365 : static_cast<XmlIdRegistry*>( new XmlIdRegistryDocument ); 366 } 367 368 XmlIdRegistry::XmlIdRegistry() 369 { 370 } 371 372 XmlIdRegistry::~XmlIdRegistry() 373 { 374 } 375 376 ::com::sun::star::uno::Reference< ::com::sun::star::rdf::XMetadatable > SAL_CALL 377 XmlIdRegistry::GetElementByMetadataReference( 378 const beans::StringPair & i_rReference) const 379 { 380 Metadatable* pObject( LookupElement(i_rReference.First, 381 i_rReference.Second) ); 382 return pObject ? pObject->MakeUnoObject() : 0; 383 } 384 385 beans::StringPair 386 XmlIdRegistry::GetXmlIdForElement(const Metadatable& i_rObject) const 387 { 388 ::rtl::OUString path; 389 ::rtl::OUString idref; 390 if (LookupXmlId(i_rObject, path, idref)) 391 { 392 if (LookupElement(path, idref) == &i_rObject) 393 { 394 return beans::StringPair(path, idref); 395 } 396 } 397 return beans::StringPair(); 398 } 399 400 401 /// generate unique xml:id 402 template< typename T > 403 /*static*/ ::rtl::OUString create_id(const 404 ::std::hash_map< ::rtl::OUString, T, ::rtl::OUStringHash > & i_rXmlIdMap) 405 { 406 static rtlRandomPool s_Pool( rtl_random_createPool() ); 407 const ::rtl::OUString prefix( ::rtl::OUString::createFromAscii(s_prefix) ); 408 typename ::std::hash_map< ::rtl::OUString, T, ::rtl::OUStringHash > 409 ::const_iterator iter; 410 ::rtl::OUString id; 411 do 412 { 413 sal_Int32 n; 414 rtl_random_getBytes(s_Pool, & n, sizeof(n)); 415 id = prefix + ::rtl::OUString::valueOf(static_cast<sal_Int32>(abs(n))); 416 iter = i_rXmlIdMap.find(id); 417 } 418 while (iter != i_rXmlIdMap.end()); 419 return id; 420 } 421 422 //============================================================================= 423 // Document XML ID Registry (_Impl) 424 425 /// element list 426 typedef ::std::list< Metadatable* > XmlIdList_t; 427 428 /// Idref -> (content.xml element list, styles.xml element list) 429 typedef ::std::hash_map< ::rtl::OUString, 430 ::std::pair< XmlIdList_t, XmlIdList_t >, ::rtl::OUStringHash > XmlIdMap_t; 431 432 /// pointer hash template 433 template<typename T> struct PtrHash 434 { 435 size_t operator() (T const * i_pT) const 436 { 437 return reinterpret_cast<size_t>(i_pT); 438 } 439 }; 440 441 /// element -> (stream name, idref) 442 typedef ::std::hash_map< const Metadatable*, 443 ::std::pair< ::rtl::OUString, ::rtl::OUString>, PtrHash<Metadatable> > 444 XmlIdReverseMap_t; 445 446 struct XmlIdRegistryDocument::XmlIdRegistry_Impl 447 { 448 XmlIdRegistry_Impl() 449 : m_XmlIdMap(), m_XmlIdReverseMap() { } 450 451 bool TryInsertMetadatable(Metadatable& i_xObject, 452 const ::rtl::OUString & i_rStream, const ::rtl::OUString & i_rIdref); 453 454 bool LookupXmlId(const Metadatable& i_xObject, 455 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const; 456 457 Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName, 458 const ::rtl::OUString & i_rIdref) const; 459 460 const XmlIdList_t * LookupElementList( 461 const ::rtl::OUString & i_rStreamName, 462 const ::rtl::OUString & i_rIdref) const; 463 464 XmlIdList_t * LookupElementList( 465 const ::rtl::OUString & i_rStreamName, 466 const ::rtl::OUString & i_rIdref) 467 { 468 return const_cast<XmlIdList_t*>( 469 const_cast<const XmlIdRegistry_Impl*>(this) 470 ->LookupElementList(i_rStreamName, i_rIdref)); 471 } 472 473 XmlIdMap_t m_XmlIdMap; 474 XmlIdReverseMap_t m_XmlIdReverseMap; 475 }; 476 477 // ------------------------------------------------------------------- 478 479 static void 480 rmIter(XmlIdMap_t & i_rXmlIdMap, XmlIdMap_t::iterator const& i_rIter, 481 ::rtl::OUString const & i_rStream, Metadatable const& i_rObject) 482 { 483 if (i_rIter != i_rXmlIdMap.end()) 484 { 485 XmlIdList_t & rList( isContentFile(i_rStream) 486 ? i_rIter->second.first : i_rIter->second.second ); 487 rList.remove(&const_cast<Metadatable&>(i_rObject)); 488 if (i_rIter->second.first.empty() && i_rIter->second.second.empty()) 489 { 490 i_rXmlIdMap.erase(i_rIter); 491 } 492 } 493 } 494 495 // ------------------------------------------------------------------- 496 497 const XmlIdList_t * 498 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElementList( 499 const ::rtl::OUString & i_rStreamName, 500 const ::rtl::OUString & i_rIdref) const 501 { 502 const XmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) ); 503 if (iter != m_XmlIdMap.end()) 504 { 505 OSL_ENSURE(!iter->second.first.empty() || !iter->second.second.empty(), 506 "null entry in m_XmlIdMap"); 507 return (isContentFile(i_rStreamName)) 508 ? &iter->second.first 509 : &iter->second.second; 510 } 511 else 512 { 513 return 0; 514 } 515 } 516 517 Metadatable* 518 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElement( 519 const ::rtl::OUString & i_rStreamName, 520 const ::rtl::OUString & i_rIdref) const 521 { 522 if (!isValidXmlId(i_rStreamName, i_rIdref)) 523 { 524 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 525 "illegal XmlId"), 0, 0); 526 } 527 528 const XmlIdList_t * pList( LookupElementList(i_rStreamName, i_rIdref) ); 529 if (pList) 530 { 531 const XmlIdList_t::const_iterator iter( 532 ::std::find_if(pList->begin(), pList->end(), 533 ::boost::bind( 534 ::std::logical_not<bool>(), 535 ::boost::bind( 536 ::std::logical_or<bool>(), 537 ::boost::bind( &Metadatable::IsInUndo, _1 ), 538 ::boost::bind( &Metadatable::IsInClipboard, _1 ) 539 ) ) ) ); 540 if (iter != pList->end()) 541 { 542 return *iter; 543 } 544 } 545 return 0; 546 } 547 548 bool 549 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupXmlId( 550 const Metadatable& i_rObject, 551 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const 552 { 553 const XmlIdReverseMap_t::const_iterator iter( 554 m_XmlIdReverseMap.find(&i_rObject) ); 555 if (iter != m_XmlIdReverseMap.end()) 556 { 557 OSL_ENSURE(!iter->second.first.equalsAscii(""), 558 "null stream in m_XmlIdReverseMap"); 559 OSL_ENSURE(!iter->second.second.equalsAscii(""), 560 "null id in m_XmlIdReverseMap"); 561 o_rStream = iter->second.first; 562 o_rIdref = iter->second.second; 563 return true; 564 } 565 else 566 { 567 return false; 568 } 569 } 570 571 bool 572 XmlIdRegistryDocument::XmlIdRegistry_Impl::TryInsertMetadatable( 573 Metadatable & i_rObject, 574 const ::rtl::OUString & i_rStreamName, const ::rtl::OUString & i_rIdref) 575 { 576 const bool bContent( isContentFile(i_rStreamName) ); 577 OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName), 578 "invalid stream"); 579 580 XmlIdList_t * pList( LookupElementList(i_rStreamName, i_rIdref) ); 581 if (pList) 582 { 583 if (pList->empty()) 584 { 585 pList->push_back( &i_rObject ); 586 return true; 587 } 588 else 589 { 590 // this is only called from TryRegister now, so check 591 // if all elements in the list are deleted (in undo) or 592 // placeholders, then "steal" the id from them 593 if ( pList->end() == ::std::find_if(pList->begin(), pList->end(), 594 ::boost::bind( 595 ::std::logical_not<bool>(), 596 ::boost::bind( 597 ::std::logical_or<bool>(), 598 ::boost::bind( &Metadatable::IsInUndo, _1 ), 599 ::boost::bind( &Metadatable::IsInClipboard, _1 ) 600 ) ) ) ) 601 { 602 // ??? this is not undoable 603 // pList->clear(); 604 // pList->push_back( &i_rObject ); 605 pList->push_front( &i_rObject ); 606 return true; 607 } 608 else 609 { 610 return false; 611 } 612 } 613 } 614 else 615 { 616 m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent 617 ? ::std::make_pair( XmlIdList_t( 1, &i_rObject ), XmlIdList_t() ) 618 : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject ) ))); 619 return true; 620 } 621 } 622 623 //============================================================================= 624 // Document XML ID Registry 625 626 627 XmlIdRegistryDocument::XmlIdRegistryDocument() 628 : m_pImpl( new XmlIdRegistry_Impl ) 629 { 630 } 631 632 static void 633 removeLink(Metadatable* i_pObject) 634 { 635 OSL_ENSURE(i_pObject, "null in list ???"); 636 if (!i_pObject) return; 637 if (i_pObject->IsInClipboard()) 638 { 639 MetadatableClipboard* pLink( 640 dynamic_cast<MetadatableClipboard*>( i_pObject ) ); 641 OSL_ENSURE(pLink, "IsInClipboard, but no MetadatableClipboard ?"); 642 if (pLink) 643 { 644 pLink->OriginNoLongerInBusinessAnymore(); 645 } 646 } 647 } 648 649 XmlIdRegistryDocument::~XmlIdRegistryDocument() 650 { 651 // notify all list elements that are actually in the clipboard 652 for (XmlIdMap_t::iterator iter(m_pImpl->m_XmlIdMap.begin()); 653 iter != m_pImpl->m_XmlIdMap.end(); ++iter) 654 { 655 ::std::for_each(iter->second.first.begin(), iter->second.first.end(), 656 removeLink); 657 ::std::for_each(iter->second.second.begin(), iter->second.second.end(), 658 removeLink); 659 } 660 } 661 662 bool 663 XmlIdRegistryDocument::LookupXmlId( 664 const Metadatable& i_rObject, 665 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const 666 { 667 return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref); 668 } 669 670 Metadatable* 671 XmlIdRegistryDocument::LookupElement( 672 const ::rtl::OUString & i_rStreamName, 673 const ::rtl::OUString & i_rIdref) const 674 { 675 return m_pImpl->LookupElement(i_rStreamName, i_rIdref); 676 } 677 678 bool 679 XmlIdRegistryDocument::TryRegisterMetadatable(Metadatable & i_rObject, 680 ::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref) 681 { 682 OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject, 683 ::rtl::OUStringToOString(i_rStreamName, RTL_TEXTENCODING_UTF8).getStr(), 684 ::rtl::OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8).getStr()); 685 686 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject), 687 "TryRegisterMetadatable called for MetadatableUndo?"); 688 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject), 689 "TryRegisterMetadatable called for MetadatableClipboard?"); 690 691 if (!isValidXmlId(i_rStreamName, i_rIdref)) 692 { 693 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 694 "illegal XmlId"), 0, 0); 695 } 696 if (i_rObject.IsInContent() 697 ? !isContentFile(i_rStreamName) 698 : !isStylesFile(i_rStreamName)) 699 { 700 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 701 "illegal XmlId: wrong stream"), 0, 0); 702 } 703 704 ::rtl::OUString old_path; 705 ::rtl::OUString old_idref; 706 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref); 707 if (old_path == i_rStreamName && old_idref == i_rIdref) 708 { 709 return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject); 710 } 711 XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() ); 712 if (!old_idref.equalsAscii("")) 713 { 714 old_id = m_pImpl->m_XmlIdMap.find(old_idref); 715 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found"); 716 } 717 if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref)) 718 { 719 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject); 720 m_pImpl->m_XmlIdReverseMap[&i_rObject] = 721 ::std::make_pair(i_rStreamName, i_rIdref); 722 return true; 723 } 724 else 725 { 726 return false; 727 } 728 } 729 730 void 731 XmlIdRegistryDocument::RegisterMetadatableAndCreateID(Metadatable & i_rObject) 732 { 733 OSL_TRACE("RegisterMetadatableAndCreateID: %p\n", &i_rObject); 734 735 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject), 736 "RegisterMetadatableAndCreateID called for MetadatableUndo?"); 737 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject), 738 "RegisterMetadatableAndCreateID called for MetadatableClipboard?"); 739 740 const bool isInContent( i_rObject.IsInContent() ); 741 const ::rtl::OUString stream( ::rtl::OUString::createFromAscii( 742 isInContent ? s_content : s_styles ) ); 743 // check if we have a latent xmlid, and if yes, remove it 744 ::rtl::OUString old_path; 745 ::rtl::OUString old_idref; 746 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref); 747 748 XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() ); 749 if (!old_idref.equalsAscii("")) 750 { 751 old_id = m_pImpl->m_XmlIdMap.find(old_idref); 752 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found"); 753 if (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject) 754 { 755 return; 756 } 757 else 758 { 759 // remove latent xmlid 760 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject); 761 } 762 } 763 764 // create id 765 const ::rtl::OUString id( create_id(m_pImpl->m_XmlIdMap) ); 766 OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(), 767 "created id is in use"); 768 m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent 769 ? ::std::make_pair( XmlIdList_t( 1, &i_rObject ), XmlIdList_t() ) 770 : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject ) ))); 771 m_pImpl->m_XmlIdReverseMap[&i_rObject] = ::std::make_pair(stream, id); 772 } 773 774 void XmlIdRegistryDocument::UnregisterMetadatable(const Metadatable& i_rObject) 775 { 776 OSL_TRACE("UnregisterMetadatable: %p\n", &i_rObject); 777 778 ::rtl::OUString path; 779 ::rtl::OUString idref; 780 if (!m_pImpl->LookupXmlId(i_rObject, path, idref)) 781 { 782 OSL_ENSURE(false, "unregister: no xml id?"); 783 return; 784 } 785 const XmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) ); 786 if (iter != m_pImpl->m_XmlIdMap.end()) 787 { 788 rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject); 789 } 790 } 791 792 void XmlIdRegistryDocument::RemoveXmlIdForElement(const Metadatable& i_rObject) 793 { 794 OSL_TRACE("RemoveXmlIdForElement: %p\n", &i_rObject); 795 796 const XmlIdReverseMap_t::iterator iter( 797 m_pImpl->m_XmlIdReverseMap.find(&i_rObject) ); 798 if (iter != m_pImpl->m_XmlIdReverseMap.end()) 799 { 800 OSL_ENSURE(!iter->second.second.equalsAscii(""), 801 "null id in m_XmlIdReverseMap"); 802 m_pImpl->m_XmlIdReverseMap.erase(iter); 803 } 804 } 805 806 // ------------------------------------------------------------------- 807 808 void XmlIdRegistryDocument::RegisterCopy(Metadatable const& i_rSource, 809 Metadatable & i_rCopy, const bool i_bCopyPrecedesSource) 810 { 811 OSL_TRACE("RegisterCopy: %p -> %p (%d)\n", 812 &i_rSource, &i_rCopy, i_bCopyPrecedesSource); 813 814 // potential sources: clipboard, undo array, splitNode 815 // assumption: stream change can only happen via clipboard, and is handled 816 // by Metadatable::RegisterAsCopyOf 817 OSL_ENSURE(i_rSource.IsInUndo() || i_rCopy.IsInUndo() || 818 (i_rSource.IsInContent() == i_rCopy.IsInContent()), 819 "RegisterCopy: not in same stream?"); 820 821 ::rtl::OUString path; 822 ::rtl::OUString idref; 823 if (!m_pImpl->LookupXmlId( i_rSource, path, idref )) 824 { 825 OSL_ENSURE(false, "no xml id?"); 826 return; 827 } 828 XmlIdList_t * pList ( m_pImpl->LookupElementList(path, idref) ); 829 OSL_ENSURE( ::std::find( pList->begin(), pList->end(), &i_rCopy ) 830 == pList->end(), "copy already registered???"); 831 XmlIdList_t::iterator srcpos( 832 ::std::find( pList->begin(), pList->end(), &i_rSource ) ); 833 OSL_ENSURE(srcpos != pList->end(), "source not in list???"); 834 if (srcpos == pList->end()) 835 { 836 return; 837 } 838 if (i_bCopyPrecedesSource) 839 { 840 pList->insert( srcpos, &i_rCopy ); 841 } 842 else 843 { 844 // for undo push_back does not work! must insert right after source 845 pList->insert( ++srcpos, &i_rCopy ); 846 } 847 m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy, 848 ::std::make_pair(path, idref))); 849 } 850 851 ::boost::shared_ptr<MetadatableUndo> 852 XmlIdRegistryDocument::CreateUndo(Metadatable const& i_rObject) 853 { 854 OSL_TRACE("CreateUndo: %p\n", &i_rObject); 855 856 return ::boost::shared_ptr<MetadatableUndo>( 857 new MetadatableUndo(i_rObject.IsInContent()) ); 858 } 859 860 /* 861 i_rMerged is both a source and the target node of the merge 862 i_rOther is the other source, and will be deleted after the merge 863 864 dimensions: none|latent|actual empty|nonempty 865 i_rMerged(1) i_rOther(2) result 866 *|empty *|empty => 1|2 (arbitrary) 867 *|empty *|nonempty => 2 868 *|nonempty *|empty => 1 869 none|nonempty none|nonempty => none 870 none|nonempty latent|nonempty => 2 871 latent|nonempty none|nonempty => 1 872 latent|nonempty latent|nonempty => 1|2 873 *|nonempty actual|nonempty => 2 874 actual|nonempty *|nonempty => 1 875 actual|nonempty actual|nonempty => 1|2 876 */ 877 void 878 XmlIdRegistryDocument::JoinMetadatables( 879 Metadatable & i_rMerged, Metadatable const & i_rOther) 880 { 881 OSL_TRACE("JoinMetadatables: %p <- %p\n", &i_rMerged, &i_rOther); 882 883 bool mergedOwnsRef; 884 ::rtl::OUString path; 885 ::rtl::OUString idref; 886 if (m_pImpl->LookupXmlId(i_rMerged, path, idref)) 887 { 888 mergedOwnsRef = (m_pImpl->LookupElement(path, idref) == &i_rMerged); 889 } 890 else 891 { 892 OSL_ENSURE(false, "JoinMetadatables: no xmlid?"); 893 return; 894 } 895 if (!mergedOwnsRef) 896 { 897 i_rMerged.RemoveMetadataReference(); 898 i_rMerged.RegisterAsCopyOf(i_rOther, true); 899 return; 900 } 901 // other cases: merged has actual ref and is nonempty, 902 // other has latent/actual ref and is nonempty: other loses => nothing to do 903 } 904 905 906 //============================================================================= 907 // Clipboard XML ID Registry (_Impl) 908 909 struct RMapEntry 910 { 911 RMapEntry() : m_pLink() { } 912 RMapEntry(::rtl::OUString const& i_rStream, 913 ::rtl::OUString const& i_rXmlId, 914 ::boost::shared_ptr<MetadatableClipboard> const& i_pLink 915 = ::boost::shared_ptr<MetadatableClipboard>()) 916 : m_Stream(i_rStream), m_XmlId(i_rXmlId), m_pLink(i_pLink) 917 {} 918 ::rtl::OUString m_Stream; 919 ::rtl::OUString m_XmlId; 920 // this would have been an auto_ptr, if only that would have compiled... 921 ::boost::shared_ptr<MetadatableClipboard> m_pLink; 922 }; 923 924 /// element -> (stream name, idref, source) 925 typedef ::std::hash_map< const Metadatable*, 926 struct RMapEntry, 927 PtrHash<Metadatable> > 928 ClipboardXmlIdReverseMap_t; 929 930 /// Idref -> (content.xml element, styles.xml element) 931 typedef ::std::hash_map< ::rtl::OUString, 932 ::std::pair< Metadatable*, Metadatable* >, ::rtl::OUStringHash > 933 ClipboardXmlIdMap_t; 934 935 struct XmlIdRegistryClipboard::XmlIdRegistry_Impl 936 { 937 XmlIdRegistry_Impl() 938 : m_XmlIdMap(), m_XmlIdReverseMap() { } 939 940 bool TryInsertMetadatable(Metadatable& i_xObject, 941 const ::rtl::OUString & i_rStream, const ::rtl::OUString & i_rIdref); 942 943 bool LookupXmlId(const Metadatable& i_xObject, 944 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref, 945 MetadatableClipboard const* &o_rpLink) const; 946 947 Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName, 948 const ::rtl::OUString & i_rIdref) const; 949 950 Metadatable* const* LookupEntry(const ::rtl::OUString & i_rStreamName, 951 const ::rtl::OUString & i_rIdref) const; 952 953 Metadatable* * LookupEntry(const ::rtl::OUString & i_rStreamName, 954 const ::rtl::OUString & i_rIdref) 955 { 956 return const_cast<Metadatable**>( 957 const_cast<const XmlIdRegistry_Impl*>(this) 958 ->LookupEntry(i_rStreamName, i_rIdref)); 959 } 960 961 ClipboardXmlIdMap_t m_XmlIdMap; 962 ClipboardXmlIdReverseMap_t m_XmlIdReverseMap; 963 }; 964 965 // ------------------------------------------------------------------- 966 967 static void 968 rmIter(ClipboardXmlIdMap_t & i_rXmlIdMap, 969 ClipboardXmlIdMap_t::iterator const& i_rIter, 970 ::rtl::OUString const & i_rStream, Metadatable const& i_rObject) 971 { 972 if (i_rIter != i_rXmlIdMap.end()) 973 { 974 Metadatable *& rMeta = isContentFile(i_rStream) 975 ? i_rIter->second.first : i_rIter->second.second; 976 if (rMeta == &i_rObject) 977 { 978 rMeta = 0; 979 } 980 if (!i_rIter->second.first && !i_rIter->second.second) 981 { 982 i_rXmlIdMap.erase(i_rIter); 983 } 984 } 985 } 986 987 // ------------------------------------------------------------------- 988 989 Metadatable* const* 990 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupEntry( 991 const ::rtl::OUString & i_rStreamName, 992 const ::rtl::OUString & i_rIdref) const 993 { 994 if (!isValidXmlId(i_rStreamName, i_rIdref)) 995 { 996 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 997 "illegal XmlId"), 0, 0); 998 } 999 1000 const ClipboardXmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) ); 1001 if (iter != m_XmlIdMap.end()) 1002 { 1003 OSL_ENSURE(iter->second.first || iter->second.second, 1004 "null entry in m_XmlIdMap"); 1005 return (isContentFile(i_rStreamName)) 1006 ? &iter->second.first 1007 : &iter->second.second; 1008 } 1009 else 1010 { 1011 return 0; 1012 } 1013 } 1014 1015 Metadatable* 1016 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupElement( 1017 const ::rtl::OUString & i_rStreamName, 1018 const ::rtl::OUString & i_rIdref) const 1019 { 1020 Metadatable * const * ppEntry = LookupEntry(i_rStreamName, i_rIdref); 1021 return ppEntry ? *ppEntry : 0; 1022 } 1023 1024 bool 1025 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupXmlId( 1026 const Metadatable& i_rObject, 1027 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref, 1028 MetadatableClipboard const* &o_rpLink) const 1029 { 1030 const ClipboardXmlIdReverseMap_t::const_iterator iter( 1031 m_XmlIdReverseMap.find(&i_rObject) ); 1032 if (iter != m_XmlIdReverseMap.end()) 1033 { 1034 OSL_ENSURE(!iter->second.m_Stream.equalsAscii(""), 1035 "null stream in m_XmlIdReverseMap"); 1036 OSL_ENSURE(!iter->second.m_XmlId.equalsAscii(""), 1037 "null id in m_XmlIdReverseMap"); 1038 o_rStream = iter->second.m_Stream; 1039 o_rIdref = iter->second.m_XmlId; 1040 o_rpLink = iter->second.m_pLink.get(); 1041 return true; 1042 } 1043 else 1044 { 1045 return false; 1046 } 1047 } 1048 1049 bool 1050 XmlIdRegistryClipboard::XmlIdRegistry_Impl::TryInsertMetadatable( 1051 Metadatable & i_rObject, 1052 const ::rtl::OUString & i_rStreamName, const ::rtl::OUString & i_rIdref) 1053 { 1054 bool bContent( isContentFile(i_rStreamName) ); 1055 OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName), 1056 "invalid stream"); 1057 1058 //wntmsci12 won't parse this: 1059 // Metadatable ** ppEntry( LookupEntry(i_rStreamName, i_rIdref) ); 1060 Metadatable ** ppEntry = LookupEntry(i_rStreamName, i_rIdref); 1061 if (ppEntry) 1062 { 1063 if (*ppEntry) 1064 { 1065 return false; 1066 } 1067 else 1068 { 1069 *ppEntry = &i_rObject; 1070 return true; 1071 } 1072 } 1073 else 1074 { 1075 m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent 1076 ? ::std::make_pair( &i_rObject, static_cast<Metadatable*>(0) ) 1077 : ::std::make_pair( static_cast<Metadatable*>(0), &i_rObject ))); 1078 return true; 1079 } 1080 } 1081 1082 //============================================================================= 1083 // Clipboard XML ID Registry 1084 1085 1086 XmlIdRegistryClipboard::XmlIdRegistryClipboard() 1087 : m_pImpl( new XmlIdRegistry_Impl ) 1088 { 1089 } 1090 1091 XmlIdRegistryClipboard::~XmlIdRegistryClipboard() 1092 { 1093 } 1094 1095 bool 1096 XmlIdRegistryClipboard::LookupXmlId( 1097 const Metadatable& i_rObject, 1098 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const 1099 { 1100 const MetadatableClipboard * pLink; 1101 return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref, pLink); 1102 } 1103 1104 Metadatable* 1105 XmlIdRegistryClipboard::LookupElement( 1106 const ::rtl::OUString & i_rStreamName, 1107 const ::rtl::OUString & i_rIdref) const 1108 { 1109 return m_pImpl->LookupElement(i_rStreamName, i_rIdref); 1110 } 1111 1112 bool 1113 XmlIdRegistryClipboard::TryRegisterMetadatable(Metadatable & i_rObject, 1114 ::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref) 1115 { 1116 OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject, 1117 ::rtl::OUStringToOString(i_rStreamName, RTL_TEXTENCODING_UTF8).getStr(), 1118 ::rtl::OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8).getStr()); 1119 1120 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject), 1121 "TryRegisterMetadatable called for MetadatableUndo?"); 1122 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject), 1123 "TryRegisterMetadatable called for MetadatableClipboard?"); 1124 1125 if (!isValidXmlId(i_rStreamName, i_rIdref)) 1126 { 1127 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 1128 "illegal XmlId"), 0, 0); 1129 } 1130 if (i_rObject.IsInContent() 1131 ? !isContentFile(i_rStreamName) 1132 : !isStylesFile(i_rStreamName)) 1133 { 1134 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 1135 "illegal XmlId: wrong stream"), 0, 0); 1136 } 1137 1138 ::rtl::OUString old_path; 1139 ::rtl::OUString old_idref; 1140 const MetadatableClipboard * pLink; 1141 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref, pLink); 1142 if (old_path == i_rStreamName && old_idref == i_rIdref) 1143 { 1144 return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject); 1145 } 1146 ClipboardXmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() ); 1147 if (!old_idref.equalsAscii("")) 1148 { 1149 old_id = m_pImpl->m_XmlIdMap.find(old_idref); 1150 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found"); 1151 } 1152 if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref)) 1153 { 1154 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject); 1155 m_pImpl->m_XmlIdReverseMap[&i_rObject] = 1156 RMapEntry(i_rStreamName, i_rIdref); 1157 return true; 1158 } 1159 else 1160 { 1161 return false; 1162 } 1163 } 1164 1165 void 1166 XmlIdRegistryClipboard::RegisterMetadatableAndCreateID(Metadatable & i_rObject) 1167 { 1168 OSL_TRACE("RegisterMetadatableAndCreateID: %p\n", &i_rObject); 1169 1170 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject), 1171 "RegisterMetadatableAndCreateID called for MetadatableUndo?"); 1172 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject), 1173 "RegisterMetadatableAndCreateID called for MetadatableClipboard?"); 1174 1175 bool isInContent( i_rObject.IsInContent() ); 1176 ::rtl::OUString stream( ::rtl::OUString::createFromAscii( 1177 isInContent ? s_content : s_styles ) ); 1178 1179 ::rtl::OUString old_path; 1180 ::rtl::OUString old_idref; 1181 LookupXmlId(i_rObject, old_path, old_idref); 1182 if (!old_idref.equalsAscii("") && 1183 (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject)) 1184 { 1185 return; 1186 } 1187 1188 // create id 1189 const ::rtl::OUString id( create_id(m_pImpl->m_XmlIdMap) ); 1190 OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(), 1191 "created id is in use"); 1192 m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent 1193 ? ::std::make_pair( &i_rObject, static_cast<Metadatable*>(0) ) 1194 : ::std::make_pair( static_cast<Metadatable*>(0), &i_rObject ))); 1195 // N.B.: if i_rObject had a latent XmlId, then we implicitly delete the 1196 // MetadatableClipboard and thus the latent XmlId here 1197 m_pImpl->m_XmlIdReverseMap[&i_rObject] = RMapEntry(stream, id); 1198 } 1199 1200 void XmlIdRegistryClipboard::UnregisterMetadatable(const Metadatable& i_rObject) 1201 { 1202 OSL_TRACE("UnregisterMetadatable: %p\n", &i_rObject); 1203 1204 ::rtl::OUString path; 1205 ::rtl::OUString idref; 1206 const MetadatableClipboard * pLink; 1207 if (!m_pImpl->LookupXmlId(i_rObject, path, idref, pLink)) 1208 { 1209 OSL_ENSURE(false, "unregister: no xml id?"); 1210 return; 1211 } 1212 const ClipboardXmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) ); 1213 if (iter != m_pImpl->m_XmlIdMap.end()) 1214 { 1215 rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject); 1216 } 1217 } 1218 1219 1220 void XmlIdRegistryClipboard::RemoveXmlIdForElement(const Metadatable& i_rObject) 1221 { 1222 OSL_TRACE("RemoveXmlIdForElement: %p\n", &i_rObject); 1223 1224 ClipboardXmlIdReverseMap_t::iterator iter( 1225 m_pImpl->m_XmlIdReverseMap.find(&i_rObject) ); 1226 if (iter != m_pImpl->m_XmlIdReverseMap.end()) 1227 { 1228 OSL_ENSURE(!iter->second.m_XmlId.equalsAscii(""), 1229 "null id in m_XmlIdReverseMap"); 1230 m_pImpl->m_XmlIdReverseMap.erase(iter); 1231 } 1232 } 1233 1234 // ------------------------------------------------------------------- 1235 1236 ::boost::shared_ptr<MetadatableClipboard> 1237 XmlIdRegistryClipboard::CreateClipboard(const bool i_isInContent) 1238 { 1239 OSL_TRACE("CreateClipboard: \n"); 1240 1241 return ::boost::shared_ptr<MetadatableClipboard>( 1242 new MetadatableClipboard(i_isInContent) ); 1243 } 1244 1245 MetadatableClipboard & 1246 XmlIdRegistryClipboard::RegisterCopyClipboard(Metadatable & i_rCopy, 1247 beans::StringPair const & i_rReference, 1248 const bool i_isLatent) 1249 { 1250 OSL_TRACE("RegisterCopyClipboard: %p -> "/*"%p"*/"(%s#%s) (%d)\n", 1251 /*&i_rSource,*/ &i_rCopy, 1252 ::rtl::OUStringToOString(i_rReference.First, 1253 RTL_TEXTENCODING_UTF8).getStr(), 1254 ::rtl::OUStringToOString(i_rReference.Second, 1255 RTL_TEXTENCODING_UTF8).getStr(), 1256 i_isLatent); 1257 1258 // N.B.: when copying to the clipboard, the selection is always inserted 1259 // into the body, even if the source is a header/footer! 1260 // so we do not check whether the stream is right in this function 1261 1262 if (!isValidXmlId(i_rReference.First, i_rReference.Second)) 1263 { 1264 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii( 1265 "illegal XmlId"), 0, 0); 1266 } 1267 1268 if (!i_isLatent) 1269 { 1270 // this should succeed assuming clipboard has a single source document 1271 const bool success( m_pImpl->TryInsertMetadatable(i_rCopy, 1272 i_rReference.First, i_rReference.Second) ); 1273 OSL_ENSURE(success, "RegisterCopyClipboard: TryInsert failed?"); 1274 (void) success; 1275 } 1276 const ::boost::shared_ptr<MetadatableClipboard> pLink( 1277 CreateClipboard( isContentFile(i_rReference.First)) ); 1278 m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy, 1279 RMapEntry(i_rReference.First, i_rReference.Second, pLink))); 1280 return *pLink.get(); 1281 } 1282 1283 MetadatableClipboard const* 1284 XmlIdRegistryClipboard::SourceLink(Metadatable const& i_rObject) 1285 { 1286 ::rtl::OUString path; 1287 ::rtl::OUString idref; 1288 const MetadatableClipboard * pLink( 0 ); 1289 m_pImpl->LookupXmlId(i_rObject, path, idref, pLink); 1290 return pLink; 1291 } 1292 1293 1294 //============================================================================= 1295 // Metadatable mixin 1296 1297 1298 Metadatable::~Metadatable() 1299 { 1300 RemoveMetadataReference(); 1301 } 1302 1303 void Metadatable::RemoveMetadataReference() 1304 { 1305 try 1306 { 1307 if (m_pReg) 1308 { 1309 m_pReg->UnregisterMetadatable( *this ); 1310 m_pReg->RemoveXmlIdForElement( *this ); 1311 m_pReg = 0; 1312 } 1313 } 1314 catch (uno::Exception &) 1315 { 1316 OSL_ENSURE(false, "Metadatable::RemoveMetadataReference: exception"); 1317 } 1318 } 1319 1320 // ::com::sun::star::rdf::XMetadatable: 1321 beans::StringPair 1322 Metadatable::GetMetadataReference() const 1323 { 1324 if (m_pReg) 1325 { 1326 return m_pReg->GetXmlIdForElement(*this); 1327 } 1328 return beans::StringPair(); 1329 } 1330 1331 void 1332 Metadatable::SetMetadataReference( 1333 const ::com::sun::star::beans::StringPair & i_rReference) 1334 { 1335 if (i_rReference.Second.equalsAscii("")) 1336 { 1337 RemoveMetadataReference(); 1338 } 1339 else 1340 { 1341 ::rtl::OUString streamName( i_rReference.First ); 1342 if (streamName.equalsAscii("")) 1343 { 1344 // handle empty stream name as auto-detect. 1345 // necessary for importing flat file format. 1346 streamName = ::rtl::OUString::createFromAscii( 1347 IsInContent() ? s_content : s_styles ); 1348 } 1349 XmlIdRegistry & rReg( dynamic_cast<XmlIdRegistry&>( GetRegistry() ) ); 1350 if (rReg.TryRegisterMetadatable(*this, streamName, i_rReference.Second)) 1351 { 1352 m_pReg = &rReg; 1353 } 1354 else 1355 { 1356 throw lang::IllegalArgumentException( 1357 ::rtl::OUString::createFromAscii("Metadatable::" 1358 "SetMetadataReference: argument is invalid"), /*this*/0, 0); 1359 } 1360 } 1361 } 1362 1363 void Metadatable::EnsureMetadataReference() 1364 { 1365 XmlIdRegistry& rReg( 1366 m_pReg ? *m_pReg : dynamic_cast<XmlIdRegistry&>( GetRegistry() ) ); 1367 rReg.RegisterMetadatableAndCreateID( *this ); 1368 m_pReg = &rReg; 1369 } 1370 1371 const ::sfx2::IXmlIdRegistry& GetRegistryConst(Metadatable const& i_rObject) 1372 { 1373 return const_cast< Metadatable& >( i_rObject ).GetRegistry(); 1374 } 1375 1376 void 1377 Metadatable::RegisterAsCopyOf(Metadatable const & i_rSource, 1378 const bool i_bCopyPrecedesSource) 1379 { 1380 OSL_ENSURE(typeid(*this) == typeid(i_rSource) 1381 || typeid(i_rSource) == typeid(MetadatableUndo) 1382 || typeid(*this) == typeid(MetadatableUndo) 1383 || typeid(i_rSource) == typeid(MetadatableClipboard) 1384 || typeid(*this) == typeid(MetadatableClipboard), 1385 "RegisterAsCopyOf element with different class?"); 1386 OSL_ENSURE(!this->m_pReg, "RegisterAsCopyOf called on element with XmlId?"); 1387 1388 if (this->m_pReg) 1389 { 1390 RemoveMetadataReference(); 1391 } 1392 1393 try 1394 { 1395 if (i_rSource.m_pReg) 1396 { 1397 XmlIdRegistry & rReg( 1398 dynamic_cast<XmlIdRegistry&>( GetRegistry() ) ); 1399 if (i_rSource.m_pReg == &rReg) 1400 { 1401 OSL_ENSURE(!IsInClipboard(), 1402 "RegisterAsCopy: both in clipboard?"); 1403 if (!IsInClipboard()) 1404 { 1405 XmlIdRegistryDocument & rRegDoc( 1406 dynamic_cast<XmlIdRegistryDocument&>( rReg ) ); 1407 rRegDoc.RegisterCopy(i_rSource, *this, 1408 i_bCopyPrecedesSource); 1409 this->m_pReg = &rRegDoc; 1410 } 1411 return; 1412 } 1413 // source is in different document 1414 XmlIdRegistryDocument * pRegDoc( 1415 dynamic_cast<XmlIdRegistryDocument *>(&rReg) ); 1416 XmlIdRegistryClipboard * pRegClp( 1417 dynamic_cast<XmlIdRegistryClipboard*>(&rReg) ); 1418 1419 if (pRegClp) 1420 { 1421 beans::StringPair SourceRef( 1422 i_rSource.m_pReg->GetXmlIdForElement(i_rSource) ); 1423 bool isLatent( SourceRef.Second.equalsAscii("") ); 1424 XmlIdRegistryDocument * pSourceRegDoc( 1425 dynamic_cast<XmlIdRegistryDocument*>(i_rSource.m_pReg) ); 1426 OSL_ENSURE(pSourceRegDoc, "RegisterAsCopyOf: 2 clipboards?"); 1427 if (!pSourceRegDoc) return; 1428 // this is a copy _to_ the clipboard 1429 if (isLatent) 1430 { 1431 pSourceRegDoc->LookupXmlId(i_rSource, 1432 SourceRef.First, SourceRef.Second); 1433 } 1434 Metadatable & rLink( 1435 pRegClp->RegisterCopyClipboard(*this, SourceRef, isLatent)); 1436 this->m_pReg = pRegClp; 1437 // register as copy in the non-clipboard registry 1438 pSourceRegDoc->RegisterCopy(i_rSource, rLink, 1439 false); // i_bCopyPrecedesSource); 1440 rLink.m_pReg = pSourceRegDoc; 1441 } 1442 else if (pRegDoc) 1443 { 1444 XmlIdRegistryClipboard * pSourceRegClp( 1445 dynamic_cast<XmlIdRegistryClipboard*>(i_rSource.m_pReg) ); 1446 OSL_ENSURE(pSourceRegClp, 1447 "RegisterAsCopyOf: 2 non-clipboards?"); 1448 if (!pSourceRegClp) return; 1449 const MetadatableClipboard * pLink( 1450 pSourceRegClp->SourceLink(i_rSource) ); 1451 // may happen if src got its id via UNO call 1452 if (!pLink) return; 1453 // only register copy if clipboard content is from this SwDoc! 1454 if (pLink && (&GetRegistryConst(*pLink) == pRegDoc)) 1455 { 1456 // this is a copy _from_ the clipboard; check if the 1457 // element is still in the same stream 1458 // N.B.: we check the stream of pLink, not of i_rSource! 1459 bool srcInContent( pLink->IsInContent() ); 1460 bool tgtInContent( this->IsInContent() ); 1461 if (srcInContent == tgtInContent) 1462 { 1463 pRegDoc->RegisterCopy(*pLink, *this, 1464 true); // i_bCopyPrecedesSource); 1465 this->m_pReg = pRegDoc; 1466 } 1467 // otherwise: stream change! do not register! 1468 } 1469 } 1470 else 1471 { 1472 OSL_ENSURE(false, "neither RegDoc nor RegClp cannot happen"); 1473 } 1474 #if 0 1475 { 1476 //FIXME: do we need this at all??? 1477 XmlIdRegistryDocument & rRegDoc( 1478 dynamic_cast<XmlIdRegistryDocument&>( rReg ) ); 1479 { 1480 if (rRegDoc.TryRegisterMetadatable(*this, SourceRef)) 1481 { 1482 this->m_pReg = &rRegDoc; 1483 } 1484 } 1485 } 1486 #endif 1487 } 1488 } 1489 catch (uno::Exception &) 1490 { 1491 OSL_ENSURE(false, "Metadatable::RegisterAsCopyOf: exception"); 1492 } 1493 } 1494 1495 ::boost::shared_ptr<MetadatableUndo> Metadatable::CreateUndo() const 1496 { 1497 OSL_ENSURE(!IsInUndo(), "CreateUndo called for object in undo?"); 1498 OSL_ENSURE(!IsInClipboard(), "CreateUndo called for object in clipboard?"); 1499 try 1500 { 1501 if (!IsInClipboard() && !IsInUndo() && m_pReg) 1502 { 1503 XmlIdRegistryDocument * pRegDoc( 1504 dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) ); 1505 ::boost::shared_ptr<MetadatableUndo> pUndo( 1506 pRegDoc->CreateUndo(*this) ); 1507 pRegDoc->RegisterCopy(*this, *pUndo, false); 1508 pUndo->m_pReg = pRegDoc; 1509 return pUndo; 1510 } 1511 } 1512 catch (uno::Exception &) 1513 { 1514 OSL_ENSURE(false, "Metadatable::CreateUndo: exception"); 1515 } 1516 return ::boost::shared_ptr<MetadatableUndo>(); 1517 } 1518 1519 ::boost::shared_ptr<MetadatableUndo> Metadatable::CreateUndoForDelete() 1520 { 1521 ::boost::shared_ptr<MetadatableUndo> const pUndo( CreateUndo() ); 1522 RemoveMetadataReference(); 1523 return pUndo; 1524 } 1525 1526 void Metadatable::RestoreMetadata( 1527 ::boost::shared_ptr<MetadatableUndo> const& i_pUndo) 1528 { 1529 OSL_ENSURE(!IsInUndo(), "RestoreMetadata called for object in undo?"); 1530 OSL_ENSURE(!IsInClipboard(), 1531 "RestoreMetadata called for object in clipboard?"); 1532 if (IsInClipboard() || IsInUndo()) return; 1533 RemoveMetadataReference(); 1534 if (i_pUndo) 1535 { 1536 this->RegisterAsCopyOf(*i_pUndo, true); 1537 } 1538 } 1539 1540 void 1541 Metadatable::JoinMetadatable(Metadatable const & i_rOther, 1542 const bool i_isMergedEmpty, const bool i_isOtherEmpty) 1543 { 1544 OSL_ENSURE(!IsInUndo(), "JoinMetadatables called for object in undo?"); 1545 OSL_ENSURE(!IsInClipboard(), 1546 "JoinMetadatables called for object in clipboard?"); 1547 if (IsInClipboard() || IsInUndo()) return; 1548 1549 if (i_isOtherEmpty && !i_isMergedEmpty) 1550 { 1551 // other is empty, thus loses => nothing to do 1552 return; 1553 } 1554 if (i_isMergedEmpty && !i_isOtherEmpty) 1555 { 1556 this->RemoveMetadataReference(); 1557 this->RegisterAsCopyOf(i_rOther, true); 1558 return; 1559 } 1560 1561 if (!i_rOther.m_pReg) 1562 { 1563 // other doesn't have xmlid, thus loses => nothing to do 1564 return; 1565 } 1566 if (!m_pReg) 1567 { 1568 this->RegisterAsCopyOf(i_rOther, true); 1569 // assumption: i_rOther will be deleted, so don't unregister it here 1570 return; 1571 } 1572 try 1573 { 1574 XmlIdRegistryDocument * pRegDoc( 1575 dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) ); 1576 OSL_ENSURE(pRegDoc, "JoinMetadatable: no pRegDoc?"); 1577 if (pRegDoc) 1578 { 1579 pRegDoc->JoinMetadatables(*this, i_rOther); 1580 } 1581 } 1582 catch (uno::Exception &) 1583 { 1584 OSL_ENSURE(false, "Metadatable::JoinMetadatable: exception"); 1585 } 1586 } 1587 1588 1589 //============================================================================= 1590 // XMetadatable mixin 1591 1592 // ::com::sun::star::rdf::XNode: 1593 ::rtl::OUString SAL_CALL MetadatableMixin::getStringValue() 1594 throw (::com::sun::star::uno::RuntimeException) 1595 { 1596 return getNamespace() + getLocalName(); 1597 } 1598 1599 // ::com::sun::star::rdf::XURI: 1600 ::rtl::OUString SAL_CALL MetadatableMixin::getLocalName() 1601 throw (::com::sun::star::uno::RuntimeException) 1602 { 1603 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 1604 beans::StringPair mdref( getMetadataReference() ); 1605 if (!mdref.Second.getLength()) 1606 { 1607 ensureMetadataReference(); // N.B.: side effect! 1608 mdref = getMetadataReference(); 1609 } 1610 ::rtl::OUStringBuffer buf; 1611 buf.append(mdref.First); 1612 buf.append(static_cast<sal_Unicode>('#')); 1613 buf.append(mdref.Second); 1614 return buf.makeStringAndClear(); 1615 } 1616 1617 ::rtl::OUString SAL_CALL MetadatableMixin::getNamespace() 1618 throw (::com::sun::star::uno::RuntimeException) 1619 { 1620 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 1621 const uno::Reference< frame::XModel > xModel( GetModel() ); 1622 const uno::Reference< rdf::XURI > xDMA( xModel, uno::UNO_QUERY_THROW ); 1623 return xDMA->getStringValue(); 1624 } 1625 1626 // ::com::sun::star::rdf::XMetadatable: 1627 beans::StringPair SAL_CALL 1628 MetadatableMixin::getMetadataReference() 1629 throw (uno::RuntimeException) 1630 { 1631 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 1632 1633 Metadatable *const pObject( GetCoreObject() ); 1634 if (!pObject) 1635 { 1636 throw uno::RuntimeException( 1637 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( 1638 "MetadatableMixin: cannot get core object; not inserted?")), 1639 *this); 1640 } 1641 return pObject->GetMetadataReference(); 1642 } 1643 1644 void SAL_CALL 1645 MetadatableMixin::setMetadataReference( 1646 const beans::StringPair & i_rReference) 1647 throw (uno::RuntimeException, lang::IllegalArgumentException) 1648 { 1649 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 1650 1651 Metadatable *const pObject( GetCoreObject() ); 1652 if (!pObject) 1653 { 1654 throw uno::RuntimeException( 1655 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( 1656 "MetadatableMixin: cannot get core object; not inserted?")), 1657 *this); 1658 } 1659 return pObject->SetMetadataReference(i_rReference); 1660 } 1661 1662 void SAL_CALL MetadatableMixin::ensureMetadataReference() 1663 throw (uno::RuntimeException) 1664 { 1665 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 1666 1667 Metadatable *const pObject( GetCoreObject() ); 1668 if (!pObject) 1669 { 1670 throw uno::RuntimeException( 1671 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( 1672 "MetadatableMixin: cannot get core object; not inserted?")), 1673 *this); 1674 } 1675 return pObject->EnsureMetadataReference(); 1676 } 1677 1678 } // namespace sfx2 1679 1680 1681 //============================================================================= 1682 1683 #if OSL_DEBUG_LEVEL > 1 1684 1685 #include <stdio.h> 1686 1687 static void dump(sfx2::XmlIdList_t * pList) 1688 #ifdef GCC 1689 __attribute__ ((unused)) 1690 #endif 1691 ; 1692 static void dump(sfx2::XmlIdList_t * pList) 1693 { 1694 fprintf(stderr, "\nXmlIdList(%p): ", pList); 1695 for (sfx2::XmlIdList_t::iterator i = pList->begin(); i != pList->end(); ++i) 1696 { 1697 fprintf(stderr, "%p ", *i); 1698 } 1699 fprintf(stderr, "\n"); 1700 } 1701 1702 #endif 1703 1704