LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / sfx2 / source / doc / Metadatable.cxx
blob1906967cabe0d495f3ddae53ecadf9cc61d23aff
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>
31 #include <tools/diagnose_ex.h>
33 #include <algorithm>
34 #include <memory>
35 #include <string_view>
36 #include <unordered_map>
37 #if OSL_DEBUG_LEVEL > 0
38 #include <typeinfo>
39 #endif
42 /** XML ID handling.
44 There is an abstract base class <type>XmlIdRegistry</type>, with
45 2 subclasses <type>XmlIdRegistryDocument</type> for "normal" documents,
46 and <type>XmlIdRegistryClipboard</type> for clipboard documents.
47 These classes are responsible for managing XML IDs for all elements
48 of the model. Only the implementation of the <type>Metadatable</type>
49 base class needs to know the registries, so they are not in the header.
51 The handling of XML IDs differs between clipboard and non-clipboard
52 documents in several aspects. Most importantly, non-clipboard documents
53 can have several elements associated with one XML ID.
54 This is necessary because of the weird undo implementation:
55 deleting a text node moves the deleted node to the undo array, but
56 executing undo will then create a <em>copy</em> of that node in the
57 document array. These 2 nodes must have the same XML ID, because
58 we cannot know whether the user will do a redo next, or something else.
60 Because we need to have a mechanism for several objects per XML ID anyway,
61 we use that also to enable some usability features:
62 The document registry has a list of Metadatables per XML ID.
63 This list is sorted by priority, i.e., the first element has highest
64 priority. When inserting copies, care must be taken that they are inserted
65 at the right position: either before or after the source.
66 This is done by <method>Metadatable::RegisterAsCopyOf</method>.
67 When a text node is split, then both resulting text nodes are inserted
68 into the list. If the user then deletes one text node, the other one
69 will have the XML ID.
70 Also, when a Metadatable is copied to the clipboard and then pasted,
71 the copy is inserted into the list. If the user then deletes the source,
72 the XML ID is not lost.
73 The goal is that it should be hard to lose an XML ID by accident, which
74 is especially important as long as we do not have an UI that displays them.
76 There are two subclasses of <type>Metadatable</type>:
77 <ul><li><type>MetadatableClipboard</type>: for copies in the clipboard</li>
78 <li><type>MetadatableUndo</type>: for undo, because a Metadatable
79 may be destroyed on delete and a new one created on undo.</li></ul>
80 These serve only to track the position in an XML ID list in a document
81 registry, so that future actions can insert objects at the right position.
82 Unfortunately, inserting dummy objects seems to be necessary:
83 <ul><li>it is not sufficient to just remember the saved id, because then
84 the relative priorities might change when executing the undo</li>
85 <li>it is not sufficient to record the position as an integer, because
86 if we delete a text node and then undo, the node will be copied(!),
87 and we will have one more node in the list.<li>
88 <li>it is not sufficient to record the pointer of the previous/next
89 Metadatable, because if we delete a text node, undo, and then
90 do something to clear the redo array, the original text node is
91 destroyed, and is replaced by the copy created by undo</li></ul>
93 If content from a non-clipboard document is copied into a clipboard
94 document, a dummy <type>MetadatableClipboard</type> is inserted into the
95 non-clipboard document registry in order to track the position of the
96 source element. When the clipboard content is pasted back into the source
97 document, this dummy object is used to associate the pasted element with
98 that same XML ID.
100 If a <type>Metadatable</type> is deleted or merged,
101 <method>Metadatable::CreateUndo</method> is called, and returns a
102 <type>MetadatableUndo<type> instance, which can be used to undo the action
103 by passing it to <method>Metadatable::RestoreMetadata</method>.
108 using namespace ::com::sun::star;
110 using ::sfx2::isValidXmlId;
113 namespace sfx2 {
115 constexpr OUStringLiteral s_content = u"content.xml";
116 constexpr OUStringLiteral s_styles = u"styles.xml";
118 static bool isContentFile(std::u16string_view i_rPath)
120 return i_rPath == s_content;
123 static bool isStylesFile (std::u16string_view i_rPath)
125 return i_rPath == s_styles;
129 // XML ID handling ---------------------------------------------------
131 /** handles registration of XMetadatable.
133 This class is responsible for guaranteeing that XMetadatable objects
134 always have XML IDs that are unique within a stream.
136 This is an abstract base class; see subclasses XmlIdRegistryDocument and
137 XmlIdRegistryClipboard.
139 @see SwDoc::GetXmlIdRegistry
140 @see SwDocShell::GetXmlIdRegistry
142 class XmlIdRegistry : public sfx2::IXmlIdRegistry
145 public:
146 XmlIdRegistry();
148 /** get the ODF element with the given metadata reference. */
149 virtual css::uno::Reference< css::rdf::XMetadatable >
150 GetElementByMetadataReference(
151 const css::beans::StringPair & i_rReference) const
152 override;
154 /** register an ODF element at a newly generated, unique metadata reference.
157 Find a fresh XML ID, and register it for the element.
158 The generated ID does not occur in any stream of the document.
159 </p>
161 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) = 0;
163 /** try to register an ODF element at a given XML ID, or update its
164 registration to a different XML ID.
167 If the given new metadata reference is not already occupied in the
168 document, unregister the element at its old metadata reference if
169 it has one, and register the new metadata reference for the element.
170 Note that this method only ensures that XML IDs are unique per stream,
171 so using the same XML ID in both content.xml and styles.xml is allowed.
172 </p>
174 @returns
175 true iff the element has successfully been registered
177 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
178 OUString const& i_rStreamName, OUString const& i_rIdref)
179 = 0;
181 /** unregister an ODF element.
184 Unregister the element at its metadata reference.
185 Does not remove the metadata reference from the element.
186 </p>
188 @see RemoveXmlIdForElement
190 virtual void UnregisterMetadatable(Metadatable const&) = 0;
192 /** get the metadata reference for the given element. */
193 css::beans::StringPair
194 GetXmlIdForElement(Metadatable const&) const;
196 /** remove the metadata reference for the given element. */
197 virtual void RemoveXmlIdForElement(Metadatable const&) = 0;
199 protected:
201 virtual bool LookupXmlId(const Metadatable& i_xObject,
202 OUString & o_rStream, OUString & o_rIdref) const = 0;
204 virtual Metadatable* LookupElement(const OUString & i_rStreamName,
205 const OUString & i_rIdref) const = 0;
208 // XmlIdRegistryDocument ---------------------------------------------
210 namespace {
212 /** non-clipboard documents */
213 class XmlIdRegistryDocument : public XmlIdRegistry
216 public:
217 XmlIdRegistryDocument();
219 virtual ~XmlIdRegistryDocument() override;
221 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) override;
223 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
224 OUString const& i_rStreamName, OUString const& i_rIdref) override;
226 virtual void UnregisterMetadatable(Metadatable const&) override;
228 virtual void RemoveXmlIdForElement(Metadatable const&) override;
230 /** register i_rCopy as a copy of i_rSource,
231 with precedence iff i_bCopyPrecedesSource is true */
232 void RegisterCopy(Metadatable const& i_rSource, Metadatable & i_rCopy,
233 const bool i_bCopyPrecedesSource);
235 /** create a Undo Metadatable for i_rObject. */
236 static std::shared_ptr<MetadatableUndo> CreateUndo(
237 Metadatable const& i_rObject);
239 /** merge i_rMerged and i_rOther into i_rMerged. */
240 void JoinMetadatables(Metadatable & i_rMerged, Metadatable const& i_rOther);
242 // unfortunately public, Metadatable::RegisterAsCopyOf needs this
243 virtual bool LookupXmlId(const Metadatable& i_xObject,
244 OUString & o_rStream, OUString & o_rIdref) const override;
246 private:
248 virtual Metadatable* LookupElement(const OUString & i_rStreamName,
249 const OUString & i_rIdref) const override;
251 struct XmlIdRegistry_Impl;
252 ::std::unique_ptr<XmlIdRegistry_Impl> m_pImpl;
257 // MetadatableUndo ---------------------------------------------------
259 /** the horrible Undo Metadatable: is inserted into lists to track position */
260 class MetadatableUndo : public Metadatable
262 /// as determined by the stream of the source in original document
263 const bool m_isInContent;
264 public:
265 explicit MetadatableUndo(const bool i_isInContent)
266 : m_isInContent(i_isInContent) { }
267 virtual ::sfx2::XmlIdRegistry& GetRegistry() override
269 // N.B. for Undo, m_pReg is initialized by registering this as copy in
270 // CreateUndo; it is never cleared
271 OSL_ENSURE(m_pReg, "no m_pReg in MetadatableUndo ?");
272 return *m_pReg;
274 virtual bool IsInClipboard() const override { return false; }
275 virtual bool IsInUndo() const override { return true; }
276 virtual bool IsInContent() const override { return m_isInContent; }
277 virtual css::uno::Reference< css::rdf::XMetadatable > MakeUnoObject() override
278 { OSL_FAIL("MetadatableUndo::MakeUnoObject"); throw; }
281 // MetadatableClipboard ----------------------------------------------
283 /** the horrible Clipboard Metadatable: inserted into lists to track position */
284 class MetadatableClipboard : public Metadatable
286 /// as determined by the stream of the source in original document
287 const bool m_isInContent;
288 public:
289 explicit MetadatableClipboard(const bool i_isInContent)
290 : m_isInContent(i_isInContent) { }
291 virtual ::sfx2::XmlIdRegistry& GetRegistry() override
293 // N.B. for Clipboard, m_pReg is initialized by registering this as copy in
294 // RegisterAsCopyOf; it is only cleared by OriginNoLongerInBusinessAnymore
295 assert(m_pReg && "no m_pReg in MetadatableClipboard ?");
296 return *m_pReg;
298 virtual bool IsInClipboard() const override { return true; }
299 virtual bool IsInUndo() const override { return false; }
300 virtual bool IsInContent() const override { return m_isInContent; }
301 virtual css::uno::Reference< css::rdf::XMetadatable > MakeUnoObject() override
302 { OSL_FAIL("MetadatableClipboard::MakeUnoObject"); throw; }
303 void OriginNoLongerInBusinessAnymore() { m_pReg = nullptr; }
306 // XmlIdRegistryClipboard --------------------------------------------
308 namespace {
310 class XmlIdRegistryClipboard : public XmlIdRegistry
313 public:
314 XmlIdRegistryClipboard();
316 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) override;
318 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
319 OUString const& i_rStreamName, OUString const& i_rIdref) override;
321 virtual void UnregisterMetadatable(Metadatable const&) override;
323 virtual void RemoveXmlIdForElement(Metadatable const&) override;
325 /** register i_rCopy as a copy of i_rSource */
326 MetadatableClipboard & RegisterCopyClipboard(Metadatable & i_rCopy,
327 beans::StringPair const & i_rReference,
328 const bool i_isLatent);
330 /** get the Metadatable that links i_rObject to its origin registry */
331 MetadatableClipboard const* SourceLink(Metadatable const& i_rObject);
333 private:
334 virtual bool LookupXmlId(const Metadatable& i_xObject,
335 OUString & o_rStream, OUString & o_rIdref) const override;
337 virtual Metadatable* LookupElement(const OUString & i_rStreamName,
338 const OUString & i_rIdref) const override;
340 /** create a Clipboard Metadatable for i_rObject. */
341 static std::shared_ptr<MetadatableClipboard> CreateClipboard(
342 const bool i_isInContent);
344 struct XmlIdRegistry_Impl;
345 ::std::unique_ptr<XmlIdRegistry_Impl> m_pImpl;
350 // XmlIdRegistry
352 ::sfx2::IXmlIdRegistry * createXmlIdRegistry(const bool i_DocIsClipboard)
354 return i_DocIsClipboard
355 ? static_cast<XmlIdRegistry*>( new XmlIdRegistryClipboard )
356 : static_cast<XmlIdRegistry*>( new XmlIdRegistryDocument );
359 XmlIdRegistry::XmlIdRegistry()
363 css::uno::Reference< css::rdf::XMetadatable >
364 XmlIdRegistry::GetElementByMetadataReference(
365 const beans::StringPair & i_rReference) const
367 Metadatable* pObject( LookupElement(i_rReference.First,
368 i_rReference.Second) );
369 return pObject ? pObject->MakeUnoObject() : nullptr;
372 beans::StringPair
373 XmlIdRegistry::GetXmlIdForElement(const Metadatable& i_rObject) const
375 OUString path;
376 OUString idref;
377 if (LookupXmlId(i_rObject, path, idref))
379 if (LookupElement(path, idref) == &i_rObject)
381 return beans::StringPair(path, idref);
384 return beans::StringPair();
388 /// generate unique xml:id
389 template< typename T >
390 static OUString create_id(const
391 std::unordered_map< OUString, T > & i_rXmlIdMap)
393 static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
394 static const char prefix[] = "id"; // prefix for generated xml:id
395 typename std::unordered_map< OUString, T >
396 ::const_iterator iter;
397 OUString id;
399 if (bHack)
401 static sal_Int64 nIdCounter = SAL_CONST_INT64(4000000000);
404 id = prefix + OUString::number(nIdCounter++);
405 iter = i_rXmlIdMap.find(id);
407 while (iter != i_rXmlIdMap.end());
409 else
413 unsigned int const n(comphelper::rng::uniform_uint_distribution(0,
414 std::numeric_limits<unsigned int>::max()));
415 id = prefix + OUString::number(n);
416 iter = i_rXmlIdMap.find(id);
418 while (iter != i_rXmlIdMap.end());
420 return id;
424 // Document XML ID Registry (_Impl)
426 /// element list
427 typedef ::std::vector< Metadatable* > XmlIdVector_t;
429 /// Idref -> (content.xml element list, styles.xml element list)
430 typedef std::unordered_map< OUString,
431 ::std::pair< XmlIdVector_t, XmlIdVector_t > > XmlIdMap_t;
433 namespace {
435 /// pointer hash template
436 template<typename T> struct PtrHash
438 size_t operator() (T const * i_pT) const
440 return reinterpret_cast<size_t>(i_pT);
446 /// element -> (stream name, idref)
447 typedef std::unordered_map< const Metadatable*,
448 ::std::pair< OUString, OUString>, PtrHash<Metadatable> >
449 XmlIdReverseMap_t;
451 struct XmlIdRegistryDocument::XmlIdRegistry_Impl
453 XmlIdRegistry_Impl() {}
455 bool TryInsertMetadatable(Metadatable& i_xObject,
456 std::u16string_view i_rStream, const OUString & i_rIdref);
458 bool LookupXmlId(const Metadatable& i_xObject,
459 OUString & o_rStream, OUString & o_rIdref) const;
461 Metadatable* LookupElement(std::u16string_view i_rStreamName,
462 const OUString & i_rIdref) const;
464 const XmlIdVector_t * LookupElementVector(
465 std::u16string_view i_rStreamName,
466 const OUString & i_rIdref) const;
468 XmlIdVector_t * LookupElementVector(
469 std::u16string_view i_rStreamName,
470 const OUString & i_rIdref)
472 return const_cast<XmlIdVector_t*>(
473 const_cast<const XmlIdRegistry_Impl*>(this)
474 ->LookupElementVector(i_rStreamName, i_rIdref));
477 XmlIdMap_t m_XmlIdMap;
478 XmlIdReverseMap_t m_XmlIdReverseMap;
482 static void
483 rmIter(XmlIdMap_t & i_rXmlIdMap, XmlIdMap_t::iterator const& i_rIter,
484 std::u16string_view i_rStream, Metadatable const& i_rObject)
486 if (i_rIter != i_rXmlIdMap.end())
488 XmlIdVector_t & rVector( isContentFile(i_rStream)
489 ? i_rIter->second.first : i_rIter->second.second );
490 rVector.erase(std::remove(rVector.begin(), rVector.end(), &const_cast<Metadatable&>(i_rObject)));
491 if (i_rIter->second.first.empty() && i_rIter->second.second.empty())
493 i_rXmlIdMap.erase(i_rIter);
499 const XmlIdVector_t *
500 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElementVector(
501 std::u16string_view i_rStreamName,
502 const OUString & i_rIdref) const
504 const XmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
505 if (iter != m_XmlIdMap.end())
507 OSL_ENSURE(!iter->second.first.empty() || !iter->second.second.empty(),
508 "null entry in m_XmlIdMap");
509 return (isContentFile(i_rStreamName))
510 ? &iter->second.first
511 : &iter->second.second;
513 else
515 return nullptr;
519 Metadatable*
520 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElement(
521 std::u16string_view i_rStreamName,
522 const OUString & i_rIdref) const
524 if (!isValidXmlId(i_rStreamName, i_rIdref))
526 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
529 const XmlIdVector_t * pList( LookupElementVector(i_rStreamName, i_rIdref) );
530 if (pList)
532 const XmlIdVector_t::const_iterator iter(
533 ::std::find_if(pList->begin(), pList->end(),
534 [](Metadatable* item)->bool {
535 return !(item->IsInUndo() || item->IsInClipboard());
536 } ) ) ;
537 if (iter != pList->end())
539 return *iter;
542 return nullptr;
545 bool
546 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupXmlId(
547 const Metadatable& i_rObject,
548 OUString & o_rStream, OUString & o_rIdref) const
550 const XmlIdReverseMap_t::const_iterator iter(
551 m_XmlIdReverseMap.find(&i_rObject) );
552 if (iter != m_XmlIdReverseMap.end())
554 OSL_ENSURE(!iter->second.first.isEmpty(),
555 "null stream in m_XmlIdReverseMap");
556 OSL_ENSURE(!iter->second.second.isEmpty(),
557 "null id in m_XmlIdReverseMap");
558 o_rStream = iter->second.first;
559 o_rIdref = iter->second.second;
560 return true;
562 else
564 return false;
568 bool
569 XmlIdRegistryDocument::XmlIdRegistry_Impl::TryInsertMetadatable(
570 Metadatable & i_rObject,
571 std::u16string_view i_rStreamName, const OUString & i_rIdref)
573 const bool bContent( isContentFile(i_rStreamName) );
574 OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName),
575 "invalid stream");
577 XmlIdVector_t * pList( LookupElementVector(i_rStreamName, i_rIdref) );
578 if (pList)
580 if (pList->empty())
582 pList->push_back( &i_rObject );
583 return true;
585 else
587 // this is only called from TryRegister now, so check
588 // if all elements in the list are deleted (in undo) or
589 // placeholders, then "steal" the id from them
590 if ( std::none_of(pList->begin(), pList->end(),
591 [](Metadatable* item)->bool {
592 return !(item->IsInUndo() || item->IsInClipboard());
593 } ) )
595 pList->insert(pList->begin(), &i_rObject );
596 return true;
598 else
600 return false;
604 else
606 m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent
607 ? ::std::make_pair( XmlIdVector_t( 1, &i_rObject ), XmlIdVector_t() )
608 : ::std::make_pair( XmlIdVector_t(), XmlIdVector_t( 1, &i_rObject ) )));
609 return true;
614 // Document XML ID Registry
617 XmlIdRegistryDocument::XmlIdRegistryDocument()
618 : m_pImpl( new XmlIdRegistry_Impl )
622 static void
623 removeLink(Metadatable* i_pObject)
625 OSL_ENSURE(i_pObject, "null in list ???");
626 if (!i_pObject) return;
627 if (i_pObject->IsInClipboard())
629 MetadatableClipboard* pLink(
630 dynamic_cast<MetadatableClipboard*>( i_pObject ) );
631 OSL_ENSURE(pLink, "IsInClipboard, but no MetadatableClipboard ?");
632 if (pLink)
634 pLink->OriginNoLongerInBusinessAnymore();
639 XmlIdRegistryDocument::~XmlIdRegistryDocument()
641 // notify all list elements that are actually in the clipboard
642 for (const auto& aXmlId : m_pImpl->m_XmlIdMap) {
643 for (auto aLink : aXmlId.second.first)
644 removeLink(aLink);
645 for (auto aLink : aXmlId.second.second)
646 removeLink(aLink);
650 bool
651 XmlIdRegistryDocument::LookupXmlId(
652 const Metadatable& i_rObject,
653 OUString & o_rStream, OUString & o_rIdref) const
655 return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref);
658 Metadatable*
659 XmlIdRegistryDocument::LookupElement(
660 const OUString & i_rStreamName,
661 const OUString & i_rIdref) const
663 return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
666 bool
667 XmlIdRegistryDocument::TryRegisterMetadatable(Metadatable & i_rObject,
668 OUString const& i_rStreamName, OUString const& i_rIdref)
670 SAL_INFO("sfx", "TryRegisterMetadatable: " << &i_rObject << " (" << i_rStreamName << "#" << i_rIdref << ")");
672 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
673 "TryRegisterMetadatable called for MetadatableUndo?");
674 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
675 "TryRegisterMetadatable called for MetadatableClipboard?");
677 if (!isValidXmlId(i_rStreamName, i_rIdref))
679 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
681 if (i_rObject.IsInContent()
682 ? !isContentFile(i_rStreamName)
683 : !isStylesFile(i_rStreamName))
685 throw lang::IllegalArgumentException("illegal XmlId: wrong stream", nullptr, 0);
688 OUString old_path;
689 OUString old_idref;
690 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
691 if (old_path == i_rStreamName && old_idref == i_rIdref)
693 return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
695 XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
696 if (!old_idref.isEmpty())
698 old_id = m_pImpl->m_XmlIdMap.find(old_idref);
699 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
701 if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
703 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
704 m_pImpl->m_XmlIdReverseMap[&i_rObject] =
705 ::std::make_pair(i_rStreamName, i_rIdref);
706 return true;
708 else
710 return false;
714 void
715 XmlIdRegistryDocument::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
717 SAL_INFO("sfx", "RegisterMetadatableAndCreateID: " << &i_rObject);
719 OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
720 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
721 OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
722 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
724 const bool isInContent( i_rObject.IsInContent() );
725 const OUString stream(
726 isInContent ? OUString(s_content) : OUString(s_styles) );
727 // check if we have a latent xmlid, and if yes, remove it
728 OUString old_path;
729 OUString old_idref;
730 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
732 XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
733 if (!old_idref.isEmpty())
735 old_id = m_pImpl->m_XmlIdMap.find(old_idref);
736 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
737 if (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject)
739 return;
741 else
743 // remove latent xmlid
744 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
748 // create id
749 const OUString id( create_id(m_pImpl->m_XmlIdMap) );
750 OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(),
751 "created id is in use");
752 m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent
753 ? ::std::make_pair( XmlIdVector_t( 1, &i_rObject ), XmlIdVector_t() )
754 : ::std::make_pair( XmlIdVector_t(), XmlIdVector_t( 1, &i_rObject ) )));
755 m_pImpl->m_XmlIdReverseMap[&i_rObject] = ::std::make_pair(stream, id);
758 void XmlIdRegistryDocument::UnregisterMetadatable(const Metadatable& i_rObject)
760 SAL_INFO("sfx", "UnregisterMetadatable: " << &i_rObject);
762 OUString path;
763 OUString idref;
764 if (!m_pImpl->LookupXmlId(i_rObject, path, idref))
766 OSL_FAIL("unregister: no xml id?");
767 return;
769 const XmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
770 if (iter != m_pImpl->m_XmlIdMap.end())
772 rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
776 void XmlIdRegistryDocument::RemoveXmlIdForElement(const Metadatable& i_rObject)
778 SAL_INFO("sfx", "RemoveXmlIdForElement: " << &i_rObject);
780 const XmlIdReverseMap_t::iterator iter(
781 m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
782 if (iter != m_pImpl->m_XmlIdReverseMap.end())
784 OSL_ENSURE(!iter->second.second.isEmpty(),
785 "null id in m_XmlIdReverseMap");
786 m_pImpl->m_XmlIdReverseMap.erase(iter);
791 void XmlIdRegistryDocument::RegisterCopy(Metadatable const& i_rSource,
792 Metadatable & i_rCopy, const bool i_bCopyPrecedesSource)
794 SAL_INFO("sfx", "RegisterCopy: " << &i_rSource << " -> " << &i_rCopy << " (" << i_bCopyPrecedesSource << ")");
796 // potential sources: clipboard, undo array, splitNode
797 // assumption: stream change can only happen via clipboard, and is handled
798 // by Metadatable::RegisterAsCopyOf
799 OSL_ENSURE(i_rSource.IsInUndo() || i_rCopy.IsInUndo() ||
800 (i_rSource.IsInContent() == i_rCopy.IsInContent()),
801 "RegisterCopy: not in same stream?");
803 OUString path;
804 OUString idref;
805 if (!m_pImpl->LookupXmlId( i_rSource, path, idref ))
807 OSL_FAIL("no xml id?");
808 return;
810 XmlIdVector_t * pList ( m_pImpl->LookupElementVector(path, idref) );
811 OSL_ENSURE( ::std::find( pList->begin(), pList->end(), &i_rCopy )
812 == pList->end(), "copy already registered???");
813 XmlIdVector_t::iterator srcpos(
814 ::std::find( pList->begin(), pList->end(), &i_rSource ) );
815 OSL_ENSURE(srcpos != pList->end(), "source not in list???");
816 if (srcpos == pList->end())
818 return;
820 if (i_bCopyPrecedesSource)
822 pList->insert( srcpos, &i_rCopy );
824 else
826 // for undo push_back does not work! must insert right after source
827 pList->insert( ++srcpos, &i_rCopy );
829 m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy,
830 ::std::make_pair(path, idref)));
833 std::shared_ptr<MetadatableUndo>
834 XmlIdRegistryDocument::CreateUndo(Metadatable const& i_rObject)
836 SAL_INFO("sfx", "CreateUndo: " << &i_rObject);
838 return std::make_shared<MetadatableUndo>(
839 i_rObject.IsInContent() );
843 i_rMerged is both a source and the target node of the merge
844 i_rOther is the other source, and will be deleted after the merge
846 dimensions: none|latent|actual empty|nonempty
847 i_rMerged(1) i_rOther(2) result
848 *|empty *|empty => 1|2 (arbitrary)
849 *|empty *|nonempty => 2
850 *|nonempty *|empty => 1
851 none|nonempty none|nonempty => none
852 none|nonempty latent|nonempty => 2
853 latent|nonempty none|nonempty => 1
854 latent|nonempty latent|nonempty => 1|2
855 *|nonempty actual|nonempty => 2
856 actual|nonempty *|nonempty => 1
857 actual|nonempty actual|nonempty => 1|2
859 void
860 XmlIdRegistryDocument::JoinMetadatables(
861 Metadatable & i_rMerged, Metadatable const & i_rOther)
863 SAL_INFO("sfx", "JoinMetadatables: " << &i_rMerged << " <- " << &i_rOther);
865 bool mergedOwnsRef;
866 OUString path;
867 OUString idref;
868 if (m_pImpl->LookupXmlId(i_rMerged, path, idref))
870 mergedOwnsRef = (m_pImpl->LookupElement(path, idref) == &i_rMerged);
872 else
874 OSL_FAIL("JoinMetadatables: no xmlid?");
875 return;
877 if (!mergedOwnsRef)
879 i_rMerged.RemoveMetadataReference();
880 i_rMerged.RegisterAsCopyOf(i_rOther, true);
881 return;
883 // other cases: merged has actual ref and is nonempty,
884 // other has latent/actual ref and is nonempty: other loses => nothing to do
888 // Clipboard XML ID Registry (_Impl)
890 namespace {
892 struct RMapEntry
894 RMapEntry() {}
895 RMapEntry(OUString const& i_rStream,
896 OUString const& i_rXmlId,
897 std::shared_ptr<MetadatableClipboard> const& i_pLink
898 = std::shared_ptr<MetadatableClipboard>())
899 : m_Stream(i_rStream), m_XmlId(i_rXmlId), m_xLink(i_pLink)
901 OUString m_Stream;
902 OUString m_XmlId;
903 // this would have been an auto_ptr, if only that would have compiled...
904 std::shared_ptr<MetadatableClipboard> m_xLink;
909 /// element -> (stream name, idref, source)
910 typedef std::unordered_map< const Metadatable*,
911 struct RMapEntry,
912 PtrHash<Metadatable> >
913 ClipboardXmlIdReverseMap_t;
915 /// Idref -> (content.xml element, styles.xml element)
916 typedef std::unordered_map< OUString,
917 ::std::pair< Metadatable*, Metadatable* > >
918 ClipboardXmlIdMap_t;
920 struct XmlIdRegistryClipboard::XmlIdRegistry_Impl
922 XmlIdRegistry_Impl() {}
924 bool TryInsertMetadatable(Metadatable& i_xObject,
925 std::u16string_view 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(std::u16string_view i_rStreamName,
932 const OUString & i_rIdref) const;
934 Metadatable* const* LookupEntry(std::u16string_view 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 std::u16string_view 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 std::u16string_view 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 std::u16string_view 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 std::u16string_view 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(
1140 isInContent ? OUString(s_content) : OUString(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 TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::RemoveMetadataReference");
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 = IsInContent() ? OUString(s_content) : OUString(s_styles);
1300 XmlIdRegistry & rReg( dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1301 if (!rReg.TryRegisterMetadatable(*this, streamName, i_rReference.Second))
1303 throw lang::IllegalArgumentException(
1304 "Metadatable::SetMetadataReference: argument is invalid", /*this*/nullptr, 0);
1307 m_pReg = &rReg;
1311 void Metadatable::EnsureMetadataReference()
1313 XmlIdRegistry& rReg(
1314 m_pReg ? *m_pReg : dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1315 rReg.RegisterMetadatableAndCreateID( *this );
1316 m_pReg = &rReg;
1319 static const ::sfx2::IXmlIdRegistry& GetRegistryConst(Metadatable const& i_rObject)
1321 return const_cast< Metadatable& >( i_rObject ).GetRegistry();
1324 void
1325 Metadatable::RegisterAsCopyOf(Metadatable const & i_rSource,
1326 const bool i_bCopyPrecedesSource)
1328 OSL_ENSURE(typeid(*this) == typeid(i_rSource)
1329 || typeid(i_rSource) == typeid(MetadatableUndo)
1330 || typeid(*this) == typeid(MetadatableUndo)
1331 || typeid(i_rSource) == typeid(MetadatableClipboard)
1332 || typeid(*this) == typeid(MetadatableClipboard),
1333 "RegisterAsCopyOf element with different class?");
1334 OSL_ENSURE(!m_pReg, "RegisterAsCopyOf called on element with XmlId?");
1336 if (m_pReg)
1338 RemoveMetadataReference();
1343 if (i_rSource.m_pReg)
1345 XmlIdRegistry & rReg(
1346 dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1347 if (i_rSource.m_pReg == &rReg)
1349 OSL_ENSURE(!IsInClipboard(),
1350 "RegisterAsCopy: both in clipboard?");
1351 if (!IsInClipboard())
1353 XmlIdRegistryDocument & rRegDoc(
1354 dynamic_cast<XmlIdRegistryDocument&>( rReg ) );
1355 rRegDoc.RegisterCopy(i_rSource, *this,
1356 i_bCopyPrecedesSource);
1357 m_pReg = &rRegDoc;
1359 return;
1361 // source is in different document
1362 XmlIdRegistryDocument * pRegDoc(
1363 dynamic_cast<XmlIdRegistryDocument *>(&rReg) );
1364 XmlIdRegistryClipboard * pRegClp(
1365 dynamic_cast<XmlIdRegistryClipboard*>(&rReg) );
1367 if (pRegClp)
1369 beans::StringPair SourceRef(
1370 i_rSource.m_pReg->GetXmlIdForElement(i_rSource) );
1371 bool isLatent( SourceRef.Second.isEmpty() );
1372 XmlIdRegistryDocument * pSourceRegDoc(
1373 dynamic_cast<XmlIdRegistryDocument*>(i_rSource.m_pReg) );
1374 OSL_ENSURE(pSourceRegDoc, "RegisterAsCopyOf: 2 clipboards?");
1375 if (!pSourceRegDoc) return;
1376 // this is a copy _to_ the clipboard
1377 if (isLatent)
1379 pSourceRegDoc->LookupXmlId(i_rSource,
1380 SourceRef.First, SourceRef.Second);
1382 Metadatable & rLink(
1383 pRegClp->RegisterCopyClipboard(*this, SourceRef, isLatent));
1384 m_pReg = pRegClp;
1385 // register as copy in the non-clipboard registry
1386 pSourceRegDoc->RegisterCopy(i_rSource, rLink,
1387 false); // i_bCopyPrecedesSource);
1388 rLink.m_pReg = pSourceRegDoc;
1390 else if (pRegDoc)
1392 XmlIdRegistryClipboard * pSourceRegClp(
1393 dynamic_cast<XmlIdRegistryClipboard*>(i_rSource.m_pReg) );
1394 OSL_ENSURE(pSourceRegClp,
1395 "RegisterAsCopyOf: 2 non-clipboards?");
1396 if (!pSourceRegClp) return;
1397 const MetadatableClipboard * pLink(
1398 pSourceRegClp->SourceLink(i_rSource) );
1399 // may happen if src got its id via UNO call
1400 if (!pLink) return;
1401 // only register copy if clipboard content is from this SwDoc!
1402 if (&GetRegistryConst(*pLink) == pRegDoc)
1404 // this is a copy _from_ the clipboard; check if the
1405 // element is still in the same stream
1406 // N.B.: we check the stream of pLink, not of i_rSource!
1407 bool srcInContent( pLink->IsInContent() );
1408 bool tgtInContent( IsInContent() );
1409 if (srcInContent == tgtInContent)
1411 pRegDoc->RegisterCopy(*pLink, *this,
1412 true); // i_bCopyPrecedesSource);
1413 m_pReg = pRegDoc;
1415 // otherwise: stream change! do not register!
1418 else
1420 OSL_FAIL("neither RegDoc nor RegClp cannot happen");
1424 catch (const uno::Exception &)
1426 TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::RegisterAsCopyOf");
1430 std::shared_ptr<MetadatableUndo> Metadatable::CreateUndo() const
1432 OSL_ENSURE(!IsInUndo(), "CreateUndo called for object in undo?");
1433 OSL_ENSURE(!IsInClipboard(), "CreateUndo called for object in clipboard?");
1436 if (!IsInClipboard() && !IsInUndo() && m_pReg)
1438 XmlIdRegistryDocument * pRegDoc(
1439 dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
1440 assert(pRegDoc);
1441 std::shared_ptr<MetadatableUndo> xUndo(
1442 sfx2::XmlIdRegistryDocument::CreateUndo(*this) );
1443 pRegDoc->RegisterCopy(*this, *xUndo, false);
1444 xUndo->m_pReg = pRegDoc;
1445 return xUndo;
1448 catch (const uno::Exception &)
1450 TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::CreateUndo");
1452 return std::shared_ptr<MetadatableUndo>();
1455 std::shared_ptr<MetadatableUndo> Metadatable::CreateUndoForDelete()
1457 std::shared_ptr<MetadatableUndo> const xUndo( CreateUndo() );
1458 RemoveMetadataReference();
1459 return xUndo;
1462 void Metadatable::RestoreMetadata(
1463 std::shared_ptr<MetadatableUndo> const& i_pUndo)
1465 OSL_ENSURE(!IsInUndo(), "RestoreMetadata called for object in undo?");
1466 OSL_ENSURE(!IsInClipboard(),
1467 "RestoreMetadata called for object in clipboard?");
1468 if (IsInClipboard() || IsInUndo()) return;
1469 RemoveMetadataReference();
1470 if (i_pUndo)
1472 RegisterAsCopyOf(*i_pUndo, true);
1476 void
1477 Metadatable::JoinMetadatable(Metadatable const & i_rOther,
1478 const bool i_isMergedEmpty, const bool i_isOtherEmpty)
1480 OSL_ENSURE(!IsInUndo(), "JoinMetadatables called for object in undo?");
1481 OSL_ENSURE(!IsInClipboard(),
1482 "JoinMetadatables called for object in clipboard?");
1483 if (IsInClipboard() || IsInUndo()) return;
1485 if (i_isOtherEmpty && !i_isMergedEmpty)
1487 // other is empty, thus loses => nothing to do
1488 return;
1490 if (i_isMergedEmpty && !i_isOtherEmpty)
1492 RemoveMetadataReference();
1493 RegisterAsCopyOf(i_rOther, true);
1494 return;
1497 if (!i_rOther.m_pReg)
1499 // other doesn't have xmlid, thus loses => nothing to do
1500 return;
1502 if (!m_pReg)
1504 RegisterAsCopyOf(i_rOther, true);
1505 // assumption: i_rOther will be deleted, so don't unregister it here
1506 return;
1510 XmlIdRegistryDocument * pRegDoc(
1511 dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
1512 OSL_ENSURE(pRegDoc, "JoinMetadatable: no pRegDoc?");
1513 if (pRegDoc)
1515 pRegDoc->JoinMetadatables(*this, i_rOther);
1518 catch (const uno::Exception &)
1520 TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::JoinMetadatable");
1525 // XMetadatable mixin
1527 // css::rdf::XNode:
1528 OUString SAL_CALL MetadatableMixin::getStringValue()
1530 return getNamespace() + getLocalName();
1533 // css::rdf::XURI:
1534 OUString SAL_CALL MetadatableMixin::getLocalName()
1536 SolarMutexGuard aGuard;
1537 beans::StringPair mdref( getMetadataReference() );
1538 if (mdref.Second.isEmpty())
1540 ensureMetadataReference(); // N.B.: side effect!
1541 mdref = getMetadataReference();
1543 return mdref.First + "#" + mdref.Second;
1546 OUString SAL_CALL MetadatableMixin::getNamespace()
1548 SolarMutexGuard aGuard;
1549 const uno::Reference< frame::XModel > xModel( GetModel() );
1550 const uno::Reference< rdf::XURI > xDMA( xModel, uno::UNO_QUERY_THROW );
1551 return xDMA->getStringValue();
1554 // css::rdf::XMetadatable:
1555 beans::StringPair SAL_CALL
1556 MetadatableMixin::getMetadataReference()
1558 SolarMutexGuard aGuard;
1560 Metadatable *const pObject( GetCoreObject() );
1561 if (!pObject)
1563 throw uno::RuntimeException(
1564 "MetadatableMixin: cannot get core object; not inserted?",
1565 *this);
1567 return pObject->GetMetadataReference();
1570 void SAL_CALL
1571 MetadatableMixin::setMetadataReference(
1572 const beans::StringPair & i_rReference)
1574 SolarMutexGuard aGuard;
1576 Metadatable *const pObject( GetCoreObject() );
1577 if (!pObject)
1579 throw uno::RuntimeException(
1580 "MetadatableMixin: cannot get core object; not inserted?",
1581 *this);
1583 return pObject->SetMetadataReference(i_rReference);
1586 void SAL_CALL MetadatableMixin::ensureMetadataReference()
1588 SolarMutexGuard aGuard;
1590 Metadatable *const pObject( GetCoreObject() );
1591 if (!pObject)
1593 throw uno::RuntimeException(
1594 "MetadatableMixin: cannot get core object; not inserted?",
1595 *this);
1597 return pObject->EnsureMetadataReference();
1600 } // namespace sfx2
1602 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */