bump product version to 4.1.6.2
[LibreOffice.git] / sfx2 / source / doc / Metadatable.cxx
blob0a9e4a2c59b51673f047823bd0b6367a61a7642c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <sfx2/Metadatable.hxx>
22 #include <sfx2/XmlIdRegistry.hxx>
24 #include <osl/mutex.hxx>
25 #include <vcl/svapp.hxx> // solarmutex
27 #include <rtl/random.h>
29 #include <boost/bind.hpp>
31 #include <memory>
32 #include <boost/unordered_map.hpp>
33 #include <list>
34 #include <algorithm>
35 #if OSL_DEBUG_LEVEL > 0
36 #include <typeinfo>
37 #endif
40 /** XML ID handling.
42 There is an abstract base class <type>XmlIdRegistry</type>, with
43 2 subclasses <type>XmlIdRegistryDocument</type> for "normal" documents,
44 and <type>XmlIdRegistryClipboard</type> for clipboard documents.
45 These classes are responsible for managing XML IDs for all elements
46 of the model. Only the implementation of the <type>Metadatable</type>
47 base class needs to know the registries, so they are not in the header.
49 The handling of XML IDs differs between clipboard and non-clipboard
50 documents in several aspects. Most importantly, non-clipboard documents
51 can have several elements associated with one XML ID.
52 This is necessary because of the weird undo implementation:
53 deleting a text node moves the deleted node to the undo array, but
54 executing undo will then create a <em>copy</em> of that node in the
55 document array. These 2 nodes must have the same XML ID, because
56 we cannot know whether the user will do a redo next, or something else.
58 Because we need to have a mechanism for several objects per XML ID anyway,
59 we use that also to enable some usability features:
60 The document registry has a list of Metadatables per XML ID.
61 This list is sorted by priority, i.e., the first element has highest
62 priority. When inserting copies, care must be taken that they are inserted
63 at the right position: either before or after the source.
64 This is done by <method>Metadatable::RegisterAsCopyOf</method>.
65 When a text node is split, then both resulting text nodes are inserted
66 into the list. If the user then deletes one text node, the other one
67 will have the XML ID.
68 Also, when a Metadatable is copied to the clipboard and then pasted,
69 the copy is inserted into the list. If the user then deletes the source,
70 the XML ID is not lost.
71 The goal is that it should be hard to lose an XML ID by accident, which
72 is especially important as long as we do not have an UI that displays them.
74 There are two subclasses of <type>Metadatable</type>:
75 <ul><li><type>MetadatableClipboard</type>: for copies in the clipboard</li>
76 <li><type>MetadatableUndo</type>: for undo, because a Metadatable
77 may be destroyed on delete and a new one created on undo.</li></ul>
78 These serve only to track the position in an XML ID list in a document
79 registry, so that future actions can insert objects at the right position.
80 Unfortunately, inserting dummy objects seems to be necessary:
81 <ul><li>it is not sufficent to just remember the saved id, because then
82 the relative priorities might change when executing the undo</li>
83 <li>it is not sufficient to record the position as an integer, because
84 if we delete a text node and then undo, the node will be copied(!),
85 and we will have one more node in the list.<li>
86 <li>it is not sufficient to record the pointer of the previous/next
87 Metadatable, because if we delete a text node, undo, and then
88 do something to clear the redo array, the original text node is
89 destroyed, and is replaced by the copy created by undo</li></ul>
91 If content from a non-clipboard document is copied into a clipboard
92 document, a dummy <type>MetadatableClipboard</type> is inserted into the
93 non-clipboard document registry in order to track the position of the
94 source element. When the clipboard content is pasted back into the source
95 document, this dummy object is used to associate the pasted element with
96 that same XML ID.
98 If a <type>Metadatable</type> is deleted or merged,
99 <method>Metadatable::CreateUndo</method> is called, and returns a
100 <type>MetadatableUndo<type> instance, which can be used to undo the action
101 by passing it to <method>Metadatable::RestoreMetadata</method>.
103 @author mst
107 using namespace ::com::sun::star;
109 using ::sfx2::isValidXmlId;
112 namespace sfx2 {
114 static const char s_content [] = "content.xml";
115 static const char s_styles [] = "styles.xml";
116 static const char s_prefix [] = "id"; // prefix for generated xml:id
118 static bool isContentFile(OUString const & i_rPath)
120 return i_rPath == s_content;
123 static bool isStylesFile (OUString const & i_rPath)
125 return i_rPath == s_styles;
129 //=============================================================================
130 // XML ID handling ---------------------------------------------------
132 /** handles registration of XMetadatable.
134 This class is responsible for guaranteeing that XMetadatable objects
135 always have XML IDs that are unique within a stream.
137 This is an abstract base class; see subclasses XmlIdRegistryDocument and
138 XmlIdRegistryClipboard.
140 @see SwDoc::GetXmlIdRegistry
141 @see SwDocShell::GetXmlIdRegistry
143 class XmlIdRegistry : public sfx2::IXmlIdRegistry
146 public:
147 XmlIdRegistry();
149 virtual ~XmlIdRegistry();
151 /** get the ODF element with the given metadata reference. */
152 virtual ::com::sun::star::uno::Reference<
153 ::com::sun::star::rdf::XMetadatable > SAL_CALL
154 GetElementByMetadataReference(
155 const ::com::sun::star::beans::StringPair & i_rReference) const;
157 /** register an ODF element at a newly generated, unique metadata reference.
160 Find a fresh XML ID, and register it for the element.
161 The generated ID does not occur in any stream of the document.
162 </p>
164 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) = 0;
166 /** try to register an ODF element at a given XML ID, or update its
167 registation to a different XML ID.
170 If the given new metadata reference is not already occupied in the
171 document, unregister the element at its old metadata reference if
172 it has one, and register the new metadata reference for the element.
173 Note that this method only ensures that XML IDs are unique per stream,
174 so using the same XML ID in both content.xml and styles.xml is allowed.
175 </p>
177 @returns
178 true iff the element has successfully been registered
180 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
181 OUString const& i_rStreamName, OUString const& i_rIdref)
182 = 0;
184 /** unregister an ODF element.
187 Unregister the element at its metadata reference.
188 Does not remove the metadata reference from the element.
189 </p>
191 @see RemoveXmlIdForElement
193 virtual void UnregisterMetadatable(Metadatable const&) = 0;
195 /** get the metadata reference for the given element. */
196 ::com::sun::star::beans::StringPair
197 GetXmlIdForElement(Metadatable const&) const;
199 /** remove the metadata reference for the given element. */
200 virtual void RemoveXmlIdForElement(Metadatable const&) = 0;
202 protected:
204 virtual bool LookupXmlId(const Metadatable& i_xObject,
205 OUString & o_rStream, OUString & o_rIdref) const = 0;
207 virtual Metadatable* LookupElement(const OUString & i_rStreamName,
208 const OUString & i_rIdref) const = 0;
211 // XmlIdRegistryDocument ---------------------------------------------
213 /** non-clipboard documents */
214 class XmlIdRegistryDocument : public XmlIdRegistry
217 public:
218 XmlIdRegistryDocument();
220 virtual ~XmlIdRegistryDocument();
222 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject);
224 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
225 OUString const& i_rStreamName, OUString const& i_rIdref);
227 virtual void UnregisterMetadatable(Metadatable const&);
229 virtual void RemoveXmlIdForElement(Metadatable const&);
231 /** register i_rCopy as a copy of i_rSource,
232 with precedence iff i_bCopyPrecedesSource is true */
233 void RegisterCopy(Metadatable const& i_rSource, Metadatable & i_rCopy,
234 const bool i_bCopyPrecedesSource);
236 /** create a Undo Metadatable for i_rObject. */
237 ::boost::shared_ptr<MetadatableUndo> CreateUndo(
238 Metadatable const& i_rObject);
240 /** merge i_rMerged and i_rOther into i_rMerged. */
241 void JoinMetadatables(Metadatable & i_rMerged, Metadatable const& i_rOther);
243 // unfortunately public, Metadatable::RegisterAsCopyOf needs this
244 virtual bool LookupXmlId(const Metadatable& i_xObject,
245 OUString & o_rStream, OUString & o_rIdref) const;
247 private:
249 virtual Metadatable* LookupElement(const OUString & i_rStreamName,
250 const OUString & i_rIdref) const;
252 struct XmlIdRegistry_Impl;
253 ::std::auto_ptr<XmlIdRegistry_Impl> m_pImpl;
256 // MetadatableUndo ---------------------------------------------------
258 /** the horrible Undo Metadatable: is inserted into lists to track position */
259 class MetadatableUndo : public Metadatable
261 /// as determined by the stream of the source in original document
262 const bool m_isInContent;
263 public:
264 MetadatableUndo(const bool i_isInContent)
265 : m_isInContent(i_isInContent) { }
266 virtual ::sfx2::XmlIdRegistry& GetRegistry()
268 // N.B. for Undo, m_pReg is initialized by registering this as copy in
269 // CreateUndo; it is never cleared
270 OSL_ENSURE(m_pReg, "no m_pReg in MetadatableUndo ?");
271 return *m_pReg;
273 virtual bool IsInClipboard() const { return false; }
274 virtual bool IsInUndo() const { return true; }
275 virtual bool IsInContent() const { return m_isInContent; }
276 virtual ::com::sun::star::uno::Reference<
277 ::com::sun::star::rdf::XMetadatable > MakeUnoObject()
278 { OSL_FAIL("MetadatableUndo::MakeUnoObject"); throw; }
281 // MetadatableClipboard ----------------------------------------------
283 /** the horrible Clipboard Metadatable: inserted into lists to track position */
284 class MetadatableClipboard : public Metadatable
286 /// as determined by the stream of the source in original document
287 const bool m_isInContent;
288 public:
289 MetadatableClipboard(const bool i_isInContent)
290 : m_isInContent(i_isInContent) { }
291 virtual ::sfx2::XmlIdRegistry& GetRegistry()
293 // N.B. for Clipboard, m_pReg is initialized by registering this as copy in
294 // RegisterAsCopyOf; it is only cleared by OriginNoLongerInBusinessAnymore
295 OSL_ENSURE(m_pReg, "no m_pReg in MetadatableClipboard ?");
296 return *m_pReg;
298 virtual bool IsInClipboard() const { return true; }
299 virtual bool IsInUndo() const { return false; }
300 virtual bool IsInContent() const { return m_isInContent; }
301 virtual ::com::sun::star::uno::Reference<
302 ::com::sun::star::rdf::XMetadatable > MakeUnoObject()
303 { OSL_FAIL("MetadatableClipboard::MakeUnoObject"); throw; }
304 void OriginNoLongerInBusinessAnymore() { m_pReg = 0; }
307 // XmlIdRegistryClipboard --------------------------------------------
309 class XmlIdRegistryClipboard : public XmlIdRegistry
312 public:
313 XmlIdRegistryClipboard();
314 virtual ~XmlIdRegistryClipboard();
316 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject);
318 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
319 OUString const& i_rStreamName, OUString const& i_rIdref);
321 virtual void UnregisterMetadatable(Metadatable const&);
323 virtual void RemoveXmlIdForElement(Metadatable const&);
325 /** register i_rCopy as a copy of i_rSource */
326 MetadatableClipboard & RegisterCopyClipboard(Metadatable & i_rCopy,
327 beans::StringPair const & i_rReference,
328 const bool i_isLatent);
330 /** get the Metadatable that links i_rObject to its origin registry */
331 MetadatableClipboard const* SourceLink(Metadatable const& i_rObject);
333 private:
334 virtual bool LookupXmlId(const Metadatable& i_xObject,
335 OUString & o_rStream, OUString & o_rIdref) const;
337 virtual Metadatable* LookupElement(const OUString & i_rStreamName,
338 const OUString & i_rIdref) const;
340 /** create a Clipboard Metadatable for i_rObject. */
341 ::boost::shared_ptr<MetadatableClipboard> CreateClipboard(
342 const bool i_isInContent);
344 struct XmlIdRegistry_Impl;
345 ::std::auto_ptr<XmlIdRegistry_Impl> m_pImpl;
349 //=============================================================================
350 // XmlIdRegistry
352 ::sfx2::IXmlIdRegistry * createXmlIdRegistry(const bool i_DocIsClipboard)
354 return i_DocIsClipboard
355 ? static_cast<XmlIdRegistry*>( new XmlIdRegistryClipboard )
356 : static_cast<XmlIdRegistry*>( new XmlIdRegistryDocument );
359 XmlIdRegistry::XmlIdRegistry()
363 XmlIdRegistry::~XmlIdRegistry()
367 ::com::sun::star::uno::Reference< ::com::sun::star::rdf::XMetadatable > SAL_CALL
368 XmlIdRegistry::GetElementByMetadataReference(
369 const beans::StringPair & i_rReference) const
371 Metadatable* pObject( LookupElement(i_rReference.First,
372 i_rReference.Second) );
373 return pObject ? pObject->MakeUnoObject() : 0;
376 beans::StringPair
377 XmlIdRegistry::GetXmlIdForElement(const Metadatable& i_rObject) const
379 OUString path;
380 OUString idref;
381 if (LookupXmlId(i_rObject, path, idref))
383 if (LookupElement(path, idref) == &i_rObject)
385 return beans::StringPair(path, idref);
388 return beans::StringPair();
392 /// generate unique xml:id
393 template< typename T >
394 /*static*/ OUString create_id(const
395 ::boost::unordered_map< OUString, T, OUStringHash > & i_rXmlIdMap)
397 static rtlRandomPool s_Pool( rtl_random_createPool() );
398 const OUString prefix(s_prefix);
399 typename ::boost::unordered_map< OUString, T, OUStringHash >
400 ::const_iterator iter;
401 OUString id;
404 sal_Int32 n;
405 rtl_random_getBytes(s_Pool, & n, sizeof(n));
406 id = prefix + OUString::valueOf(static_cast<sal_Int32>(abs(n)));
407 iter = i_rXmlIdMap.find(id);
409 while (iter != i_rXmlIdMap.end());
410 return id;
413 //=============================================================================
414 // Document XML ID Registry (_Impl)
416 /// element list
417 typedef ::std::list< Metadatable* > XmlIdList_t;
419 /// Idref -> (content.xml element list, styles.xml element list)
420 typedef ::boost::unordered_map< OUString,
421 ::std::pair< XmlIdList_t, XmlIdList_t >, OUStringHash > XmlIdMap_t;
423 /// pointer hash template
424 template<typename T> struct PtrHash
426 size_t operator() (T const * i_pT) const
428 return reinterpret_cast<size_t>(i_pT);
432 /// element -> (stream name, idref)
433 typedef ::boost::unordered_map< const Metadatable*,
434 ::std::pair< OUString, OUString>, PtrHash<Metadatable> >
435 XmlIdReverseMap_t;
437 struct XmlIdRegistryDocument::XmlIdRegistry_Impl
439 XmlIdRegistry_Impl()
440 : m_XmlIdMap(), m_XmlIdReverseMap() { }
442 bool TryInsertMetadatable(Metadatable& i_xObject,
443 const OUString & i_rStream, const OUString & i_rIdref);
445 bool LookupXmlId(const Metadatable& i_xObject,
446 OUString & o_rStream, OUString & o_rIdref) const;
448 Metadatable* LookupElement(const OUString & i_rStreamName,
449 const OUString & i_rIdref) const;
451 const XmlIdList_t * LookupElementList(
452 const OUString & i_rStreamName,
453 const OUString & i_rIdref) const;
455 XmlIdList_t * LookupElementList(
456 const OUString & i_rStreamName,
457 const OUString & i_rIdref)
459 return const_cast<XmlIdList_t*>(
460 const_cast<const XmlIdRegistry_Impl*>(this)
461 ->LookupElementList(i_rStreamName, i_rIdref));
464 XmlIdMap_t m_XmlIdMap;
465 XmlIdReverseMap_t m_XmlIdReverseMap;
468 // -------------------------------------------------------------------
470 static void
471 rmIter(XmlIdMap_t & i_rXmlIdMap, XmlIdMap_t::iterator const& i_rIter,
472 OUString const & i_rStream, Metadatable const& i_rObject)
474 if (i_rIter != i_rXmlIdMap.end())
476 XmlIdList_t & rList( isContentFile(i_rStream)
477 ? i_rIter->second.first : i_rIter->second.second );
478 rList.remove(&const_cast<Metadatable&>(i_rObject));
479 if (i_rIter->second.first.empty() && i_rIter->second.second.empty())
481 i_rXmlIdMap.erase(i_rIter);
486 // -------------------------------------------------------------------
488 const XmlIdList_t *
489 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElementList(
490 const OUString & i_rStreamName,
491 const OUString & i_rIdref) const
493 const XmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
494 if (iter != m_XmlIdMap.end())
496 OSL_ENSURE(!iter->second.first.empty() || !iter->second.second.empty(),
497 "null entry in m_XmlIdMap");
498 return (isContentFile(i_rStreamName))
499 ? &iter->second.first
500 : &iter->second.second;
502 else
504 return 0;
508 Metadatable*
509 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElement(
510 const OUString & i_rStreamName,
511 const OUString & i_rIdref) const
513 if (!isValidXmlId(i_rStreamName, i_rIdref))
515 throw lang::IllegalArgumentException(OUString(
516 "illegal XmlId"), 0, 0);
519 const XmlIdList_t * pList( LookupElementList(i_rStreamName, i_rIdref) );
520 if (pList)
522 const XmlIdList_t::const_iterator iter(
523 ::std::find_if(pList->begin(), pList->end(),
524 ::boost::bind(
525 ::std::logical_not<bool>(),
526 ::boost::bind(
527 ::std::logical_or<bool>(),
528 ::boost::bind( &Metadatable::IsInUndo, _1 ),
529 ::boost::bind( &Metadatable::IsInClipboard, _1 )
530 ) ) ) );
531 if (iter != pList->end())
533 return *iter;
536 return 0;
539 bool
540 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupXmlId(
541 const Metadatable& i_rObject,
542 OUString & o_rStream, OUString & o_rIdref) const
544 const XmlIdReverseMap_t::const_iterator iter(
545 m_XmlIdReverseMap.find(&i_rObject) );
546 if (iter != m_XmlIdReverseMap.end())
548 OSL_ENSURE(!iter->second.first.isEmpty(),
549 "null stream in m_XmlIdReverseMap");
550 OSL_ENSURE(!iter->second.second.isEmpty(),
551 "null id in m_XmlIdReverseMap");
552 o_rStream = iter->second.first;
553 o_rIdref = iter->second.second;
554 return true;
556 else
558 return false;
562 bool
563 XmlIdRegistryDocument::XmlIdRegistry_Impl::TryInsertMetadatable(
564 Metadatable & i_rObject,
565 const OUString & i_rStreamName, const OUString & i_rIdref)
567 const bool bContent( isContentFile(i_rStreamName) );
568 OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName),
569 "invalid stream");
571 XmlIdList_t * pList( LookupElementList(i_rStreamName, i_rIdref) );
572 if (pList)
574 if (pList->empty())
576 pList->push_back( &i_rObject );
577 return true;
579 else
581 // this is only called from TryRegister now, so check
582 // if all elements in the list are deleted (in undo) or
583 // placeholders, then "steal" the id from them
584 if ( pList->end() == ::std::find_if(pList->begin(), pList->end(),
585 ::boost::bind(
586 ::std::logical_not<bool>(),
587 ::boost::bind(
588 ::std::logical_or<bool>(),
589 ::boost::bind( &Metadatable::IsInUndo, _1 ),
590 ::boost::bind( &Metadatable::IsInClipboard, _1 )
591 ) ) ) )
593 pList->push_front( &i_rObject );
594 return true;
596 else
598 return false;
602 else
604 m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent
605 ? ::std::make_pair( XmlIdList_t( 1, &i_rObject ), XmlIdList_t() )
606 : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject ) )));
607 return true;
611 //=============================================================================
612 // Document XML ID Registry
615 XmlIdRegistryDocument::XmlIdRegistryDocument()
616 : m_pImpl( new XmlIdRegistry_Impl )
620 static void
621 removeLink(Metadatable* i_pObject)
623 OSL_ENSURE(i_pObject, "null in list ???");
624 if (!i_pObject) return;
625 if (i_pObject->IsInClipboard())
627 MetadatableClipboard* pLink(
628 dynamic_cast<MetadatableClipboard*>( i_pObject ) );
629 OSL_ENSURE(pLink, "IsInClipboard, but no MetadatableClipboard ?");
630 if (pLink)
632 pLink->OriginNoLongerInBusinessAnymore();
637 XmlIdRegistryDocument::~XmlIdRegistryDocument()
639 // notify all list elements that are actually in the clipboard
640 for (XmlIdMap_t::iterator iter(m_pImpl->m_XmlIdMap.begin());
641 iter != m_pImpl->m_XmlIdMap.end(); ++iter)
643 ::std::for_each(iter->second.first.begin(), iter->second.first.end(),
644 removeLink);
645 ::std::for_each(iter->second.second.begin(), iter->second.second.end(),
646 removeLink);
650 bool
651 XmlIdRegistryDocument::LookupXmlId(
652 const Metadatable& i_rObject,
653 OUString & o_rStream, OUString & o_rIdref) const
655 return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref);
658 Metadatable*
659 XmlIdRegistryDocument::LookupElement(
660 const OUString & i_rStreamName,
661 const OUString & i_rIdref) const
663 return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
666 bool
667 XmlIdRegistryDocument::TryRegisterMetadatable(Metadatable & i_rObject,
668 OUString const& i_rStreamName, OUString const& i_rIdref)
670 OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject,
671 OUStringToOString(i_rStreamName, RTL_TEXTENCODING_UTF8).getStr(),
672 OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8).getStr());
674 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
675 "TryRegisterMetadatable called for MetadatableUndo?");
676 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
677 "TryRegisterMetadatable called for MetadatableClipboard?");
679 if (!isValidXmlId(i_rStreamName, i_rIdref))
681 throw lang::IllegalArgumentException(OUString(
682 "illegal XmlId"), 0, 0);
684 if (i_rObject.IsInContent()
685 ? !isContentFile(i_rStreamName)
686 : !isStylesFile(i_rStreamName))
688 throw lang::IllegalArgumentException(OUString(
689 "illegal XmlId: wrong stream"), 0, 0);
692 OUString old_path;
693 OUString old_idref;
694 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
695 if (old_path == i_rStreamName && old_idref == i_rIdref)
697 return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
699 XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
700 if (!old_idref.isEmpty())
702 old_id = m_pImpl->m_XmlIdMap.find(old_idref);
703 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
705 if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
707 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
708 m_pImpl->m_XmlIdReverseMap[&i_rObject] =
709 ::std::make_pair(i_rStreamName, i_rIdref);
710 return true;
712 else
714 return false;
718 void
719 XmlIdRegistryDocument::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
721 OSL_TRACE("RegisterMetadatableAndCreateID: %p", &i_rObject);
723 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
724 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
725 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
726 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
728 const bool isInContent( i_rObject.IsInContent() );
729 const OUString stream( OUString::createFromAscii(
730 isInContent ? s_content : s_styles ) );
731 // check if we have a latent xmlid, and if yes, remove it
732 OUString old_path;
733 OUString old_idref;
734 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
736 XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
737 if (!old_idref.isEmpty())
739 old_id = m_pImpl->m_XmlIdMap.find(old_idref);
740 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
741 if (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject)
743 return;
745 else
747 // remove latent xmlid
748 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
752 // create id
753 const OUString id( create_id(m_pImpl->m_XmlIdMap) );
754 OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(),
755 "created id is in use");
756 m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent
757 ? ::std::make_pair( XmlIdList_t( 1, &i_rObject ), XmlIdList_t() )
758 : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject ) )));
759 m_pImpl->m_XmlIdReverseMap[&i_rObject] = ::std::make_pair(stream, id);
762 void XmlIdRegistryDocument::UnregisterMetadatable(const Metadatable& i_rObject)
764 OSL_TRACE("UnregisterMetadatable: %p", &i_rObject);
766 OUString path;
767 OUString idref;
768 if (!m_pImpl->LookupXmlId(i_rObject, path, idref))
770 OSL_FAIL("unregister: no xml id?");
771 return;
773 const XmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
774 if (iter != m_pImpl->m_XmlIdMap.end())
776 rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
780 void XmlIdRegistryDocument::RemoveXmlIdForElement(const Metadatable& i_rObject)
782 OSL_TRACE("RemoveXmlIdForElement: %p", &i_rObject);
784 const XmlIdReverseMap_t::iterator iter(
785 m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
786 if (iter != m_pImpl->m_XmlIdReverseMap.end())
788 OSL_ENSURE(!iter->second.second.isEmpty(),
789 "null id in m_XmlIdReverseMap");
790 m_pImpl->m_XmlIdReverseMap.erase(iter);
794 // -------------------------------------------------------------------
796 void XmlIdRegistryDocument::RegisterCopy(Metadatable const& i_rSource,
797 Metadatable & i_rCopy, const bool i_bCopyPrecedesSource)
799 OSL_TRACE("RegisterCopy: %p -> %p (%d)\n",
800 &i_rSource, &i_rCopy, i_bCopyPrecedesSource);
802 // potential sources: clipboard, undo array, splitNode
803 // assumption: stream change can only happen via clipboard, and is handled
804 // by Metadatable::RegisterAsCopyOf
805 OSL_ENSURE(i_rSource.IsInUndo() || i_rCopy.IsInUndo() ||
806 (i_rSource.IsInContent() == i_rCopy.IsInContent()),
807 "RegisterCopy: not in same stream?");
809 OUString path;
810 OUString idref;
811 if (!m_pImpl->LookupXmlId( i_rSource, path, idref ))
813 OSL_FAIL("no xml id?");
814 return;
816 XmlIdList_t * pList ( m_pImpl->LookupElementList(path, idref) );
817 OSL_ENSURE( ::std::find( pList->begin(), pList->end(), &i_rCopy )
818 == pList->end(), "copy already registered???");
819 XmlIdList_t::iterator srcpos(
820 ::std::find( pList->begin(), pList->end(), &i_rSource ) );
821 OSL_ENSURE(srcpos != pList->end(), "source not in list???");
822 if (srcpos == pList->end())
824 return;
826 if (i_bCopyPrecedesSource)
828 pList->insert( srcpos, &i_rCopy );
830 else
832 // for undo push_back does not work! must insert right after source
833 pList->insert( ++srcpos, &i_rCopy );
835 m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy,
836 ::std::make_pair(path, idref)));
839 ::boost::shared_ptr<MetadatableUndo>
840 XmlIdRegistryDocument::CreateUndo(Metadatable const& i_rObject)
842 OSL_TRACE("CreateUndo: %p", &i_rObject);
844 return ::boost::shared_ptr<MetadatableUndo>(
845 new MetadatableUndo(i_rObject.IsInContent()) );
849 i_rMerged is both a source and the target node of the merge
850 i_rOther is the other source, and will be deleted after the merge
852 dimensions: none|latent|actual empty|nonempty
853 i_rMerged(1) i_rOther(2) result
854 *|empty *|empty => 1|2 (arbitrary)
855 *|empty *|nonempty => 2
856 *|nonempty *|empty => 1
857 none|nonempty none|nonempty => none
858 none|nonempty latent|nonempty => 2
859 latent|nonempty none|nonempty => 1
860 latent|nonempty latent|nonempty => 1|2
861 *|nonempty actual|nonempty => 2
862 actual|nonempty *|nonempty => 1
863 actual|nonempty actual|nonempty => 1|2
865 void
866 XmlIdRegistryDocument::JoinMetadatables(
867 Metadatable & i_rMerged, Metadatable const & i_rOther)
869 OSL_TRACE("JoinMetadatables: %p <- %p", &i_rMerged, &i_rOther);
871 bool mergedOwnsRef;
872 OUString path;
873 OUString idref;
874 if (m_pImpl->LookupXmlId(i_rMerged, path, idref))
876 mergedOwnsRef = (m_pImpl->LookupElement(path, idref) == &i_rMerged);
878 else
880 OSL_FAIL("JoinMetadatables: no xmlid?");
881 return;
883 if (!mergedOwnsRef)
885 i_rMerged.RemoveMetadataReference();
886 i_rMerged.RegisterAsCopyOf(i_rOther, true);
887 return;
889 // other cases: merged has actual ref and is nonempty,
890 // other has latent/actual ref and is nonempty: other loses => nothing to do
894 //=============================================================================
895 // Clipboard XML ID Registry (_Impl)
897 struct RMapEntry
899 RMapEntry() : m_pLink() { }
900 RMapEntry(OUString const& i_rStream,
901 OUString const& i_rXmlId,
902 ::boost::shared_ptr<MetadatableClipboard> const& i_pLink
903 = ::boost::shared_ptr<MetadatableClipboard>())
904 : m_Stream(i_rStream), m_XmlId(i_rXmlId), m_pLink(i_pLink)
906 OUString m_Stream;
907 OUString m_XmlId;
908 // this would have been an auto_ptr, if only that would have compiled...
909 ::boost::shared_ptr<MetadatableClipboard> m_pLink;
912 /// element -> (stream name, idref, source)
913 typedef ::boost::unordered_map< const Metadatable*,
914 struct RMapEntry,
915 PtrHash<Metadatable> >
916 ClipboardXmlIdReverseMap_t;
918 /// Idref -> (content.xml element, styles.xml element)
919 typedef ::boost::unordered_map< OUString,
920 ::std::pair< Metadatable*, Metadatable* >, OUStringHash >
921 ClipboardXmlIdMap_t;
923 struct XmlIdRegistryClipboard::XmlIdRegistry_Impl
925 XmlIdRegistry_Impl()
926 : m_XmlIdMap(), m_XmlIdReverseMap() { }
928 bool TryInsertMetadatable(Metadatable& i_xObject,
929 const OUString & i_rStream, const OUString & i_rIdref);
931 bool LookupXmlId(const Metadatable& i_xObject,
932 OUString & o_rStream, OUString & o_rIdref,
933 MetadatableClipboard const* &o_rpLink) const;
935 Metadatable* LookupElement(const OUString & i_rStreamName,
936 const OUString & i_rIdref) const;
938 Metadatable* const* LookupEntry(const OUString & i_rStreamName,
939 const OUString & i_rIdref) const;
941 Metadatable* * LookupEntry(const OUString & i_rStreamName,
942 const OUString & i_rIdref)
944 return const_cast<Metadatable**>(
945 const_cast<const XmlIdRegistry_Impl*>(this)
946 ->LookupEntry(i_rStreamName, i_rIdref));
949 ClipboardXmlIdMap_t m_XmlIdMap;
950 ClipboardXmlIdReverseMap_t m_XmlIdReverseMap;
953 // -------------------------------------------------------------------
955 static void
956 rmIter(ClipboardXmlIdMap_t & i_rXmlIdMap,
957 ClipboardXmlIdMap_t::iterator const& i_rIter,
958 OUString const & i_rStream, Metadatable const& i_rObject)
960 if (i_rIter != i_rXmlIdMap.end())
962 Metadatable *& rMeta = isContentFile(i_rStream)
963 ? i_rIter->second.first : i_rIter->second.second;
964 if (rMeta == &i_rObject)
966 rMeta = 0;
968 if (!i_rIter->second.first && !i_rIter->second.second)
970 i_rXmlIdMap.erase(i_rIter);
975 // -------------------------------------------------------------------
977 Metadatable* const*
978 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupEntry(
979 const OUString & i_rStreamName,
980 const OUString & i_rIdref) const
982 if (!isValidXmlId(i_rStreamName, i_rIdref))
984 throw lang::IllegalArgumentException(OUString(
985 "illegal XmlId"), 0, 0);
988 const ClipboardXmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
989 if (iter != m_XmlIdMap.end())
991 OSL_ENSURE(iter->second.first || iter->second.second,
992 "null entry in m_XmlIdMap");
993 return (isContentFile(i_rStreamName))
994 ? &iter->second.first
995 : &iter->second.second;
997 else
999 return 0;
1003 Metadatable*
1004 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupElement(
1005 const OUString & i_rStreamName,
1006 const OUString & i_rIdref) const
1008 Metadatable * const * ppEntry = LookupEntry(i_rStreamName, i_rIdref);
1009 return ppEntry ? *ppEntry : 0;
1012 bool
1013 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupXmlId(
1014 const Metadatable& i_rObject,
1015 OUString & o_rStream, OUString & o_rIdref,
1016 MetadatableClipboard const* &o_rpLink) const
1018 const ClipboardXmlIdReverseMap_t::const_iterator iter(
1019 m_XmlIdReverseMap.find(&i_rObject) );
1020 if (iter != m_XmlIdReverseMap.end())
1022 OSL_ENSURE(!iter->second.m_Stream.isEmpty(),
1023 "null stream in m_XmlIdReverseMap");
1024 OSL_ENSURE(!iter->second.m_XmlId.isEmpty(),
1025 "null id in m_XmlIdReverseMap");
1026 o_rStream = iter->second.m_Stream;
1027 o_rIdref = iter->second.m_XmlId;
1028 o_rpLink = iter->second.m_pLink.get();
1029 return true;
1031 else
1033 return false;
1037 bool
1038 XmlIdRegistryClipboard::XmlIdRegistry_Impl::TryInsertMetadatable(
1039 Metadatable & i_rObject,
1040 const OUString & i_rStreamName, const OUString & i_rIdref)
1042 bool bContent( isContentFile(i_rStreamName) );
1043 OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName),
1044 "invalid stream");
1046 Metadatable ** ppEntry = LookupEntry(i_rStreamName, i_rIdref);
1047 if (ppEntry)
1049 if (*ppEntry)
1051 return false;
1053 else
1055 *ppEntry = &i_rObject;
1056 return true;
1059 else
1061 m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent
1062 ? ::std::make_pair( &i_rObject, static_cast<Metadatable*>(0) )
1063 : ::std::make_pair( static_cast<Metadatable*>(0), &i_rObject )));
1064 return true;
1068 //=============================================================================
1069 // Clipboard XML ID Registry
1072 XmlIdRegistryClipboard::XmlIdRegistryClipboard()
1073 : m_pImpl( new XmlIdRegistry_Impl )
1077 XmlIdRegistryClipboard::~XmlIdRegistryClipboard()
1081 bool
1082 XmlIdRegistryClipboard::LookupXmlId(
1083 const Metadatable& i_rObject,
1084 OUString & o_rStream, OUString & o_rIdref) const
1086 const MetadatableClipboard * pLink;
1087 return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref, pLink);
1090 Metadatable*
1091 XmlIdRegistryClipboard::LookupElement(
1092 const OUString & i_rStreamName,
1093 const OUString & i_rIdref) const
1095 return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
1098 bool
1099 XmlIdRegistryClipboard::TryRegisterMetadatable(Metadatable & i_rObject,
1100 OUString const& i_rStreamName, OUString const& i_rIdref)
1102 OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject,
1103 OUStringToOString(i_rStreamName, RTL_TEXTENCODING_UTF8).getStr(),
1104 OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8).getStr());
1106 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
1107 "TryRegisterMetadatable called for MetadatableUndo?");
1108 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
1109 "TryRegisterMetadatable called for MetadatableClipboard?");
1111 if (!isValidXmlId(i_rStreamName, i_rIdref))
1113 throw lang::IllegalArgumentException(OUString(
1114 "illegal XmlId"), 0, 0);
1116 if (i_rObject.IsInContent()
1117 ? !isContentFile(i_rStreamName)
1118 : !isStylesFile(i_rStreamName))
1120 throw lang::IllegalArgumentException(OUString(
1121 "illegal XmlId: wrong stream"), 0, 0);
1124 OUString old_path;
1125 OUString old_idref;
1126 const MetadatableClipboard * pLink;
1127 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref, pLink);
1128 if (old_path == i_rStreamName && old_idref == i_rIdref)
1130 return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
1132 ClipboardXmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
1133 if (!old_idref.isEmpty())
1135 old_id = m_pImpl->m_XmlIdMap.find(old_idref);
1136 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
1138 if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
1140 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
1141 m_pImpl->m_XmlIdReverseMap[&i_rObject] =
1142 RMapEntry(i_rStreamName, i_rIdref);
1143 return true;
1145 else
1147 return false;
1151 void
1152 XmlIdRegistryClipboard::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
1154 OSL_TRACE("RegisterMetadatableAndCreateID: %p", &i_rObject);
1156 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
1157 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
1158 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
1159 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
1161 bool isInContent( i_rObject.IsInContent() );
1162 OUString stream( OUString::createFromAscii(
1163 isInContent ? s_content : s_styles ) );
1165 OUString old_path;
1166 OUString old_idref;
1167 LookupXmlId(i_rObject, old_path, old_idref);
1168 if (!old_idref.isEmpty() &&
1169 (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject))
1171 return;
1174 // create id
1175 const OUString id( create_id(m_pImpl->m_XmlIdMap) );
1176 OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(),
1177 "created id is in use");
1178 m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent
1179 ? ::std::make_pair( &i_rObject, static_cast<Metadatable*>(0) )
1180 : ::std::make_pair( static_cast<Metadatable*>(0), &i_rObject )));
1181 // N.B.: if i_rObject had a latent XmlId, then we implicitly delete the
1182 // MetadatableClipboard and thus the latent XmlId here
1183 m_pImpl->m_XmlIdReverseMap[&i_rObject] = RMapEntry(stream, id);
1186 void XmlIdRegistryClipboard::UnregisterMetadatable(const Metadatable& i_rObject)
1188 OSL_TRACE("UnregisterMetadatable: %p", &i_rObject);
1190 OUString path;
1191 OUString idref;
1192 const MetadatableClipboard * pLink;
1193 if (!m_pImpl->LookupXmlId(i_rObject, path, idref, pLink))
1195 OSL_FAIL("unregister: no xml id?");
1196 return;
1198 const ClipboardXmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
1199 if (iter != m_pImpl->m_XmlIdMap.end())
1201 rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
1206 void XmlIdRegistryClipboard::RemoveXmlIdForElement(const Metadatable& i_rObject)
1208 OSL_TRACE("RemoveXmlIdForElement: %p", &i_rObject);
1210 ClipboardXmlIdReverseMap_t::iterator iter(
1211 m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
1212 if (iter != m_pImpl->m_XmlIdReverseMap.end())
1214 OSL_ENSURE(!iter->second.m_XmlId.isEmpty(),
1215 "null id in m_XmlIdReverseMap");
1216 m_pImpl->m_XmlIdReverseMap.erase(iter);
1220 // -------------------------------------------------------------------
1222 ::boost::shared_ptr<MetadatableClipboard>
1223 XmlIdRegistryClipboard::CreateClipboard(const bool i_isInContent)
1225 OSL_TRACE("CreateClipboard:");
1227 return ::boost::shared_ptr<MetadatableClipboard>(
1228 new MetadatableClipboard(i_isInContent) );
1231 MetadatableClipboard &
1232 XmlIdRegistryClipboard::RegisterCopyClipboard(Metadatable & i_rCopy,
1233 beans::StringPair const & i_rReference,
1234 const bool i_isLatent)
1236 OSL_TRACE("RegisterCopyClipboard: %p -> "/*"%p"*/"(%s#%s) (%d)\n",
1237 /*&i_rSource,*/ &i_rCopy,
1238 OUStringToOString(i_rReference.First,
1239 RTL_TEXTENCODING_UTF8).getStr(),
1240 OUStringToOString(i_rReference.Second,
1241 RTL_TEXTENCODING_UTF8).getStr(),
1242 i_isLatent);
1244 // N.B.: when copying to the clipboard, the selection is always inserted
1245 // into the body, even if the source is a header/footer!
1246 // so we do not check whether the stream is right in this function
1248 if (!isValidXmlId(i_rReference.First, i_rReference.Second))
1250 throw lang::IllegalArgumentException(OUString(
1251 "illegal XmlId"), 0, 0);
1254 if (!i_isLatent)
1256 // this should succeed assuming clipboard has a single source document
1257 const bool success( m_pImpl->TryInsertMetadatable(i_rCopy,
1258 i_rReference.First, i_rReference.Second) );
1259 OSL_ENSURE(success, "RegisterCopyClipboard: TryInsert failed?");
1260 (void) success;
1262 const ::boost::shared_ptr<MetadatableClipboard> pLink(
1263 CreateClipboard( isContentFile(i_rReference.First)) );
1264 m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy,
1265 RMapEntry(i_rReference.First, i_rReference.Second, pLink)));
1266 return *pLink.get();
1269 MetadatableClipboard const*
1270 XmlIdRegistryClipboard::SourceLink(Metadatable const& i_rObject)
1272 OUString path;
1273 OUString idref;
1274 const MetadatableClipboard * pLink( 0 );
1275 m_pImpl->LookupXmlId(i_rObject, path, idref, pLink);
1276 return pLink;
1280 //=============================================================================
1281 // Metadatable mixin
1284 Metadatable::~Metadatable()
1286 RemoveMetadataReference();
1289 void Metadatable::RemoveMetadataReference()
1293 if (m_pReg)
1295 m_pReg->UnregisterMetadatable( *this );
1296 m_pReg->RemoveXmlIdForElement( *this );
1297 m_pReg = 0;
1300 catch (const uno::Exception &)
1302 OSL_FAIL("Metadatable::RemoveMetadataReference: exception");
1306 // ::com::sun::star::rdf::XMetadatable:
1307 beans::StringPair
1308 Metadatable::GetMetadataReference() const
1310 if (m_pReg)
1312 return m_pReg->GetXmlIdForElement(*this);
1314 return beans::StringPair();
1317 void
1318 Metadatable::SetMetadataReference(
1319 const ::com::sun::star::beans::StringPair & i_rReference)
1321 if (i_rReference.Second.isEmpty())
1323 RemoveMetadataReference();
1325 else
1327 OUString streamName( i_rReference.First );
1328 if (streamName.isEmpty())
1330 // handle empty stream name as auto-detect.
1331 // necessary for importing flat file format.
1332 streamName = OUString::createFromAscii(
1333 IsInContent() ? s_content : s_styles );
1335 XmlIdRegistry & rReg( dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1336 if (rReg.TryRegisterMetadatable(*this, streamName, i_rReference.Second))
1338 m_pReg = &rReg;
1340 else
1342 throw lang::IllegalArgumentException(
1343 OUString("Metadatable::"
1344 "SetMetadataReference: argument is invalid"), /*this*/0, 0);
1349 void Metadatable::EnsureMetadataReference()
1351 XmlIdRegistry& rReg(
1352 m_pReg ? *m_pReg : dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1353 rReg.RegisterMetadatableAndCreateID( *this );
1354 m_pReg = &rReg;
1357 const ::sfx2::IXmlIdRegistry& GetRegistryConst(Metadatable const& i_rObject)
1359 return const_cast< Metadatable& >( i_rObject ).GetRegistry();
1362 void
1363 Metadatable::RegisterAsCopyOf(Metadatable const & i_rSource,
1364 const bool i_bCopyPrecedesSource)
1366 OSL_ENSURE(typeid(*this) == typeid(i_rSource)
1367 || typeid(i_rSource) == typeid(MetadatableUndo)
1368 || typeid(*this) == typeid(MetadatableUndo)
1369 || typeid(i_rSource) == typeid(MetadatableClipboard)
1370 || typeid(*this) == typeid(MetadatableClipboard),
1371 "RegisterAsCopyOf element with different class?");
1372 OSL_ENSURE(!this->m_pReg, "RegisterAsCopyOf called on element with XmlId?");
1374 if (this->m_pReg)
1376 RemoveMetadataReference();
1381 if (i_rSource.m_pReg)
1383 XmlIdRegistry & rReg(
1384 dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1385 if (i_rSource.m_pReg == &rReg)
1387 OSL_ENSURE(!IsInClipboard(),
1388 "RegisterAsCopy: both in clipboard?");
1389 if (!IsInClipboard())
1391 XmlIdRegistryDocument & rRegDoc(
1392 dynamic_cast<XmlIdRegistryDocument&>( rReg ) );
1393 rRegDoc.RegisterCopy(i_rSource, *this,
1394 i_bCopyPrecedesSource);
1395 this->m_pReg = &rRegDoc;
1397 return;
1399 // source is in different document
1400 XmlIdRegistryDocument * pRegDoc(
1401 dynamic_cast<XmlIdRegistryDocument *>(&rReg) );
1402 XmlIdRegistryClipboard * pRegClp(
1403 dynamic_cast<XmlIdRegistryClipboard*>(&rReg) );
1405 if (pRegClp)
1407 beans::StringPair SourceRef(
1408 i_rSource.m_pReg->GetXmlIdForElement(i_rSource) );
1409 bool isLatent( SourceRef.Second.isEmpty() );
1410 XmlIdRegistryDocument * pSourceRegDoc(
1411 dynamic_cast<XmlIdRegistryDocument*>(i_rSource.m_pReg) );
1412 OSL_ENSURE(pSourceRegDoc, "RegisterAsCopyOf: 2 clipboards?");
1413 if (!pSourceRegDoc) return;
1414 // this is a copy _to_ the clipboard
1415 if (isLatent)
1417 pSourceRegDoc->LookupXmlId(i_rSource,
1418 SourceRef.First, SourceRef.Second);
1420 Metadatable & rLink(
1421 pRegClp->RegisterCopyClipboard(*this, SourceRef, isLatent));
1422 this->m_pReg = pRegClp;
1423 // register as copy in the non-clipboard registry
1424 pSourceRegDoc->RegisterCopy(i_rSource, rLink,
1425 false); // i_bCopyPrecedesSource);
1426 rLink.m_pReg = pSourceRegDoc;
1428 else if (pRegDoc)
1430 XmlIdRegistryClipboard * pSourceRegClp(
1431 dynamic_cast<XmlIdRegistryClipboard*>(i_rSource.m_pReg) );
1432 OSL_ENSURE(pSourceRegClp,
1433 "RegisterAsCopyOf: 2 non-clipboards?");
1434 if (!pSourceRegClp) return;
1435 const MetadatableClipboard * pLink(
1436 pSourceRegClp->SourceLink(i_rSource) );
1437 // may happen if src got its id via UNO call
1438 if (!pLink) return;
1439 // only register copy if clipboard content is from this SwDoc!
1440 if (pLink && (&GetRegistryConst(*pLink) == pRegDoc))
1442 // this is a copy _from_ the clipboard; check if the
1443 // element is still in the same stream
1444 // N.B.: we check the stream of pLink, not of i_rSource!
1445 bool srcInContent( pLink->IsInContent() );
1446 bool tgtInContent( this->IsInContent() );
1447 if (srcInContent == tgtInContent)
1449 pRegDoc->RegisterCopy(*pLink, *this,
1450 true); // i_bCopyPrecedesSource);
1451 this->m_pReg = pRegDoc;
1453 // otherwise: stream change! do not register!
1456 else
1458 OSL_FAIL("neither RegDoc nor RegClp cannot happen");
1462 catch (const uno::Exception &)
1464 OSL_FAIL("Metadatable::RegisterAsCopyOf: exception");
1468 ::boost::shared_ptr<MetadatableUndo> Metadatable::CreateUndo() const
1470 OSL_ENSURE(!IsInUndo(), "CreateUndo called for object in undo?");
1471 OSL_ENSURE(!IsInClipboard(), "CreateUndo called for object in clipboard?");
1474 if (!IsInClipboard() && !IsInUndo() && m_pReg)
1476 XmlIdRegistryDocument * pRegDoc(
1477 dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
1478 ::boost::shared_ptr<MetadatableUndo> pUndo(
1479 pRegDoc->CreateUndo(*this) );
1480 pRegDoc->RegisterCopy(*this, *pUndo, false);
1481 pUndo->m_pReg = pRegDoc;
1482 return pUndo;
1485 catch (const uno::Exception &)
1487 OSL_FAIL("Metadatable::CreateUndo: exception");
1489 return ::boost::shared_ptr<MetadatableUndo>();
1492 ::boost::shared_ptr<MetadatableUndo> Metadatable::CreateUndoForDelete()
1494 ::boost::shared_ptr<MetadatableUndo> const pUndo( CreateUndo() );
1495 RemoveMetadataReference();
1496 return pUndo;
1499 void Metadatable::RestoreMetadata(
1500 ::boost::shared_ptr<MetadatableUndo> const& i_pUndo)
1502 OSL_ENSURE(!IsInUndo(), "RestoreMetadata called for object in undo?");
1503 OSL_ENSURE(!IsInClipboard(),
1504 "RestoreMetadata called for object in clipboard?");
1505 if (IsInClipboard() || IsInUndo()) return;
1506 RemoveMetadataReference();
1507 if (i_pUndo)
1509 this->RegisterAsCopyOf(*i_pUndo, true);
1513 void
1514 Metadatable::JoinMetadatable(Metadatable const & i_rOther,
1515 const bool i_isMergedEmpty, const bool i_isOtherEmpty)
1517 OSL_ENSURE(!IsInUndo(), "JoinMetadatables called for object in undo?");
1518 OSL_ENSURE(!IsInClipboard(),
1519 "JoinMetadatables called for object in clipboard?");
1520 if (IsInClipboard() || IsInUndo()) return;
1522 if (i_isOtherEmpty && !i_isMergedEmpty)
1524 // other is empty, thus loses => nothing to do
1525 return;
1527 if (i_isMergedEmpty && !i_isOtherEmpty)
1529 this->RemoveMetadataReference();
1530 this->RegisterAsCopyOf(i_rOther, true);
1531 return;
1534 if (!i_rOther.m_pReg)
1536 // other doesn't have xmlid, thus loses => nothing to do
1537 return;
1539 if (!m_pReg)
1541 this->RegisterAsCopyOf(i_rOther, true);
1542 // assumption: i_rOther will be deleted, so don't unregister it here
1543 return;
1547 XmlIdRegistryDocument * pRegDoc(
1548 dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
1549 OSL_ENSURE(pRegDoc, "JoinMetadatable: no pRegDoc?");
1550 if (pRegDoc)
1552 pRegDoc->JoinMetadatables(*this, i_rOther);
1555 catch (const uno::Exception &)
1557 OSL_FAIL("Metadatable::JoinMetadatable: exception");
1562 //=============================================================================
1563 // XMetadatable mixin
1565 // ::com::sun::star::rdf::XNode:
1566 OUString SAL_CALL MetadatableMixin::getStringValue()
1567 throw (::com::sun::star::uno::RuntimeException)
1569 return getNamespace() + getLocalName();
1572 // ::com::sun::star::rdf::XURI:
1573 OUString SAL_CALL MetadatableMixin::getLocalName()
1574 throw (::com::sun::star::uno::RuntimeException)
1576 SolarMutexGuard aGuard;
1577 beans::StringPair mdref( getMetadataReference() );
1578 if (mdref.Second.isEmpty())
1580 ensureMetadataReference(); // N.B.: side effect!
1581 mdref = getMetadataReference();
1583 OUStringBuffer buf;
1584 buf.append(mdref.First);
1585 buf.append(static_cast<sal_Unicode>('#'));
1586 buf.append(mdref.Second);
1587 return buf.makeStringAndClear();
1590 OUString SAL_CALL MetadatableMixin::getNamespace()
1591 throw (::com::sun::star::uno::RuntimeException)
1593 SolarMutexGuard aGuard;
1594 const uno::Reference< frame::XModel > xModel( GetModel() );
1595 const uno::Reference< rdf::XURI > xDMA( xModel, uno::UNO_QUERY_THROW );
1596 return xDMA->getStringValue();
1599 // ::com::sun::star::rdf::XMetadatable:
1600 beans::StringPair SAL_CALL
1601 MetadatableMixin::getMetadataReference()
1602 throw (uno::RuntimeException)
1604 SolarMutexGuard aGuard;
1606 Metadatable *const pObject( GetCoreObject() );
1607 if (!pObject)
1609 throw uno::RuntimeException(
1610 OUString(
1611 "MetadatableMixin: cannot get core object; not inserted?"),
1612 *this);
1614 return pObject->GetMetadataReference();
1617 void SAL_CALL
1618 MetadatableMixin::setMetadataReference(
1619 const beans::StringPair & i_rReference)
1620 throw (uno::RuntimeException, lang::IllegalArgumentException)
1622 SolarMutexGuard aGuard;
1624 Metadatable *const pObject( GetCoreObject() );
1625 if (!pObject)
1627 throw uno::RuntimeException(
1628 OUString(
1629 "MetadatableMixin: cannot get core object; not inserted?"),
1630 *this);
1632 return pObject->SetMetadataReference(i_rReference);
1635 void SAL_CALL MetadatableMixin::ensureMetadataReference()
1636 throw (uno::RuntimeException)
1638 SolarMutexGuard aGuard;
1640 Metadatable *const pObject( GetCoreObject() );
1641 if (!pObject)
1643 throw uno::RuntimeException(
1644 OUString(
1645 "MetadatableMixin: cannot get core object; not inserted?"),
1646 *this);
1648 return pObject->EnsureMetadataReference();
1651 } // namespace sfx2
1654 //=============================================================================
1656 #if OSL_DEBUG_LEVEL > 1
1658 #include <stdio.h>
1660 static void dump(sfx2::XmlIdList_t * pList)
1661 #ifdef __GNUC__
1662 __attribute__ ((unused))
1663 #endif
1665 static void dump(sfx2::XmlIdList_t * pList)
1667 fprintf(stderr, "\nXmlIdList(%p): ", pList);
1668 for (sfx2::XmlIdList_t::iterator i = pList->begin(); i != pList->end(); ++i)
1670 fprintf(stderr, "%p ", *i);
1672 fprintf(stderr, "\n");
1675 #endif
1677 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */