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