1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
35 #include <string_view>
36 #include <unordered_map>
37 #if OSL_DEBUG_LEVEL > 0
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
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
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
;
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
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
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.
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.
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
)
181 /** unregister an ODF element.
184 Unregister the element at its metadata reference.
185 Does not remove the metadata reference from the element.
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;
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 ---------------------------------------------
212 /** non-clipboard documents */
213 class XmlIdRegistryDocument
: public XmlIdRegistry
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
;
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
;
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 ?");
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
;
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 ?");
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 --------------------------------------------
310 class XmlIdRegistryClipboard
: public XmlIdRegistry
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
);
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
;
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;
373 XmlIdRegistry::GetXmlIdForElement(const Metadatable
& i_rObject
) const
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
;
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());
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());
424 // Document XML ID Registry (_Impl)
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
;
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
> >
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
;
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
;
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
) );
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());
537 if (iter
!= pList
->end())
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
;
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
),
577 XmlIdVector_t
* pList( LookupElementVector(i_rStreamName
, i_rIdref
) );
582 pList
->push_back( &i_rObject
);
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());
595 pList
->insert(pList
->begin(), &i_rObject
);
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
) )));
614 // Document XML ID Registry
617 XmlIdRegistryDocument::XmlIdRegistryDocument()
618 : m_pImpl( new XmlIdRegistry_Impl
)
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 ?");
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
)
645 for (auto aLink
: aXmlId
.second
.second
)
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
);
659 XmlIdRegistryDocument::LookupElement(
660 const OUString
& i_rStreamName
,
661 const OUString
& i_rIdref
) const
663 return m_pImpl
->LookupElement(i_rStreamName
, i_rIdref
);
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);
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
);
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
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
)
743 // remove latent xmlid
744 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
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
);
764 if (!m_pImpl
->LookupXmlId(i_rObject
, path
, idref
))
766 OSL_FAIL("unregister: no xml id?");
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?");
805 if (!m_pImpl
->LookupXmlId( i_rSource
, path
, idref
))
807 OSL_FAIL("no xml id?");
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())
820 if (i_bCopyPrecedesSource
)
822 pList
->insert( srcpos
, &i_rCopy
);
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
860 XmlIdRegistryDocument::JoinMetadatables(
861 Metadatable
& i_rMerged
, Metadatable
const & i_rOther
)
863 SAL_INFO("sfx", "JoinMetadatables: " << &i_rMerged
<< " <- " << &i_rOther
);
868 if (m_pImpl
->LookupXmlId(i_rMerged
, path
, idref
))
870 mergedOwnsRef
= (m_pImpl
->LookupElement(path
, idref
) == &i_rMerged
);
874 OSL_FAIL("JoinMetadatables: no xmlid?");
879 i_rMerged
.RemoveMetadataReference();
880 i_rMerged
.RegisterAsCopyOf(i_rOther
, true);
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)
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
)
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
*,
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
* > >
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
;
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())
950 Metadatable
*& rMeta
= isContentFile(i_rStream
)
951 ? i_rIter
->second
.first
: i_rIter
->second
.second
;
952 if (rMeta
== &i_rObject
)
956 if (!i_rIter
->second
.first
&& !i_rIter
->second
.second
)
958 i_rXmlIdMap
.erase(i_rIter
);
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
;
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;
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();
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
),
1031 Metadatable
** ppEntry
= const_cast<Metadatable
**>(LookupEntry(i_rStreamName
, i_rIdref
));
1040 *ppEntry
= &i_rObject
;
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
)));
1054 // Clipboard XML ID Registry
1057 XmlIdRegistryClipboard::XmlIdRegistryClipboard()
1058 : m_pImpl( new XmlIdRegistry_Impl
)
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
);
1072 XmlIdRegistryClipboard::LookupElement(
1073 const OUString
& i_rStreamName
,
1074 const OUString
& i_rIdref
) const
1076 return m_pImpl
->LookupElement(i_rStreamName
, i_rIdref
);
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);
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
);
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() );
1140 isInContent
? OUString(s_content
) : OUString(s_styles
) );
1144 LookupXmlId(i_rObject
, old_path
, old_idref
);
1145 if (!old_idref
.isEmpty() &&
1146 (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
))
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
);
1169 const MetadatableClipboard
* pLink
;
1170 if (!m_pImpl
->LookupXmlId(i_rObject
, path
, idref
, pLink
))
1172 OSL_FAIL("unregister: no xml id?");
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
>(
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);
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
)));
1238 MetadatableClipboard
const*
1239 XmlIdRegistryClipboard::SourceLink(Metadatable
const& i_rObject
)
1243 const MetadatableClipboard
* pLink( nullptr );
1244 m_pImpl
->LookupXmlId(i_rObject
, path
, idref
, pLink
);
1249 // Metadatable mixin
1252 Metadatable::~Metadatable()
1254 RemoveMetadataReference();
1257 void Metadatable::RemoveMetadataReference()
1263 m_pReg
->UnregisterMetadatable( *this );
1264 m_pReg
->RemoveXmlIdForElement( *this );
1268 catch (const uno::Exception
&)
1270 TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::RemoveMetadataReference");
1274 // css::rdf::XMetadatable:
1276 Metadatable::GetMetadataReference() const
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();
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);
1311 void Metadatable::EnsureMetadataReference()
1313 XmlIdRegistry
& rReg(
1314 m_pReg
? *m_pReg
: dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1315 rReg
.RegisterMetadatableAndCreateID( *this );
1319 static const ::sfx2::IXmlIdRegistry
& GetRegistryConst(Metadatable
const& i_rObject
)
1321 return const_cast< Metadatable
& >( i_rObject
).GetRegistry();
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?");
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
);
1361 // source is in different document
1362 XmlIdRegistryDocument
* pRegDoc(
1363 dynamic_cast<XmlIdRegistryDocument
*>(&rReg
) );
1364 XmlIdRegistryClipboard
* pRegClp(
1365 dynamic_cast<XmlIdRegistryClipboard
*>(&rReg
) );
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
1379 pSourceRegDoc
->LookupXmlId(i_rSource
,
1380 SourceRef
.First
, SourceRef
.Second
);
1382 Metadatable
& rLink(
1383 pRegClp
->RegisterCopyClipboard(*this, SourceRef
, isLatent
));
1385 // register as copy in the non-clipboard registry
1386 pSourceRegDoc
->RegisterCopy(i_rSource
, rLink
,
1387 false); // i_bCopyPrecedesSource);
1388 rLink
.m_pReg
= pSourceRegDoc
;
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
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);
1415 // otherwise: stream change! do not register!
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
) );
1441 std::shared_ptr
<MetadatableUndo
> xUndo(
1442 sfx2::XmlIdRegistryDocument::CreateUndo(*this) );
1443 pRegDoc
->RegisterCopy(*this, *xUndo
, false);
1444 xUndo
->m_pReg
= pRegDoc
;
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();
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();
1472 RegisterAsCopyOf(*i_pUndo
, true);
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
1490 if (i_isMergedEmpty
&& !i_isOtherEmpty
)
1492 RemoveMetadataReference();
1493 RegisterAsCopyOf(i_rOther
, true);
1497 if (!i_rOther
.m_pReg
)
1499 // other doesn't have xmlid, thus loses => nothing to do
1504 RegisterAsCopyOf(i_rOther
, true);
1505 // assumption: i_rOther will be deleted, so don't unregister it here
1510 XmlIdRegistryDocument
* pRegDoc(
1511 dynamic_cast<XmlIdRegistryDocument
*>( m_pReg
) );
1512 OSL_ENSURE(pRegDoc
, "JoinMetadatable: no pRegDoc?");
1515 pRegDoc
->JoinMetadatables(*this, i_rOther
);
1518 catch (const uno::Exception
&)
1520 TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::JoinMetadatable");
1525 // XMetadatable mixin
1528 OUString SAL_CALL
MetadatableMixin::getStringValue()
1530 return getNamespace() + getLocalName();
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() );
1563 throw uno::RuntimeException(
1564 "MetadatableMixin: cannot get core object; not inserted?",
1567 return pObject
->GetMetadataReference();
1571 MetadatableMixin::setMetadataReference(
1572 const beans::StringPair
& i_rReference
)
1574 SolarMutexGuard aGuard
;
1576 Metadatable
*const pObject( GetCoreObject() );
1579 throw uno::RuntimeException(
1580 "MetadatableMixin: cannot get core object; not inserted?",
1583 return pObject
->SetMetadataReference(i_rReference
);
1586 void SAL_CALL
MetadatableMixin::ensureMetadataReference()
1588 SolarMutexGuard aGuard
;
1590 Metadatable
*const pObject( GetCoreObject() );
1593 throw uno::RuntimeException(
1594 "MetadatableMixin: cannot get core object; not inserted?",
1597 return pObject
->EnsureMetadataReference();
1602 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */