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>
34 #include <unordered_map>
35 #if OSL_DEBUG_LEVEL > 0
42 There is an abstract base class <type>XmlIdRegistry</type>, with
43 2 subclasses <type>XmlIdRegistryDocument</type> for "normal" documents,
44 and <type>XmlIdRegistryClipboard</type> for clipboard documents.
45 These classes are responsible for managing XML IDs for all elements
46 of the model. Only the implementation of the <type>Metadatable</type>
47 base class needs to know the registries, so they are not in the header.
49 The handling of XML IDs differs between clipboard and non-clipboard
50 documents in several aspects. Most importantly, non-clipboard documents
51 can have several elements associated with one XML ID.
52 This is necessary because of the weird undo implementation:
53 deleting a text node moves the deleted node to the undo array, but
54 executing undo will then create a <em>copy</em> of that node in the
55 document array. These 2 nodes must have the same XML ID, because
56 we cannot know whether the user will do a redo next, or something else.
58 Because we need to have a mechanism for several objects per XML ID anyway,
59 we use that also to enable some usability features:
60 The document registry has a list of Metadatables per XML ID.
61 This list is sorted by priority, i.e., the first element has highest
62 priority. When inserting copies, care must be taken that they are inserted
63 at the right position: either before or after the source.
64 This is done by <method>Metadatable::RegisterAsCopyOf</method>.
65 When a text node is split, then both resulting text nodes are inserted
66 into the list. If the user then deletes one text node, the other one
68 Also, when a Metadatable is copied to the clipboard and then pasted,
69 the copy is inserted into the list. If the user then deletes the source,
70 the XML ID is not lost.
71 The goal is that it should be hard to lose an XML ID by accident, which
72 is especially important as long as we do not have an UI that displays them.
74 There are two subclasses of <type>Metadatable</type>:
75 <ul><li><type>MetadatableClipboard</type>: for copies in the clipboard</li>
76 <li><type>MetadatableUndo</type>: for undo, because a Metadatable
77 may be destroyed on delete and a new one created on undo.</li></ul>
78 These serve only to track the position in an XML ID list in a document
79 registry, so that future actions can insert objects at the right position.
80 Unfortunately, inserting dummy objects seems to be necessary:
81 <ul><li>it is not sufficient to just remember the saved id, because then
82 the relative priorities might change when executing the undo</li>
83 <li>it is not sufficient to record the position as an integer, because
84 if we delete a text node and then undo, the node will be copied(!),
85 and we will have one more node in the list.<li>
86 <li>it is not sufficient to record the pointer of the previous/next
87 Metadatable, because if we delete a text node, undo, and then
88 do something to clear the redo array, the original text node is
89 destroyed, and is replaced by the copy created by undo</li></ul>
91 If content from a non-clipboard document is copied into a clipboard
92 document, a dummy <type>MetadatableClipboard</type> is inserted into the
93 non-clipboard document registry in order to track the position of the
94 source element. When the clipboard content is pasted back into the source
95 document, this dummy object is used to associate the pasted element with
98 If a <type>Metadatable</type> is deleted or merged,
99 <method>Metadatable::CreateUndo</method> is called, and returns a
100 <type>MetadatableUndo<type> instance, which can be used to undo the action
101 by passing it to <method>Metadatable::RestoreMetadata</method>.
106 using namespace ::com::sun::star
;
108 using ::sfx2::isValidXmlId
;
113 const char s_content
[] = "content.xml";
114 const char s_styles
[] = "styles.xml";
116 static bool isContentFile(OUString
const & i_rPath
)
118 return i_rPath
== s_content
;
121 static bool isStylesFile (OUString
const & i_rPath
)
123 return i_rPath
== s_styles
;
127 // XML ID handling ---------------------------------------------------
129 /** handles registration of XMetadatable.
131 This class is responsible for guaranteeing that XMetadatable objects
132 always have XML IDs that are unique within a stream.
134 This is an abstract base class; see subclasses XmlIdRegistryDocument and
135 XmlIdRegistryClipboard.
137 @see SwDoc::GetXmlIdRegistry
138 @see SwDocShell::GetXmlIdRegistry
140 class XmlIdRegistry
: public sfx2::IXmlIdRegistry
146 /** get the ODF element with the given metadata reference. */
147 virtual css::uno::Reference
< css::rdf::XMetadatable
>
148 GetElementByMetadataReference(
149 const css::beans::StringPair
& i_rReference
) const
152 /** register an ODF element at a newly generated, unique metadata reference.
155 Find a fresh XML ID, and register it for the element.
156 The generated ID does not occur in any stream of the document.
159 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
) = 0;
161 /** try to register an ODF element at a given XML ID, or update its
162 registration to a different XML ID.
165 If the given new metadata reference is not already occupied in the
166 document, unregister the element at its old metadata reference if
167 it has one, and register the new metadata reference for the element.
168 Note that this method only ensures that XML IDs are unique per stream,
169 so using the same XML ID in both content.xml and styles.xml is allowed.
173 true iff the element has successfully been registered
175 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
176 OUString
const& i_rStreamName
, OUString
const& i_rIdref
)
179 /** unregister an ODF element.
182 Unregister the element at its metadata reference.
183 Does not remove the metadata reference from the element.
186 @see RemoveXmlIdForElement
188 virtual void UnregisterMetadatable(Metadatable
const&) = 0;
190 /** get the metadata reference for the given element. */
191 css::beans::StringPair
192 GetXmlIdForElement(Metadatable
const&) const;
194 /** remove the metadata reference for the given element. */
195 virtual void RemoveXmlIdForElement(Metadatable
const&) = 0;
199 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
200 OUString
& o_rStream
, OUString
& o_rIdref
) const = 0;
202 virtual Metadatable
* LookupElement(const OUString
& i_rStreamName
,
203 const OUString
& i_rIdref
) const = 0;
206 // XmlIdRegistryDocument ---------------------------------------------
210 /** non-clipboard documents */
211 class XmlIdRegistryDocument
: public XmlIdRegistry
215 XmlIdRegistryDocument();
217 virtual ~XmlIdRegistryDocument() override
;
219 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
) override
;
221 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
222 OUString
const& i_rStreamName
, OUString
const& i_rIdref
) override
;
224 virtual void UnregisterMetadatable(Metadatable
const&) override
;
226 virtual void RemoveXmlIdForElement(Metadatable
const&) override
;
228 /** register i_rCopy as a copy of i_rSource,
229 with precedence iff i_bCopyPrecedesSource is true */
230 void RegisterCopy(Metadatable
const& i_rSource
, Metadatable
& i_rCopy
,
231 const bool i_bCopyPrecedesSource
);
233 /** create a Undo Metadatable for i_rObject. */
234 static std::shared_ptr
<MetadatableUndo
> CreateUndo(
235 Metadatable
const& i_rObject
);
237 /** merge i_rMerged and i_rOther into i_rMerged. */
238 void JoinMetadatables(Metadatable
& i_rMerged
, Metadatable
const& i_rOther
);
240 // unfortunately public, Metadatable::RegisterAsCopyOf needs this
241 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
242 OUString
& o_rStream
, OUString
& o_rIdref
) const override
;
246 virtual Metadatable
* LookupElement(const OUString
& i_rStreamName
,
247 const OUString
& i_rIdref
) const override
;
249 struct XmlIdRegistry_Impl
;
250 ::std::unique_ptr
<XmlIdRegistry_Impl
> m_pImpl
;
255 // MetadatableUndo ---------------------------------------------------
257 /** the horrible Undo Metadatable: is inserted into lists to track position */
258 class MetadatableUndo
: public Metadatable
260 /// as determined by the stream of the source in original document
261 const bool m_isInContent
;
263 explicit MetadatableUndo(const bool i_isInContent
)
264 : m_isInContent(i_isInContent
) { }
265 virtual ::sfx2::XmlIdRegistry
& GetRegistry() override
267 // N.B. for Undo, m_pReg is initialized by registering this as copy in
268 // CreateUndo; it is never cleared
269 OSL_ENSURE(m_pReg
, "no m_pReg in MetadatableUndo ?");
272 virtual bool IsInClipboard() const override
{ return false; }
273 virtual bool IsInUndo() const override
{ return true; }
274 virtual bool IsInContent() const override
{ return m_isInContent
; }
275 virtual css::uno::Reference
< css::rdf::XMetadatable
> MakeUnoObject() override
276 { OSL_FAIL("MetadatableUndo::MakeUnoObject"); throw; }
279 // MetadatableClipboard ----------------------------------------------
281 /** the horrible Clipboard Metadatable: inserted into lists to track position */
282 class MetadatableClipboard
: public Metadatable
284 /// as determined by the stream of the source in original document
285 const bool m_isInContent
;
287 explicit MetadatableClipboard(const bool i_isInContent
)
288 : m_isInContent(i_isInContent
) { }
289 virtual ::sfx2::XmlIdRegistry
& GetRegistry() override
291 // N.B. for Clipboard, m_pReg is initialized by registering this as copy in
292 // RegisterAsCopyOf; it is only cleared by OriginNoLongerInBusinessAnymore
293 assert(m_pReg
&& "no m_pReg in MetadatableClipboard ?");
296 virtual bool IsInClipboard() const override
{ return true; }
297 virtual bool IsInUndo() const override
{ return false; }
298 virtual bool IsInContent() const override
{ return m_isInContent
; }
299 virtual css::uno::Reference
< css::rdf::XMetadatable
> MakeUnoObject() override
300 { OSL_FAIL("MetadatableClipboard::MakeUnoObject"); throw; }
301 void OriginNoLongerInBusinessAnymore() { m_pReg
= nullptr; }
304 // XmlIdRegistryClipboard --------------------------------------------
308 class XmlIdRegistryClipboard
: public XmlIdRegistry
312 XmlIdRegistryClipboard();
314 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
) override
;
316 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
317 OUString
const& i_rStreamName
, OUString
const& i_rIdref
) override
;
319 virtual void UnregisterMetadatable(Metadatable
const&) override
;
321 virtual void RemoveXmlIdForElement(Metadatable
const&) override
;
323 /** register i_rCopy as a copy of i_rSource */
324 MetadatableClipboard
& RegisterCopyClipboard(Metadatable
& i_rCopy
,
325 beans::StringPair
const & i_rReference
,
326 const bool i_isLatent
);
328 /** get the Metadatable that links i_rObject to its origin registry */
329 MetadatableClipboard
const* SourceLink(Metadatable
const& i_rObject
);
332 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
333 OUString
& o_rStream
, OUString
& o_rIdref
) const override
;
335 virtual Metadatable
* LookupElement(const OUString
& i_rStreamName
,
336 const OUString
& i_rIdref
) const override
;
338 /** create a Clipboard Metadatable for i_rObject. */
339 static std::shared_ptr
<MetadatableClipboard
> CreateClipboard(
340 const bool i_isInContent
);
342 struct XmlIdRegistry_Impl
;
343 ::std::unique_ptr
<XmlIdRegistry_Impl
> m_pImpl
;
350 ::sfx2::IXmlIdRegistry
* createXmlIdRegistry(const bool i_DocIsClipboard
)
352 return i_DocIsClipboard
353 ? static_cast<XmlIdRegistry
*>( new XmlIdRegistryClipboard
)
354 : static_cast<XmlIdRegistry
*>( new XmlIdRegistryDocument
);
357 XmlIdRegistry::XmlIdRegistry()
361 css::uno::Reference
< css::rdf::XMetadatable
>
362 XmlIdRegistry::GetElementByMetadataReference(
363 const beans::StringPair
& i_rReference
) const
365 Metadatable
* pObject( LookupElement(i_rReference
.First
,
366 i_rReference
.Second
) );
367 return pObject
? pObject
->MakeUnoObject() : nullptr;
371 XmlIdRegistry::GetXmlIdForElement(const Metadatable
& i_rObject
) const
375 if (LookupXmlId(i_rObject
, path
, idref
))
377 if (LookupElement(path
, idref
) == &i_rObject
)
379 return beans::StringPair(path
, idref
);
382 return beans::StringPair();
386 /// generate unique xml:id
387 template< typename T
>
388 static OUString
create_id(const
389 std::unordered_map
< OUString
, T
> & i_rXmlIdMap
)
391 static bool bHack
= (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
392 static const char prefix
[] = "id"; // prefix for generated xml:id
393 typename
std::unordered_map
< OUString
, T
>
394 ::const_iterator iter
;
399 static sal_Int64 nIdCounter
= SAL_CONST_INT64(4000000000);
402 id
= prefix
+ OUString::number(nIdCounter
++);
403 iter
= i_rXmlIdMap
.find(id
);
405 while (iter
!= i_rXmlIdMap
.end());
411 unsigned int const n(comphelper::rng::uniform_uint_distribution(0,
412 std::numeric_limits
<unsigned int>::max()));
413 id
= prefix
+ OUString::number(n
);
414 iter
= i_rXmlIdMap
.find(id
);
416 while (iter
!= i_rXmlIdMap
.end());
422 // Document XML ID Registry (_Impl)
425 typedef ::std::vector
< Metadatable
* > XmlIdVector_t
;
427 /// Idref -> (content.xml element list, styles.xml element list)
428 typedef std::unordered_map
< OUString
,
429 ::std::pair
< XmlIdVector_t
, XmlIdVector_t
> > XmlIdMap_t
;
433 /// pointer hash template
434 template<typename T
> struct PtrHash
436 size_t operator() (T
const * i_pT
) const
438 return reinterpret_cast<size_t>(i_pT
);
444 /// element -> (stream name, idref)
445 typedef std::unordered_map
< const Metadatable
*,
446 ::std::pair
< OUString
, OUString
>, PtrHash
<Metadatable
> >
449 struct XmlIdRegistryDocument::XmlIdRegistry_Impl
452 : m_XmlIdMap(), m_XmlIdReverseMap() { }
454 bool TryInsertMetadatable(Metadatable
& i_xObject
,
455 const OUString
& i_rStream
, const OUString
& i_rIdref
);
457 bool LookupXmlId(const Metadatable
& i_xObject
,
458 OUString
& o_rStream
, OUString
& o_rIdref
) const;
460 Metadatable
* LookupElement(const OUString
& i_rStreamName
,
461 const OUString
& i_rIdref
) const;
463 const XmlIdVector_t
* LookupElementVector(
464 const OUString
& i_rStreamName
,
465 const OUString
& i_rIdref
) const;
467 XmlIdVector_t
* LookupElementVector(
468 const OUString
& i_rStreamName
,
469 const OUString
& i_rIdref
)
471 return const_cast<XmlIdVector_t
*>(
472 const_cast<const XmlIdRegistry_Impl
*>(this)
473 ->LookupElementVector(i_rStreamName
, i_rIdref
));
476 XmlIdMap_t m_XmlIdMap
;
477 XmlIdReverseMap_t m_XmlIdReverseMap
;
482 rmIter(XmlIdMap_t
& i_rXmlIdMap
, XmlIdMap_t::iterator
const& i_rIter
,
483 OUString
const & i_rStream
, Metadatable
const& i_rObject
)
485 if (i_rIter
!= i_rXmlIdMap
.end())
487 XmlIdVector_t
& rVector( isContentFile(i_rStream
)
488 ? i_rIter
->second
.first
: i_rIter
->second
.second
);
489 rVector
.erase(std::remove(rVector
.begin(), rVector
.end(), &const_cast<Metadatable
&>(i_rObject
)));
490 if (i_rIter
->second
.first
.empty() && i_rIter
->second
.second
.empty())
492 i_rXmlIdMap
.erase(i_rIter
);
498 const XmlIdVector_t
*
499 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElementVector(
500 const OUString
& i_rStreamName
,
501 const OUString
& i_rIdref
) const
503 const XmlIdMap_t::const_iterator
iter( m_XmlIdMap
.find(i_rIdref
) );
504 if (iter
!= m_XmlIdMap
.end())
506 OSL_ENSURE(!iter
->second
.first
.empty() || !iter
->second
.second
.empty(),
507 "null entry in m_XmlIdMap");
508 return (isContentFile(i_rStreamName
))
509 ? &iter
->second
.first
510 : &iter
->second
.second
;
519 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElement(
520 const OUString
& i_rStreamName
,
521 const OUString
& i_rIdref
) const
523 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
525 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
528 const XmlIdVector_t
* pList( LookupElementVector(i_rStreamName
, i_rIdref
) );
531 const XmlIdVector_t::const_iterator
iter(
532 ::std::find_if(pList
->begin(), pList
->end(),
533 [](Metadatable
* item
)->bool {
534 return !(item
->IsInUndo() || item
->IsInClipboard());
536 if (iter
!= pList
->end())
545 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupXmlId(
546 const Metadatable
& i_rObject
,
547 OUString
& o_rStream
, OUString
& o_rIdref
) const
549 const XmlIdReverseMap_t::const_iterator
iter(
550 m_XmlIdReverseMap
.find(&i_rObject
) );
551 if (iter
!= m_XmlIdReverseMap
.end())
553 OSL_ENSURE(!iter
->second
.first
.isEmpty(),
554 "null stream in m_XmlIdReverseMap");
555 OSL_ENSURE(!iter
->second
.second
.isEmpty(),
556 "null id in m_XmlIdReverseMap");
557 o_rStream
= iter
->second
.first
;
558 o_rIdref
= iter
->second
.second
;
568 XmlIdRegistryDocument::XmlIdRegistry_Impl::TryInsertMetadatable(
569 Metadatable
& i_rObject
,
570 const OUString
& i_rStreamName
, const OUString
& i_rIdref
)
572 const bool bContent( isContentFile(i_rStreamName
) );
573 OSL_ENSURE(isContentFile(i_rStreamName
) || isStylesFile(i_rStreamName
),
576 XmlIdVector_t
* pList( LookupElementVector(i_rStreamName
, i_rIdref
) );
581 pList
->push_back( &i_rObject
);
586 // this is only called from TryRegister now, so check
587 // if all elements in the list are deleted (in undo) or
588 // placeholders, then "steal" the id from them
589 if ( std::none_of(pList
->begin(), pList
->end(),
590 [](Metadatable
* item
)->bool {
591 return !(item
->IsInUndo() || item
->IsInClipboard());
594 pList
->insert(pList
->begin(), &i_rObject
);
605 m_XmlIdMap
.insert(::std::make_pair(i_rIdref
, bContent
606 ? ::std::make_pair( XmlIdVector_t( 1, &i_rObject
), XmlIdVector_t() )
607 : ::std::make_pair( XmlIdVector_t(), XmlIdVector_t( 1, &i_rObject
) )));
613 // Document XML ID Registry
616 XmlIdRegistryDocument::XmlIdRegistryDocument()
617 : m_pImpl( new XmlIdRegistry_Impl
)
622 removeLink(Metadatable
* i_pObject
)
624 OSL_ENSURE(i_pObject
, "null in list ???");
625 if (!i_pObject
) return;
626 if (i_pObject
->IsInClipboard())
628 MetadatableClipboard
* pLink(
629 dynamic_cast<MetadatableClipboard
*>( i_pObject
) );
630 OSL_ENSURE(pLink
, "IsInClipboard, but no MetadatableClipboard ?");
633 pLink
->OriginNoLongerInBusinessAnymore();
638 XmlIdRegistryDocument::~XmlIdRegistryDocument()
640 // notify all list elements that are actually in the clipboard
641 for (const auto& aXmlId
: m_pImpl
->m_XmlIdMap
) {
642 for (auto aLink
: aXmlId
.second
.first
)
644 for (auto aLink
: aXmlId
.second
.second
)
650 XmlIdRegistryDocument::LookupXmlId(
651 const Metadatable
& i_rObject
,
652 OUString
& o_rStream
, OUString
& o_rIdref
) const
654 return m_pImpl
->LookupXmlId(i_rObject
, o_rStream
, o_rIdref
);
658 XmlIdRegistryDocument::LookupElement(
659 const OUString
& i_rStreamName
,
660 const OUString
& i_rIdref
) const
662 return m_pImpl
->LookupElement(i_rStreamName
, i_rIdref
);
666 XmlIdRegistryDocument::TryRegisterMetadatable(Metadatable
& i_rObject
,
667 OUString
const& i_rStreamName
, OUString
const& i_rIdref
)
669 SAL_INFO("sfx", "TryRegisterMetadatable: " << &i_rObject
<< " (" << i_rStreamName
<< "#" << i_rIdref
<< ")");
671 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
672 "TryRegisterMetadatable called for MetadatableUndo?");
673 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
674 "TryRegisterMetadatable called for MetadatableClipboard?");
676 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
678 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
680 if (i_rObject
.IsInContent()
681 ? !isContentFile(i_rStreamName
)
682 : !isStylesFile(i_rStreamName
))
684 throw lang::IllegalArgumentException("illegal XmlId: wrong stream", nullptr, 0);
689 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
);
690 if (old_path
== i_rStreamName
&& old_idref
== i_rIdref
)
692 return (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
);
694 XmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
695 if (!old_idref
.isEmpty())
697 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
698 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
700 if (m_pImpl
->TryInsertMetadatable(i_rObject
, i_rStreamName
, i_rIdref
))
702 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
703 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] =
704 ::std::make_pair(i_rStreamName
, i_rIdref
);
714 XmlIdRegistryDocument::RegisterMetadatableAndCreateID(Metadatable
& i_rObject
)
716 SAL_INFO("sfx", "RegisterMetadatableAndCreateID: " << &i_rObject
);
718 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
719 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
720 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
721 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
723 const bool isInContent( i_rObject
.IsInContent() );
724 const OUString
stream( OUString::createFromAscii(
725 isInContent
? s_content
: s_styles
) );
726 // check if we have a latent xmlid, and if yes, remove it
729 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
);
731 XmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
732 if (!old_idref
.isEmpty())
734 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
735 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
736 if (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
)
742 // remove latent xmlid
743 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
748 const OUString
id( create_id(m_pImpl
->m_XmlIdMap
) );
749 OSL_ENSURE(m_pImpl
->m_XmlIdMap
.find(id
) == m_pImpl
->m_XmlIdMap
.end(),
750 "created id is in use");
751 m_pImpl
->m_XmlIdMap
.insert(::std::make_pair(id
, isInContent
752 ? ::std::make_pair( XmlIdVector_t( 1, &i_rObject
), XmlIdVector_t() )
753 : ::std::make_pair( XmlIdVector_t(), XmlIdVector_t( 1, &i_rObject
) )));
754 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] = ::std::make_pair(stream
, id
);
757 void XmlIdRegistryDocument::UnregisterMetadatable(const Metadatable
& i_rObject
)
759 SAL_INFO("sfx", "UnregisterMetadatable: " << &i_rObject
);
763 if (!m_pImpl
->LookupXmlId(i_rObject
, path
, idref
))
765 OSL_FAIL("unregister: no xml id?");
768 const XmlIdMap_t::iterator
iter( m_pImpl
->m_XmlIdMap
.find(idref
) );
769 if (iter
!= m_pImpl
->m_XmlIdMap
.end())
771 rmIter(m_pImpl
->m_XmlIdMap
, iter
, path
, i_rObject
);
775 void XmlIdRegistryDocument::RemoveXmlIdForElement(const Metadatable
& i_rObject
)
777 SAL_INFO("sfx", "RemoveXmlIdForElement: " << &i_rObject
);
779 const XmlIdReverseMap_t::iterator
iter(
780 m_pImpl
->m_XmlIdReverseMap
.find(&i_rObject
) );
781 if (iter
!= m_pImpl
->m_XmlIdReverseMap
.end())
783 OSL_ENSURE(!iter
->second
.second
.isEmpty(),
784 "null id in m_XmlIdReverseMap");
785 m_pImpl
->m_XmlIdReverseMap
.erase(iter
);
790 void XmlIdRegistryDocument::RegisterCopy(Metadatable
const& i_rSource
,
791 Metadatable
& i_rCopy
, const bool i_bCopyPrecedesSource
)
793 SAL_INFO("sfx", "RegisterCopy: " << &i_rSource
<< " -> " << &i_rCopy
<< " (" << i_bCopyPrecedesSource
<< ")");
795 // potential sources: clipboard, undo array, splitNode
796 // assumption: stream change can only happen via clipboard, and is handled
797 // by Metadatable::RegisterAsCopyOf
798 OSL_ENSURE(i_rSource
.IsInUndo() || i_rCopy
.IsInUndo() ||
799 (i_rSource
.IsInContent() == i_rCopy
.IsInContent()),
800 "RegisterCopy: not in same stream?");
804 if (!m_pImpl
->LookupXmlId( i_rSource
, path
, idref
))
806 OSL_FAIL("no xml id?");
809 XmlIdVector_t
* pList ( m_pImpl
->LookupElementVector(path
, idref
) );
810 OSL_ENSURE( ::std::find( pList
->begin(), pList
->end(), &i_rCopy
)
811 == pList
->end(), "copy already registered???");
812 XmlIdVector_t::iterator
srcpos(
813 ::std::find( pList
->begin(), pList
->end(), &i_rSource
) );
814 OSL_ENSURE(srcpos
!= pList
->end(), "source not in list???");
815 if (srcpos
== pList
->end())
819 if (i_bCopyPrecedesSource
)
821 pList
->insert( srcpos
, &i_rCopy
);
825 // for undo push_back does not work! must insert right after source
826 pList
->insert( ++srcpos
, &i_rCopy
);
828 m_pImpl
->m_XmlIdReverseMap
.insert(::std::make_pair(&i_rCopy
,
829 ::std::make_pair(path
, idref
)));
832 std::shared_ptr
<MetadatableUndo
>
833 XmlIdRegistryDocument::CreateUndo(Metadatable
const& i_rObject
)
835 SAL_INFO("sfx", "CreateUndo: " << &i_rObject
);
837 return std::make_shared
<MetadatableUndo
>(
838 i_rObject
.IsInContent() );
842 i_rMerged is both a source and the target node of the merge
843 i_rOther is the other source, and will be deleted after the merge
845 dimensions: none|latent|actual empty|nonempty
846 i_rMerged(1) i_rOther(2) result
847 *|empty *|empty => 1|2 (arbitrary)
848 *|empty *|nonempty => 2
849 *|nonempty *|empty => 1
850 none|nonempty none|nonempty => none
851 none|nonempty latent|nonempty => 2
852 latent|nonempty none|nonempty => 1
853 latent|nonempty latent|nonempty => 1|2
854 *|nonempty actual|nonempty => 2
855 actual|nonempty *|nonempty => 1
856 actual|nonempty actual|nonempty => 1|2
859 XmlIdRegistryDocument::JoinMetadatables(
860 Metadatable
& i_rMerged
, Metadatable
const & i_rOther
)
862 SAL_INFO("sfx", "JoinMetadatables: " << &i_rMerged
<< " <- " << &i_rOther
);
867 if (m_pImpl
->LookupXmlId(i_rMerged
, path
, idref
))
869 mergedOwnsRef
= (m_pImpl
->LookupElement(path
, idref
) == &i_rMerged
);
873 OSL_FAIL("JoinMetadatables: no xmlid?");
878 i_rMerged
.RemoveMetadataReference();
879 i_rMerged
.RegisterAsCopyOf(i_rOther
, true);
882 // other cases: merged has actual ref and is nonempty,
883 // other has latent/actual ref and is nonempty: other loses => nothing to do
887 // Clipboard XML ID Registry (_Impl)
893 RMapEntry() : m_xLink() { }
894 RMapEntry(OUString
const& i_rStream
,
895 OUString
const& i_rXmlId
,
896 std::shared_ptr
<MetadatableClipboard
> const& i_pLink
897 = std::shared_ptr
<MetadatableClipboard
>())
898 : m_Stream(i_rStream
), m_XmlId(i_rXmlId
), m_xLink(i_pLink
)
902 // this would have been an auto_ptr, if only that would have compiled...
903 std::shared_ptr
<MetadatableClipboard
> m_xLink
;
908 /// element -> (stream name, idref, source)
909 typedef std::unordered_map
< const Metadatable
*,
911 PtrHash
<Metadatable
> >
912 ClipboardXmlIdReverseMap_t
;
914 /// Idref -> (content.xml element, styles.xml element)
915 typedef std::unordered_map
< OUString
,
916 ::std::pair
< Metadatable
*, Metadatable
* > >
919 struct XmlIdRegistryClipboard::XmlIdRegistry_Impl
922 : m_XmlIdMap(), m_XmlIdReverseMap() { }
924 bool TryInsertMetadatable(Metadatable
& i_xObject
,
925 const OUString
& i_rStream
, const OUString
& i_rIdref
);
927 bool LookupXmlId(const Metadatable
& i_xObject
,
928 OUString
& o_rStream
, OUString
& o_rIdref
,
929 MetadatableClipboard
const* &o_rpLink
) const;
931 Metadatable
* LookupElement(const OUString
& i_rStreamName
,
932 const OUString
& i_rIdref
) const;
934 Metadatable
* const* LookupEntry(const OUString
& i_rStreamName
,
935 const OUString
& i_rIdref
) const;
937 ClipboardXmlIdMap_t m_XmlIdMap
;
938 ClipboardXmlIdReverseMap_t m_XmlIdReverseMap
;
943 rmIter(ClipboardXmlIdMap_t
& i_rXmlIdMap
,
944 ClipboardXmlIdMap_t::iterator
const& i_rIter
,
945 OUString
const & i_rStream
, Metadatable
const& i_rObject
)
947 if (i_rIter
== i_rXmlIdMap
.end())
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 const OUString
& i_rStreamName
,
966 const OUString
& i_rIdref
) const
968 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
970 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
973 const ClipboardXmlIdMap_t::const_iterator
iter( m_XmlIdMap
.find(i_rIdref
) );
974 if (iter
!= m_XmlIdMap
.end())
976 OSL_ENSURE(iter
->second
.first
|| iter
->second
.second
,
977 "null entry in m_XmlIdMap");
978 return (isContentFile(i_rStreamName
))
979 ? &iter
->second
.first
980 : &iter
->second
.second
;
989 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupElement(
990 const OUString
& i_rStreamName
,
991 const OUString
& i_rIdref
) const
993 Metadatable
* const * ppEntry
= LookupEntry(i_rStreamName
, i_rIdref
);
994 return ppEntry
? *ppEntry
: nullptr;
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 const OUString
& 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() );
1139 OUString
stream( OUString::createFromAscii(
1140 isInContent
? s_content
: 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 OSL_FAIL("Metadatable::RemoveMetadataReference: exception");
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
= OUString::createFromAscii(
1299 IsInContent() ? s_content
: s_styles
);
1301 XmlIdRegistry
& rReg( dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1302 if (!rReg
.TryRegisterMetadatable(*this, streamName
, i_rReference
.Second
))
1304 throw lang::IllegalArgumentException(
1305 "Metadatable::SetMetadataReference: argument is invalid", /*this*/nullptr, 0);
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 OSL_FAIL("Metadatable::RegisterAsCopyOf: exception");
1431 std::shared_ptr
<MetadatableUndo
> Metadatable::CreateUndo() const
1433 OSL_ENSURE(!IsInUndo(), "CreateUndo called for object in undo?");
1434 OSL_ENSURE(!IsInClipboard(), "CreateUndo called for object in clipboard?");
1437 if (!IsInClipboard() && !IsInUndo() && m_pReg
)
1439 XmlIdRegistryDocument
* pRegDoc(
1440 dynamic_cast<XmlIdRegistryDocument
*>( m_pReg
) );
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 OSL_FAIL("Metadatable::CreateUndo: exception");
1453 return std::shared_ptr
<MetadatableUndo
>();
1456 std::shared_ptr
<MetadatableUndo
> Metadatable::CreateUndoForDelete()
1458 std::shared_ptr
<MetadatableUndo
> const xUndo( CreateUndo() );
1459 RemoveMetadataReference();
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 OSL_FAIL("Metadatable::JoinMetadatable: exception");
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: */