Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / sfx2 / source / doc / Metadatable.cxx
blobb12d251924f16f6f9fcffc6f7ea6f70d3821ff57
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 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <osl/diagnose.h>
24 #include <sfx2/Metadatable.hxx>
25 #include <sfx2/XmlIdRegistry.hxx>
27 #include <vcl/svapp.hxx>
28 #include <com/sun/star/frame/XModel.hpp>
29 #include <com/sun/star/lang/IllegalArgumentException.hpp>
30 #include <comphelper/random.hxx>
32 #include <algorithm>
33 #include <memory>
34 #include <unordered_map>
35 #if OSL_DEBUG_LEVEL > 0
36 #include <typeinfo>
37 #endif
40 /** XML ID handling.
42 There is an abstract base class <type>XmlIdRegistry</type>, with
43 2 subclasses <type>XmlIdRegistryDocument</type> for "normal" documents,
44 and <type>XmlIdRegistryClipboard</type> for clipboard documents.
45 These classes are responsible for managing XML IDs for all elements
46 of the model. Only the implementation of the <type>Metadatable</type>
47 base class needs to know the registries, so they are not in the header.
49 The handling of XML IDs differs between clipboard and non-clipboard
50 documents in several aspects. Most importantly, non-clipboard documents
51 can have several elements associated with one XML ID.
52 This is necessary because of the weird undo implementation:
53 deleting a text node moves the deleted node to the undo array, but
54 executing undo will then create a <em>copy</em> of that node in the
55 document array. These 2 nodes must have the same XML ID, because
56 we cannot know whether the user will do a redo next, or something else.
58 Because we need to have a mechanism for several objects per XML ID anyway,
59 we use that also to enable some usability features:
60 The document registry has a list of Metadatables per XML ID.
61 This list is sorted by priority, i.e., the first element has highest
62 priority. When inserting copies, care must be taken that they are inserted
63 at the right position: either before or after the source.
64 This is done by <method>Metadatable::RegisterAsCopyOf</method>.
65 When a text node is split, then both resulting text nodes are inserted
66 into the list. If the user then deletes one text node, the other one
67 will have the XML ID.
68 Also, when a Metadatable is copied to the clipboard and then pasted,
69 the copy is inserted into the list. If the user then deletes the source,
70 the XML ID is not lost.
71 The goal is that it should be hard to lose an XML ID by accident, which
72 is especially important as long as we do not have an UI that displays them.
74 There are two subclasses of <type>Metadatable</type>:
75 <ul><li><type>MetadatableClipboard</type>: for copies in the clipboard</li>
76 <li><type>MetadatableUndo</type>: for undo, because a Metadatable
77 may be destroyed on delete and a new one created on undo.</li></ul>
78 These serve only to track the position in an XML ID list in a document
79 registry, so that future actions can insert objects at the right position.
80 Unfortunately, inserting dummy objects seems to be necessary:
81 <ul><li>it is not sufficient to just remember the saved id, because then
82 the relative priorities might change when executing the undo</li>
83 <li>it is not sufficient to record the position as an integer, because
84 if we delete a text node and then undo, the node will be copied(!),
85 and we will have one more node in the list.<li>
86 <li>it is not sufficient to record the pointer of the previous/next
87 Metadatable, because if we delete a text node, undo, and then
88 do something to clear the redo array, the original text node is
89 destroyed, and is replaced by the copy created by undo</li></ul>
91 If content from a non-clipboard document is copied into a clipboard
92 document, a dummy <type>MetadatableClipboard</type> is inserted into the
93 non-clipboard document registry in order to track the position of the
94 source element. When the clipboard content is pasted back into the source
95 document, this dummy object is used to associate the pasted element with
96 that same XML ID.
98 If a <type>Metadatable</type> is deleted or merged,
99 <method>Metadatable::CreateUndo</method> is called, and returns a
100 <type>MetadatableUndo<type> instance, which can be used to undo the action
101 by passing it to <method>Metadatable::RestoreMetadata</method>.
106 using namespace ::com::sun::star;
108 using ::sfx2::isValidXmlId;
111 namespace sfx2 {
113 const char s_content [] = "content.xml";
114 const char s_styles [] = "styles.xml";
116 static bool isContentFile(OUString const & i_rPath)
118 return i_rPath == s_content;
121 static bool isStylesFile (OUString const & i_rPath)
123 return i_rPath == s_styles;
127 // XML ID handling ---------------------------------------------------
129 /** handles registration of XMetadatable.
131 This class is responsible for guaranteeing that XMetadatable objects
132 always have XML IDs that are unique within a stream.
134 This is an abstract base class; see subclasses XmlIdRegistryDocument and
135 XmlIdRegistryClipboard.
137 @see SwDoc::GetXmlIdRegistry
138 @see SwDocShell::GetXmlIdRegistry
140 class XmlIdRegistry : public sfx2::IXmlIdRegistry
143 public:
144 XmlIdRegistry();
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 namespace {
210 /** non-clipboard documents */
211 class XmlIdRegistryDocument : public XmlIdRegistry
214 public:
215 XmlIdRegistryDocument();
217 virtual ~XmlIdRegistryDocument() override;
219 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) override;
221 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
222 OUString const& i_rStreamName, OUString const& i_rIdref) override;
224 virtual void UnregisterMetadatable(Metadatable const&) override;
226 virtual void RemoveXmlIdForElement(Metadatable const&) override;
228 /** register i_rCopy as a copy of i_rSource,
229 with precedence iff i_bCopyPrecedesSource is true */
230 void RegisterCopy(Metadatable const& i_rSource, Metadatable & i_rCopy,
231 const bool i_bCopyPrecedesSource);
233 /** create a Undo Metadatable for i_rObject. */
234 static std::shared_ptr<MetadatableUndo> CreateUndo(
235 Metadatable const& i_rObject);
237 /** merge i_rMerged and i_rOther into i_rMerged. */
238 void JoinMetadatables(Metadatable & i_rMerged, Metadatable const& i_rOther);
240 // unfortunately public, Metadatable::RegisterAsCopyOf needs this
241 virtual bool LookupXmlId(const Metadatable& i_xObject,
242 OUString & o_rStream, OUString & o_rIdref) const override;
244 private:
246 virtual Metadatable* LookupElement(const OUString & i_rStreamName,
247 const OUString & i_rIdref) const override;
249 struct XmlIdRegistry_Impl;
250 ::std::unique_ptr<XmlIdRegistry_Impl> m_pImpl;
255 // MetadatableUndo ---------------------------------------------------
257 /** the horrible Undo Metadatable: is inserted into lists to track position */
258 class MetadatableUndo : public Metadatable
260 /// as determined by the stream of the source in original document
261 const bool m_isInContent;
262 public:
263 explicit MetadatableUndo(const bool i_isInContent)
264 : m_isInContent(i_isInContent) { }
265 virtual ::sfx2::XmlIdRegistry& GetRegistry() override
267 // N.B. for Undo, m_pReg is initialized by registering this as copy in
268 // CreateUndo; it is never cleared
269 OSL_ENSURE(m_pReg, "no m_pReg in MetadatableUndo ?");
270 return *m_pReg;
272 virtual bool IsInClipboard() const override { return false; }
273 virtual bool IsInUndo() const override { return true; }
274 virtual bool IsInContent() const override { return m_isInContent; }
275 virtual css::uno::Reference< css::rdf::XMetadatable > MakeUnoObject() override
276 { OSL_FAIL("MetadatableUndo::MakeUnoObject"); throw; }
279 // MetadatableClipboard ----------------------------------------------
281 /** the horrible Clipboard Metadatable: inserted into lists to track position */
282 class MetadatableClipboard : public Metadatable
284 /// as determined by the stream of the source in original document
285 const bool m_isInContent;
286 public:
287 explicit MetadatableClipboard(const bool i_isInContent)
288 : m_isInContent(i_isInContent) { }
289 virtual ::sfx2::XmlIdRegistry& GetRegistry() override
291 // N.B. for Clipboard, m_pReg is initialized by registering this as copy in
292 // RegisterAsCopyOf; it is only cleared by OriginNoLongerInBusinessAnymore
293 assert(m_pReg && "no m_pReg in MetadatableClipboard ?");
294 return *m_pReg;
296 virtual bool IsInClipboard() const override { return true; }
297 virtual bool IsInUndo() const override { return false; }
298 virtual bool IsInContent() const override { return m_isInContent; }
299 virtual css::uno::Reference< css::rdf::XMetadatable > MakeUnoObject() override
300 { OSL_FAIL("MetadatableClipboard::MakeUnoObject"); throw; }
301 void OriginNoLongerInBusinessAnymore() { m_pReg = nullptr; }
304 // XmlIdRegistryClipboard --------------------------------------------
306 namespace {
308 class XmlIdRegistryClipboard : public XmlIdRegistry
311 public:
312 XmlIdRegistryClipboard();
314 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) override;
316 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
317 OUString const& i_rStreamName, OUString const& i_rIdref) override;
319 virtual void UnregisterMetadatable(Metadatable const&) override;
321 virtual void RemoveXmlIdForElement(Metadatable const&) override;
323 /** register i_rCopy as a copy of i_rSource */
324 MetadatableClipboard & RegisterCopyClipboard(Metadatable & i_rCopy,
325 beans::StringPair const & i_rReference,
326 const bool i_isLatent);
328 /** get the Metadatable that links i_rObject to its origin registry */
329 MetadatableClipboard const* SourceLink(Metadatable const& i_rObject);
331 private:
332 virtual bool LookupXmlId(const Metadatable& i_xObject,
333 OUString & o_rStream, OUString & o_rIdref) const override;
335 virtual Metadatable* LookupElement(const OUString & i_rStreamName,
336 const OUString & i_rIdref) const override;
338 /** create a Clipboard Metadatable for i_rObject. */
339 static std::shared_ptr<MetadatableClipboard> CreateClipboard(
340 const bool i_isInContent);
342 struct XmlIdRegistry_Impl;
343 ::std::unique_ptr<XmlIdRegistry_Impl> m_pImpl;
348 // XmlIdRegistry
350 ::sfx2::IXmlIdRegistry * createXmlIdRegistry(const bool i_DocIsClipboard)
352 return i_DocIsClipboard
353 ? static_cast<XmlIdRegistry*>( new XmlIdRegistryClipboard )
354 : static_cast<XmlIdRegistry*>( new XmlIdRegistryDocument );
357 XmlIdRegistry::XmlIdRegistry()
361 css::uno::Reference< css::rdf::XMetadatable >
362 XmlIdRegistry::GetElementByMetadataReference(
363 const beans::StringPair & i_rReference) const
365 Metadatable* pObject( LookupElement(i_rReference.First,
366 i_rReference.Second) );
367 return pObject ? pObject->MakeUnoObject() : nullptr;
370 beans::StringPair
371 XmlIdRegistry::GetXmlIdForElement(const Metadatable& i_rObject) const
373 OUString path;
374 OUString idref;
375 if (LookupXmlId(i_rObject, path, idref))
377 if (LookupElement(path, idref) == &i_rObject)
379 return beans::StringPair(path, idref);
382 return beans::StringPair();
386 /// generate unique xml:id
387 template< typename T >
388 static OUString create_id(const
389 std::unordered_map< OUString, T > & i_rXmlIdMap)
391 static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
392 static const char prefix[] = "id"; // prefix for generated xml:id
393 typename std::unordered_map< OUString, T >
394 ::const_iterator iter;
395 OUString id;
397 if (bHack)
399 static sal_Int64 nIdCounter = SAL_CONST_INT64(4000000000);
402 id = prefix + OUString::number(nIdCounter++);
403 iter = i_rXmlIdMap.find(id);
405 while (iter != i_rXmlIdMap.end());
407 else
411 unsigned int const n(comphelper::rng::uniform_uint_distribution(0,
412 std::numeric_limits<unsigned int>::max()));
413 id = prefix + OUString::number(n);
414 iter = i_rXmlIdMap.find(id);
416 while (iter != i_rXmlIdMap.end());
418 return id;
422 // Document XML ID Registry (_Impl)
424 /// element list
425 typedef ::std::vector< Metadatable* > XmlIdVector_t;
427 /// Idref -> (content.xml element list, styles.xml element list)
428 typedef std::unordered_map< OUString,
429 ::std::pair< XmlIdVector_t, XmlIdVector_t > > XmlIdMap_t;
431 namespace {
433 /// pointer hash template
434 template<typename T> struct PtrHash
436 size_t operator() (T const * i_pT) const
438 return reinterpret_cast<size_t>(i_pT);
444 /// element -> (stream name, idref)
445 typedef std::unordered_map< const Metadatable*,
446 ::std::pair< OUString, OUString>, PtrHash<Metadatable> >
447 XmlIdReverseMap_t;
449 struct XmlIdRegistryDocument::XmlIdRegistry_Impl
451 XmlIdRegistry_Impl()
452 : m_XmlIdMap(), m_XmlIdReverseMap() { }
454 bool TryInsertMetadatable(Metadatable& i_xObject,
455 const OUString & i_rStream, const OUString & i_rIdref);
457 bool LookupXmlId(const Metadatable& i_xObject,
458 OUString & o_rStream, OUString & o_rIdref) const;
460 Metadatable* LookupElement(const OUString & i_rStreamName,
461 const OUString & i_rIdref) const;
463 const XmlIdVector_t * LookupElementVector(
464 const OUString & i_rStreamName,
465 const OUString & i_rIdref) const;
467 XmlIdVector_t * LookupElementVector(
468 const OUString & i_rStreamName,
469 const OUString & i_rIdref)
471 return const_cast<XmlIdVector_t*>(
472 const_cast<const XmlIdRegistry_Impl*>(this)
473 ->LookupElementVector(i_rStreamName, i_rIdref));
476 XmlIdMap_t m_XmlIdMap;
477 XmlIdReverseMap_t m_XmlIdReverseMap;
481 static void
482 rmIter(XmlIdMap_t & i_rXmlIdMap, XmlIdMap_t::iterator const& i_rIter,
483 OUString const & i_rStream, Metadatable const& i_rObject)
485 if (i_rIter != i_rXmlIdMap.end())
487 XmlIdVector_t & rVector( isContentFile(i_rStream)
488 ? i_rIter->second.first : i_rIter->second.second );
489 rVector.erase(std::remove(rVector.begin(), rVector.end(), &const_cast<Metadatable&>(i_rObject)));
490 if (i_rIter->second.first.empty() && i_rIter->second.second.empty())
492 i_rXmlIdMap.erase(i_rIter);
498 const XmlIdVector_t *
499 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElementVector(
500 const OUString & i_rStreamName,
501 const OUString & i_rIdref) const
503 const XmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
504 if (iter != m_XmlIdMap.end())
506 OSL_ENSURE(!iter->second.first.empty() || !iter->second.second.empty(),
507 "null entry in m_XmlIdMap");
508 return (isContentFile(i_rStreamName))
509 ? &iter->second.first
510 : &iter->second.second;
512 else
514 return nullptr;
518 Metadatable*
519 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElement(
520 const OUString & i_rStreamName,
521 const OUString & i_rIdref) const
523 if (!isValidXmlId(i_rStreamName, i_rIdref))
525 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
528 const XmlIdVector_t * pList( LookupElementVector(i_rStreamName, i_rIdref) );
529 if (pList)
531 const XmlIdVector_t::const_iterator iter(
532 ::std::find_if(pList->begin(), pList->end(),
533 [](Metadatable* item)->bool {
534 return !(item->IsInUndo() || item->IsInClipboard());
535 } ) ) ;
536 if (iter != pList->end())
538 return *iter;
541 return nullptr;
544 bool
545 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupXmlId(
546 const Metadatable& i_rObject,
547 OUString & o_rStream, OUString & o_rIdref) const
549 const XmlIdReverseMap_t::const_iterator iter(
550 m_XmlIdReverseMap.find(&i_rObject) );
551 if (iter != m_XmlIdReverseMap.end())
553 OSL_ENSURE(!iter->second.first.isEmpty(),
554 "null stream in m_XmlIdReverseMap");
555 OSL_ENSURE(!iter->second.second.isEmpty(),
556 "null id in m_XmlIdReverseMap");
557 o_rStream = iter->second.first;
558 o_rIdref = iter->second.second;
559 return true;
561 else
563 return false;
567 bool
568 XmlIdRegistryDocument::XmlIdRegistry_Impl::TryInsertMetadatable(
569 Metadatable & i_rObject,
570 const OUString & i_rStreamName, const OUString & i_rIdref)
572 const bool bContent( isContentFile(i_rStreamName) );
573 OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName),
574 "invalid stream");
576 XmlIdVector_t * pList( LookupElementVector(i_rStreamName, i_rIdref) );
577 if (pList)
579 if (pList->empty())
581 pList->push_back( &i_rObject );
582 return true;
584 else
586 // this is only called from TryRegister now, so check
587 // if all elements in the list are deleted (in undo) or
588 // placeholders, then "steal" the id from them
589 if ( std::none_of(pList->begin(), pList->end(),
590 [](Metadatable* item)->bool {
591 return !(item->IsInUndo() || item->IsInClipboard());
592 } ) )
594 pList->insert(pList->begin(), &i_rObject );
595 return true;
597 else
599 return false;
603 else
605 m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent
606 ? ::std::make_pair( XmlIdVector_t( 1, &i_rObject ), XmlIdVector_t() )
607 : ::std::make_pair( XmlIdVector_t(), XmlIdVector_t( 1, &i_rObject ) )));
608 return true;
613 // Document XML ID Registry
616 XmlIdRegistryDocument::XmlIdRegistryDocument()
617 : m_pImpl( new XmlIdRegistry_Impl )
621 static void
622 removeLink(Metadatable* i_pObject)
624 OSL_ENSURE(i_pObject, "null in list ???");
625 if (!i_pObject) return;
626 if (i_pObject->IsInClipboard())
628 MetadatableClipboard* pLink(
629 dynamic_cast<MetadatableClipboard*>( i_pObject ) );
630 OSL_ENSURE(pLink, "IsInClipboard, but no MetadatableClipboard ?");
631 if (pLink)
633 pLink->OriginNoLongerInBusinessAnymore();
638 XmlIdRegistryDocument::~XmlIdRegistryDocument()
640 // notify all list elements that are actually in the clipboard
641 for (const auto& aXmlId : m_pImpl->m_XmlIdMap) {
642 for (auto aLink : aXmlId.second.first)
643 removeLink(aLink);
644 for (auto aLink : aXmlId.second.second)
645 removeLink(aLink);
649 bool
650 XmlIdRegistryDocument::LookupXmlId(
651 const Metadatable& i_rObject,
652 OUString & o_rStream, OUString & o_rIdref) const
654 return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref);
657 Metadatable*
658 XmlIdRegistryDocument::LookupElement(
659 const OUString & i_rStreamName,
660 const OUString & i_rIdref) const
662 return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
665 bool
666 XmlIdRegistryDocument::TryRegisterMetadatable(Metadatable & i_rObject,
667 OUString const& i_rStreamName, OUString const& i_rIdref)
669 SAL_INFO("sfx", "TryRegisterMetadatable: " << &i_rObject << " (" << i_rStreamName << "#" << i_rIdref << ")");
671 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
672 "TryRegisterMetadatable called for MetadatableUndo?");
673 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
674 "TryRegisterMetadatable called for MetadatableClipboard?");
676 if (!isValidXmlId(i_rStreamName, i_rIdref))
678 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
680 if (i_rObject.IsInContent()
681 ? !isContentFile(i_rStreamName)
682 : !isStylesFile(i_rStreamName))
684 throw lang::IllegalArgumentException("illegal XmlId: wrong stream", nullptr, 0);
687 OUString old_path;
688 OUString old_idref;
689 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
690 if (old_path == i_rStreamName && old_idref == i_rIdref)
692 return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
694 XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
695 if (!old_idref.isEmpty())
697 old_id = m_pImpl->m_XmlIdMap.find(old_idref);
698 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
700 if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
702 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
703 m_pImpl->m_XmlIdReverseMap[&i_rObject] =
704 ::std::make_pair(i_rStreamName, i_rIdref);
705 return true;
707 else
709 return false;
713 void
714 XmlIdRegistryDocument::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
716 SAL_INFO("sfx", "RegisterMetadatableAndCreateID: " << &i_rObject);
718 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
719 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
720 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
721 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
723 const bool isInContent( i_rObject.IsInContent() );
724 const OUString stream( OUString::createFromAscii(
725 isInContent ? s_content : s_styles ) );
726 // check if we have a latent xmlid, and if yes, remove it
727 OUString old_path;
728 OUString old_idref;
729 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
731 XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
732 if (!old_idref.isEmpty())
734 old_id = m_pImpl->m_XmlIdMap.find(old_idref);
735 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
736 if (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject)
738 return;
740 else
742 // remove latent xmlid
743 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
747 // create id
748 const OUString id( create_id(m_pImpl->m_XmlIdMap) );
749 OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(),
750 "created id is in use");
751 m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent
752 ? ::std::make_pair( XmlIdVector_t( 1, &i_rObject ), XmlIdVector_t() )
753 : ::std::make_pair( XmlIdVector_t(), XmlIdVector_t( 1, &i_rObject ) )));
754 m_pImpl->m_XmlIdReverseMap[&i_rObject] = ::std::make_pair(stream, id);
757 void XmlIdRegistryDocument::UnregisterMetadatable(const Metadatable& i_rObject)
759 SAL_INFO("sfx", "UnregisterMetadatable: " << &i_rObject);
761 OUString path;
762 OUString idref;
763 if (!m_pImpl->LookupXmlId(i_rObject, path, idref))
765 OSL_FAIL("unregister: no xml id?");
766 return;
768 const XmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
769 if (iter != m_pImpl->m_XmlIdMap.end())
771 rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
775 void XmlIdRegistryDocument::RemoveXmlIdForElement(const Metadatable& i_rObject)
777 SAL_INFO("sfx", "RemoveXmlIdForElement: " << &i_rObject);
779 const XmlIdReverseMap_t::iterator iter(
780 m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
781 if (iter != m_pImpl->m_XmlIdReverseMap.end())
783 OSL_ENSURE(!iter->second.second.isEmpty(),
784 "null id in m_XmlIdReverseMap");
785 m_pImpl->m_XmlIdReverseMap.erase(iter);
790 void XmlIdRegistryDocument::RegisterCopy(Metadatable const& i_rSource,
791 Metadatable & i_rCopy, const bool i_bCopyPrecedesSource)
793 SAL_INFO("sfx", "RegisterCopy: " << &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 XmlIdVector_t * pList ( m_pImpl->LookupElementVector(path, idref) );
810 OSL_ENSURE( ::std::find( pList->begin(), pList->end(), &i_rCopy )
811 == pList->end(), "copy already registered???");
812 XmlIdVector_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 SAL_INFO("sfx", "CreateUndo: " << &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 SAL_INFO("sfx", "JoinMetadatables: " << &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 namespace {
891 struct RMapEntry
893 RMapEntry() : m_xLink() { }
894 RMapEntry(OUString const& i_rStream,
895 OUString const& i_rXmlId,
896 std::shared_ptr<MetadatableClipboard> const& i_pLink
897 = std::shared_ptr<MetadatableClipboard>())
898 : m_Stream(i_rStream), m_XmlId(i_rXmlId), m_xLink(i_pLink)
900 OUString m_Stream;
901 OUString m_XmlId;
902 // this would have been an auto_ptr, if only that would have compiled...
903 std::shared_ptr<MetadatableClipboard> m_xLink;
908 /// element -> (stream name, idref, source)
909 typedef std::unordered_map< const Metadatable*,
910 struct RMapEntry,
911 PtrHash<Metadatable> >
912 ClipboardXmlIdReverseMap_t;
914 /// Idref -> (content.xml element, styles.xml element)
915 typedef std::unordered_map< OUString,
916 ::std::pair< Metadatable*, Metadatable* > >
917 ClipboardXmlIdMap_t;
919 struct XmlIdRegistryClipboard::XmlIdRegistry_Impl
921 XmlIdRegistry_Impl()
922 : m_XmlIdMap(), m_XmlIdReverseMap() { }
924 bool TryInsertMetadatable(Metadatable& i_xObject,
925 const OUString & i_rStream, const OUString & i_rIdref);
927 bool LookupXmlId(const Metadatable& i_xObject,
928 OUString & o_rStream, OUString & o_rIdref,
929 MetadatableClipboard const* &o_rpLink) const;
931 Metadatable* LookupElement(const OUString & i_rStreamName,
932 const OUString & i_rIdref) const;
934 Metadatable* const* LookupEntry(const OUString & i_rStreamName,
935 const OUString & i_rIdref) const;
937 ClipboardXmlIdMap_t m_XmlIdMap;
938 ClipboardXmlIdReverseMap_t m_XmlIdReverseMap;
942 static void
943 rmIter(ClipboardXmlIdMap_t & i_rXmlIdMap,
944 ClipboardXmlIdMap_t::iterator const& i_rIter,
945 OUString const & i_rStream, Metadatable const& i_rObject)
947 if (i_rIter == i_rXmlIdMap.end())
948 return;
950 Metadatable *& rMeta = isContentFile(i_rStream)
951 ? i_rIter->second.first : i_rIter->second.second;
952 if (rMeta == &i_rObject)
954 rMeta = nullptr;
956 if (!i_rIter->second.first && !i_rIter->second.second)
958 i_rXmlIdMap.erase(i_rIter);
963 Metadatable* const*
964 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupEntry(
965 const OUString & i_rStreamName,
966 const OUString & i_rIdref) const
968 if (!isValidXmlId(i_rStreamName, i_rIdref))
970 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
973 const ClipboardXmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
974 if (iter != m_XmlIdMap.end())
976 OSL_ENSURE(iter->second.first || iter->second.second,
977 "null entry in m_XmlIdMap");
978 return (isContentFile(i_rStreamName))
979 ? &iter->second.first
980 : &iter->second.second;
982 else
984 return nullptr;
988 Metadatable*
989 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupElement(
990 const OUString & i_rStreamName,
991 const OUString & i_rIdref) const
993 Metadatable * const * ppEntry = LookupEntry(i_rStreamName, i_rIdref);
994 return ppEntry ? *ppEntry : nullptr;
997 bool
998 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupXmlId(
999 const Metadatable& i_rObject,
1000 OUString & o_rStream, OUString & o_rIdref,
1001 MetadatableClipboard const* &o_rpLink) const
1003 const ClipboardXmlIdReverseMap_t::const_iterator iter(
1004 m_XmlIdReverseMap.find(&i_rObject) );
1005 if (iter != m_XmlIdReverseMap.end())
1007 OSL_ENSURE(!iter->second.m_Stream.isEmpty(),
1008 "null stream in m_XmlIdReverseMap");
1009 OSL_ENSURE(!iter->second.m_XmlId.isEmpty(),
1010 "null id in m_XmlIdReverseMap");
1011 o_rStream = iter->second.m_Stream;
1012 o_rIdref = iter->second.m_XmlId;
1013 o_rpLink = iter->second.m_xLink.get();
1014 return true;
1016 else
1018 return false;
1022 bool
1023 XmlIdRegistryClipboard::XmlIdRegistry_Impl::TryInsertMetadatable(
1024 Metadatable & i_rObject,
1025 const OUString & i_rStreamName, const OUString & i_rIdref)
1027 bool bContent( isContentFile(i_rStreamName) );
1028 OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName),
1029 "invalid stream");
1031 Metadatable ** ppEntry = const_cast<Metadatable**>(LookupEntry(i_rStreamName, i_rIdref));
1032 if (ppEntry)
1034 if (*ppEntry)
1036 return false;
1038 else
1040 *ppEntry = &i_rObject;
1041 return true;
1044 else
1046 m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent
1047 ? ::std::make_pair( &i_rObject, static_cast<Metadatable*>(nullptr) )
1048 : ::std::make_pair( static_cast<Metadatable*>(nullptr), &i_rObject )));
1049 return true;
1054 // Clipboard XML ID Registry
1057 XmlIdRegistryClipboard::XmlIdRegistryClipboard()
1058 : m_pImpl( new XmlIdRegistry_Impl )
1062 bool
1063 XmlIdRegistryClipboard::LookupXmlId(
1064 const Metadatable& i_rObject,
1065 OUString & o_rStream, OUString & o_rIdref) const
1067 const MetadatableClipboard * pLink;
1068 return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref, pLink);
1071 Metadatable*
1072 XmlIdRegistryClipboard::LookupElement(
1073 const OUString & i_rStreamName,
1074 const OUString & i_rIdref) const
1076 return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
1079 bool
1080 XmlIdRegistryClipboard::TryRegisterMetadatable(Metadatable & i_rObject,
1081 OUString const& i_rStreamName, OUString const& i_rIdref)
1083 SAL_INFO("sfx", "TryRegisterMetadatable: " << &i_rObject << " (" << i_rStreamName << "#" << i_rIdref <<")");
1085 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
1086 "TryRegisterMetadatable called for MetadatableUndo?");
1087 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
1088 "TryRegisterMetadatable called for MetadatableClipboard?");
1090 if (!isValidXmlId(i_rStreamName, i_rIdref))
1092 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
1094 if (i_rObject.IsInContent()
1095 ? !isContentFile(i_rStreamName)
1096 : !isStylesFile(i_rStreamName))
1098 throw lang::IllegalArgumentException("illegal XmlId: wrong stream", nullptr, 0);
1101 OUString old_path;
1102 OUString old_idref;
1103 const MetadatableClipboard * pLink;
1104 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref, pLink);
1105 if (old_path == i_rStreamName && old_idref == i_rIdref)
1107 return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
1109 ClipboardXmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
1110 if (!old_idref.isEmpty())
1112 old_id = m_pImpl->m_XmlIdMap.find(old_idref);
1113 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
1115 if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
1117 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
1118 m_pImpl->m_XmlIdReverseMap[&i_rObject] =
1119 RMapEntry(i_rStreamName, i_rIdref);
1120 return true;
1122 else
1124 return false;
1128 void
1129 XmlIdRegistryClipboard::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
1131 SAL_INFO("sfx", "RegisterMetadatableAndCreateID: " << &i_rObject);
1133 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
1134 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
1135 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
1136 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
1138 bool isInContent( i_rObject.IsInContent() );
1139 OUString stream( OUString::createFromAscii(
1140 isInContent ? s_content : s_styles ) );
1142 OUString old_path;
1143 OUString old_idref;
1144 LookupXmlId(i_rObject, old_path, old_idref);
1145 if (!old_idref.isEmpty() &&
1146 (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject))
1148 return;
1151 // create id
1152 const OUString id( create_id(m_pImpl->m_XmlIdMap) );
1153 OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(),
1154 "created id is in use");
1155 m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent
1156 ? ::std::make_pair( &i_rObject, static_cast<Metadatable*>(nullptr) )
1157 : ::std::make_pair( static_cast<Metadatable*>(nullptr), &i_rObject )));
1158 // N.B.: if i_rObject had a latent XmlId, then we implicitly delete the
1159 // MetadatableClipboard and thus the latent XmlId here
1160 m_pImpl->m_XmlIdReverseMap[&i_rObject] = RMapEntry(stream, id);
1163 void XmlIdRegistryClipboard::UnregisterMetadatable(const Metadatable& i_rObject)
1165 SAL_INFO("sfx", "UnregisterMetadatable: " << &i_rObject);
1167 OUString path;
1168 OUString idref;
1169 const MetadatableClipboard * pLink;
1170 if (!m_pImpl->LookupXmlId(i_rObject, path, idref, pLink))
1172 OSL_FAIL("unregister: no xml id?");
1173 return;
1175 const ClipboardXmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
1176 if (iter != m_pImpl->m_XmlIdMap.end())
1178 rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
1183 void XmlIdRegistryClipboard::RemoveXmlIdForElement(const Metadatable& i_rObject)
1185 SAL_INFO("sfx", "RemoveXmlIdForElement: " << &i_rObject);
1187 ClipboardXmlIdReverseMap_t::iterator iter(
1188 m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
1189 if (iter != m_pImpl->m_XmlIdReverseMap.end())
1191 OSL_ENSURE(!iter->second.m_XmlId.isEmpty(),
1192 "null id in m_XmlIdReverseMap");
1193 m_pImpl->m_XmlIdReverseMap.erase(iter);
1198 std::shared_ptr<MetadatableClipboard>
1199 XmlIdRegistryClipboard::CreateClipboard(const bool i_isInContent)
1201 SAL_INFO("sfx", "CreateClipboard:");
1203 return std::make_shared<MetadatableClipboard>(
1204 i_isInContent );
1207 MetadatableClipboard &
1208 XmlIdRegistryClipboard::RegisterCopyClipboard(Metadatable & i_rCopy,
1209 beans::StringPair const & i_rReference,
1210 const bool i_isLatent)
1212 SAL_INFO("sfx", "RegisterCopyClipboard: " << &i_rCopy
1213 << " -> (" << i_rReference.First << "#" << i_rReference.Second << ") (" << i_isLatent << ")");
1215 // N.B.: when copying to the clipboard, the selection is always inserted
1216 // into the body, even if the source is a header/footer!
1217 // so we do not check whether the stream is right in this function
1219 if (!isValidXmlId(i_rReference.First, i_rReference.Second))
1221 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
1224 if (!i_isLatent)
1226 // this should succeed assuming clipboard has a single source document
1227 const bool success( m_pImpl->TryInsertMetadatable(i_rCopy,
1228 i_rReference.First, i_rReference.Second) );
1229 OSL_ENSURE(success, "RegisterCopyClipboard: TryInsert failed?");
1231 const std::shared_ptr<MetadatableClipboard> xLink(
1232 CreateClipboard( isContentFile(i_rReference.First)) );
1233 m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy,
1234 RMapEntry(i_rReference.First, i_rReference.Second, xLink)));
1235 return *xLink;
1238 MetadatableClipboard const*
1239 XmlIdRegistryClipboard::SourceLink(Metadatable const& i_rObject)
1241 OUString path;
1242 OUString idref;
1243 const MetadatableClipboard * pLink( nullptr );
1244 m_pImpl->LookupXmlId(i_rObject, path, idref, pLink);
1245 return pLink;
1249 // Metadatable mixin
1252 Metadatable::~Metadatable()
1254 RemoveMetadataReference();
1257 void Metadatable::RemoveMetadataReference()
1261 if (m_pReg)
1263 m_pReg->UnregisterMetadatable( *this );
1264 m_pReg->RemoveXmlIdForElement( *this );
1265 m_pReg = nullptr;
1268 catch (const uno::Exception &)
1270 OSL_FAIL("Metadatable::RemoveMetadataReference: exception");
1274 // css::rdf::XMetadatable:
1275 beans::StringPair
1276 Metadatable::GetMetadataReference() const
1278 if (m_pReg)
1280 return m_pReg->GetXmlIdForElement(*this);
1282 return beans::StringPair();
1285 void Metadatable::SetMetadataReference( const css::beans::StringPair & i_rReference)
1287 if (i_rReference.Second.isEmpty())
1289 RemoveMetadataReference();
1291 else
1293 OUString streamName( i_rReference.First );
1294 if (streamName.isEmpty())
1296 // handle empty stream name as auto-detect.
1297 // necessary for importing flat file format.
1298 streamName = OUString::createFromAscii(
1299 IsInContent() ? s_content : s_styles );
1301 XmlIdRegistry & rReg( dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1302 if (!rReg.TryRegisterMetadatable(*this, streamName, i_rReference.Second))
1304 throw lang::IllegalArgumentException(
1305 "Metadatable::SetMetadataReference: argument is invalid", /*this*/nullptr, 0);
1308 m_pReg = &rReg;
1312 void Metadatable::EnsureMetadataReference()
1314 XmlIdRegistry& rReg(
1315 m_pReg ? *m_pReg : dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1316 rReg.RegisterMetadatableAndCreateID( *this );
1317 m_pReg = &rReg;
1320 static const ::sfx2::IXmlIdRegistry& GetRegistryConst(Metadatable const& i_rObject)
1322 return const_cast< Metadatable& >( i_rObject ).GetRegistry();
1325 void
1326 Metadatable::RegisterAsCopyOf(Metadatable const & i_rSource,
1327 const bool i_bCopyPrecedesSource)
1329 OSL_ENSURE(typeid(*this) == typeid(i_rSource)
1330 || typeid(i_rSource) == typeid(MetadatableUndo)
1331 || typeid(*this) == typeid(MetadatableUndo)
1332 || typeid(i_rSource) == typeid(MetadatableClipboard)
1333 || typeid(*this) == typeid(MetadatableClipboard),
1334 "RegisterAsCopyOf element with different class?");
1335 OSL_ENSURE(!m_pReg, "RegisterAsCopyOf called on element with XmlId?");
1337 if (m_pReg)
1339 RemoveMetadataReference();
1344 if (i_rSource.m_pReg)
1346 XmlIdRegistry & rReg(
1347 dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1348 if (i_rSource.m_pReg == &rReg)
1350 OSL_ENSURE(!IsInClipboard(),
1351 "RegisterAsCopy: both in clipboard?");
1352 if (!IsInClipboard())
1354 XmlIdRegistryDocument & rRegDoc(
1355 dynamic_cast<XmlIdRegistryDocument&>( rReg ) );
1356 rRegDoc.RegisterCopy(i_rSource, *this,
1357 i_bCopyPrecedesSource);
1358 m_pReg = &rRegDoc;
1360 return;
1362 // source is in different document
1363 XmlIdRegistryDocument * pRegDoc(
1364 dynamic_cast<XmlIdRegistryDocument *>(&rReg) );
1365 XmlIdRegistryClipboard * pRegClp(
1366 dynamic_cast<XmlIdRegistryClipboard*>(&rReg) );
1368 if (pRegClp)
1370 beans::StringPair SourceRef(
1371 i_rSource.m_pReg->GetXmlIdForElement(i_rSource) );
1372 bool isLatent( SourceRef.Second.isEmpty() );
1373 XmlIdRegistryDocument * pSourceRegDoc(
1374 dynamic_cast<XmlIdRegistryDocument*>(i_rSource.m_pReg) );
1375 OSL_ENSURE(pSourceRegDoc, "RegisterAsCopyOf: 2 clipboards?");
1376 if (!pSourceRegDoc) return;
1377 // this is a copy _to_ the clipboard
1378 if (isLatent)
1380 pSourceRegDoc->LookupXmlId(i_rSource,
1381 SourceRef.First, SourceRef.Second);
1383 Metadatable & rLink(
1384 pRegClp->RegisterCopyClipboard(*this, SourceRef, isLatent));
1385 m_pReg = pRegClp;
1386 // register as copy in the non-clipboard registry
1387 pSourceRegDoc->RegisterCopy(i_rSource, rLink,
1388 false); // i_bCopyPrecedesSource);
1389 rLink.m_pReg = pSourceRegDoc;
1391 else if (pRegDoc)
1393 XmlIdRegistryClipboard * pSourceRegClp(
1394 dynamic_cast<XmlIdRegistryClipboard*>(i_rSource.m_pReg) );
1395 OSL_ENSURE(pSourceRegClp,
1396 "RegisterAsCopyOf: 2 non-clipboards?");
1397 if (!pSourceRegClp) return;
1398 const MetadatableClipboard * pLink(
1399 pSourceRegClp->SourceLink(i_rSource) );
1400 // may happen if src got its id via UNO call
1401 if (!pLink) return;
1402 // only register copy if clipboard content is from this SwDoc!
1403 if (&GetRegistryConst(*pLink) == pRegDoc)
1405 // this is a copy _from_ the clipboard; check if the
1406 // element is still in the same stream
1407 // N.B.: we check the stream of pLink, not of i_rSource!
1408 bool srcInContent( pLink->IsInContent() );
1409 bool tgtInContent( IsInContent() );
1410 if (srcInContent == tgtInContent)
1412 pRegDoc->RegisterCopy(*pLink, *this,
1413 true); // i_bCopyPrecedesSource);
1414 m_pReg = pRegDoc;
1416 // otherwise: stream change! do not register!
1419 else
1421 OSL_FAIL("neither RegDoc nor RegClp cannot happen");
1425 catch (const uno::Exception &)
1427 OSL_FAIL("Metadatable::RegisterAsCopyOf: exception");
1431 std::shared_ptr<MetadatableUndo> Metadatable::CreateUndo() const
1433 OSL_ENSURE(!IsInUndo(), "CreateUndo called for object in undo?");
1434 OSL_ENSURE(!IsInClipboard(), "CreateUndo called for object in clipboard?");
1437 if (!IsInClipboard() && !IsInUndo() && m_pReg)
1439 XmlIdRegistryDocument * pRegDoc(
1440 dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
1441 assert(pRegDoc);
1442 std::shared_ptr<MetadatableUndo> xUndo(
1443 sfx2::XmlIdRegistryDocument::CreateUndo(*this) );
1444 pRegDoc->RegisterCopy(*this, *xUndo, false);
1445 xUndo->m_pReg = pRegDoc;
1446 return xUndo;
1449 catch (const uno::Exception &)
1451 OSL_FAIL("Metadatable::CreateUndo: exception");
1453 return std::shared_ptr<MetadatableUndo>();
1456 std::shared_ptr<MetadatableUndo> Metadatable::CreateUndoForDelete()
1458 std::shared_ptr<MetadatableUndo> const xUndo( CreateUndo() );
1459 RemoveMetadataReference();
1460 return xUndo;
1463 void Metadatable::RestoreMetadata(
1464 std::shared_ptr<MetadatableUndo> const& i_pUndo)
1466 OSL_ENSURE(!IsInUndo(), "RestoreMetadata called for object in undo?");
1467 OSL_ENSURE(!IsInClipboard(),
1468 "RestoreMetadata called for object in clipboard?");
1469 if (IsInClipboard() || IsInUndo()) return;
1470 RemoveMetadataReference();
1471 if (i_pUndo)
1473 RegisterAsCopyOf(*i_pUndo, true);
1477 void
1478 Metadatable::JoinMetadatable(Metadatable const & i_rOther,
1479 const bool i_isMergedEmpty, const bool i_isOtherEmpty)
1481 OSL_ENSURE(!IsInUndo(), "JoinMetadatables called for object in undo?");
1482 OSL_ENSURE(!IsInClipboard(),
1483 "JoinMetadatables called for object in clipboard?");
1484 if (IsInClipboard() || IsInUndo()) return;
1486 if (i_isOtherEmpty && !i_isMergedEmpty)
1488 // other is empty, thus loses => nothing to do
1489 return;
1491 if (i_isMergedEmpty && !i_isOtherEmpty)
1493 RemoveMetadataReference();
1494 RegisterAsCopyOf(i_rOther, true);
1495 return;
1498 if (!i_rOther.m_pReg)
1500 // other doesn't have xmlid, thus loses => nothing to do
1501 return;
1503 if (!m_pReg)
1505 RegisterAsCopyOf(i_rOther, true);
1506 // assumption: i_rOther will be deleted, so don't unregister it here
1507 return;
1511 XmlIdRegistryDocument * pRegDoc(
1512 dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
1513 OSL_ENSURE(pRegDoc, "JoinMetadatable: no pRegDoc?");
1514 if (pRegDoc)
1516 pRegDoc->JoinMetadatables(*this, i_rOther);
1519 catch (const uno::Exception &)
1521 OSL_FAIL("Metadatable::JoinMetadatable: exception");
1526 // XMetadatable mixin
1528 // css::rdf::XNode:
1529 OUString SAL_CALL MetadatableMixin::getStringValue()
1531 return getNamespace() + getLocalName();
1534 // css::rdf::XURI:
1535 OUString SAL_CALL MetadatableMixin::getLocalName()
1537 SolarMutexGuard aGuard;
1538 beans::StringPair mdref( getMetadataReference() );
1539 if (mdref.Second.isEmpty())
1541 ensureMetadataReference(); // N.B.: side effect!
1542 mdref = getMetadataReference();
1544 return mdref.First + "#" + mdref.Second;
1547 OUString SAL_CALL MetadatableMixin::getNamespace()
1549 SolarMutexGuard aGuard;
1550 const uno::Reference< frame::XModel > xModel( GetModel() );
1551 const uno::Reference< rdf::XURI > xDMA( xModel, uno::UNO_QUERY_THROW );
1552 return xDMA->getStringValue();
1555 // css::rdf::XMetadatable:
1556 beans::StringPair SAL_CALL
1557 MetadatableMixin::getMetadataReference()
1559 SolarMutexGuard aGuard;
1561 Metadatable *const pObject( GetCoreObject() );
1562 if (!pObject)
1564 throw uno::RuntimeException(
1565 "MetadatableMixin: cannot get core object; not inserted?",
1566 *this);
1568 return pObject->GetMetadataReference();
1571 void SAL_CALL
1572 MetadatableMixin::setMetadataReference(
1573 const beans::StringPair & i_rReference)
1575 SolarMutexGuard aGuard;
1577 Metadatable *const pObject( GetCoreObject() );
1578 if (!pObject)
1580 throw uno::RuntimeException(
1581 "MetadatableMixin: cannot get core object; not inserted?",
1582 *this);
1584 return pObject->SetMetadataReference(i_rReference);
1587 void SAL_CALL MetadatableMixin::ensureMetadataReference()
1589 SolarMutexGuard aGuard;
1591 Metadatable *const pObject( GetCoreObject() );
1592 if (!pObject)
1594 throw uno::RuntimeException(
1595 "MetadatableMixin: cannot get core object; not inserted?",
1596 *this);
1598 return pObject->EnsureMetadataReference();
1601 } // namespace sfx2
1603 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */