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>
28 #include <vcl/svapp.hxx>
29 #include <com/sun/star/frame/XModel.hpp>
30 #include <com/sun/star/lang/IllegalArgumentException.hpp>
31 #include <comphelper/random.hxx>
32 #include <comphelper/diagnose_ex.hxx>
36 #include <string_view>
37 #include <unordered_map>
38 #if OSL_DEBUG_LEVEL > 0
45 There is an abstract base class <type>XmlIdRegistry</type>, with
46 2 subclasses <type>XmlIdRegistryDocument</type> for "normal" documents,
47 and <type>XmlIdRegistryClipboard</type> for clipboard documents.
48 These classes are responsible for managing XML IDs for all elements
49 of the model. Only the implementation of the <type>Metadatable</type>
50 base class needs to know the registries, so they are not in the header.
52 The handling of XML IDs differs between clipboard and non-clipboard
53 documents in several aspects. Most importantly, non-clipboard documents
54 can have several elements associated with one XML ID.
55 This is necessary because of the weird undo implementation:
56 deleting a text node moves the deleted node to the undo array, but
57 executing undo will then create a <em>copy</em> of that node in the
58 document array. These 2 nodes must have the same XML ID, because
59 we cannot know whether the user will do a redo next, or something else.
61 Because we need to have a mechanism for several objects per XML ID anyway,
62 we use that also to enable some usability features:
63 The document registry has a list of Metadatables per XML ID.
64 This list is sorted by priority, i.e., the first element has highest
65 priority. When inserting copies, care must be taken that they are inserted
66 at the right position: either before or after the source.
67 This is done by <method>Metadatable::RegisterAsCopyOf</method>.
68 When a text node is split, then both resulting text nodes are inserted
69 into the list. If the user then deletes one text node, the other one
71 Also, when a Metadatable is copied to the clipboard and then pasted,
72 the copy is inserted into the list. If the user then deletes the source,
73 the XML ID is not lost.
74 The goal is that it should be hard to lose an XML ID by accident, which
75 is especially important as long as we do not have an UI that displays them.
77 There are two subclasses of <type>Metadatable</type>:
78 <ul><li><type>MetadatableClipboard</type>: for copies in the clipboard</li>
79 <li><type>MetadatableUndo</type>: for undo, because a Metadatable
80 may be destroyed on delete and a new one created on undo.</li></ul>
81 These serve only to track the position in an XML ID list in a document
82 registry, so that future actions can insert objects at the right position.
83 Unfortunately, inserting dummy objects seems to be necessary:
84 <ul><li>it is not sufficient to just remember the saved id, because then
85 the relative priorities might change when executing the undo</li>
86 <li>it is not sufficient to record the position as an integer, because
87 if we delete a text node and then undo, the node will be copied(!),
88 and we will have one more node in the list.<li>
89 <li>it is not sufficient to record the pointer of the previous/next
90 Metadatable, because if we delete a text node, undo, and then
91 do something to clear the redo array, the original text node is
92 destroyed, and is replaced by the copy created by undo</li></ul>
94 If content from a non-clipboard document is copied into a clipboard
95 document, a dummy <type>MetadatableClipboard</type> is inserted into the
96 non-clipboard document registry in order to track the position of the
97 source element. When the clipboard content is pasted back into the source
98 document, this dummy object is used to associate the pasted element with
101 If a <type>Metadatable</type> is deleted or merged,
102 <method>Metadatable::CreateUndo</method> is called, and returns a
103 <type>MetadatableUndo<type> instance, which can be used to undo the action
104 by passing it to <method>Metadatable::RestoreMetadata</method>.
109 using namespace ::com::sun::star
;
111 using ::sfx2::isValidXmlId
;
116 constexpr OUString s_content
= u
"content.xml"_ustr
;
117 constexpr OUString s_styles
= u
"styles.xml"_ustr
;
119 static bool isContentFile(std::u16string_view i_rPath
)
121 return i_rPath
== s_content
;
124 static bool isStylesFile (std::u16string_view i_rPath
)
126 return i_rPath
== s_styles
;
130 // XML ID handling ---------------------------------------------------
132 /** handles registration of XMetadatable.
134 This class is responsible for guaranteeing that XMetadatable objects
135 always have XML IDs that are unique within a stream.
137 This is an abstract base class; see subclasses XmlIdRegistryDocument and
138 XmlIdRegistryClipboard.
140 @see SwDoc::GetXmlIdRegistry
141 @see SwDocShell::GetXmlIdRegistry
143 class XmlIdRegistry
: public sfx2::IXmlIdRegistry
149 /** get the ODF element with the given metadata reference. */
150 virtual css::uno::Reference
< css::rdf::XMetadatable
>
151 GetElementByMetadataReference(
152 const css::beans::StringPair
& i_rReference
) const
155 /** register an ODF element at a newly generated, unique metadata reference.
158 Find a fresh XML ID, and register it for the element.
159 The generated ID does not occur in any stream of the document.
162 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
) = 0;
164 /** try to register an ODF element at a given XML ID, or update its
165 registration to a different XML ID.
168 If the given new metadata reference is not already occupied in the
169 document, unregister the element at its old metadata reference if
170 it has one, and register the new metadata reference for the element.
171 Note that this method only ensures that XML IDs are unique per stream,
172 so using the same XML ID in both content.xml and styles.xml is allowed.
176 true iff the element has successfully been registered
178 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
179 OUString
const& i_rStreamName
, OUString
const& i_rIdref
)
182 /** unregister an ODF element.
185 Unregister the element at its metadata reference.
186 Does not remove the metadata reference from the element.
189 @see RemoveXmlIdForElement
191 virtual void UnregisterMetadatable(Metadatable
const&) = 0;
193 /** get the metadata reference for the given element. */
194 css::beans::StringPair
195 GetXmlIdForElement(Metadatable
const&) const;
197 /** remove the metadata reference for the given element. */
198 virtual void RemoveXmlIdForElement(Metadatable
const&) = 0;
202 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
203 OUString
& o_rStream
, OUString
& o_rIdref
) const = 0;
205 virtual Metadatable
* LookupElement(const OUString
& i_rStreamName
,
206 const OUString
& i_rIdref
) const = 0;
209 // XmlIdRegistryDocument ---------------------------------------------
213 /** non-clipboard documents */
214 class XmlIdRegistryDocument
: public XmlIdRegistry
218 XmlIdRegistryDocument();
220 virtual ~XmlIdRegistryDocument() override
;
222 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
) override
;
224 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
225 OUString
const& i_rStreamName
, OUString
const& i_rIdref
) override
;
227 virtual void UnregisterMetadatable(Metadatable
const&) override
;
229 virtual void RemoveXmlIdForElement(Metadatable
const&) override
;
231 /** register i_rCopy as a copy of i_rSource,
232 with precedence iff i_bCopyPrecedesSource is true */
233 void RegisterCopy(Metadatable
const& i_rSource
, Metadatable
& i_rCopy
,
234 const bool i_bCopyPrecedesSource
);
236 /** create a Undo Metadatable for i_rObject. */
237 static std::shared_ptr
<MetadatableUndo
> CreateUndo(
238 Metadatable
const& i_rObject
);
240 /** merge i_rMerged and i_rOther into i_rMerged. */
241 void JoinMetadatables(Metadatable
& i_rMerged
, Metadatable
const& i_rOther
);
243 // unfortunately public, Metadatable::RegisterAsCopyOf needs this
244 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
245 OUString
& o_rStream
, OUString
& o_rIdref
) const override
;
249 virtual Metadatable
* LookupElement(const OUString
& i_rStreamName
,
250 const OUString
& i_rIdref
) const override
;
252 struct XmlIdRegistry_Impl
;
253 ::std::unique_ptr
<XmlIdRegistry_Impl
> m_pImpl
;
258 // MetadatableUndo ---------------------------------------------------
260 /** the horrible Undo Metadatable: is inserted into lists to track position */
261 class MetadatableUndo
: public Metadatable
263 /// as determined by the stream of the source in original document
264 const bool m_isInContent
;
266 explicit MetadatableUndo(const bool i_isInContent
)
267 : m_isInContent(i_isInContent
) { }
268 virtual ::sfx2::XmlIdRegistry
& GetRegistry() override
270 // N.B. for Undo, m_pReg is initialized by registering this as copy in
271 // CreateUndo; it is never cleared
272 OSL_ENSURE(m_pReg
, "no m_pReg in MetadatableUndo ?");
275 virtual bool IsInClipboard() const override
{ return false; }
276 virtual bool IsInUndo() const override
{ return true; }
277 virtual bool IsInContent() const override
{ return m_isInContent
; }
278 virtual css::uno::Reference
< css::rdf::XMetadatable
> MakeUnoObject() override
279 { OSL_FAIL("MetadatableUndo::MakeUnoObject"); throw; }
282 // MetadatableClipboard ----------------------------------------------
284 /** the horrible Clipboard Metadatable: inserted into lists to track position */
285 class MetadatableClipboard
: public Metadatable
287 /// as determined by the stream of the source in original document
288 const bool m_isInContent
;
290 explicit MetadatableClipboard(const bool i_isInContent
)
291 : m_isInContent(i_isInContent
) { }
292 virtual ::sfx2::XmlIdRegistry
& GetRegistry() override
294 // N.B. for Clipboard, m_pReg is initialized by registering this as copy in
295 // RegisterAsCopyOf; it is only cleared by OriginNoLongerInBusinessAnymore
296 assert(m_pReg
&& "no m_pReg in MetadatableClipboard ?");
299 virtual bool IsInClipboard() const override
{ return true; }
300 virtual bool IsInUndo() const override
{ return false; }
301 virtual bool IsInContent() const override
{ return m_isInContent
; }
302 virtual css::uno::Reference
< css::rdf::XMetadatable
> MakeUnoObject() override
303 { OSL_FAIL("MetadatableClipboard::MakeUnoObject"); throw; }
304 void OriginNoLongerInBusinessAnymore() { m_pReg
= nullptr; }
307 // XmlIdRegistryClipboard --------------------------------------------
311 class XmlIdRegistryClipboard
: public XmlIdRegistry
315 XmlIdRegistryClipboard();
317 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
) override
;
319 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
320 OUString
const& i_rStreamName
, OUString
const& i_rIdref
) override
;
322 virtual void UnregisterMetadatable(Metadatable
const&) override
;
324 virtual void RemoveXmlIdForElement(Metadatable
const&) override
;
326 /** register i_rCopy as a copy of i_rSource */
327 MetadatableClipboard
& RegisterCopyClipboard(Metadatable
& i_rCopy
,
328 beans::StringPair
const & i_rReference
,
329 const bool i_isLatent
);
331 /** get the Metadatable that links i_rObject to its origin registry */
332 MetadatableClipboard
const* SourceLink(Metadatable
const& i_rObject
);
335 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
336 OUString
& o_rStream
, OUString
& o_rIdref
) const override
;
338 virtual Metadatable
* LookupElement(const OUString
& i_rStreamName
,
339 const OUString
& i_rIdref
) const override
;
341 /** create a Clipboard Metadatable for i_rObject. */
342 static std::shared_ptr
<MetadatableClipboard
> CreateClipboard(
343 const bool i_isInContent
);
345 struct XmlIdRegistry_Impl
;
346 ::std::unique_ptr
<XmlIdRegistry_Impl
> m_pImpl
;
353 ::sfx2::IXmlIdRegistry
* createXmlIdRegistry(const bool i_DocIsClipboard
)
355 return i_DocIsClipboard
356 ? static_cast<XmlIdRegistry
*>( new XmlIdRegistryClipboard
)
357 : static_cast<XmlIdRegistry
*>( new XmlIdRegistryDocument
);
360 XmlIdRegistry::XmlIdRegistry()
364 css::uno::Reference
< css::rdf::XMetadatable
>
365 XmlIdRegistry::GetElementByMetadataReference(
366 const beans::StringPair
& i_rReference
) const
368 Metadatable
* pObject( LookupElement(i_rReference
.First
,
369 i_rReference
.Second
) );
370 return pObject
? pObject
->MakeUnoObject() : nullptr;
374 XmlIdRegistry::GetXmlIdForElement(const Metadatable
& i_rObject
) const
378 if (LookupXmlId(i_rObject
, path
, idref
))
380 if (LookupElement(path
, idref
) == &i_rObject
)
382 return beans::StringPair(path
, idref
);
385 return beans::StringPair();
389 /// generate unique xml:id
390 template< typename T
>
391 static OUString
create_id(const
392 std::unordered_map
< OUString
, T
> & i_rXmlIdMap
)
394 static bool bHack
= (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
395 static const char prefix
[] = "id"; // prefix for generated xml:id
396 typename
std::unordered_map
< OUString
, T
>
397 ::const_iterator iter
;
402 static sal_Int64 nIdCounter
= SAL_CONST_INT64(4000000000);
405 id
= prefix
+ OUString::number(nIdCounter
++);
406 iter
= i_rXmlIdMap
.find(id
);
408 while (iter
!= i_rXmlIdMap
.end());
414 unsigned int const n(comphelper::rng::uniform_uint_distribution(0,
415 std::numeric_limits
<unsigned int>::max()));
416 id
= prefix
+ OUString::number(n
);
417 iter
= i_rXmlIdMap
.find(id
);
419 while (iter
!= i_rXmlIdMap
.end());
425 // Document XML ID Registry (_Impl)
428 typedef ::std::vector
< Metadatable
* > XmlIdVector_t
;
430 /// Idref -> (content.xml element list, styles.xml element list)
431 typedef std::unordered_map
< OUString
,
432 ::std::pair
< XmlIdVector_t
, XmlIdVector_t
> > XmlIdMap_t
;
436 /// pointer hash template
437 template<typename T
> struct PtrHash
439 size_t operator() (T
const * i_pT
) const
441 return reinterpret_cast<size_t>(i_pT
);
447 /// element -> (stream name, idref)
448 typedef std::unordered_map
< const Metadatable
*,
449 ::std::pair
< OUString
, OUString
>, PtrHash
<Metadatable
> >
452 struct XmlIdRegistryDocument::XmlIdRegistry_Impl
454 XmlIdRegistry_Impl() {}
456 bool TryInsertMetadatable(Metadatable
& i_xObject
,
457 std::u16string_view i_rStream
, const OUString
& i_rIdref
);
459 bool LookupXmlId(const Metadatable
& i_xObject
,
460 OUString
& o_rStream
, OUString
& o_rIdref
) const;
462 Metadatable
* LookupElement(std::u16string_view i_rStreamName
,
463 const OUString
& i_rIdref
) const;
465 const XmlIdVector_t
* LookupElementVector(
466 std::u16string_view i_rStreamName
,
467 const OUString
& i_rIdref
) const;
469 XmlIdVector_t
* LookupElementVector(
470 std::u16string_view i_rStreamName
,
471 const OUString
& i_rIdref
)
473 return const_cast<XmlIdVector_t
*>(
474 const_cast<const XmlIdRegistry_Impl
*>(this)
475 ->LookupElementVector(i_rStreamName
, i_rIdref
));
478 XmlIdMap_t m_XmlIdMap
;
479 XmlIdReverseMap_t m_XmlIdReverseMap
;
484 rmIter(XmlIdMap_t
& i_rXmlIdMap
, XmlIdMap_t::iterator
const& i_rIter
,
485 std::u16string_view i_rStream
, Metadatable
const& i_rObject
)
487 if (i_rIter
!= i_rXmlIdMap
.end())
489 XmlIdVector_t
& rVector( isContentFile(i_rStream
)
490 ? i_rIter
->second
.first
: i_rIter
->second
.second
);
491 std::erase(rVector
, &const_cast<Metadatable
&>(i_rObject
));
492 if (i_rIter
->second
.first
.empty() && i_rIter
->second
.second
.empty())
494 i_rXmlIdMap
.erase(i_rIter
);
500 const XmlIdVector_t
*
501 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElementVector(
502 std::u16string_view i_rStreamName
,
503 const OUString
& i_rIdref
) const
505 const XmlIdMap_t::const_iterator
iter( m_XmlIdMap
.find(i_rIdref
) );
506 if (iter
!= m_XmlIdMap
.end())
508 OSL_ENSURE(!iter
->second
.first
.empty() || !iter
->second
.second
.empty(),
509 "null entry in m_XmlIdMap");
510 return (isContentFile(i_rStreamName
))
511 ? &iter
->second
.first
512 : &iter
->second
.second
;
521 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElement(
522 std::u16string_view i_rStreamName
,
523 const OUString
& i_rIdref
) const
525 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
527 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
530 const XmlIdVector_t
* pList( LookupElementVector(i_rStreamName
, i_rIdref
) );
533 const XmlIdVector_t::const_iterator
iter(
534 ::std::find_if(pList
->begin(), pList
->end(),
535 [](Metadatable
* item
)->bool {
536 return !(item
->IsInUndo() || item
->IsInClipboard());
538 if (iter
!= pList
->end())
547 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupXmlId(
548 const Metadatable
& i_rObject
,
549 OUString
& o_rStream
, OUString
& o_rIdref
) const
551 const XmlIdReverseMap_t::const_iterator
iter(
552 m_XmlIdReverseMap
.find(&i_rObject
) );
553 if (iter
!= m_XmlIdReverseMap
.end())
555 OSL_ENSURE(!iter
->second
.first
.isEmpty(),
556 "null stream in m_XmlIdReverseMap");
557 OSL_ENSURE(!iter
->second
.second
.isEmpty(),
558 "null id in m_XmlIdReverseMap");
559 o_rStream
= iter
->second
.first
;
560 o_rIdref
= iter
->second
.second
;
570 XmlIdRegistryDocument::XmlIdRegistry_Impl::TryInsertMetadatable(
571 Metadatable
& i_rObject
,
572 std::u16string_view i_rStreamName
, const OUString
& i_rIdref
)
574 const bool bContent( isContentFile(i_rStreamName
) );
575 OSL_ENSURE(isContentFile(i_rStreamName
) || isStylesFile(i_rStreamName
),
578 XmlIdVector_t
* pList( LookupElementVector(i_rStreamName
, i_rIdref
) );
583 pList
->push_back( &i_rObject
);
588 // this is only called from TryRegister now, so check
589 // if all elements in the list are deleted (in undo) or
590 // placeholders, then "steal" the id from them
591 if ( std::none_of(pList
->begin(), pList
->end(),
592 [](Metadatable
* item
)->bool {
593 return !(item
->IsInUndo() || item
->IsInClipboard());
596 pList
->insert(pList
->begin(), &i_rObject
);
607 m_XmlIdMap
.insert(::std::make_pair(i_rIdref
, bContent
608 ? ::std::make_pair( XmlIdVector_t( 1, &i_rObject
), XmlIdVector_t() )
609 : ::std::make_pair( XmlIdVector_t(), XmlIdVector_t( 1, &i_rObject
) )));
615 // Document XML ID Registry
618 XmlIdRegistryDocument::XmlIdRegistryDocument()
619 : m_pImpl( new XmlIdRegistry_Impl
)
624 removeLink(Metadatable
* i_pObject
)
626 OSL_ENSURE(i_pObject
, "null in list ???");
627 if (!i_pObject
) return;
628 if (i_pObject
->IsInClipboard())
630 MetadatableClipboard
* pLink(
631 dynamic_cast<MetadatableClipboard
*>( i_pObject
) );
632 OSL_ENSURE(pLink
, "IsInClipboard, but no MetadatableClipboard ?");
635 pLink
->OriginNoLongerInBusinessAnymore();
640 XmlIdRegistryDocument::~XmlIdRegistryDocument()
642 // notify all list elements that are actually in the clipboard
643 for (const auto& aXmlId
: m_pImpl
->m_XmlIdMap
) {
644 for (auto aLink
: aXmlId
.second
.first
)
646 for (auto aLink
: aXmlId
.second
.second
)
652 XmlIdRegistryDocument::LookupXmlId(
653 const Metadatable
& i_rObject
,
654 OUString
& o_rStream
, OUString
& o_rIdref
) const
656 return m_pImpl
->LookupXmlId(i_rObject
, o_rStream
, o_rIdref
);
660 XmlIdRegistryDocument::LookupElement(
661 const OUString
& i_rStreamName
,
662 const OUString
& i_rIdref
) const
664 return m_pImpl
->LookupElement(i_rStreamName
, i_rIdref
);
668 XmlIdRegistryDocument::TryRegisterMetadatable(Metadatable
& i_rObject
,
669 OUString
const& i_rStreamName
, OUString
const& i_rIdref
)
671 SAL_INFO("sfx", "TryRegisterMetadatable: " << &i_rObject
<< " (" << i_rStreamName
<< "#" << i_rIdref
<< ")");
673 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
674 "TryRegisterMetadatable called for MetadatableUndo?");
675 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
676 "TryRegisterMetadatable called for MetadatableClipboard?");
678 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
680 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
682 if (i_rObject
.IsInContent()
683 ? !isContentFile(i_rStreamName
)
684 : !isStylesFile(i_rStreamName
))
686 throw lang::IllegalArgumentException("illegal XmlId: wrong stream", nullptr, 0);
691 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
);
692 if (old_path
== i_rStreamName
&& old_idref
== i_rIdref
)
694 return (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
);
696 XmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
697 if (!old_idref
.isEmpty())
699 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
700 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
702 if (m_pImpl
->TryInsertMetadatable(i_rObject
, i_rStreamName
, i_rIdref
))
704 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
705 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] =
706 ::std::make_pair(i_rStreamName
, i_rIdref
);
716 XmlIdRegistryDocument::RegisterMetadatableAndCreateID(Metadatable
& i_rObject
)
718 SAL_INFO("sfx", "RegisterMetadatableAndCreateID: " << &i_rObject
);
720 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
721 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
722 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
723 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
725 const bool isInContent( i_rObject
.IsInContent() );
726 const OUString
stream(
727 isInContent
? s_content
: s_styles
);
728 // check if we have a latent xmlid, and if yes, remove it
731 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
);
733 XmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
734 if (!old_idref
.isEmpty())
736 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
737 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
738 if (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
)
744 // remove latent xmlid
745 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
750 const OUString
id( create_id(m_pImpl
->m_XmlIdMap
) );
751 OSL_ENSURE(m_pImpl
->m_XmlIdMap
.find(id
) == m_pImpl
->m_XmlIdMap
.end(),
752 "created id is in use");
753 m_pImpl
->m_XmlIdMap
.insert(::std::make_pair(id
, isInContent
754 ? ::std::make_pair( XmlIdVector_t( 1, &i_rObject
), XmlIdVector_t() )
755 : ::std::make_pair( XmlIdVector_t(), XmlIdVector_t( 1, &i_rObject
) )));
756 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] = ::std::make_pair(stream
, id
);
759 void XmlIdRegistryDocument::UnregisterMetadatable(const Metadatable
& i_rObject
)
761 SAL_INFO("sfx", "UnregisterMetadatable: " << &i_rObject
);
765 if (!m_pImpl
->LookupXmlId(i_rObject
, path
, idref
))
767 OSL_FAIL("unregister: no xml id?");
770 const XmlIdMap_t::iterator
iter( m_pImpl
->m_XmlIdMap
.find(idref
) );
771 if (iter
!= m_pImpl
->m_XmlIdMap
.end())
773 rmIter(m_pImpl
->m_XmlIdMap
, iter
, path
, i_rObject
);
777 void XmlIdRegistryDocument::RemoveXmlIdForElement(const Metadatable
& i_rObject
)
779 SAL_INFO("sfx", "RemoveXmlIdForElement: " << &i_rObject
);
781 const XmlIdReverseMap_t::iterator
iter(
782 m_pImpl
->m_XmlIdReverseMap
.find(&i_rObject
) );
783 if (iter
!= m_pImpl
->m_XmlIdReverseMap
.end())
785 OSL_ENSURE(!iter
->second
.second
.isEmpty(),
786 "null id in m_XmlIdReverseMap");
787 m_pImpl
->m_XmlIdReverseMap
.erase(iter
);
792 void XmlIdRegistryDocument::RegisterCopy(Metadatable
const& i_rSource
,
793 Metadatable
& i_rCopy
, const bool i_bCopyPrecedesSource
)
795 SAL_INFO("sfx", "RegisterCopy: " << &i_rSource
<< " -> " << &i_rCopy
<< " (" << i_bCopyPrecedesSource
<< ")");
797 // potential sources: clipboard, undo array, splitNode
798 // assumption: stream change can only happen via clipboard, and is handled
799 // by Metadatable::RegisterAsCopyOf
800 OSL_ENSURE(i_rSource
.IsInUndo() || i_rCopy
.IsInUndo() ||
801 (i_rSource
.IsInContent() == i_rCopy
.IsInContent()),
802 "RegisterCopy: not in same stream?");
806 if (!m_pImpl
->LookupXmlId( i_rSource
, path
, idref
))
808 OSL_FAIL("no xml id?");
811 XmlIdVector_t
* pList ( m_pImpl
->LookupElementVector(path
, idref
) );
812 OSL_ENSURE( ::std::find( pList
->begin(), pList
->end(), &i_rCopy
)
813 == pList
->end(), "copy already registered???");
814 XmlIdVector_t::iterator
srcpos(
815 ::std::find( pList
->begin(), pList
->end(), &i_rSource
) );
816 OSL_ENSURE(srcpos
!= pList
->end(), "source not in list???");
817 if (srcpos
== pList
->end())
821 if (i_bCopyPrecedesSource
)
823 pList
->insert( srcpos
, &i_rCopy
);
827 // for undo push_back does not work! must insert right after source
828 pList
->insert( ++srcpos
, &i_rCopy
);
830 m_pImpl
->m_XmlIdReverseMap
.insert(::std::make_pair(&i_rCopy
,
831 ::std::make_pair(path
, idref
)));
834 std::shared_ptr
<MetadatableUndo
>
835 XmlIdRegistryDocument::CreateUndo(Metadatable
const& i_rObject
)
837 SAL_INFO("sfx", "CreateUndo: " << &i_rObject
);
839 return std::make_shared
<MetadatableUndo
>(
840 i_rObject
.IsInContent() );
844 i_rMerged is both a source and the target node of the merge
845 i_rOther is the other source, and will be deleted after the merge
847 dimensions: none|latent|actual empty|nonempty
848 i_rMerged(1) i_rOther(2) result
849 *|empty *|empty => 1|2 (arbitrary)
850 *|empty *|nonempty => 2
851 *|nonempty *|empty => 1
852 none|nonempty none|nonempty => none
853 none|nonempty latent|nonempty => 2
854 latent|nonempty none|nonempty => 1
855 latent|nonempty latent|nonempty => 1|2
856 *|nonempty actual|nonempty => 2
857 actual|nonempty *|nonempty => 1
858 actual|nonempty actual|nonempty => 1|2
861 XmlIdRegistryDocument::JoinMetadatables(
862 Metadatable
& i_rMerged
, Metadatable
const & i_rOther
)
864 SAL_INFO("sfx", "JoinMetadatables: " << &i_rMerged
<< " <- " << &i_rOther
);
869 if (m_pImpl
->LookupXmlId(i_rMerged
, path
, idref
))
871 mergedOwnsRef
= (m_pImpl
->LookupElement(path
, idref
) == &i_rMerged
);
875 OSL_FAIL("JoinMetadatables: no xmlid?");
880 i_rMerged
.RemoveMetadataReference();
881 i_rMerged
.RegisterAsCopyOf(i_rOther
, true);
884 // other cases: merged has actual ref and is nonempty,
885 // other has latent/actual ref and is nonempty: other loses => nothing to do
889 // Clipboard XML ID Registry (_Impl)
896 RMapEntry(OUString i_aStream
,
898 std::shared_ptr
<MetadatableClipboard
> i_pLink
899 = std::shared_ptr
<MetadatableClipboard
>())
900 : m_Stream(std::move(i_aStream
)), m_XmlId(std::move(i_aXmlId
)), m_xLink(std::move(i_pLink
))
904 // this would have been an auto_ptr, if only that would have compiled...
905 std::shared_ptr
<MetadatableClipboard
> m_xLink
;
910 /// element -> (stream name, idref, source)
911 typedef std::unordered_map
< const Metadatable
*,
913 PtrHash
<Metadatable
> >
914 ClipboardXmlIdReverseMap_t
;
916 /// Idref -> (content.xml element, styles.xml element)
917 typedef std::unordered_map
< OUString
,
918 ::std::pair
< Metadatable
*, Metadatable
* > >
921 struct XmlIdRegistryClipboard::XmlIdRegistry_Impl
923 XmlIdRegistry_Impl() {}
925 bool TryInsertMetadatable(Metadatable
& i_xObject
,
926 std::u16string_view i_rStream
, const OUString
& i_rIdref
);
928 bool LookupXmlId(const Metadatable
& i_xObject
,
929 OUString
& o_rStream
, OUString
& o_rIdref
,
930 MetadatableClipboard
const* &o_rpLink
) const;
932 Metadatable
* LookupElement(std::u16string_view i_rStreamName
,
933 const OUString
& i_rIdref
) const;
935 Metadatable
* const* LookupEntry(std::u16string_view i_rStreamName
,
936 const OUString
& i_rIdref
) const;
938 ClipboardXmlIdMap_t m_XmlIdMap
;
939 ClipboardXmlIdReverseMap_t m_XmlIdReverseMap
;
944 rmIter(ClipboardXmlIdMap_t
& i_rXmlIdMap
,
945 ClipboardXmlIdMap_t::iterator
const& i_rIter
,
946 std::u16string_view i_rStream
, Metadatable
const& i_rObject
)
948 if (i_rIter
== i_rXmlIdMap
.end())
951 Metadatable
*& rMeta
= isContentFile(i_rStream
)
952 ? i_rIter
->second
.first
: i_rIter
->second
.second
;
953 if (rMeta
== &i_rObject
)
957 if (!i_rIter
->second
.first
&& !i_rIter
->second
.second
)
959 i_rXmlIdMap
.erase(i_rIter
);
965 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupEntry(
966 std::u16string_view i_rStreamName
,
967 const OUString
& i_rIdref
) const
969 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
971 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
974 const ClipboardXmlIdMap_t::const_iterator
iter( m_XmlIdMap
.find(i_rIdref
) );
975 if (iter
!= m_XmlIdMap
.end())
977 OSL_ENSURE(iter
->second
.first
|| iter
->second
.second
,
978 "null entry in m_XmlIdMap");
979 return (isContentFile(i_rStreamName
))
980 ? &iter
->second
.first
981 : &iter
->second
.second
;
990 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupElement(
991 std::u16string_view i_rStreamName
,
992 const OUString
& i_rIdref
) const
994 Metadatable
* const * ppEntry
= LookupEntry(i_rStreamName
, i_rIdref
);
995 return ppEntry
? *ppEntry
: nullptr;
999 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupXmlId(
1000 const Metadatable
& i_rObject
,
1001 OUString
& o_rStream
, OUString
& o_rIdref
,
1002 MetadatableClipboard
const* &o_rpLink
) const
1004 const ClipboardXmlIdReverseMap_t::const_iterator
iter(
1005 m_XmlIdReverseMap
.find(&i_rObject
) );
1006 if (iter
!= m_XmlIdReverseMap
.end())
1008 OSL_ENSURE(!iter
->second
.m_Stream
.isEmpty(),
1009 "null stream in m_XmlIdReverseMap");
1010 OSL_ENSURE(!iter
->second
.m_XmlId
.isEmpty(),
1011 "null id in m_XmlIdReverseMap");
1012 o_rStream
= iter
->second
.m_Stream
;
1013 o_rIdref
= iter
->second
.m_XmlId
;
1014 o_rpLink
= iter
->second
.m_xLink
.get();
1024 XmlIdRegistryClipboard::XmlIdRegistry_Impl::TryInsertMetadatable(
1025 Metadatable
& i_rObject
,
1026 std::u16string_view i_rStreamName
, const OUString
& i_rIdref
)
1028 bool bContent( isContentFile(i_rStreamName
) );
1029 OSL_ENSURE(isContentFile(i_rStreamName
) || isStylesFile(i_rStreamName
),
1032 Metadatable
** ppEntry
= const_cast<Metadatable
**>(LookupEntry(i_rStreamName
, i_rIdref
));
1041 *ppEntry
= &i_rObject
;
1047 m_XmlIdMap
.insert(::std::make_pair(i_rIdref
, bContent
1048 ? ::std::make_pair( &i_rObject
, static_cast<Metadatable
*>(nullptr) )
1049 : ::std::make_pair( static_cast<Metadatable
*>(nullptr), &i_rObject
)));
1055 // Clipboard XML ID Registry
1058 XmlIdRegistryClipboard::XmlIdRegistryClipboard()
1059 : m_pImpl( new XmlIdRegistry_Impl
)
1064 XmlIdRegistryClipboard::LookupXmlId(
1065 const Metadatable
& i_rObject
,
1066 OUString
& o_rStream
, OUString
& o_rIdref
) const
1068 const MetadatableClipboard
* pLink
;
1069 return m_pImpl
->LookupXmlId(i_rObject
, o_rStream
, o_rIdref
, pLink
);
1073 XmlIdRegistryClipboard::LookupElement(
1074 const OUString
& i_rStreamName
,
1075 const OUString
& i_rIdref
) const
1077 return m_pImpl
->LookupElement(i_rStreamName
, i_rIdref
);
1081 XmlIdRegistryClipboard::TryRegisterMetadatable(Metadatable
& i_rObject
,
1082 OUString
const& i_rStreamName
, OUString
const& i_rIdref
)
1084 SAL_INFO("sfx", "TryRegisterMetadatable: " << &i_rObject
<< " (" << i_rStreamName
<< "#" << i_rIdref
<<")");
1086 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
1087 "TryRegisterMetadatable called for MetadatableUndo?");
1088 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
1089 "TryRegisterMetadatable called for MetadatableClipboard?");
1091 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
1093 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
1095 if (i_rObject
.IsInContent()
1096 ? !isContentFile(i_rStreamName
)
1097 : !isStylesFile(i_rStreamName
))
1099 throw lang::IllegalArgumentException("illegal XmlId: wrong stream", nullptr, 0);
1104 const MetadatableClipboard
* pLink
;
1105 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
, pLink
);
1106 if (old_path
== i_rStreamName
&& old_idref
== i_rIdref
)
1108 return (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
);
1110 ClipboardXmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
1111 if (!old_idref
.isEmpty())
1113 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
1114 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
1116 if (m_pImpl
->TryInsertMetadatable(i_rObject
, i_rStreamName
, i_rIdref
))
1118 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
1119 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] =
1120 RMapEntry(i_rStreamName
, i_rIdref
);
1130 XmlIdRegistryClipboard::RegisterMetadatableAndCreateID(Metadatable
& i_rObject
)
1132 SAL_INFO("sfx", "RegisterMetadatableAndCreateID: " << &i_rObject
);
1134 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
1135 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
1136 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
1137 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
1139 bool isInContent( i_rObject
.IsInContent() );
1141 isInContent
? s_content
: s_styles
);
1145 LookupXmlId(i_rObject
, old_path
, old_idref
);
1146 if (!old_idref
.isEmpty() &&
1147 (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
))
1153 const OUString
id( create_id(m_pImpl
->m_XmlIdMap
) );
1154 OSL_ENSURE(m_pImpl
->m_XmlIdMap
.find(id
) == m_pImpl
->m_XmlIdMap
.end(),
1155 "created id is in use");
1156 m_pImpl
->m_XmlIdMap
.insert(::std::make_pair(id
, isInContent
1157 ? ::std::make_pair( &i_rObject
, static_cast<Metadatable
*>(nullptr) )
1158 : ::std::make_pair( static_cast<Metadatable
*>(nullptr), &i_rObject
)));
1159 // N.B.: if i_rObject had a latent XmlId, then we implicitly delete the
1160 // MetadatableClipboard and thus the latent XmlId here
1161 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] = RMapEntry(stream
, id
);
1164 void XmlIdRegistryClipboard::UnregisterMetadatable(const Metadatable
& i_rObject
)
1166 SAL_INFO("sfx", "UnregisterMetadatable: " << &i_rObject
);
1170 const MetadatableClipboard
* pLink
;
1171 if (!m_pImpl
->LookupXmlId(i_rObject
, path
, idref
, pLink
))
1173 OSL_FAIL("unregister: no xml id?");
1176 const ClipboardXmlIdMap_t::iterator
iter( m_pImpl
->m_XmlIdMap
.find(idref
) );
1177 if (iter
!= m_pImpl
->m_XmlIdMap
.end())
1179 rmIter(m_pImpl
->m_XmlIdMap
, iter
, path
, i_rObject
);
1184 void XmlIdRegistryClipboard::RemoveXmlIdForElement(const Metadatable
& i_rObject
)
1186 SAL_INFO("sfx", "RemoveXmlIdForElement: " << &i_rObject
);
1188 ClipboardXmlIdReverseMap_t::iterator
iter(
1189 m_pImpl
->m_XmlIdReverseMap
.find(&i_rObject
) );
1190 if (iter
!= m_pImpl
->m_XmlIdReverseMap
.end())
1192 OSL_ENSURE(!iter
->second
.m_XmlId
.isEmpty(),
1193 "null id in m_XmlIdReverseMap");
1194 m_pImpl
->m_XmlIdReverseMap
.erase(iter
);
1199 std::shared_ptr
<MetadatableClipboard
>
1200 XmlIdRegistryClipboard::CreateClipboard(const bool i_isInContent
)
1202 SAL_INFO("sfx", "CreateClipboard:");
1204 return std::make_shared
<MetadatableClipboard
>(
1208 MetadatableClipboard
&
1209 XmlIdRegistryClipboard::RegisterCopyClipboard(Metadatable
& i_rCopy
,
1210 beans::StringPair
const & i_rReference
,
1211 const bool i_isLatent
)
1213 SAL_INFO("sfx", "RegisterCopyClipboard: " << &i_rCopy
1214 << " -> (" << i_rReference
.First
<< "#" << i_rReference
.Second
<< ") (" << i_isLatent
<< ")");
1216 // N.B.: when copying to the clipboard, the selection is always inserted
1217 // into the body, even if the source is a header/footer!
1218 // so we do not check whether the stream is right in this function
1220 if (!isValidXmlId(i_rReference
.First
, i_rReference
.Second
))
1222 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
1227 // this should succeed assuming clipboard has a single source document
1228 const bool success( m_pImpl
->TryInsertMetadatable(i_rCopy
,
1229 i_rReference
.First
, i_rReference
.Second
) );
1230 OSL_ENSURE(success
, "RegisterCopyClipboard: TryInsert failed?");
1232 const std::shared_ptr
<MetadatableClipboard
> xLink(
1233 CreateClipboard( isContentFile(i_rReference
.First
)) );
1234 m_pImpl
->m_XmlIdReverseMap
.insert(::std::make_pair(&i_rCopy
,
1235 RMapEntry(i_rReference
.First
, i_rReference
.Second
, xLink
)));
1239 MetadatableClipboard
const*
1240 XmlIdRegistryClipboard::SourceLink(Metadatable
const& i_rObject
)
1244 const MetadatableClipboard
* pLink( nullptr );
1245 m_pImpl
->LookupXmlId(i_rObject
, path
, idref
, pLink
);
1250 // Metadatable mixin
1253 Metadatable::~Metadatable()
1255 RemoveMetadataReference();
1258 void Metadatable::RemoveMetadataReference()
1264 m_pReg
->UnregisterMetadatable( *this );
1265 m_pReg
->RemoveXmlIdForElement( *this );
1269 catch (const uno::Exception
&)
1271 TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::RemoveMetadataReference");
1275 // css::rdf::XMetadatable:
1277 Metadatable::GetMetadataReference() const
1281 return m_pReg
->GetXmlIdForElement(*this);
1283 return beans::StringPair();
1286 void Metadatable::SetMetadataReference( const css::beans::StringPair
& i_rReference
)
1288 if (i_rReference
.Second
.isEmpty())
1290 RemoveMetadataReference();
1294 OUString
streamName( i_rReference
.First
);
1295 if (streamName
.isEmpty())
1297 // handle empty stream name as auto-detect.
1298 // necessary for importing flat file format.
1299 streamName
= IsInContent() ? s_content
: s_styles
;
1301 XmlIdRegistry
& rReg( dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1302 if (!rReg
.TryRegisterMetadatable(*this, streamName
, i_rReference
.Second
))
1304 throw lang::IllegalArgumentException(
1305 "Metadatable::SetMetadataReference: argument is invalid", /*this*/nullptr, 0);
1312 void Metadatable::EnsureMetadataReference()
1314 XmlIdRegistry
& rReg(
1315 m_pReg
? *m_pReg
: dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1316 rReg
.RegisterMetadatableAndCreateID( *this );
1320 static const ::sfx2::IXmlIdRegistry
& GetRegistryConst(Metadatable
const& i_rObject
)
1322 return const_cast< Metadatable
& >( i_rObject
).GetRegistry();
1326 Metadatable::RegisterAsCopyOf(Metadatable
const & i_rSource
,
1327 const bool i_bCopyPrecedesSource
)
1329 OSL_ENSURE(typeid(*this) == typeid(i_rSource
)
1330 || typeid(i_rSource
) == typeid(MetadatableUndo
)
1331 || typeid(*this) == typeid(MetadatableUndo
)
1332 || typeid(i_rSource
) == typeid(MetadatableClipboard
)
1333 || typeid(*this) == typeid(MetadatableClipboard
),
1334 "RegisterAsCopyOf element with different class?");
1335 OSL_ENSURE(!m_pReg
, "RegisterAsCopyOf called on element with XmlId?");
1339 RemoveMetadataReference();
1344 if (i_rSource
.m_pReg
)
1346 XmlIdRegistry
& rReg(
1347 dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1348 if (i_rSource
.m_pReg
== &rReg
)
1350 OSL_ENSURE(!IsInClipboard(),
1351 "RegisterAsCopy: both in clipboard?");
1352 if (!IsInClipboard())
1354 XmlIdRegistryDocument
& rRegDoc(
1355 dynamic_cast<XmlIdRegistryDocument
&>( rReg
) );
1356 rRegDoc
.RegisterCopy(i_rSource
, *this,
1357 i_bCopyPrecedesSource
);
1362 // source is in different document
1363 XmlIdRegistryDocument
* pRegDoc(
1364 dynamic_cast<XmlIdRegistryDocument
*>(&rReg
) );
1365 XmlIdRegistryClipboard
* pRegClp(
1366 dynamic_cast<XmlIdRegistryClipboard
*>(&rReg
) );
1370 beans::StringPair
SourceRef(
1371 i_rSource
.m_pReg
->GetXmlIdForElement(i_rSource
) );
1372 bool isLatent( SourceRef
.Second
.isEmpty() );
1373 XmlIdRegistryDocument
* pSourceRegDoc(
1374 dynamic_cast<XmlIdRegistryDocument
*>(i_rSource
.m_pReg
) );
1375 OSL_ENSURE(pSourceRegDoc
, "RegisterAsCopyOf: 2 clipboards?");
1376 if (!pSourceRegDoc
) return;
1377 // this is a copy _to_ the clipboard
1380 pSourceRegDoc
->LookupXmlId(i_rSource
,
1381 SourceRef
.First
, SourceRef
.Second
);
1383 Metadatable
& rLink(
1384 pRegClp
->RegisterCopyClipboard(*this, SourceRef
, isLatent
));
1386 // register as copy in the non-clipboard registry
1387 pSourceRegDoc
->RegisterCopy(i_rSource
, rLink
,
1388 false); // i_bCopyPrecedesSource);
1389 rLink
.m_pReg
= pSourceRegDoc
;
1393 XmlIdRegistryClipboard
* pSourceRegClp(
1394 dynamic_cast<XmlIdRegistryClipboard
*>(i_rSource
.m_pReg
) );
1395 OSL_ENSURE(pSourceRegClp
,
1396 "RegisterAsCopyOf: 2 non-clipboards?");
1397 if (!pSourceRegClp
) return;
1398 const MetadatableClipboard
* pLink(
1399 pSourceRegClp
->SourceLink(i_rSource
) );
1400 // may happen if src got its id via UNO call
1402 // only register copy if clipboard content is from this SwDoc!
1403 if (&GetRegistryConst(*pLink
) == pRegDoc
)
1405 // this is a copy _from_ the clipboard; check if the
1406 // element is still in the same stream
1407 // N.B.: we check the stream of pLink, not of i_rSource!
1408 bool srcInContent( pLink
->IsInContent() );
1409 bool tgtInContent( IsInContent() );
1410 if (srcInContent
== tgtInContent
)
1412 pRegDoc
->RegisterCopy(*pLink
, *this,
1413 true); // i_bCopyPrecedesSource);
1416 // otherwise: stream change! do not register!
1421 OSL_FAIL("neither RegDoc nor RegClp cannot happen");
1425 catch (const uno::Exception
&)
1427 TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::RegisterAsCopyOf");
1431 std::shared_ptr
<MetadatableUndo
> Metadatable::CreateUndo() const
1433 OSL_ENSURE(!IsInUndo(), "CreateUndo called for object in undo?");
1434 OSL_ENSURE(!IsInClipboard(), "CreateUndo called for object in clipboard?");
1437 if (!IsInClipboard() && !IsInUndo() && m_pReg
)
1439 XmlIdRegistryDocument
* pRegDoc(
1440 dynamic_cast<XmlIdRegistryDocument
*>( m_pReg
) );
1442 std::shared_ptr
<MetadatableUndo
> xUndo(
1443 sfx2::XmlIdRegistryDocument::CreateUndo(*this) );
1444 pRegDoc
->RegisterCopy(*this, *xUndo
, false);
1445 xUndo
->m_pReg
= pRegDoc
;
1449 catch (const uno::Exception
&)
1451 TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::CreateUndo");
1453 return std::shared_ptr
<MetadatableUndo
>();
1456 std::shared_ptr
<MetadatableUndo
> Metadatable::CreateUndoForDelete()
1458 std::shared_ptr
<MetadatableUndo
> const xUndo( CreateUndo() );
1459 RemoveMetadataReference();
1463 void Metadatable::RestoreMetadata(
1464 std::shared_ptr
<MetadatableUndo
> const& i_pUndo
)
1466 OSL_ENSURE(!IsInUndo(), "RestoreMetadata called for object in undo?");
1467 OSL_ENSURE(!IsInClipboard(),
1468 "RestoreMetadata called for object in clipboard?");
1469 if (IsInClipboard() || IsInUndo()) return;
1470 RemoveMetadataReference();
1473 RegisterAsCopyOf(*i_pUndo
, true);
1478 Metadatable::JoinMetadatable(Metadatable
const & i_rOther
,
1479 const bool i_isMergedEmpty
, const bool i_isOtherEmpty
)
1481 OSL_ENSURE(!IsInUndo(), "JoinMetadatables called for object in undo?");
1482 OSL_ENSURE(!IsInClipboard(),
1483 "JoinMetadatables called for object in clipboard?");
1484 if (IsInClipboard() || IsInUndo()) return;
1486 if (i_isOtherEmpty
&& !i_isMergedEmpty
)
1488 // other is empty, thus loses => nothing to do
1491 if (i_isMergedEmpty
&& !i_isOtherEmpty
)
1493 RemoveMetadataReference();
1494 RegisterAsCopyOf(i_rOther
, true);
1498 if (!i_rOther
.m_pReg
)
1500 // other doesn't have xmlid, thus loses => nothing to do
1505 RegisterAsCopyOf(i_rOther
, true);
1506 // assumption: i_rOther will be deleted, so don't unregister it here
1511 XmlIdRegistryDocument
* pRegDoc(
1512 dynamic_cast<XmlIdRegistryDocument
*>( m_pReg
) );
1513 OSL_ENSURE(pRegDoc
, "JoinMetadatable: no pRegDoc?");
1516 pRegDoc
->JoinMetadatables(*this, i_rOther
);
1519 catch (const uno::Exception
&)
1521 TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::JoinMetadatable");
1526 // XMetadatable mixin
1529 OUString SAL_CALL
MetadatableMixin::getStringValue()
1531 return getNamespace() + getLocalName();
1535 OUString SAL_CALL
MetadatableMixin::getLocalName()
1537 SolarMutexGuard aGuard
;
1538 beans::StringPair
mdref( getMetadataReference() );
1539 if (mdref
.Second
.isEmpty())
1541 ensureMetadataReference(); // N.B.: side effect!
1542 mdref
= getMetadataReference();
1544 return mdref
.First
+ "#" + mdref
.Second
;
1547 OUString SAL_CALL
MetadatableMixin::getNamespace()
1549 SolarMutexGuard aGuard
;
1550 const uno::Reference
< frame::XModel
> xModel( GetModel() );
1551 const uno::Reference
< rdf::XURI
> xDMA( xModel
, uno::UNO_QUERY_THROW
);
1552 return xDMA
->getStringValue();
1555 // css::rdf::XMetadatable:
1556 beans::StringPair SAL_CALL
1557 MetadatableMixin::getMetadataReference()
1559 SolarMutexGuard aGuard
;
1561 Metadatable
*const pObject( GetCoreObject() );
1564 throw uno::RuntimeException(
1565 "MetadatableMixin: cannot get core object; not inserted?",
1568 return pObject
->GetMetadataReference();
1572 MetadatableMixin::setMetadataReference(
1573 const beans::StringPair
& i_rReference
)
1575 SolarMutexGuard aGuard
;
1577 Metadatable
*const pObject( GetCoreObject() );
1580 throw uno::RuntimeException(
1581 "MetadatableMixin: cannot get core object; not inserted?",
1584 return pObject
->SetMetadataReference(i_rReference
);
1587 void SAL_CALL
MetadatableMixin::ensureMetadataReference()
1589 SolarMutexGuard aGuard
;
1591 Metadatable
*const pObject( GetCoreObject() );
1594 throw uno::RuntimeException(
1595 "MetadatableMixin: cannot get core object; not inserted?",
1598 return pObject
->EnsureMetadataReference();
1603 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */