Version 3.6.0.4, tag libreoffice-3.6.0.4
[LibreOffice.git] / sfx2 / source / doc / Metadatable.cxx
blob15639622b00269821a5c4facc72a2ae5a9e49fb4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 #include <sfx2/Metadatable.hxx>
31 #include <sfx2/XmlIdRegistry.hxx>
33 #include <osl/mutex.hxx>
34 #include <vcl/svapp.hxx> // solarmutex
36 #include <rtl/random.h>
38 #include <boost/bind.hpp>
40 #include <memory>
41 #include <boost/unordered_map.hpp>
42 #include <list>
43 #include <algorithm>
44 #if OSL_DEBUG_LEVEL > 0
45 #include <typeinfo>
46 #endif
49 /** XML ID handling.
51 There is an abstract base class <type>XmlIdRegistry</type>, with
52 2 subclasses <type>XmlIdRegistryDocument</type> for "normal" documents,
53 and <type>XmlIdRegistryClipboard</type> for clipboard documents.
54 These classes are responsible for managing XML IDs for all elements
55 of the model. Only the implementation of the <type>Metadatable</type>
56 base class needs to know the registries, so they are not in the header.
58 The handling of XML IDs differs between clipboard and non-clipboard
59 documents in several aspects. Most importantly, non-clipboard documents
60 can have several elements associated with one XML ID.
61 This is necessary because of the weird undo implementation:
62 deleting a text node moves the deleted node to the undo array, but
63 executing undo will then create a <em>copy</em> of that node in the
64 document array. These 2 nodes must have the same XML ID, because
65 we cannot know whether the user will do a redo next, or something else.
67 Because we need to have a mechanism for several objects per XML ID anyway,
68 we use that also to enable some usability features:
69 The document registry has a list of Metadatables per XML ID.
70 This list is sorted by priority, i.e., the first element has highest
71 priority. When inserting copies, care must be taken that they are inserted
72 at the right position: either before or after the source.
73 This is done by <method>Metadatable::RegisterAsCopyOf</method>.
74 When a text node is split, then both resulting text nodes are inserted
75 into the list. If the user then deletes one text node, the other one
76 will have the XML ID.
77 Also, when a Metadatable is copied to the clipboard and then pasted,
78 the copy is inserted into the list. If the user then deletes the source,
79 the XML ID is not lost.
80 The goal is that it should be hard to lose an XML ID by accident, which
81 is especially important as long as we do not have an UI that displays them.
83 There are two subclasses of <type>Metadatable</type>:
84 <ul><li><type>MetadatableClipboard</type>: for copies in the clipboard</li>
85 <li><type>MetadatableUndo</type>: for undo, because a Metadatable
86 may be destroyed on delete and a new one created on undo.</li></ul>
87 These serve only to track the position in an XML ID list in a document
88 registry, so that future actions can insert objects at the right position.
89 Unfortunately, inserting dummy objects seems to be necessary:
90 <ul><li>it is not sufficent to just remember the saved id, because then
91 the relative priorities might change when executing the undo</li>
92 <li>it is not sufficient to record the position as an integer, because
93 if we delete a text node and then undo, the node will be copied(!),
94 and we will have one more node in the list.<li>
95 <li>it is not sufficient to record the pointer of the previous/next
96 Metadatable, because if we delete a text node, undo, and then
97 do something to clear the redo array, the original text node is
98 destroyed, and is replaced by the copy created by undo</li></ul>
100 If content from a non-clipboard document is copied into a clipboard
101 document, a dummy <type>MetadatableClipboard</type> is inserted into the
102 non-clipboard document registry in order to track the position of the
103 source element. When the clipboard content is pasted back into the source
104 document, this dummy object is used to associate the pasted element with
105 that same XML ID.
107 If a <type>Metadatable</type> is deleted or merged,
108 <method>Metadatable::CreateUndo</method> is called, and returns a
109 <type>MetadatableUndo<type> instance, which can be used to undo the action
110 by passing it to <method>Metadatable::RestoreMetadata</method>.
112 @author mst
116 using namespace ::com::sun::star;
118 using ::sfx2::isValidXmlId;
121 namespace sfx2 {
123 static const char s_content [] = "content.xml";
124 static const char s_styles [] = "styles.xml";
125 static const char s_prefix [] = "id"; // prefix for generated xml:id
127 static bool isContentFile(::rtl::OUString const & i_rPath)
129 return i_rPath == s_content;
132 static bool isStylesFile (::rtl::OUString const & i_rPath)
134 return i_rPath == s_styles;
138 //=============================================================================
139 // XML ID handling ---------------------------------------------------
141 /** handles registration of XMetadatable.
143 This class is responsible for guaranteeing that XMetadatable objects
144 always have XML IDs that are unique within a stream.
146 This is an abstract base class; see subclasses XmlIdRegistryDocument and
147 XmlIdRegistryClipboard.
149 @see SwDoc::GetXmlIdRegistry
150 @see SwDocShell::GetXmlIdRegistry
152 class XmlIdRegistry : public sfx2::IXmlIdRegistry
155 public:
156 XmlIdRegistry();
158 virtual ~XmlIdRegistry();
160 /** get the ODF element with the given metadata reference. */
161 virtual ::com::sun::star::uno::Reference<
162 ::com::sun::star::rdf::XMetadatable > SAL_CALL
163 GetElementByMetadataReference(
164 const ::com::sun::star::beans::StringPair & i_rReference) const;
166 /** register an ODF element at a newly generated, unique metadata reference.
169 Find a fresh XML ID, and register it for the element.
170 The generated ID does not occur in any stream of the document.
171 </p>
173 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) = 0;
175 /** try to register an ODF element at a given XML ID, or update its
176 registation to a different XML ID.
179 If the given new metadata reference is not already occupied in the
180 document, unregister the element at its old metadata reference if
181 it has one, and register the new metadata reference for the element.
182 Note that this method only ensures that XML IDs are unique per stream,
183 so using the same XML ID in both content.xml and styles.xml is allowed.
184 </p>
186 @returns
187 true iff the element has successfully been registered
189 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
190 ::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref)
191 = 0;
193 /** unregister an ODF element.
196 Unregister the element at its metadata reference.
197 Does not remove the metadata reference from the element.
198 </p>
200 @see RemoveXmlIdForElement
202 virtual void UnregisterMetadatable(Metadatable const&) = 0;
204 /** get the metadata reference for the given element. */
205 ::com::sun::star::beans::StringPair
206 GetXmlIdForElement(Metadatable const&) const;
208 /** remove the metadata reference for the given element. */
209 virtual void RemoveXmlIdForElement(Metadatable const&) = 0;
211 protected:
213 virtual bool LookupXmlId(const Metadatable& i_xObject,
214 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const = 0;
216 virtual Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName,
217 const ::rtl::OUString & i_rIdref) const = 0;
220 // XmlIdRegistryDocument ---------------------------------------------
222 /** non-clipboard documents */
223 class XmlIdRegistryDocument : public XmlIdRegistry
226 public:
227 XmlIdRegistryDocument();
229 virtual ~XmlIdRegistryDocument();
231 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject);
233 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
234 ::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref);
236 virtual void UnregisterMetadatable(Metadatable const&);
238 virtual void RemoveXmlIdForElement(Metadatable const&);
240 /** register i_rCopy as a copy of i_rSource,
241 with precedence iff i_bCopyPrecedesSource is true */
242 void RegisterCopy(Metadatable const& i_rSource, Metadatable & i_rCopy,
243 const bool i_bCopyPrecedesSource);
245 /** create a Undo Metadatable for i_rObject. */
246 ::boost::shared_ptr<MetadatableUndo> CreateUndo(
247 Metadatable const& i_rObject);
249 /** merge i_rMerged and i_rOther into i_rMerged. */
250 void JoinMetadatables(Metadatable & i_rMerged, Metadatable const& i_rOther);
252 // unfortunately public, Metadatable::RegisterAsCopyOf needs this
253 virtual bool LookupXmlId(const Metadatable& i_xObject,
254 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const;
256 private:
258 virtual Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName,
259 const ::rtl::OUString & i_rIdref) const;
261 struct XmlIdRegistry_Impl;
262 ::std::auto_ptr<XmlIdRegistry_Impl> m_pImpl;
265 // MetadatableUndo ---------------------------------------------------
267 /** the horrible Undo Metadatable: is inserted into lists to track position */
268 class MetadatableUndo : public Metadatable
270 /// as determined by the stream of the source in original document
271 const bool m_isInContent;
272 public:
273 MetadatableUndo(const bool i_isInContent)
274 : m_isInContent(i_isInContent) { }
275 virtual ::sfx2::XmlIdRegistry& GetRegistry()
277 // N.B. for Undo, m_pReg is initialized by registering this as copy in
278 // CreateUndo; it is never cleared
279 OSL_ENSURE(m_pReg, "no m_pReg in MetadatableUndo ?");
280 return *m_pReg;
282 virtual bool IsInClipboard() const { return false; }
283 virtual bool IsInUndo() const { return true; }
284 virtual bool IsInContent() const { return m_isInContent; }
285 virtual ::com::sun::star::uno::Reference<
286 ::com::sun::star::rdf::XMetadatable > MakeUnoObject()
287 { OSL_FAIL("MetadatableUndo::MakeUnoObject"); throw; }
290 // MetadatableClipboard ----------------------------------------------
292 /** the horrible Clipboard Metadatable: inserted into lists to track position */
293 class MetadatableClipboard : public Metadatable
295 /// as determined by the stream of the source in original document
296 const bool m_isInContent;
297 public:
298 MetadatableClipboard(const bool i_isInContent)
299 : m_isInContent(i_isInContent) { }
300 virtual ::sfx2::XmlIdRegistry& GetRegistry()
302 // N.B. for Clipboard, m_pReg is initialized by registering this as copy in
303 // RegisterAsCopyOf; it is only cleared by OriginNoLongerInBusinessAnymore
304 OSL_ENSURE(m_pReg, "no m_pReg in MetadatableClipboard ?");
305 return *m_pReg;
307 virtual bool IsInClipboard() const { return true; }
308 virtual bool IsInUndo() const { return false; }
309 virtual bool IsInContent() const { return m_isInContent; }
310 virtual ::com::sun::star::uno::Reference<
311 ::com::sun::star::rdf::XMetadatable > MakeUnoObject()
312 { OSL_FAIL("MetadatableClipboard::MakeUnoObject"); throw; }
313 void OriginNoLongerInBusinessAnymore() { m_pReg = 0; }
316 // XmlIdRegistryClipboard --------------------------------------------
318 class XmlIdRegistryClipboard : public XmlIdRegistry
321 public:
322 XmlIdRegistryClipboard();
323 virtual ~XmlIdRegistryClipboard();
325 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject);
327 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
328 ::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref);
330 virtual void UnregisterMetadatable(Metadatable const&);
332 virtual void RemoveXmlIdForElement(Metadatable const&);
334 /** register i_rCopy as a copy of i_rSource */
335 MetadatableClipboard & RegisterCopyClipboard(Metadatable & i_rCopy,
336 beans::StringPair const & i_rReference,
337 const bool i_isLatent);
339 /** get the Metadatable that links i_rObject to its origin registry */
340 MetadatableClipboard const* SourceLink(Metadatable const& i_rObject);
342 private:
343 virtual bool LookupXmlId(const Metadatable& i_xObject,
344 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const;
346 virtual Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName,
347 const ::rtl::OUString & i_rIdref) const;
349 /** create a Clipboard Metadatable for i_rObject. */
350 ::boost::shared_ptr<MetadatableClipboard> CreateClipboard(
351 const bool i_isInContent);
353 struct XmlIdRegistry_Impl;
354 ::std::auto_ptr<XmlIdRegistry_Impl> m_pImpl;
358 //=============================================================================
359 // XmlIdRegistry
361 ::sfx2::IXmlIdRegistry * createXmlIdRegistry(const bool i_DocIsClipboard)
363 return i_DocIsClipboard
364 ? static_cast<XmlIdRegistry*>( new XmlIdRegistryClipboard )
365 : static_cast<XmlIdRegistry*>( new XmlIdRegistryDocument );
368 XmlIdRegistry::XmlIdRegistry()
372 XmlIdRegistry::~XmlIdRegistry()
376 ::com::sun::star::uno::Reference< ::com::sun::star::rdf::XMetadatable > SAL_CALL
377 XmlIdRegistry::GetElementByMetadataReference(
378 const beans::StringPair & i_rReference) const
380 Metadatable* pObject( LookupElement(i_rReference.First,
381 i_rReference.Second) );
382 return pObject ? pObject->MakeUnoObject() : 0;
385 beans::StringPair
386 XmlIdRegistry::GetXmlIdForElement(const Metadatable& i_rObject) const
388 ::rtl::OUString path;
389 ::rtl::OUString idref;
390 if (LookupXmlId(i_rObject, path, idref))
392 if (LookupElement(path, idref) == &i_rObject)
394 return beans::StringPair(path, idref);
397 return beans::StringPair();
401 /// generate unique xml:id
402 template< typename T >
403 /*static*/ ::rtl::OUString create_id(const
404 ::boost::unordered_map< ::rtl::OUString, T, ::rtl::OUStringHash > & i_rXmlIdMap)
406 static rtlRandomPool s_Pool( rtl_random_createPool() );
407 const ::rtl::OUString prefix(s_prefix);
408 typename ::boost::unordered_map< ::rtl::OUString, T, ::rtl::OUStringHash >
409 ::const_iterator iter;
410 ::rtl::OUString id;
413 sal_Int32 n;
414 rtl_random_getBytes(s_Pool, & n, sizeof(n));
415 id = prefix + ::rtl::OUString::valueOf(static_cast<sal_Int32>(abs(n)));
416 iter = i_rXmlIdMap.find(id);
418 while (iter != i_rXmlIdMap.end());
419 return id;
422 //=============================================================================
423 // Document XML ID Registry (_Impl)
425 /// element list
426 typedef ::std::list< Metadatable* > XmlIdList_t;
428 /// Idref -> (content.xml element list, styles.xml element list)
429 typedef ::boost::unordered_map< ::rtl::OUString,
430 ::std::pair< XmlIdList_t, XmlIdList_t >, ::rtl::OUStringHash > XmlIdMap_t;
432 /// pointer hash template
433 template<typename T> struct PtrHash
435 size_t operator() (T const * i_pT) const
437 return reinterpret_cast<size_t>(i_pT);
441 /// element -> (stream name, idref)
442 typedef ::boost::unordered_map< const Metadatable*,
443 ::std::pair< ::rtl::OUString, ::rtl::OUString>, PtrHash<Metadatable> >
444 XmlIdReverseMap_t;
446 struct XmlIdRegistryDocument::XmlIdRegistry_Impl
448 XmlIdRegistry_Impl()
449 : m_XmlIdMap(), m_XmlIdReverseMap() { }
451 bool TryInsertMetadatable(Metadatable& i_xObject,
452 const ::rtl::OUString & i_rStream, const ::rtl::OUString & i_rIdref);
454 bool LookupXmlId(const Metadatable& i_xObject,
455 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const;
457 Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName,
458 const ::rtl::OUString & i_rIdref) const;
460 const XmlIdList_t * LookupElementList(
461 const ::rtl::OUString & i_rStreamName,
462 const ::rtl::OUString & i_rIdref) const;
464 XmlIdList_t * LookupElementList(
465 const ::rtl::OUString & i_rStreamName,
466 const ::rtl::OUString & i_rIdref)
468 return const_cast<XmlIdList_t*>(
469 const_cast<const XmlIdRegistry_Impl*>(this)
470 ->LookupElementList(i_rStreamName, i_rIdref));
473 XmlIdMap_t m_XmlIdMap;
474 XmlIdReverseMap_t m_XmlIdReverseMap;
477 // -------------------------------------------------------------------
479 static void
480 rmIter(XmlIdMap_t & i_rXmlIdMap, XmlIdMap_t::iterator const& i_rIter,
481 ::rtl::OUString const & i_rStream, Metadatable const& i_rObject)
483 if (i_rIter != i_rXmlIdMap.end())
485 XmlIdList_t & rList( isContentFile(i_rStream)
486 ? i_rIter->second.first : i_rIter->second.second );
487 rList.remove(&const_cast<Metadatable&>(i_rObject));
488 if (i_rIter->second.first.empty() && i_rIter->second.second.empty())
490 i_rXmlIdMap.erase(i_rIter);
495 // -------------------------------------------------------------------
497 const XmlIdList_t *
498 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElementList(
499 const ::rtl::OUString & i_rStreamName,
500 const ::rtl::OUString & i_rIdref) const
502 const XmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
503 if (iter != m_XmlIdMap.end())
505 OSL_ENSURE(!iter->second.first.empty() || !iter->second.second.empty(),
506 "null entry in m_XmlIdMap");
507 return (isContentFile(i_rStreamName))
508 ? &iter->second.first
509 : &iter->second.second;
511 else
513 return 0;
517 Metadatable*
518 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElement(
519 const ::rtl::OUString & i_rStreamName,
520 const ::rtl::OUString & i_rIdref) const
522 if (!isValidXmlId(i_rStreamName, i_rIdref))
524 throw lang::IllegalArgumentException(::rtl::OUString(
525 "illegal XmlId"), 0, 0);
528 const XmlIdList_t * pList( LookupElementList(i_rStreamName, i_rIdref) );
529 if (pList)
531 const XmlIdList_t::const_iterator iter(
532 ::std::find_if(pList->begin(), pList->end(),
533 ::boost::bind(
534 ::std::logical_not<bool>(),
535 ::boost::bind(
536 ::std::logical_or<bool>(),
537 ::boost::bind( &Metadatable::IsInUndo, _1 ),
538 ::boost::bind( &Metadatable::IsInClipboard, _1 )
539 ) ) ) );
540 if (iter != pList->end())
542 return *iter;
545 return 0;
548 bool
549 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupXmlId(
550 const Metadatable& i_rObject,
551 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const
553 const XmlIdReverseMap_t::const_iterator iter(
554 m_XmlIdReverseMap.find(&i_rObject) );
555 if (iter != m_XmlIdReverseMap.end())
557 OSL_ENSURE(!iter->second.first.isEmpty(),
558 "null stream in m_XmlIdReverseMap");
559 OSL_ENSURE(!iter->second.second.isEmpty(),
560 "null id in m_XmlIdReverseMap");
561 o_rStream = iter->second.first;
562 o_rIdref = iter->second.second;
563 return true;
565 else
567 return false;
571 bool
572 XmlIdRegistryDocument::XmlIdRegistry_Impl::TryInsertMetadatable(
573 Metadatable & i_rObject,
574 const ::rtl::OUString & i_rStreamName, const ::rtl::OUString & i_rIdref)
576 const bool bContent( isContentFile(i_rStreamName) );
577 OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName),
578 "invalid stream");
580 XmlIdList_t * pList( LookupElementList(i_rStreamName, i_rIdref) );
581 if (pList)
583 if (pList->empty())
585 pList->push_back( &i_rObject );
586 return true;
588 else
590 // this is only called from TryRegister now, so check
591 // if all elements in the list are deleted (in undo) or
592 // placeholders, then "steal" the id from them
593 if ( pList->end() == ::std::find_if(pList->begin(), pList->end(),
594 ::boost::bind(
595 ::std::logical_not<bool>(),
596 ::boost::bind(
597 ::std::logical_or<bool>(),
598 ::boost::bind( &Metadatable::IsInUndo, _1 ),
599 ::boost::bind( &Metadatable::IsInClipboard, _1 )
600 ) ) ) )
602 pList->push_front( &i_rObject );
603 return true;
605 else
607 return false;
611 else
613 m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent
614 ? ::std::make_pair( XmlIdList_t( 1, &i_rObject ), XmlIdList_t() )
615 : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject ) )));
616 return true;
620 //=============================================================================
621 // Document XML ID Registry
624 XmlIdRegistryDocument::XmlIdRegistryDocument()
625 : m_pImpl( new XmlIdRegistry_Impl )
629 static void
630 removeLink(Metadatable* i_pObject)
632 OSL_ENSURE(i_pObject, "null in list ???");
633 if (!i_pObject) return;
634 if (i_pObject->IsInClipboard())
636 MetadatableClipboard* pLink(
637 dynamic_cast<MetadatableClipboard*>( i_pObject ) );
638 OSL_ENSURE(pLink, "IsInClipboard, but no MetadatableClipboard ?");
639 if (pLink)
641 pLink->OriginNoLongerInBusinessAnymore();
646 XmlIdRegistryDocument::~XmlIdRegistryDocument()
648 // notify all list elements that are actually in the clipboard
649 for (XmlIdMap_t::iterator iter(m_pImpl->m_XmlIdMap.begin());
650 iter != m_pImpl->m_XmlIdMap.end(); ++iter)
652 ::std::for_each(iter->second.first.begin(), iter->second.first.end(),
653 removeLink);
654 ::std::for_each(iter->second.second.begin(), iter->second.second.end(),
655 removeLink);
659 bool
660 XmlIdRegistryDocument::LookupXmlId(
661 const Metadatable& i_rObject,
662 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const
664 return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref);
667 Metadatable*
668 XmlIdRegistryDocument::LookupElement(
669 const ::rtl::OUString & i_rStreamName,
670 const ::rtl::OUString & i_rIdref) const
672 return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
675 bool
676 XmlIdRegistryDocument::TryRegisterMetadatable(Metadatable & i_rObject,
677 ::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref)
679 OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject,
680 ::rtl::OUStringToOString(i_rStreamName, RTL_TEXTENCODING_UTF8).getStr(),
681 ::rtl::OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8).getStr());
683 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
684 "TryRegisterMetadatable called for MetadatableUndo?");
685 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
686 "TryRegisterMetadatable called for MetadatableClipboard?");
688 if (!isValidXmlId(i_rStreamName, i_rIdref))
690 throw lang::IllegalArgumentException(::rtl::OUString(
691 "illegal XmlId"), 0, 0);
693 if (i_rObject.IsInContent()
694 ? !isContentFile(i_rStreamName)
695 : !isStylesFile(i_rStreamName))
697 throw lang::IllegalArgumentException(::rtl::OUString(
698 "illegal XmlId: wrong stream"), 0, 0);
701 ::rtl::OUString old_path;
702 ::rtl::OUString old_idref;
703 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
704 if (old_path == i_rStreamName && old_idref == i_rIdref)
706 return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
708 XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
709 if (!old_idref.isEmpty())
711 old_id = m_pImpl->m_XmlIdMap.find(old_idref);
712 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
714 if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
716 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
717 m_pImpl->m_XmlIdReverseMap[&i_rObject] =
718 ::std::make_pair(i_rStreamName, i_rIdref);
719 return true;
721 else
723 return false;
727 void
728 XmlIdRegistryDocument::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
730 OSL_TRACE("RegisterMetadatableAndCreateID: %p", &i_rObject);
732 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
733 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
734 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
735 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
737 const bool isInContent( i_rObject.IsInContent() );
738 const ::rtl::OUString stream( ::rtl::OUString::createFromAscii(
739 isInContent ? s_content : s_styles ) );
740 // check if we have a latent xmlid, and if yes, remove it
741 ::rtl::OUString old_path;
742 ::rtl::OUString old_idref;
743 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
745 XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
746 if (!old_idref.isEmpty())
748 old_id = m_pImpl->m_XmlIdMap.find(old_idref);
749 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
750 if (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject)
752 return;
754 else
756 // remove latent xmlid
757 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
761 // create id
762 const ::rtl::OUString id( create_id(m_pImpl->m_XmlIdMap) );
763 OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(),
764 "created id is in use");
765 m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent
766 ? ::std::make_pair( XmlIdList_t( 1, &i_rObject ), XmlIdList_t() )
767 : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject ) )));
768 m_pImpl->m_XmlIdReverseMap[&i_rObject] = ::std::make_pair(stream, id);
771 void XmlIdRegistryDocument::UnregisterMetadatable(const Metadatable& i_rObject)
773 OSL_TRACE("UnregisterMetadatable: %p", &i_rObject);
775 ::rtl::OUString path;
776 ::rtl::OUString idref;
777 if (!m_pImpl->LookupXmlId(i_rObject, path, idref))
779 OSL_FAIL("unregister: no xml id?");
780 return;
782 const XmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
783 if (iter != m_pImpl->m_XmlIdMap.end())
785 rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
789 void XmlIdRegistryDocument::RemoveXmlIdForElement(const Metadatable& i_rObject)
791 OSL_TRACE("RemoveXmlIdForElement: %p", &i_rObject);
793 const XmlIdReverseMap_t::iterator iter(
794 m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
795 if (iter != m_pImpl->m_XmlIdReverseMap.end())
797 OSL_ENSURE(!iter->second.second.isEmpty(),
798 "null id in m_XmlIdReverseMap");
799 m_pImpl->m_XmlIdReverseMap.erase(iter);
803 // -------------------------------------------------------------------
805 void XmlIdRegistryDocument::RegisterCopy(Metadatable const& i_rSource,
806 Metadatable & i_rCopy, const bool i_bCopyPrecedesSource)
808 OSL_TRACE("RegisterCopy: %p -> %p (%d)\n",
809 &i_rSource, &i_rCopy, i_bCopyPrecedesSource);
811 // potential sources: clipboard, undo array, splitNode
812 // assumption: stream change can only happen via clipboard, and is handled
813 // by Metadatable::RegisterAsCopyOf
814 OSL_ENSURE(i_rSource.IsInUndo() || i_rCopy.IsInUndo() ||
815 (i_rSource.IsInContent() == i_rCopy.IsInContent()),
816 "RegisterCopy: not in same stream?");
818 ::rtl::OUString path;
819 ::rtl::OUString idref;
820 if (!m_pImpl->LookupXmlId( i_rSource, path, idref ))
822 OSL_FAIL("no xml id?");
823 return;
825 XmlIdList_t * pList ( m_pImpl->LookupElementList(path, idref) );
826 OSL_ENSURE( ::std::find( pList->begin(), pList->end(), &i_rCopy )
827 == pList->end(), "copy already registered???");
828 XmlIdList_t::iterator srcpos(
829 ::std::find( pList->begin(), pList->end(), &i_rSource ) );
830 OSL_ENSURE(srcpos != pList->end(), "source not in list???");
831 if (srcpos == pList->end())
833 return;
835 if (i_bCopyPrecedesSource)
837 pList->insert( srcpos, &i_rCopy );
839 else
841 // for undo push_back does not work! must insert right after source
842 pList->insert( ++srcpos, &i_rCopy );
844 m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy,
845 ::std::make_pair(path, idref)));
848 ::boost::shared_ptr<MetadatableUndo>
849 XmlIdRegistryDocument::CreateUndo(Metadatable const& i_rObject)
851 OSL_TRACE("CreateUndo: %p", &i_rObject);
853 return ::boost::shared_ptr<MetadatableUndo>(
854 new MetadatableUndo(i_rObject.IsInContent()) );
858 i_rMerged is both a source and the target node of the merge
859 i_rOther is the other source, and will be deleted after the merge
861 dimensions: none|latent|actual empty|nonempty
862 i_rMerged(1) i_rOther(2) result
863 *|empty *|empty => 1|2 (arbitrary)
864 *|empty *|nonempty => 2
865 *|nonempty *|empty => 1
866 none|nonempty none|nonempty => none
867 none|nonempty latent|nonempty => 2
868 latent|nonempty none|nonempty => 1
869 latent|nonempty latent|nonempty => 1|2
870 *|nonempty actual|nonempty => 2
871 actual|nonempty *|nonempty => 1
872 actual|nonempty actual|nonempty => 1|2
874 void
875 XmlIdRegistryDocument::JoinMetadatables(
876 Metadatable & i_rMerged, Metadatable const & i_rOther)
878 OSL_TRACE("JoinMetadatables: %p <- %p", &i_rMerged, &i_rOther);
880 bool mergedOwnsRef;
881 ::rtl::OUString path;
882 ::rtl::OUString idref;
883 if (m_pImpl->LookupXmlId(i_rMerged, path, idref))
885 mergedOwnsRef = (m_pImpl->LookupElement(path, idref) == &i_rMerged);
887 else
889 OSL_FAIL("JoinMetadatables: no xmlid?");
890 return;
892 if (!mergedOwnsRef)
894 i_rMerged.RemoveMetadataReference();
895 i_rMerged.RegisterAsCopyOf(i_rOther, true);
896 return;
898 // other cases: merged has actual ref and is nonempty,
899 // other has latent/actual ref and is nonempty: other loses => nothing to do
903 //=============================================================================
904 // Clipboard XML ID Registry (_Impl)
906 struct RMapEntry
908 RMapEntry() : m_pLink() { }
909 RMapEntry(::rtl::OUString const& i_rStream,
910 ::rtl::OUString const& i_rXmlId,
911 ::boost::shared_ptr<MetadatableClipboard> const& i_pLink
912 = ::boost::shared_ptr<MetadatableClipboard>())
913 : m_Stream(i_rStream), m_XmlId(i_rXmlId), m_pLink(i_pLink)
915 ::rtl::OUString m_Stream;
916 ::rtl::OUString m_XmlId;
917 // this would have been an auto_ptr, if only that would have compiled...
918 ::boost::shared_ptr<MetadatableClipboard> m_pLink;
921 /// element -> (stream name, idref, source)
922 typedef ::boost::unordered_map< const Metadatable*,
923 struct RMapEntry,
924 PtrHash<Metadatable> >
925 ClipboardXmlIdReverseMap_t;
927 /// Idref -> (content.xml element, styles.xml element)
928 typedef ::boost::unordered_map< ::rtl::OUString,
929 ::std::pair< Metadatable*, Metadatable* >, ::rtl::OUStringHash >
930 ClipboardXmlIdMap_t;
932 struct XmlIdRegistryClipboard::XmlIdRegistry_Impl
934 XmlIdRegistry_Impl()
935 : m_XmlIdMap(), m_XmlIdReverseMap() { }
937 bool TryInsertMetadatable(Metadatable& i_xObject,
938 const ::rtl::OUString & i_rStream, const ::rtl::OUString & i_rIdref);
940 bool LookupXmlId(const Metadatable& i_xObject,
941 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref,
942 MetadatableClipboard const* &o_rpLink) const;
944 Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName,
945 const ::rtl::OUString & i_rIdref) const;
947 Metadatable* const* LookupEntry(const ::rtl::OUString & i_rStreamName,
948 const ::rtl::OUString & i_rIdref) const;
950 Metadatable* * LookupEntry(const ::rtl::OUString & i_rStreamName,
951 const ::rtl::OUString & i_rIdref)
953 return const_cast<Metadatable**>(
954 const_cast<const XmlIdRegistry_Impl*>(this)
955 ->LookupEntry(i_rStreamName, i_rIdref));
958 ClipboardXmlIdMap_t m_XmlIdMap;
959 ClipboardXmlIdReverseMap_t m_XmlIdReverseMap;
962 // -------------------------------------------------------------------
964 static void
965 rmIter(ClipboardXmlIdMap_t & i_rXmlIdMap,
966 ClipboardXmlIdMap_t::iterator const& i_rIter,
967 ::rtl::OUString const & i_rStream, Metadatable const& i_rObject)
969 if (i_rIter != i_rXmlIdMap.end())
971 Metadatable *& rMeta = isContentFile(i_rStream)
972 ? i_rIter->second.first : i_rIter->second.second;
973 if (rMeta == &i_rObject)
975 rMeta = 0;
977 if (!i_rIter->second.first && !i_rIter->second.second)
979 i_rXmlIdMap.erase(i_rIter);
984 // -------------------------------------------------------------------
986 Metadatable* const*
987 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupEntry(
988 const ::rtl::OUString & i_rStreamName,
989 const ::rtl::OUString & i_rIdref) const
991 if (!isValidXmlId(i_rStreamName, i_rIdref))
993 throw lang::IllegalArgumentException(::rtl::OUString(
994 "illegal XmlId"), 0, 0);
997 const ClipboardXmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
998 if (iter != m_XmlIdMap.end())
1000 OSL_ENSURE(iter->second.first || iter->second.second,
1001 "null entry in m_XmlIdMap");
1002 return (isContentFile(i_rStreamName))
1003 ? &iter->second.first
1004 : &iter->second.second;
1006 else
1008 return 0;
1012 Metadatable*
1013 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupElement(
1014 const ::rtl::OUString & i_rStreamName,
1015 const ::rtl::OUString & i_rIdref) const
1017 Metadatable * const * ppEntry = LookupEntry(i_rStreamName, i_rIdref);
1018 return ppEntry ? *ppEntry : 0;
1021 bool
1022 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupXmlId(
1023 const Metadatable& i_rObject,
1024 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref,
1025 MetadatableClipboard const* &o_rpLink) const
1027 const ClipboardXmlIdReverseMap_t::const_iterator iter(
1028 m_XmlIdReverseMap.find(&i_rObject) );
1029 if (iter != m_XmlIdReverseMap.end())
1031 OSL_ENSURE(!iter->second.m_Stream.isEmpty(),
1032 "null stream in m_XmlIdReverseMap");
1033 OSL_ENSURE(!iter->second.m_XmlId.isEmpty(),
1034 "null id in m_XmlIdReverseMap");
1035 o_rStream = iter->second.m_Stream;
1036 o_rIdref = iter->second.m_XmlId;
1037 o_rpLink = iter->second.m_pLink.get();
1038 return true;
1040 else
1042 return false;
1046 bool
1047 XmlIdRegistryClipboard::XmlIdRegistry_Impl::TryInsertMetadatable(
1048 Metadatable & i_rObject,
1049 const ::rtl::OUString & i_rStreamName, const ::rtl::OUString & i_rIdref)
1051 bool bContent( isContentFile(i_rStreamName) );
1052 OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName),
1053 "invalid stream");
1055 Metadatable ** ppEntry = LookupEntry(i_rStreamName, i_rIdref);
1056 if (ppEntry)
1058 if (*ppEntry)
1060 return false;
1062 else
1064 *ppEntry = &i_rObject;
1065 return true;
1068 else
1070 m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent
1071 ? ::std::make_pair( &i_rObject, static_cast<Metadatable*>(0) )
1072 : ::std::make_pair( static_cast<Metadatable*>(0), &i_rObject )));
1073 return true;
1077 //=============================================================================
1078 // Clipboard XML ID Registry
1081 XmlIdRegistryClipboard::XmlIdRegistryClipboard()
1082 : m_pImpl( new XmlIdRegistry_Impl )
1086 XmlIdRegistryClipboard::~XmlIdRegistryClipboard()
1090 bool
1091 XmlIdRegistryClipboard::LookupXmlId(
1092 const Metadatable& i_rObject,
1093 ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const
1095 const MetadatableClipboard * pLink;
1096 return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref, pLink);
1099 Metadatable*
1100 XmlIdRegistryClipboard::LookupElement(
1101 const ::rtl::OUString & i_rStreamName,
1102 const ::rtl::OUString & i_rIdref) const
1104 return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
1107 bool
1108 XmlIdRegistryClipboard::TryRegisterMetadatable(Metadatable & i_rObject,
1109 ::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref)
1111 OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject,
1112 ::rtl::OUStringToOString(i_rStreamName, RTL_TEXTENCODING_UTF8).getStr(),
1113 ::rtl::OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8).getStr());
1115 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
1116 "TryRegisterMetadatable called for MetadatableUndo?");
1117 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
1118 "TryRegisterMetadatable called for MetadatableClipboard?");
1120 if (!isValidXmlId(i_rStreamName, i_rIdref))
1122 throw lang::IllegalArgumentException(::rtl::OUString(
1123 "illegal XmlId"), 0, 0);
1125 if (i_rObject.IsInContent()
1126 ? !isContentFile(i_rStreamName)
1127 : !isStylesFile(i_rStreamName))
1129 throw lang::IllegalArgumentException(::rtl::OUString(
1130 "illegal XmlId: wrong stream"), 0, 0);
1133 ::rtl::OUString old_path;
1134 ::rtl::OUString old_idref;
1135 const MetadatableClipboard * pLink;
1136 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref, pLink);
1137 if (old_path == i_rStreamName && old_idref == i_rIdref)
1139 return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
1141 ClipboardXmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
1142 if (!old_idref.isEmpty())
1144 old_id = m_pImpl->m_XmlIdMap.find(old_idref);
1145 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
1147 if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
1149 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
1150 m_pImpl->m_XmlIdReverseMap[&i_rObject] =
1151 RMapEntry(i_rStreamName, i_rIdref);
1152 return true;
1154 else
1156 return false;
1160 void
1161 XmlIdRegistryClipboard::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
1163 OSL_TRACE("RegisterMetadatableAndCreateID: %p", &i_rObject);
1165 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
1166 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
1167 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
1168 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
1170 bool isInContent( i_rObject.IsInContent() );
1171 ::rtl::OUString stream( ::rtl::OUString::createFromAscii(
1172 isInContent ? s_content : s_styles ) );
1174 ::rtl::OUString old_path;
1175 ::rtl::OUString old_idref;
1176 LookupXmlId(i_rObject, old_path, old_idref);
1177 if (!old_idref.isEmpty() &&
1178 (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject))
1180 return;
1183 // create id
1184 const ::rtl::OUString id( create_id(m_pImpl->m_XmlIdMap) );
1185 OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(),
1186 "created id is in use");
1187 m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent
1188 ? ::std::make_pair( &i_rObject, static_cast<Metadatable*>(0) )
1189 : ::std::make_pair( static_cast<Metadatable*>(0), &i_rObject )));
1190 // N.B.: if i_rObject had a latent XmlId, then we implicitly delete the
1191 // MetadatableClipboard and thus the latent XmlId here
1192 m_pImpl->m_XmlIdReverseMap[&i_rObject] = RMapEntry(stream, id);
1195 void XmlIdRegistryClipboard::UnregisterMetadatable(const Metadatable& i_rObject)
1197 OSL_TRACE("UnregisterMetadatable: %p", &i_rObject);
1199 ::rtl::OUString path;
1200 ::rtl::OUString idref;
1201 const MetadatableClipboard * pLink;
1202 if (!m_pImpl->LookupXmlId(i_rObject, path, idref, pLink))
1204 OSL_FAIL("unregister: no xml id?");
1205 return;
1207 const ClipboardXmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
1208 if (iter != m_pImpl->m_XmlIdMap.end())
1210 rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
1215 void XmlIdRegistryClipboard::RemoveXmlIdForElement(const Metadatable& i_rObject)
1217 OSL_TRACE("RemoveXmlIdForElement: %p", &i_rObject);
1219 ClipboardXmlIdReverseMap_t::iterator iter(
1220 m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
1221 if (iter != m_pImpl->m_XmlIdReverseMap.end())
1223 OSL_ENSURE(!iter->second.m_XmlId.isEmpty(),
1224 "null id in m_XmlIdReverseMap");
1225 m_pImpl->m_XmlIdReverseMap.erase(iter);
1229 // -------------------------------------------------------------------
1231 ::boost::shared_ptr<MetadatableClipboard>
1232 XmlIdRegistryClipboard::CreateClipboard(const bool i_isInContent)
1234 OSL_TRACE("CreateClipboard:");
1236 return ::boost::shared_ptr<MetadatableClipboard>(
1237 new MetadatableClipboard(i_isInContent) );
1240 MetadatableClipboard &
1241 XmlIdRegistryClipboard::RegisterCopyClipboard(Metadatable & i_rCopy,
1242 beans::StringPair const & i_rReference,
1243 const bool i_isLatent)
1245 OSL_TRACE("RegisterCopyClipboard: %p -> "/*"%p"*/"(%s#%s) (%d)\n",
1246 /*&i_rSource,*/ &i_rCopy,
1247 ::rtl::OUStringToOString(i_rReference.First,
1248 RTL_TEXTENCODING_UTF8).getStr(),
1249 ::rtl::OUStringToOString(i_rReference.Second,
1250 RTL_TEXTENCODING_UTF8).getStr(),
1251 i_isLatent);
1253 // N.B.: when copying to the clipboard, the selection is always inserted
1254 // into the body, even if the source is a header/footer!
1255 // so we do not check whether the stream is right in this function
1257 if (!isValidXmlId(i_rReference.First, i_rReference.Second))
1259 throw lang::IllegalArgumentException(::rtl::OUString(
1260 "illegal XmlId"), 0, 0);
1263 if (!i_isLatent)
1265 // this should succeed assuming clipboard has a single source document
1266 const bool success( m_pImpl->TryInsertMetadatable(i_rCopy,
1267 i_rReference.First, i_rReference.Second) );
1268 OSL_ENSURE(success, "RegisterCopyClipboard: TryInsert failed?");
1269 (void) success;
1271 const ::boost::shared_ptr<MetadatableClipboard> pLink(
1272 CreateClipboard( isContentFile(i_rReference.First)) );
1273 m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy,
1274 RMapEntry(i_rReference.First, i_rReference.Second, pLink)));
1275 return *pLink.get();
1278 MetadatableClipboard const*
1279 XmlIdRegistryClipboard::SourceLink(Metadatable const& i_rObject)
1281 ::rtl::OUString path;
1282 ::rtl::OUString idref;
1283 const MetadatableClipboard * pLink( 0 );
1284 m_pImpl->LookupXmlId(i_rObject, path, idref, pLink);
1285 return pLink;
1289 //=============================================================================
1290 // Metadatable mixin
1293 Metadatable::~Metadatable()
1295 RemoveMetadataReference();
1298 void Metadatable::RemoveMetadataReference()
1302 if (m_pReg)
1304 m_pReg->UnregisterMetadatable( *this );
1305 m_pReg->RemoveXmlIdForElement( *this );
1306 m_pReg = 0;
1309 catch (const uno::Exception &)
1311 OSL_FAIL("Metadatable::RemoveMetadataReference: exception");
1315 // ::com::sun::star::rdf::XMetadatable:
1316 beans::StringPair
1317 Metadatable::GetMetadataReference() const
1319 if (m_pReg)
1321 return m_pReg->GetXmlIdForElement(*this);
1323 return beans::StringPair();
1326 void
1327 Metadatable::SetMetadataReference(
1328 const ::com::sun::star::beans::StringPair & i_rReference)
1330 if (i_rReference.Second.isEmpty())
1332 RemoveMetadataReference();
1334 else
1336 ::rtl::OUString streamName( i_rReference.First );
1337 if (streamName.isEmpty())
1339 // handle empty stream name as auto-detect.
1340 // necessary for importing flat file format.
1341 streamName = ::rtl::OUString::createFromAscii(
1342 IsInContent() ? s_content : s_styles );
1344 XmlIdRegistry & rReg( dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1345 if (rReg.TryRegisterMetadatable(*this, streamName, i_rReference.Second))
1347 m_pReg = &rReg;
1349 else
1351 throw lang::IllegalArgumentException(
1352 ::rtl::OUString("Metadatable::"
1353 "SetMetadataReference: argument is invalid"), /*this*/0, 0);
1358 void Metadatable::EnsureMetadataReference()
1360 XmlIdRegistry& rReg(
1361 m_pReg ? *m_pReg : dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1362 rReg.RegisterMetadatableAndCreateID( *this );
1363 m_pReg = &rReg;
1366 const ::sfx2::IXmlIdRegistry& GetRegistryConst(Metadatable const& i_rObject)
1368 return const_cast< Metadatable& >( i_rObject ).GetRegistry();
1371 void
1372 Metadatable::RegisterAsCopyOf(Metadatable const & i_rSource,
1373 const bool i_bCopyPrecedesSource)
1375 OSL_ENSURE(typeid(*this) == typeid(i_rSource)
1376 || typeid(i_rSource) == typeid(MetadatableUndo)
1377 || typeid(*this) == typeid(MetadatableUndo)
1378 || typeid(i_rSource) == typeid(MetadatableClipboard)
1379 || typeid(*this) == typeid(MetadatableClipboard),
1380 "RegisterAsCopyOf element with different class?");
1381 OSL_ENSURE(!this->m_pReg, "RegisterAsCopyOf called on element with XmlId?");
1383 if (this->m_pReg)
1385 RemoveMetadataReference();
1390 if (i_rSource.m_pReg)
1392 XmlIdRegistry & rReg(
1393 dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1394 if (i_rSource.m_pReg == &rReg)
1396 OSL_ENSURE(!IsInClipboard(),
1397 "RegisterAsCopy: both in clipboard?");
1398 if (!IsInClipboard())
1400 XmlIdRegistryDocument & rRegDoc(
1401 dynamic_cast<XmlIdRegistryDocument&>( rReg ) );
1402 rRegDoc.RegisterCopy(i_rSource, *this,
1403 i_bCopyPrecedesSource);
1404 this->m_pReg = &rRegDoc;
1406 return;
1408 // source is in different document
1409 XmlIdRegistryDocument * pRegDoc(
1410 dynamic_cast<XmlIdRegistryDocument *>(&rReg) );
1411 XmlIdRegistryClipboard * pRegClp(
1412 dynamic_cast<XmlIdRegistryClipboard*>(&rReg) );
1414 if (pRegClp)
1416 beans::StringPair SourceRef(
1417 i_rSource.m_pReg->GetXmlIdForElement(i_rSource) );
1418 bool isLatent( SourceRef.Second.isEmpty() );
1419 XmlIdRegistryDocument * pSourceRegDoc(
1420 dynamic_cast<XmlIdRegistryDocument*>(i_rSource.m_pReg) );
1421 OSL_ENSURE(pSourceRegDoc, "RegisterAsCopyOf: 2 clipboards?");
1422 if (!pSourceRegDoc) return;
1423 // this is a copy _to_ the clipboard
1424 if (isLatent)
1426 pSourceRegDoc->LookupXmlId(i_rSource,
1427 SourceRef.First, SourceRef.Second);
1429 Metadatable & rLink(
1430 pRegClp->RegisterCopyClipboard(*this, SourceRef, isLatent));
1431 this->m_pReg = pRegClp;
1432 // register as copy in the non-clipboard registry
1433 pSourceRegDoc->RegisterCopy(i_rSource, rLink,
1434 false); // i_bCopyPrecedesSource);
1435 rLink.m_pReg = pSourceRegDoc;
1437 else if (pRegDoc)
1439 XmlIdRegistryClipboard * pSourceRegClp(
1440 dynamic_cast<XmlIdRegistryClipboard*>(i_rSource.m_pReg) );
1441 OSL_ENSURE(pSourceRegClp,
1442 "RegisterAsCopyOf: 2 non-clipboards?");
1443 if (!pSourceRegClp) return;
1444 const MetadatableClipboard * pLink(
1445 pSourceRegClp->SourceLink(i_rSource) );
1446 // may happen if src got its id via UNO call
1447 if (!pLink) return;
1448 // only register copy if clipboard content is from this SwDoc!
1449 if (pLink && (&GetRegistryConst(*pLink) == pRegDoc))
1451 // this is a copy _from_ the clipboard; check if the
1452 // element is still in the same stream
1453 // N.B.: we check the stream of pLink, not of i_rSource!
1454 bool srcInContent( pLink->IsInContent() );
1455 bool tgtInContent( this->IsInContent() );
1456 if (srcInContent == tgtInContent)
1458 pRegDoc->RegisterCopy(*pLink, *this,
1459 true); // i_bCopyPrecedesSource);
1460 this->m_pReg = pRegDoc;
1462 // otherwise: stream change! do not register!
1465 else
1467 OSL_FAIL("neither RegDoc nor RegClp cannot happen");
1471 catch (const uno::Exception &)
1473 OSL_FAIL("Metadatable::RegisterAsCopyOf: exception");
1477 ::boost::shared_ptr<MetadatableUndo> Metadatable::CreateUndo() const
1479 OSL_ENSURE(!IsInUndo(), "CreateUndo called for object in undo?");
1480 OSL_ENSURE(!IsInClipboard(), "CreateUndo called for object in clipboard?");
1483 if (!IsInClipboard() && !IsInUndo() && m_pReg)
1485 XmlIdRegistryDocument * pRegDoc(
1486 dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
1487 ::boost::shared_ptr<MetadatableUndo> pUndo(
1488 pRegDoc->CreateUndo(*this) );
1489 pRegDoc->RegisterCopy(*this, *pUndo, false);
1490 pUndo->m_pReg = pRegDoc;
1491 return pUndo;
1494 catch (const uno::Exception &)
1496 OSL_FAIL("Metadatable::CreateUndo: exception");
1498 return ::boost::shared_ptr<MetadatableUndo>();
1501 ::boost::shared_ptr<MetadatableUndo> Metadatable::CreateUndoForDelete()
1503 ::boost::shared_ptr<MetadatableUndo> const pUndo( CreateUndo() );
1504 RemoveMetadataReference();
1505 return pUndo;
1508 void Metadatable::RestoreMetadata(
1509 ::boost::shared_ptr<MetadatableUndo> const& i_pUndo)
1511 OSL_ENSURE(!IsInUndo(), "RestoreMetadata called for object in undo?");
1512 OSL_ENSURE(!IsInClipboard(),
1513 "RestoreMetadata called for object in clipboard?");
1514 if (IsInClipboard() || IsInUndo()) return;
1515 RemoveMetadataReference();
1516 if (i_pUndo)
1518 this->RegisterAsCopyOf(*i_pUndo, true);
1522 void
1523 Metadatable::JoinMetadatable(Metadatable const & i_rOther,
1524 const bool i_isMergedEmpty, const bool i_isOtherEmpty)
1526 OSL_ENSURE(!IsInUndo(), "JoinMetadatables called for object in undo?");
1527 OSL_ENSURE(!IsInClipboard(),
1528 "JoinMetadatables called for object in clipboard?");
1529 if (IsInClipboard() || IsInUndo()) return;
1531 if (i_isOtherEmpty && !i_isMergedEmpty)
1533 // other is empty, thus loses => nothing to do
1534 return;
1536 if (i_isMergedEmpty && !i_isOtherEmpty)
1538 this->RemoveMetadataReference();
1539 this->RegisterAsCopyOf(i_rOther, true);
1540 return;
1543 if (!i_rOther.m_pReg)
1545 // other doesn't have xmlid, thus loses => nothing to do
1546 return;
1548 if (!m_pReg)
1550 this->RegisterAsCopyOf(i_rOther, true);
1551 // assumption: i_rOther will be deleted, so don't unregister it here
1552 return;
1556 XmlIdRegistryDocument * pRegDoc(
1557 dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
1558 OSL_ENSURE(pRegDoc, "JoinMetadatable: no pRegDoc?");
1559 if (pRegDoc)
1561 pRegDoc->JoinMetadatables(*this, i_rOther);
1564 catch (const uno::Exception &)
1566 OSL_FAIL("Metadatable::JoinMetadatable: exception");
1571 //=============================================================================
1572 // XMetadatable mixin
1574 // ::com::sun::star::rdf::XNode:
1575 ::rtl::OUString SAL_CALL MetadatableMixin::getStringValue()
1576 throw (::com::sun::star::uno::RuntimeException)
1578 return getNamespace() + getLocalName();
1581 // ::com::sun::star::rdf::XURI:
1582 ::rtl::OUString SAL_CALL MetadatableMixin::getLocalName()
1583 throw (::com::sun::star::uno::RuntimeException)
1585 SolarMutexGuard aGuard;
1586 beans::StringPair mdref( getMetadataReference() );
1587 if (mdref.Second.isEmpty())
1589 ensureMetadataReference(); // N.B.: side effect!
1590 mdref = getMetadataReference();
1592 ::rtl::OUStringBuffer buf;
1593 buf.append(mdref.First);
1594 buf.append(static_cast<sal_Unicode>('#'));
1595 buf.append(mdref.Second);
1596 return buf.makeStringAndClear();
1599 ::rtl::OUString SAL_CALL MetadatableMixin::getNamespace()
1600 throw (::com::sun::star::uno::RuntimeException)
1602 SolarMutexGuard aGuard;
1603 const uno::Reference< frame::XModel > xModel( GetModel() );
1604 const uno::Reference< rdf::XURI > xDMA( xModel, uno::UNO_QUERY_THROW );
1605 return xDMA->getStringValue();
1608 // ::com::sun::star::rdf::XMetadatable:
1609 beans::StringPair SAL_CALL
1610 MetadatableMixin::getMetadataReference()
1611 throw (uno::RuntimeException)
1613 SolarMutexGuard aGuard;
1615 Metadatable *const pObject( GetCoreObject() );
1616 if (!pObject)
1618 throw uno::RuntimeException(
1619 ::rtl::OUString(
1620 "MetadatableMixin: cannot get core object; not inserted?"),
1621 *this);
1623 return pObject->GetMetadataReference();
1626 void SAL_CALL
1627 MetadatableMixin::setMetadataReference(
1628 const beans::StringPair & i_rReference)
1629 throw (uno::RuntimeException, lang::IllegalArgumentException)
1631 SolarMutexGuard aGuard;
1633 Metadatable *const pObject( GetCoreObject() );
1634 if (!pObject)
1636 throw uno::RuntimeException(
1637 ::rtl::OUString(
1638 "MetadatableMixin: cannot get core object; not inserted?"),
1639 *this);
1641 return pObject->SetMetadataReference(i_rReference);
1644 void SAL_CALL MetadatableMixin::ensureMetadataReference()
1645 throw (uno::RuntimeException)
1647 SolarMutexGuard aGuard;
1649 Metadatable *const pObject( GetCoreObject() );
1650 if (!pObject)
1652 throw uno::RuntimeException(
1653 ::rtl::OUString(
1654 "MetadatableMixin: cannot get core object; not inserted?"),
1655 *this);
1657 return pObject->EnsureMetadataReference();
1660 } // namespace sfx2
1663 //=============================================================================
1665 #if OSL_DEBUG_LEVEL > 1
1667 #include <stdio.h>
1669 static void dump(sfx2::XmlIdList_t * pList)
1670 #ifdef GCC
1671 __attribute__ ((unused))
1672 #endif
1674 static void dump(sfx2::XmlIdList_t * pList)
1676 fprintf(stderr, "\nXmlIdList(%p): ", pList);
1677 for (sfx2::XmlIdList_t::iterator i = pList->begin(); i != pList->end(); ++i)
1679 fprintf(stderr, "%p ", *i);
1681 fprintf(stderr, "\n");
1684 #endif
1686 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */