build fix
[LibreOffice.git] / sfx2 / source / doc / Metadatable.cxx
blob5382cc3db4271569253d2b1a9b057281e4d3dc75
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>
27 #include <comphelper/random.hxx>
29 #include <algorithm>
30 #include <list>
31 #include <memory>
32 #include <unordered_map>
33 #if OSL_DEBUG_LEVEL > 0
34 #include <typeinfo>
35 #endif
38 /** XML ID handling.
40 There is an abstract base class <type>XmlIdRegistry</type>, with
41 2 subclasses <type>XmlIdRegistryDocument</type> for "normal" documents,
42 and <type>XmlIdRegistryClipboard</type> for clipboard documents.
43 These classes are responsible for managing XML IDs for all elements
44 of the model. Only the implementation of the <type>Metadatable</type>
45 base class needs to know the registries, so they are not in the header.
47 The handling of XML IDs differs between clipboard and non-clipboard
48 documents in several aspects. Most importantly, non-clipboard documents
49 can have several elements associated with one XML ID.
50 This is necessary because of the weird undo implementation:
51 deleting a text node moves the deleted node to the undo array, but
52 executing undo will then create a <em>copy</em> of that node in the
53 document array. These 2 nodes must have the same XML ID, because
54 we cannot know whether the user will do a redo next, or something else.
56 Because we need to have a mechanism for several objects per XML ID anyway,
57 we use that also to enable some usability features:
58 The document registry has a list of Metadatables per XML ID.
59 This list is sorted by priority, i.e., the first element has highest
60 priority. When inserting copies, care must be taken that they are inserted
61 at the right position: either before or after the source.
62 This is done by <method>Metadatable::RegisterAsCopyOf</method>.
63 When a text node is split, then both resulting text nodes are inserted
64 into the list. If the user then deletes one text node, the other one
65 will have the XML ID.
66 Also, when a Metadatable is copied to the clipboard and then pasted,
67 the copy is inserted into the list. If the user then deletes the source,
68 the XML ID is not lost.
69 The goal is that it should be hard to lose an XML ID by accident, which
70 is especially important as long as we do not have an UI that displays them.
72 There are two subclasses of <type>Metadatable</type>:
73 <ul><li><type>MetadatableClipboard</type>: for copies in the clipboard</li>
74 <li><type>MetadatableUndo</type>: for undo, because a Metadatable
75 may be destroyed on delete and a new one created on undo.</li></ul>
76 These serve only to track the position in an XML ID list in a document
77 registry, so that future actions can insert objects at the right position.
78 Unfortunately, inserting dummy objects seems to be necessary:
79 <ul><li>it is not sufficient to just remember the saved id, because then
80 the relative priorities might change when executing the undo</li>
81 <li>it is not sufficient to record the position as an integer, because
82 if we delete a text node and then undo, the node will be copied(!),
83 and we will have one more node in the list.<li>
84 <li>it is not sufficient to record the pointer of the previous/next
85 Metadatable, because if we delete a text node, undo, and then
86 do something to clear the redo array, the original text node is
87 destroyed, and is replaced by the copy created by undo</li></ul>
89 If content from a non-clipboard document is copied into a clipboard
90 document, a dummy <type>MetadatableClipboard</type> is inserted into the
91 non-clipboard document registry in order to track the position of the
92 source element. When the clipboard content is pasted back into the source
93 document, this dummy object is used to associate the pasted element with
94 that same XML ID.
96 If a <type>Metadatable</type> is deleted or merged,
97 <method>Metadatable::CreateUndo</method> is called, and returns a
98 <type>MetadatableUndo<type> instance, which can be used to undo the action
99 by passing it to <method>Metadatable::RestoreMetadata</method>.
104 using namespace ::com::sun::star;
106 using ::sfx2::isValidXmlId;
109 namespace sfx2 {
111 static const char s_content [] = "content.xml";
112 static const char s_styles [] = "styles.xml";
114 static bool isContentFile(OUString const & i_rPath)
116 return i_rPath == s_content;
119 static bool isStylesFile (OUString const & i_rPath)
121 return i_rPath == s_styles;
125 // XML ID handling ---------------------------------------------------
127 /** handles registration of XMetadatable.
129 This class is responsible for guaranteeing that XMetadatable objects
130 always have XML IDs that are unique within a stream.
132 This is an abstract base class; see subclasses XmlIdRegistryDocument and
133 XmlIdRegistryClipboard.
135 @see SwDoc::GetXmlIdRegistry
136 @see SwDocShell::GetXmlIdRegistry
138 class XmlIdRegistry : public sfx2::IXmlIdRegistry
141 public:
142 XmlIdRegistry();
144 virtual ~XmlIdRegistry() override;
146 /** get the ODF element with the given metadata reference. */
147 virtual css::uno::Reference< css::rdf::XMetadatable >
148 GetElementByMetadataReference(
149 const css::beans::StringPair & i_rReference) const
150 override;
152 /** register an ODF element at a newly generated, unique metadata reference.
155 Find a fresh XML ID, and register it for the element.
156 The generated ID does not occur in any stream of the document.
157 </p>
159 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) = 0;
161 /** try to register an ODF element at a given XML ID, or update its
162 registration to a different XML ID.
165 If the given new metadata reference is not already occupied in the
166 document, unregister the element at its old metadata reference if
167 it has one, and register the new metadata reference for the element.
168 Note that this method only ensures that XML IDs are unique per stream,
169 so using the same XML ID in both content.xml and styles.xml is allowed.
170 </p>
172 @returns
173 true iff the element has successfully been registered
175 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
176 OUString const& i_rStreamName, OUString const& i_rIdref)
177 = 0;
179 /** unregister an ODF element.
182 Unregister the element at its metadata reference.
183 Does not remove the metadata reference from the element.
184 </p>
186 @see RemoveXmlIdForElement
188 virtual void UnregisterMetadatable(Metadatable const&) = 0;
190 /** get the metadata reference for the given element. */
191 css::beans::StringPair
192 GetXmlIdForElement(Metadatable const&) const;
194 /** remove the metadata reference for the given element. */
195 virtual void RemoveXmlIdForElement(Metadatable const&) = 0;
197 protected:
199 virtual bool LookupXmlId(const Metadatable& i_xObject,
200 OUString & o_rStream, OUString & o_rIdref) const = 0;
202 virtual Metadatable* LookupElement(const OUString & i_rStreamName,
203 const OUString & i_rIdref) const = 0;
206 // XmlIdRegistryDocument ---------------------------------------------
208 /** non-clipboard documents */
209 class XmlIdRegistryDocument : public XmlIdRegistry
212 public:
213 XmlIdRegistryDocument();
215 virtual ~XmlIdRegistryDocument() override;
217 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) override;
219 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
220 OUString const& i_rStreamName, OUString const& i_rIdref) override;
222 virtual void UnregisterMetadatable(Metadatable const&) override;
224 virtual void RemoveXmlIdForElement(Metadatable const&) override;
226 /** register i_rCopy as a copy of i_rSource,
227 with precedence iff i_bCopyPrecedesSource is true */
228 void RegisterCopy(Metadatable const& i_rSource, Metadatable & i_rCopy,
229 const bool i_bCopyPrecedesSource);
231 /** create a Undo Metadatable for i_rObject. */
232 static std::shared_ptr<MetadatableUndo> CreateUndo(
233 Metadatable const& i_rObject);
235 /** merge i_rMerged and i_rOther into i_rMerged. */
236 void JoinMetadatables(Metadatable & i_rMerged, Metadatable const& i_rOther);
238 // unfortunately public, Metadatable::RegisterAsCopyOf needs this
239 virtual bool LookupXmlId(const Metadatable& i_xObject,
240 OUString & o_rStream, OUString & o_rIdref) const override;
242 private:
244 virtual Metadatable* LookupElement(const OUString & i_rStreamName,
245 const OUString & i_rIdref) const override;
247 struct XmlIdRegistry_Impl;
248 ::std::unique_ptr<XmlIdRegistry_Impl> m_pImpl;
251 // MetadatableUndo ---------------------------------------------------
253 /** the horrible Undo Metadatable: is inserted into lists to track position */
254 class MetadatableUndo : public Metadatable
256 /// as determined by the stream of the source in original document
257 const bool m_isInContent;
258 public:
259 explicit MetadatableUndo(const bool i_isInContent)
260 : m_isInContent(i_isInContent) { }
261 virtual ::sfx2::XmlIdRegistry& GetRegistry() override
263 // N.B. for Undo, m_pReg is initialized by registering this as copy in
264 // CreateUndo; it is never cleared
265 OSL_ENSURE(m_pReg, "no m_pReg in MetadatableUndo ?");
266 return *m_pReg;
268 virtual bool IsInClipboard() const override { return false; }
269 virtual bool IsInUndo() const override { return true; }
270 virtual bool IsInContent() const override { return m_isInContent; }
271 virtual css::uno::Reference< css::rdf::XMetadatable > MakeUnoObject() override
272 { OSL_FAIL("MetadatableUndo::MakeUnoObject"); throw; }
275 // MetadatableClipboard ----------------------------------------------
277 /** the horrible Clipboard Metadatable: inserted into lists to track position */
278 class MetadatableClipboard : public Metadatable
280 /// as determined by the stream of the source in original document
281 const bool m_isInContent;
282 public:
283 explicit MetadatableClipboard(const bool i_isInContent)
284 : m_isInContent(i_isInContent) { }
285 virtual ::sfx2::XmlIdRegistry& GetRegistry() override
287 // N.B. for Clipboard, m_pReg is initialized by registering this as copy in
288 // RegisterAsCopyOf; it is only cleared by OriginNoLongerInBusinessAnymore
289 assert(m_pReg && "no m_pReg in MetadatableClipboard ?");
290 return *m_pReg;
292 virtual bool IsInClipboard() const override { return true; }
293 virtual bool IsInUndo() const override { return false; }
294 virtual bool IsInContent() const override { return m_isInContent; }
295 virtual css::uno::Reference< css::rdf::XMetadatable > MakeUnoObject() override
296 { OSL_FAIL("MetadatableClipboard::MakeUnoObject"); throw; }
297 void OriginNoLongerInBusinessAnymore() { m_pReg = nullptr; }
300 // XmlIdRegistryClipboard --------------------------------------------
302 class XmlIdRegistryClipboard : public XmlIdRegistry
305 public:
306 XmlIdRegistryClipboard();
307 virtual ~XmlIdRegistryClipboard() override;
309 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) override;
311 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
312 OUString const& i_rStreamName, OUString const& i_rIdref) override;
314 virtual void UnregisterMetadatable(Metadatable const&) override;
316 virtual void RemoveXmlIdForElement(Metadatable const&) override;
318 /** register i_rCopy as a copy of i_rSource */
319 MetadatableClipboard & RegisterCopyClipboard(Metadatable & i_rCopy,
320 beans::StringPair const & i_rReference,
321 const bool i_isLatent);
323 /** get the Metadatable that links i_rObject to its origin registry */
324 MetadatableClipboard const* SourceLink(Metadatable const& i_rObject);
326 private:
327 virtual bool LookupXmlId(const Metadatable& i_xObject,
328 OUString & o_rStream, OUString & o_rIdref) const override;
330 virtual Metadatable* LookupElement(const OUString & i_rStreamName,
331 const OUString & i_rIdref) const override;
333 /** create a Clipboard Metadatable for i_rObject. */
334 static std::shared_ptr<MetadatableClipboard> CreateClipboard(
335 const bool i_isInContent);
337 struct XmlIdRegistry_Impl;
338 ::std::unique_ptr<XmlIdRegistry_Impl> m_pImpl;
342 // XmlIdRegistry
344 ::sfx2::IXmlIdRegistry * createXmlIdRegistry(const bool i_DocIsClipboard)
346 return i_DocIsClipboard
347 ? static_cast<XmlIdRegistry*>( new XmlIdRegistryClipboard )
348 : static_cast<XmlIdRegistry*>( new XmlIdRegistryDocument );
351 XmlIdRegistry::XmlIdRegistry()
355 XmlIdRegistry::~XmlIdRegistry()
359 css::uno::Reference< css::rdf::XMetadatable >
360 XmlIdRegistry::GetElementByMetadataReference(
361 const beans::StringPair & i_rReference) const
363 Metadatable* pObject( LookupElement(i_rReference.First,
364 i_rReference.Second) );
365 return pObject ? pObject->MakeUnoObject() : nullptr;
368 beans::StringPair
369 XmlIdRegistry::GetXmlIdForElement(const Metadatable& i_rObject) const
371 OUString path;
372 OUString idref;
373 if (LookupXmlId(i_rObject, path, idref))
375 if (LookupElement(path, idref) == &i_rObject)
377 return beans::StringPair(path, idref);
380 return beans::StringPair();
384 /// generate unique xml:id
385 template< typename T >
386 /*static*/ OUString create_id(const
387 std::unordered_map< OUString, T, OUStringHash > & i_rXmlIdMap)
389 static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
390 static const char prefix[] = "id"; // prefix for generated xml:id
391 typename std::unordered_map< OUString, T, OUStringHash >
392 ::const_iterator iter;
393 OUString id;
395 if (bHack)
397 static sal_Int64 nIdCounter = SAL_CONST_INT64(4000000000);
400 id = prefix + OUString::number(nIdCounter++);
401 iter = i_rXmlIdMap.find(id);
403 while (iter != i_rXmlIdMap.end());
405 else
409 unsigned int const n(comphelper::rng::uniform_uint_distribution(0,
410 std::numeric_limits<unsigned int>::max()));
411 id = prefix + OUString::number(n);
412 iter = i_rXmlIdMap.find(id);
414 while (iter != i_rXmlIdMap.end());
416 return id;
420 // Document XML ID Registry (_Impl)
422 /// element list
423 typedef ::std::list< Metadatable* > XmlIdList_t;
425 /// Idref -> (content.xml element list, styles.xml element list)
426 typedef std::unordered_map< OUString,
427 ::std::pair< XmlIdList_t, XmlIdList_t >, OUStringHash > XmlIdMap_t;
429 /// pointer hash template
430 template<typename T> struct PtrHash
432 size_t operator() (T const * i_pT) const
434 return reinterpret_cast<size_t>(i_pT);
438 /// element -> (stream name, idref)
439 typedef std::unordered_map< const Metadatable*,
440 ::std::pair< OUString, OUString>, PtrHash<Metadatable> >
441 XmlIdReverseMap_t;
443 struct XmlIdRegistryDocument::XmlIdRegistry_Impl
445 XmlIdRegistry_Impl()
446 : m_XmlIdMap(), m_XmlIdReverseMap() { }
448 bool TryInsertMetadatable(Metadatable& i_xObject,
449 const OUString & i_rStream, const OUString & i_rIdref);
451 bool LookupXmlId(const Metadatable& i_xObject,
452 OUString & o_rStream, OUString & o_rIdref) const;
454 Metadatable* LookupElement(const OUString & i_rStreamName,
455 const OUString & i_rIdref) const;
457 const XmlIdList_t * LookupElementList(
458 const OUString & i_rStreamName,
459 const OUString & i_rIdref) const;
461 XmlIdList_t * LookupElementList(
462 const OUString & i_rStreamName,
463 const OUString & i_rIdref)
465 return const_cast<XmlIdList_t*>(
466 const_cast<const XmlIdRegistry_Impl*>(this)
467 ->LookupElementList(i_rStreamName, i_rIdref));
470 XmlIdMap_t m_XmlIdMap;
471 XmlIdReverseMap_t m_XmlIdReverseMap;
475 static void
476 rmIter(XmlIdMap_t & i_rXmlIdMap, XmlIdMap_t::iterator const& i_rIter,
477 OUString const & i_rStream, Metadatable const& i_rObject)
479 if (i_rIter != i_rXmlIdMap.end())
481 XmlIdList_t & rList( isContentFile(i_rStream)
482 ? i_rIter->second.first : i_rIter->second.second );
483 rList.remove(&const_cast<Metadatable&>(i_rObject));
484 if (i_rIter->second.first.empty() && i_rIter->second.second.empty())
486 i_rXmlIdMap.erase(i_rIter);
492 const XmlIdList_t *
493 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElementList(
494 const OUString & i_rStreamName,
495 const OUString & i_rIdref) const
497 const XmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
498 if (iter != m_XmlIdMap.end())
500 OSL_ENSURE(!iter->second.first.empty() || !iter->second.second.empty(),
501 "null entry in m_XmlIdMap");
502 return (isContentFile(i_rStreamName))
503 ? &iter->second.first
504 : &iter->second.second;
506 else
508 return nullptr;
512 Metadatable*
513 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElement(
514 const OUString & i_rStreamName,
515 const OUString & i_rIdref) const
517 if (!isValidXmlId(i_rStreamName, i_rIdref))
519 throw lang::IllegalArgumentException(OUString(
520 "illegal XmlId"), nullptr, 0);
523 const XmlIdList_t * pList( LookupElementList(i_rStreamName, i_rIdref) );
524 if (pList)
526 const XmlIdList_t::const_iterator iter(
527 ::std::find_if(pList->begin(), pList->end(),
528 [](Metadatable* item)->bool {
529 return !(item->IsInUndo() || item->IsInClipboard());
530 } ) ) ;
531 if (iter != pList->end())
533 return *iter;
536 return nullptr;
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 [](Metadatable* item)->bool {
586 return !(item->IsInUndo() || item->IsInClipboard());
587 } ) )
589 pList->push_front( &i_rObject );
590 return true;
592 else
594 return false;
598 else
600 m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent
601 ? ::std::make_pair( XmlIdList_t( 1, &i_rObject ), XmlIdList_t() )
602 : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject ) )));
603 return true;
608 // Document XML ID Registry
611 XmlIdRegistryDocument::XmlIdRegistryDocument()
612 : m_pImpl( new XmlIdRegistry_Impl )
616 static void
617 removeLink(Metadatable* i_pObject)
619 OSL_ENSURE(i_pObject, "null in list ???");
620 if (!i_pObject) return;
621 if (i_pObject->IsInClipboard())
623 MetadatableClipboard* pLink(
624 dynamic_cast<MetadatableClipboard*>( i_pObject ) );
625 OSL_ENSURE(pLink, "IsInClipboard, but no MetadatableClipboard ?");
626 if (pLink)
628 pLink->OriginNoLongerInBusinessAnymore();
633 XmlIdRegistryDocument::~XmlIdRegistryDocument()
635 // notify all list elements that are actually in the clipboard
636 for (auto& aXmlId : m_pImpl->m_XmlIdMap) {
637 for (auto aLink : aXmlId.second.first)
638 removeLink(aLink);
639 for (auto aLink : aXmlId.second.second)
640 removeLink(aLink);
644 bool
645 XmlIdRegistryDocument::LookupXmlId(
646 const Metadatable& i_rObject,
647 OUString & o_rStream, OUString & o_rIdref) const
649 return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref);
652 Metadatable*
653 XmlIdRegistryDocument::LookupElement(
654 const OUString & i_rStreamName,
655 const OUString & i_rIdref) const
657 return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
660 bool
661 XmlIdRegistryDocument::TryRegisterMetadatable(Metadatable & i_rObject,
662 OUString const& i_rStreamName, OUString const& i_rIdref)
664 OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject,
665 OUStringToOString(i_rStreamName, RTL_TEXTENCODING_UTF8).getStr(),
666 OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8).getStr());
668 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
669 "TryRegisterMetadatable called for MetadatableUndo?");
670 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
671 "TryRegisterMetadatable called for MetadatableClipboard?");
673 if (!isValidXmlId(i_rStreamName, i_rIdref))
675 throw lang::IllegalArgumentException(OUString(
676 "illegal XmlId"), nullptr, 0);
678 if (i_rObject.IsInContent()
679 ? !isContentFile(i_rStreamName)
680 : !isStylesFile(i_rStreamName))
682 throw lang::IllegalArgumentException(OUString(
683 "illegal XmlId: wrong stream"), nullptr, 0);
686 OUString old_path;
687 OUString old_idref;
688 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
689 if (old_path == i_rStreamName && old_idref == i_rIdref)
691 return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
693 XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
694 if (!old_idref.isEmpty())
696 old_id = m_pImpl->m_XmlIdMap.find(old_idref);
697 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
699 if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
701 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
702 m_pImpl->m_XmlIdReverseMap[&i_rObject] =
703 ::std::make_pair(i_rStreamName, i_rIdref);
704 return true;
706 else
708 return false;
712 void
713 XmlIdRegistryDocument::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
715 OSL_TRACE("RegisterMetadatableAndCreateID: %p", &i_rObject);
717 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
718 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
719 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
720 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
722 const bool isInContent( i_rObject.IsInContent() );
723 const OUString stream( OUString::createFromAscii(
724 isInContent ? s_content : s_styles ) );
725 // check if we have a latent xmlid, and if yes, remove it
726 OUString old_path;
727 OUString old_idref;
728 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
730 XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
731 if (!old_idref.isEmpty())
733 old_id = m_pImpl->m_XmlIdMap.find(old_idref);
734 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
735 if (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject)
737 return;
739 else
741 // remove latent xmlid
742 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
746 // create id
747 const OUString id( create_id(m_pImpl->m_XmlIdMap) );
748 OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(),
749 "created id is in use");
750 m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent
751 ? ::std::make_pair( XmlIdList_t( 1, &i_rObject ), XmlIdList_t() )
752 : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject ) )));
753 m_pImpl->m_XmlIdReverseMap[&i_rObject] = ::std::make_pair(stream, id);
756 void XmlIdRegistryDocument::UnregisterMetadatable(const Metadatable& i_rObject)
758 OSL_TRACE("UnregisterMetadatable: %p", &i_rObject);
760 OUString path;
761 OUString idref;
762 if (!m_pImpl->LookupXmlId(i_rObject, path, idref))
764 OSL_FAIL("unregister: no xml id?");
765 return;
767 const XmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
768 if (iter != m_pImpl->m_XmlIdMap.end())
770 rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
774 void XmlIdRegistryDocument::RemoveXmlIdForElement(const Metadatable& i_rObject)
776 OSL_TRACE("RemoveXmlIdForElement: %p", &i_rObject);
778 const XmlIdReverseMap_t::iterator iter(
779 m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
780 if (iter != m_pImpl->m_XmlIdReverseMap.end())
782 OSL_ENSURE(!iter->second.second.isEmpty(),
783 "null id in m_XmlIdReverseMap");
784 m_pImpl->m_XmlIdReverseMap.erase(iter);
789 void XmlIdRegistryDocument::RegisterCopy(Metadatable const& i_rSource,
790 Metadatable & i_rCopy, const bool i_bCopyPrecedesSource)
792 OSL_TRACE("RegisterCopy: %p -> %p (%d)\n",
793 &i_rSource, &i_rCopy, i_bCopyPrecedesSource);
795 // potential sources: clipboard, undo array, splitNode
796 // assumption: stream change can only happen via clipboard, and is handled
797 // by Metadatable::RegisterAsCopyOf
798 OSL_ENSURE(i_rSource.IsInUndo() || i_rCopy.IsInUndo() ||
799 (i_rSource.IsInContent() == i_rCopy.IsInContent()),
800 "RegisterCopy: not in same stream?");
802 OUString path;
803 OUString idref;
804 if (!m_pImpl->LookupXmlId( i_rSource, path, idref ))
806 OSL_FAIL("no xml id?");
807 return;
809 XmlIdList_t * pList ( m_pImpl->LookupElementList(path, idref) );
810 OSL_ENSURE( ::std::find( pList->begin(), pList->end(), &i_rCopy )
811 == pList->end(), "copy already registered???");
812 XmlIdList_t::iterator srcpos(
813 ::std::find( pList->begin(), pList->end(), &i_rSource ) );
814 OSL_ENSURE(srcpos != pList->end(), "source not in list???");
815 if (srcpos == pList->end())
817 return;
819 if (i_bCopyPrecedesSource)
821 pList->insert( srcpos, &i_rCopy );
823 else
825 // for undo push_back does not work! must insert right after source
826 pList->insert( ++srcpos, &i_rCopy );
828 m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy,
829 ::std::make_pair(path, idref)));
832 std::shared_ptr<MetadatableUndo>
833 XmlIdRegistryDocument::CreateUndo(Metadatable const& i_rObject)
835 OSL_TRACE("CreateUndo: %p", &i_rObject);
837 return std::make_shared<MetadatableUndo>(
838 i_rObject.IsInContent() );
842 i_rMerged is both a source and the target node of the merge
843 i_rOther is the other source, and will be deleted after the merge
845 dimensions: none|latent|actual empty|nonempty
846 i_rMerged(1) i_rOther(2) result
847 *|empty *|empty => 1|2 (arbitrary)
848 *|empty *|nonempty => 2
849 *|nonempty *|empty => 1
850 none|nonempty none|nonempty => none
851 none|nonempty latent|nonempty => 2
852 latent|nonempty none|nonempty => 1
853 latent|nonempty latent|nonempty => 1|2
854 *|nonempty actual|nonempty => 2
855 actual|nonempty *|nonempty => 1
856 actual|nonempty actual|nonempty => 1|2
858 void
859 XmlIdRegistryDocument::JoinMetadatables(
860 Metadatable & i_rMerged, Metadatable const & i_rOther)
862 OSL_TRACE("JoinMetadatables: %p <- %p", &i_rMerged, &i_rOther);
864 bool mergedOwnsRef;
865 OUString path;
866 OUString idref;
867 if (m_pImpl->LookupXmlId(i_rMerged, path, idref))
869 mergedOwnsRef = (m_pImpl->LookupElement(path, idref) == &i_rMerged);
871 else
873 OSL_FAIL("JoinMetadatables: no xmlid?");
874 return;
876 if (!mergedOwnsRef)
878 i_rMerged.RemoveMetadataReference();
879 i_rMerged.RegisterAsCopyOf(i_rOther, true);
880 return;
882 // other cases: merged has actual ref and is nonempty,
883 // other has latent/actual ref and is nonempty: other loses => nothing to do
887 // Clipboard XML ID Registry (_Impl)
889 struct RMapEntry
891 RMapEntry() : m_xLink() { }
892 RMapEntry(OUString const& i_rStream,
893 OUString const& i_rXmlId,
894 std::shared_ptr<MetadatableClipboard> const& i_pLink
895 = std::shared_ptr<MetadatableClipboard>())
896 : m_Stream(i_rStream), m_XmlId(i_rXmlId), m_xLink(i_pLink)
898 OUString m_Stream;
899 OUString m_XmlId;
900 // this would have been an auto_ptr, if only that would have compiled...
901 std::shared_ptr<MetadatableClipboard> m_xLink;
904 /// element -> (stream name, idref, source)
905 typedef std::unordered_map< const Metadatable*,
906 struct RMapEntry,
907 PtrHash<Metadatable> >
908 ClipboardXmlIdReverseMap_t;
910 /// Idref -> (content.xml element, styles.xml element)
911 typedef std::unordered_map< OUString,
912 ::std::pair< Metadatable*, Metadatable* >, OUStringHash >
913 ClipboardXmlIdMap_t;
915 struct XmlIdRegistryClipboard::XmlIdRegistry_Impl
917 XmlIdRegistry_Impl()
918 : m_XmlIdMap(), m_XmlIdReverseMap() { }
920 bool TryInsertMetadatable(Metadatable& i_xObject,
921 const OUString & i_rStream, const OUString & i_rIdref);
923 bool LookupXmlId(const Metadatable& i_xObject,
924 OUString & o_rStream, OUString & o_rIdref,
925 MetadatableClipboard const* &o_rpLink) const;
927 Metadatable* LookupElement(const OUString & i_rStreamName,
928 const OUString & i_rIdref) const;
930 Metadatable* const* LookupEntry(const OUString & i_rStreamName,
931 const OUString & i_rIdref) const;
933 ClipboardXmlIdMap_t m_XmlIdMap;
934 ClipboardXmlIdReverseMap_t m_XmlIdReverseMap;
938 static void
939 rmIter(ClipboardXmlIdMap_t & i_rXmlIdMap,
940 ClipboardXmlIdMap_t::iterator const& i_rIter,
941 OUString const & i_rStream, Metadatable const& i_rObject)
943 if (i_rIter != i_rXmlIdMap.end())
945 Metadatable *& rMeta = isContentFile(i_rStream)
946 ? i_rIter->second.first : i_rIter->second.second;
947 if (rMeta == &i_rObject)
949 rMeta = nullptr;
951 if (!i_rIter->second.first && !i_rIter->second.second)
953 i_rXmlIdMap.erase(i_rIter);
959 Metadatable* const*
960 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupEntry(
961 const OUString & i_rStreamName,
962 const OUString & i_rIdref) const
964 if (!isValidXmlId(i_rStreamName, i_rIdref))
966 throw lang::IllegalArgumentException(OUString(
967 "illegal XmlId"), nullptr, 0);
970 const ClipboardXmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
971 if (iter != m_XmlIdMap.end())
973 OSL_ENSURE(iter->second.first || iter->second.second,
974 "null entry in m_XmlIdMap");
975 return (isContentFile(i_rStreamName))
976 ? &iter->second.first
977 : &iter->second.second;
979 else
981 return nullptr;
985 Metadatable*
986 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupElement(
987 const OUString & i_rStreamName,
988 const OUString & i_rIdref) const
990 Metadatable * const * ppEntry = LookupEntry(i_rStreamName, i_rIdref);
991 return ppEntry ? *ppEntry : nullptr;
994 bool
995 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupXmlId(
996 const Metadatable& i_rObject,
997 OUString & o_rStream, OUString & o_rIdref,
998 MetadatableClipboard const* &o_rpLink) const
1000 const ClipboardXmlIdReverseMap_t::const_iterator iter(
1001 m_XmlIdReverseMap.find(&i_rObject) );
1002 if (iter != m_XmlIdReverseMap.end())
1004 OSL_ENSURE(!iter->second.m_Stream.isEmpty(),
1005 "null stream in m_XmlIdReverseMap");
1006 OSL_ENSURE(!iter->second.m_XmlId.isEmpty(),
1007 "null id in m_XmlIdReverseMap");
1008 o_rStream = iter->second.m_Stream;
1009 o_rIdref = iter->second.m_XmlId;
1010 o_rpLink = iter->second.m_xLink.get();
1011 return true;
1013 else
1015 return false;
1019 bool
1020 XmlIdRegistryClipboard::XmlIdRegistry_Impl::TryInsertMetadatable(
1021 Metadatable & i_rObject,
1022 const OUString & i_rStreamName, const OUString & i_rIdref)
1024 bool bContent( isContentFile(i_rStreamName) );
1025 OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName),
1026 "invalid stream");
1028 Metadatable ** ppEntry = const_cast<Metadatable**>(LookupEntry(i_rStreamName, i_rIdref));
1029 if (ppEntry)
1031 if (*ppEntry)
1033 return false;
1035 else
1037 *ppEntry = &i_rObject;
1038 return true;
1041 else
1043 m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent
1044 ? ::std::make_pair( &i_rObject, static_cast<Metadatable*>(nullptr) )
1045 : ::std::make_pair( static_cast<Metadatable*>(nullptr), &i_rObject )));
1046 return true;
1051 // Clipboard XML ID Registry
1054 XmlIdRegistryClipboard::XmlIdRegistryClipboard()
1055 : m_pImpl( new XmlIdRegistry_Impl )
1059 XmlIdRegistryClipboard::~XmlIdRegistryClipboard()
1063 bool
1064 XmlIdRegistryClipboard::LookupXmlId(
1065 const Metadatable& i_rObject,
1066 OUString & o_rStream, OUString & o_rIdref) const
1068 const MetadatableClipboard * pLink;
1069 return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref, pLink);
1072 Metadatable*
1073 XmlIdRegistryClipboard::LookupElement(
1074 const OUString & i_rStreamName,
1075 const OUString & i_rIdref) const
1077 return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
1080 bool
1081 XmlIdRegistryClipboard::TryRegisterMetadatable(Metadatable & i_rObject,
1082 OUString const& i_rStreamName, OUString const& i_rIdref)
1084 OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject,
1085 OUStringToOString(i_rStreamName, RTL_TEXTENCODING_UTF8).getStr(),
1086 OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8).getStr());
1088 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
1089 "TryRegisterMetadatable called for MetadatableUndo?");
1090 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
1091 "TryRegisterMetadatable called for MetadatableClipboard?");
1093 if (!isValidXmlId(i_rStreamName, i_rIdref))
1095 throw lang::IllegalArgumentException(OUString(
1096 "illegal XmlId"), nullptr, 0);
1098 if (i_rObject.IsInContent()
1099 ? !isContentFile(i_rStreamName)
1100 : !isStylesFile(i_rStreamName))
1102 throw lang::IllegalArgumentException(OUString(
1103 "illegal XmlId: wrong stream"), nullptr, 0);
1106 OUString old_path;
1107 OUString old_idref;
1108 const MetadatableClipboard * pLink;
1109 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref, pLink);
1110 if (old_path == i_rStreamName && old_idref == i_rIdref)
1112 return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
1114 ClipboardXmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
1115 if (!old_idref.isEmpty())
1117 old_id = m_pImpl->m_XmlIdMap.find(old_idref);
1118 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
1120 if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
1122 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
1123 m_pImpl->m_XmlIdReverseMap[&i_rObject] =
1124 RMapEntry(i_rStreamName, i_rIdref);
1125 return true;
1127 else
1129 return false;
1133 void
1134 XmlIdRegistryClipboard::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
1136 OSL_TRACE("RegisterMetadatableAndCreateID: %p", &i_rObject);
1138 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
1139 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
1140 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
1141 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
1143 bool isInContent( i_rObject.IsInContent() );
1144 OUString stream( OUString::createFromAscii(
1145 isInContent ? s_content : s_styles ) );
1147 OUString old_path;
1148 OUString old_idref;
1149 LookupXmlId(i_rObject, old_path, old_idref);
1150 if (!old_idref.isEmpty() &&
1151 (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject))
1153 return;
1156 // create id
1157 const OUString id( create_id(m_pImpl->m_XmlIdMap) );
1158 OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(),
1159 "created id is in use");
1160 m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent
1161 ? ::std::make_pair( &i_rObject, static_cast<Metadatable*>(nullptr) )
1162 : ::std::make_pair( static_cast<Metadatable*>(nullptr), &i_rObject )));
1163 // N.B.: if i_rObject had a latent XmlId, then we implicitly delete the
1164 // MetadatableClipboard and thus the latent XmlId here
1165 m_pImpl->m_XmlIdReverseMap[&i_rObject] = RMapEntry(stream, id);
1168 void XmlIdRegistryClipboard::UnregisterMetadatable(const Metadatable& i_rObject)
1170 OSL_TRACE("UnregisterMetadatable: %p", &i_rObject);
1172 OUString path;
1173 OUString idref;
1174 const MetadatableClipboard * pLink;
1175 if (!m_pImpl->LookupXmlId(i_rObject, path, idref, pLink))
1177 OSL_FAIL("unregister: no xml id?");
1178 return;
1180 const ClipboardXmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
1181 if (iter != m_pImpl->m_XmlIdMap.end())
1183 rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
1188 void XmlIdRegistryClipboard::RemoveXmlIdForElement(const Metadatable& i_rObject)
1190 OSL_TRACE("RemoveXmlIdForElement: %p", &i_rObject);
1192 ClipboardXmlIdReverseMap_t::iterator iter(
1193 m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
1194 if (iter != m_pImpl->m_XmlIdReverseMap.end())
1196 OSL_ENSURE(!iter->second.m_XmlId.isEmpty(),
1197 "null id in m_XmlIdReverseMap");
1198 m_pImpl->m_XmlIdReverseMap.erase(iter);
1203 std::shared_ptr<MetadatableClipboard>
1204 XmlIdRegistryClipboard::CreateClipboard(const bool i_isInContent)
1206 OSL_TRACE("CreateClipboard:");
1208 return std::make_shared<MetadatableClipboard>(
1209 i_isInContent );
1212 MetadatableClipboard &
1213 XmlIdRegistryClipboard::RegisterCopyClipboard(Metadatable & i_rCopy,
1214 beans::StringPair const & i_rReference,
1215 const bool i_isLatent)
1217 OSL_TRACE("RegisterCopyClipboard: %p -> "/*"%p"*/"(%s#%s) (%d)\n",
1218 /*&i_rSource,*/ &i_rCopy,
1219 OUStringToOString(i_rReference.First,
1220 RTL_TEXTENCODING_UTF8).getStr(),
1221 OUStringToOString(i_rReference.Second,
1222 RTL_TEXTENCODING_UTF8).getStr(),
1223 i_isLatent);
1225 // N.B.: when copying to the clipboard, the selection is always inserted
1226 // into the body, even if the source is a header/footer!
1227 // so we do not check whether the stream is right in this function
1229 if (!isValidXmlId(i_rReference.First, i_rReference.Second))
1231 throw lang::IllegalArgumentException(OUString(
1232 "illegal XmlId"), nullptr, 0);
1235 if (!i_isLatent)
1237 // this should succeed assuming clipboard has a single source document
1238 const bool success( m_pImpl->TryInsertMetadatable(i_rCopy,
1239 i_rReference.First, i_rReference.Second) );
1240 OSL_ENSURE(success, "RegisterCopyClipboard: TryInsert failed?");
1241 (void) success;
1243 const std::shared_ptr<MetadatableClipboard> xLink(
1244 CreateClipboard( isContentFile(i_rReference.First)) );
1245 m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy,
1246 RMapEntry(i_rReference.First, i_rReference.Second, xLink)));
1247 return *xLink.get();
1250 MetadatableClipboard const*
1251 XmlIdRegistryClipboard::SourceLink(Metadatable const& i_rObject)
1253 OUString path;
1254 OUString idref;
1255 const MetadatableClipboard * pLink( nullptr );
1256 m_pImpl->LookupXmlId(i_rObject, path, idref, pLink);
1257 return pLink;
1261 // Metadatable mixin
1264 Metadatable::~Metadatable()
1266 RemoveMetadataReference();
1269 void Metadatable::RemoveMetadataReference()
1273 if (m_pReg)
1275 m_pReg->UnregisterMetadatable( *this );
1276 m_pReg->RemoveXmlIdForElement( *this );
1277 m_pReg = nullptr;
1280 catch (const uno::Exception &)
1282 OSL_FAIL("Metadatable::RemoveMetadataReference: exception");
1286 // css::rdf::XMetadatable:
1287 beans::StringPair
1288 Metadatable::GetMetadataReference() const
1290 if (m_pReg)
1292 return m_pReg->GetXmlIdForElement(*this);
1294 return beans::StringPair();
1297 void Metadatable::SetMetadataReference( const css::beans::StringPair & i_rReference)
1299 if (i_rReference.Second.isEmpty())
1301 RemoveMetadataReference();
1303 else
1305 OUString streamName( i_rReference.First );
1306 if (streamName.isEmpty())
1308 // handle empty stream name as auto-detect.
1309 // necessary for importing flat file format.
1310 streamName = OUString::createFromAscii(
1311 IsInContent() ? s_content : s_styles );
1313 XmlIdRegistry & rReg( dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1314 if (rReg.TryRegisterMetadatable(*this, streamName, i_rReference.Second))
1316 m_pReg = &rReg;
1318 else
1320 throw lang::IllegalArgumentException(
1321 OUString("Metadatable::"
1322 "SetMetadataReference: argument is invalid"), /*this*/nullptr, 0);
1327 void Metadatable::EnsureMetadataReference()
1329 XmlIdRegistry& rReg(
1330 m_pReg ? *m_pReg : dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1331 rReg.RegisterMetadatableAndCreateID( *this );
1332 m_pReg = &rReg;
1335 const ::sfx2::IXmlIdRegistry& GetRegistryConst(Metadatable const& i_rObject)
1337 return const_cast< Metadatable& >( i_rObject ).GetRegistry();
1340 void
1341 Metadatable::RegisterAsCopyOf(Metadatable const & i_rSource,
1342 const bool i_bCopyPrecedesSource)
1344 OSL_ENSURE(typeid(*this) == typeid(i_rSource)
1345 || typeid(i_rSource) == typeid(MetadatableUndo)
1346 || typeid(*this) == typeid(MetadatableUndo)
1347 || typeid(i_rSource) == typeid(MetadatableClipboard)
1348 || typeid(*this) == typeid(MetadatableClipboard),
1349 "RegisterAsCopyOf element with different class?");
1350 OSL_ENSURE(!this->m_pReg, "RegisterAsCopyOf called on element with XmlId?");
1352 if (this->m_pReg)
1354 RemoveMetadataReference();
1359 if (i_rSource.m_pReg)
1361 XmlIdRegistry & rReg(
1362 dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1363 if (i_rSource.m_pReg == &rReg)
1365 OSL_ENSURE(!IsInClipboard(),
1366 "RegisterAsCopy: both in clipboard?");
1367 if (!IsInClipboard())
1369 XmlIdRegistryDocument & rRegDoc(
1370 dynamic_cast<XmlIdRegistryDocument&>( rReg ) );
1371 rRegDoc.RegisterCopy(i_rSource, *this,
1372 i_bCopyPrecedesSource);
1373 this->m_pReg = &rRegDoc;
1375 return;
1377 // source is in different document
1378 XmlIdRegistryDocument * pRegDoc(
1379 dynamic_cast<XmlIdRegistryDocument *>(&rReg) );
1380 XmlIdRegistryClipboard * pRegClp(
1381 dynamic_cast<XmlIdRegistryClipboard*>(&rReg) );
1383 if (pRegClp)
1385 beans::StringPair SourceRef(
1386 i_rSource.m_pReg->GetXmlIdForElement(i_rSource) );
1387 bool isLatent( SourceRef.Second.isEmpty() );
1388 XmlIdRegistryDocument * pSourceRegDoc(
1389 dynamic_cast<XmlIdRegistryDocument*>(i_rSource.m_pReg) );
1390 OSL_ENSURE(pSourceRegDoc, "RegisterAsCopyOf: 2 clipboards?");
1391 if (!pSourceRegDoc) return;
1392 // this is a copy _to_ the clipboard
1393 if (isLatent)
1395 pSourceRegDoc->LookupXmlId(i_rSource,
1396 SourceRef.First, SourceRef.Second);
1398 Metadatable & rLink(
1399 pRegClp->RegisterCopyClipboard(*this, SourceRef, isLatent));
1400 this->m_pReg = pRegClp;
1401 // register as copy in the non-clipboard registry
1402 pSourceRegDoc->RegisterCopy(i_rSource, rLink,
1403 false); // i_bCopyPrecedesSource);
1404 rLink.m_pReg = pSourceRegDoc;
1406 else if (pRegDoc)
1408 XmlIdRegistryClipboard * pSourceRegClp(
1409 dynamic_cast<XmlIdRegistryClipboard*>(i_rSource.m_pReg) );
1410 OSL_ENSURE(pSourceRegClp,
1411 "RegisterAsCopyOf: 2 non-clipboards?");
1412 if (!pSourceRegClp) return;
1413 const MetadatableClipboard * pLink(
1414 pSourceRegClp->SourceLink(i_rSource) );
1415 // may happen if src got its id via UNO call
1416 if (!pLink) return;
1417 // only register copy if clipboard content is from this SwDoc!
1418 if (pLink && (&GetRegistryConst(*pLink) == pRegDoc))
1420 // this is a copy _from_ the clipboard; check if the
1421 // element is still in the same stream
1422 // N.B.: we check the stream of pLink, not of i_rSource!
1423 bool srcInContent( pLink->IsInContent() );
1424 bool tgtInContent( this->IsInContent() );
1425 if (srcInContent == tgtInContent)
1427 pRegDoc->RegisterCopy(*pLink, *this,
1428 true); // i_bCopyPrecedesSource);
1429 this->m_pReg = pRegDoc;
1431 // otherwise: stream change! do not register!
1434 else
1436 OSL_FAIL("neither RegDoc nor RegClp cannot happen");
1440 catch (const uno::Exception &)
1442 OSL_FAIL("Metadatable::RegisterAsCopyOf: exception");
1446 std::shared_ptr<MetadatableUndo> Metadatable::CreateUndo() const
1448 OSL_ENSURE(!IsInUndo(), "CreateUndo called for object in undo?");
1449 OSL_ENSURE(!IsInClipboard(), "CreateUndo called for object in clipboard?");
1452 if (!IsInClipboard() && !IsInUndo() && m_pReg)
1454 XmlIdRegistryDocument * pRegDoc(
1455 dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
1456 std::shared_ptr<MetadatableUndo> xUndo(
1457 sfx2::XmlIdRegistryDocument::CreateUndo(*this) );
1458 pRegDoc->RegisterCopy(*this, *xUndo, false);
1459 xUndo->m_pReg = pRegDoc;
1460 return xUndo;
1463 catch (const uno::Exception &)
1465 OSL_FAIL("Metadatable::CreateUndo: exception");
1467 return std::shared_ptr<MetadatableUndo>();
1470 std::shared_ptr<MetadatableUndo> Metadatable::CreateUndoForDelete()
1472 std::shared_ptr<MetadatableUndo> const xUndo( CreateUndo() );
1473 RemoveMetadataReference();
1474 return xUndo;
1477 void Metadatable::RestoreMetadata(
1478 std::shared_ptr<MetadatableUndo> const& i_pUndo)
1480 OSL_ENSURE(!IsInUndo(), "RestoreMetadata called for object in undo?");
1481 OSL_ENSURE(!IsInClipboard(),
1482 "RestoreMetadata called for object in clipboard?");
1483 if (IsInClipboard() || IsInUndo()) return;
1484 RemoveMetadataReference();
1485 if (i_pUndo)
1487 this->RegisterAsCopyOf(*i_pUndo, true);
1491 void
1492 Metadatable::JoinMetadatable(Metadatable const & i_rOther,
1493 const bool i_isMergedEmpty, const bool i_isOtherEmpty)
1495 OSL_ENSURE(!IsInUndo(), "JoinMetadatables called for object in undo?");
1496 OSL_ENSURE(!IsInClipboard(),
1497 "JoinMetadatables called for object in clipboard?");
1498 if (IsInClipboard() || IsInUndo()) return;
1500 if (i_isOtherEmpty && !i_isMergedEmpty)
1502 // other is empty, thus loses => nothing to do
1503 return;
1505 if (i_isMergedEmpty && !i_isOtherEmpty)
1507 this->RemoveMetadataReference();
1508 this->RegisterAsCopyOf(i_rOther, true);
1509 return;
1512 if (!i_rOther.m_pReg)
1514 // other doesn't have xmlid, thus loses => nothing to do
1515 return;
1517 if (!m_pReg)
1519 this->RegisterAsCopyOf(i_rOther, true);
1520 // assumption: i_rOther will be deleted, so don't unregister it here
1521 return;
1525 XmlIdRegistryDocument * pRegDoc(
1526 dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
1527 OSL_ENSURE(pRegDoc, "JoinMetadatable: no pRegDoc?");
1528 if (pRegDoc)
1530 pRegDoc->JoinMetadatables(*this, i_rOther);
1533 catch (const uno::Exception &)
1535 OSL_FAIL("Metadatable::JoinMetadatable: exception");
1540 // XMetadatable mixin
1542 // css::rdf::XNode:
1543 OUString SAL_CALL MetadatableMixin::getStringValue()
1544 throw (css::uno::RuntimeException, std::exception)
1546 return getNamespace() + getLocalName();
1549 // css::rdf::XURI:
1550 OUString SAL_CALL MetadatableMixin::getLocalName()
1551 throw (css::uno::RuntimeException, std::exception)
1553 SolarMutexGuard aGuard;
1554 beans::StringPair mdref( getMetadataReference() );
1555 if (mdref.Second.isEmpty())
1557 ensureMetadataReference(); // N.B.: side effect!
1558 mdref = getMetadataReference();
1560 OUStringBuffer buf;
1561 buf.append(mdref.First);
1562 buf.append('#');
1563 buf.append(mdref.Second);
1564 return buf.makeStringAndClear();
1567 OUString SAL_CALL MetadatableMixin::getNamespace()
1568 throw (css::uno::RuntimeException, std::exception)
1570 SolarMutexGuard aGuard;
1571 const uno::Reference< frame::XModel > xModel( GetModel() );
1572 const uno::Reference< rdf::XURI > xDMA( xModel, uno::UNO_QUERY_THROW );
1573 return xDMA->getStringValue();
1576 // css::rdf::XMetadatable:
1577 beans::StringPair SAL_CALL
1578 MetadatableMixin::getMetadataReference()
1579 throw (uno::RuntimeException, std::exception)
1581 SolarMutexGuard aGuard;
1583 Metadatable *const pObject( GetCoreObject() );
1584 if (!pObject)
1586 throw uno::RuntimeException(
1587 OUString(
1588 "MetadatableMixin: cannot get core object; not inserted?"),
1589 *this);
1591 return pObject->GetMetadataReference();
1594 void SAL_CALL
1595 MetadatableMixin::setMetadataReference(
1596 const beans::StringPair & i_rReference)
1597 throw (uno::RuntimeException, lang::IllegalArgumentException, std::exception)
1599 SolarMutexGuard aGuard;
1601 Metadatable *const pObject( GetCoreObject() );
1602 if (!pObject)
1604 throw uno::RuntimeException(
1605 OUString(
1606 "MetadatableMixin: cannot get core object; not inserted?"),
1607 *this);
1609 return pObject->SetMetadataReference(i_rReference);
1612 void SAL_CALL MetadatableMixin::ensureMetadataReference()
1613 throw (uno::RuntimeException, std::exception)
1615 SolarMutexGuard aGuard;
1617 Metadatable *const pObject( GetCoreObject() );
1618 if (!pObject)
1620 throw uno::RuntimeException(
1621 OUString(
1622 "MetadatableMixin: cannot get core object; not inserted?"),
1623 *this);
1625 return pObject->EnsureMetadataReference();
1628 } // namespace sfx2
1630 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */