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 .
21 #include <sfx2/Metadatable.hxx>
22 #include <sfx2/XmlIdRegistry.hxx>
24 #include <osl/mutex.hxx>
25 #include <vcl/svapp.hxx>
27 #include <comphelper/random.hxx>
32 #include <unordered_map>
33 #if OSL_DEBUG_LEVEL > 0
40 There is an abstract base class <type>XmlIdRegistry</type>, with
41 2 subclasses <type>XmlIdRegistryDocument</type> for "normal" documents,
42 and <type>XmlIdRegistryClipboard</type> for clipboard documents.
43 These classes are responsible for managing XML IDs for all elements
44 of the model. Only the implementation of the <type>Metadatable</type>
45 base class needs to know the registries, so they are not in the header.
47 The handling of XML IDs differs between clipboard and non-clipboard
48 documents in several aspects. Most importantly, non-clipboard documents
49 can have several elements associated with one XML ID.
50 This is necessary because of the weird undo implementation:
51 deleting a text node moves the deleted node to the undo array, but
52 executing undo will then create a <em>copy</em> of that node in the
53 document array. These 2 nodes must have the same XML ID, because
54 we cannot know whether the user will do a redo next, or something else.
56 Because we need to have a mechanism for several objects per XML ID anyway,
57 we use that also to enable some usability features:
58 The document registry has a list of Metadatables per XML ID.
59 This list is sorted by priority, i.e., the first element has highest
60 priority. When inserting copies, care must be taken that they are inserted
61 at the right position: either before or after the source.
62 This is done by <method>Metadatable::RegisterAsCopyOf</method>.
63 When a text node is split, then both resulting text nodes are inserted
64 into the list. If the user then deletes one text node, the other one
66 Also, when a Metadatable is copied to the clipboard and then pasted,
67 the copy is inserted into the list. If the user then deletes the source,
68 the XML ID is not lost.
69 The goal is that it should be hard to lose an XML ID by accident, which
70 is especially important as long as we do not have an UI that displays them.
72 There are two subclasses of <type>Metadatable</type>:
73 <ul><li><type>MetadatableClipboard</type>: for copies in the clipboard</li>
74 <li><type>MetadatableUndo</type>: for undo, because a Metadatable
75 may be destroyed on delete and a new one created on undo.</li></ul>
76 These serve only to track the position in an XML ID list in a document
77 registry, so that future actions can insert objects at the right position.
78 Unfortunately, inserting dummy objects seems to be necessary:
79 <ul><li>it is not sufficient to just remember the saved id, because then
80 the relative priorities might change when executing the undo</li>
81 <li>it is not sufficient to record the position as an integer, because
82 if we delete a text node and then undo, the node will be copied(!),
83 and we will have one more node in the list.<li>
84 <li>it is not sufficient to record the pointer of the previous/next
85 Metadatable, because if we delete a text node, undo, and then
86 do something to clear the redo array, the original text node is
87 destroyed, and is replaced by the copy created by undo</li></ul>
89 If content from a non-clipboard document is copied into a clipboard
90 document, a dummy <type>MetadatableClipboard</type> is inserted into the
91 non-clipboard document registry in order to track the position of the
92 source element. When the clipboard content is pasted back into the source
93 document, this dummy object is used to associate the pasted element with
96 If a <type>Metadatable</type> is deleted or merged,
97 <method>Metadatable::CreateUndo</method> is called, and returns a
98 <type>MetadatableUndo<type> instance, which can be used to undo the action
99 by passing it to <method>Metadatable::RestoreMetadata</method>.
104 using namespace ::com::sun::star
;
106 using ::sfx2::isValidXmlId
;
111 static const char s_content
[] = "content.xml";
112 static const char s_styles
[] = "styles.xml";
114 static bool isContentFile(OUString
const & i_rPath
)
116 return i_rPath
== s_content
;
119 static bool isStylesFile (OUString
const & i_rPath
)
121 return i_rPath
== s_styles
;
125 // XML ID handling ---------------------------------------------------
127 /** handles registration of XMetadatable.
129 This class is responsible for guaranteeing that XMetadatable objects
130 always have XML IDs that are unique within a stream.
132 This is an abstract base class; see subclasses XmlIdRegistryDocument and
133 XmlIdRegistryClipboard.
135 @see SwDoc::GetXmlIdRegistry
136 @see SwDocShell::GetXmlIdRegistry
138 class XmlIdRegistry
: public sfx2::IXmlIdRegistry
144 virtual ~XmlIdRegistry() override
;
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 ---------------------------------------------
208 /** non-clipboard documents */
209 class XmlIdRegistryDocument
: public XmlIdRegistry
213 XmlIdRegistryDocument();
215 virtual ~XmlIdRegistryDocument() override
;
217 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
) override
;
219 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
220 OUString
const& i_rStreamName
, OUString
const& i_rIdref
) override
;
222 virtual void UnregisterMetadatable(Metadatable
const&) override
;
224 virtual void RemoveXmlIdForElement(Metadatable
const&) override
;
226 /** register i_rCopy as a copy of i_rSource,
227 with precedence iff i_bCopyPrecedesSource is true */
228 void RegisterCopy(Metadatable
const& i_rSource
, Metadatable
& i_rCopy
,
229 const bool i_bCopyPrecedesSource
);
231 /** create a Undo Metadatable for i_rObject. */
232 static std::shared_ptr
<MetadatableUndo
> CreateUndo(
233 Metadatable
const& i_rObject
);
235 /** merge i_rMerged and i_rOther into i_rMerged. */
236 void JoinMetadatables(Metadatable
& i_rMerged
, Metadatable
const& i_rOther
);
238 // unfortunately public, Metadatable::RegisterAsCopyOf needs this
239 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
240 OUString
& o_rStream
, OUString
& o_rIdref
) const override
;
244 virtual Metadatable
* LookupElement(const OUString
& i_rStreamName
,
245 const OUString
& i_rIdref
) const override
;
247 struct XmlIdRegistry_Impl
;
248 ::std::unique_ptr
<XmlIdRegistry_Impl
> m_pImpl
;
251 // MetadatableUndo ---------------------------------------------------
253 /** the horrible Undo Metadatable: is inserted into lists to track position */
254 class MetadatableUndo
: public Metadatable
256 /// as determined by the stream of the source in original document
257 const bool m_isInContent
;
259 explicit MetadatableUndo(const bool i_isInContent
)
260 : m_isInContent(i_isInContent
) { }
261 virtual ::sfx2::XmlIdRegistry
& GetRegistry() override
263 // N.B. for Undo, m_pReg is initialized by registering this as copy in
264 // CreateUndo; it is never cleared
265 OSL_ENSURE(m_pReg
, "no m_pReg in MetadatableUndo ?");
268 virtual bool IsInClipboard() const override
{ return false; }
269 virtual bool IsInUndo() const override
{ return true; }
270 virtual bool IsInContent() const override
{ return m_isInContent
; }
271 virtual css::uno::Reference
< css::rdf::XMetadatable
> MakeUnoObject() override
272 { OSL_FAIL("MetadatableUndo::MakeUnoObject"); throw; }
275 // MetadatableClipboard ----------------------------------------------
277 /** the horrible Clipboard Metadatable: inserted into lists to track position */
278 class MetadatableClipboard
: public Metadatable
280 /// as determined by the stream of the source in original document
281 const bool m_isInContent
;
283 explicit MetadatableClipboard(const bool i_isInContent
)
284 : m_isInContent(i_isInContent
) { }
285 virtual ::sfx2::XmlIdRegistry
& GetRegistry() override
287 // N.B. for Clipboard, m_pReg is initialized by registering this as copy in
288 // RegisterAsCopyOf; it is only cleared by OriginNoLongerInBusinessAnymore
289 assert(m_pReg
&& "no m_pReg in MetadatableClipboard ?");
292 virtual bool IsInClipboard() const override
{ return true; }
293 virtual bool IsInUndo() const override
{ return false; }
294 virtual bool IsInContent() const override
{ return m_isInContent
; }
295 virtual css::uno::Reference
< css::rdf::XMetadatable
> MakeUnoObject() override
296 { OSL_FAIL("MetadatableClipboard::MakeUnoObject"); throw; }
297 void OriginNoLongerInBusinessAnymore() { m_pReg
= nullptr; }
300 // XmlIdRegistryClipboard --------------------------------------------
302 class XmlIdRegistryClipboard
: public XmlIdRegistry
306 XmlIdRegistryClipboard();
307 virtual ~XmlIdRegistryClipboard() override
;
309 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
) override
;
311 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
312 OUString
const& i_rStreamName
, OUString
const& i_rIdref
) override
;
314 virtual void UnregisterMetadatable(Metadatable
const&) override
;
316 virtual void RemoveXmlIdForElement(Metadatable
const&) override
;
318 /** register i_rCopy as a copy of i_rSource */
319 MetadatableClipboard
& RegisterCopyClipboard(Metadatable
& i_rCopy
,
320 beans::StringPair
const & i_rReference
,
321 const bool i_isLatent
);
323 /** get the Metadatable that links i_rObject to its origin registry */
324 MetadatableClipboard
const* SourceLink(Metadatable
const& i_rObject
);
327 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
328 OUString
& o_rStream
, OUString
& o_rIdref
) const override
;
330 virtual Metadatable
* LookupElement(const OUString
& i_rStreamName
,
331 const OUString
& i_rIdref
) const override
;
333 /** create a Clipboard Metadatable for i_rObject. */
334 static std::shared_ptr
<MetadatableClipboard
> CreateClipboard(
335 const bool i_isInContent
);
337 struct XmlIdRegistry_Impl
;
338 ::std::unique_ptr
<XmlIdRegistry_Impl
> m_pImpl
;
344 ::sfx2::IXmlIdRegistry
* createXmlIdRegistry(const bool i_DocIsClipboard
)
346 return i_DocIsClipboard
347 ? static_cast<XmlIdRegistry
*>( new XmlIdRegistryClipboard
)
348 : static_cast<XmlIdRegistry
*>( new XmlIdRegistryDocument
);
351 XmlIdRegistry::XmlIdRegistry()
355 XmlIdRegistry::~XmlIdRegistry()
359 css::uno::Reference
< css::rdf::XMetadatable
>
360 XmlIdRegistry::GetElementByMetadataReference(
361 const beans::StringPair
& i_rReference
) const
363 Metadatable
* pObject( LookupElement(i_rReference
.First
,
364 i_rReference
.Second
) );
365 return pObject
? pObject
->MakeUnoObject() : nullptr;
369 XmlIdRegistry::GetXmlIdForElement(const Metadatable
& i_rObject
) const
373 if (LookupXmlId(i_rObject
, path
, idref
))
375 if (LookupElement(path
, idref
) == &i_rObject
)
377 return beans::StringPair(path
, idref
);
380 return beans::StringPair();
384 /// generate unique xml:id
385 template< typename T
>
386 /*static*/ OUString
create_id(const
387 std::unordered_map
< OUString
, T
, OUStringHash
> & i_rXmlIdMap
)
389 static bool bHack
= (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
390 static const char prefix
[] = "id"; // prefix for generated xml:id
391 typename
std::unordered_map
< OUString
, T
, OUStringHash
>
392 ::const_iterator iter
;
397 static sal_Int64 nIdCounter
= SAL_CONST_INT64(4000000000);
400 id
= prefix
+ OUString::number(nIdCounter
++);
401 iter
= i_rXmlIdMap
.find(id
);
403 while (iter
!= i_rXmlIdMap
.end());
409 unsigned int const n(comphelper::rng::uniform_uint_distribution(0,
410 std::numeric_limits
<unsigned int>::max()));
411 id
= prefix
+ OUString::number(n
);
412 iter
= i_rXmlIdMap
.find(id
);
414 while (iter
!= i_rXmlIdMap
.end());
420 // Document XML ID Registry (_Impl)
423 typedef ::std::list
< Metadatable
* > XmlIdList_t
;
425 /// Idref -> (content.xml element list, styles.xml element list)
426 typedef std::unordered_map
< OUString
,
427 ::std::pair
< XmlIdList_t
, XmlIdList_t
>, OUStringHash
> XmlIdMap_t
;
429 /// pointer hash template
430 template<typename T
> struct PtrHash
432 size_t operator() (T
const * i_pT
) const
434 return reinterpret_cast<size_t>(i_pT
);
438 /// element -> (stream name, idref)
439 typedef std::unordered_map
< const Metadatable
*,
440 ::std::pair
< OUString
, OUString
>, PtrHash
<Metadatable
> >
443 struct XmlIdRegistryDocument::XmlIdRegistry_Impl
446 : m_XmlIdMap(), m_XmlIdReverseMap() { }
448 bool TryInsertMetadatable(Metadatable
& i_xObject
,
449 const OUString
& i_rStream
, const OUString
& i_rIdref
);
451 bool LookupXmlId(const Metadatable
& i_xObject
,
452 OUString
& o_rStream
, OUString
& o_rIdref
) const;
454 Metadatable
* LookupElement(const OUString
& i_rStreamName
,
455 const OUString
& i_rIdref
) const;
457 const XmlIdList_t
* LookupElementList(
458 const OUString
& i_rStreamName
,
459 const OUString
& i_rIdref
) const;
461 XmlIdList_t
* LookupElementList(
462 const OUString
& i_rStreamName
,
463 const OUString
& i_rIdref
)
465 return const_cast<XmlIdList_t
*>(
466 const_cast<const XmlIdRegistry_Impl
*>(this)
467 ->LookupElementList(i_rStreamName
, i_rIdref
));
470 XmlIdMap_t m_XmlIdMap
;
471 XmlIdReverseMap_t m_XmlIdReverseMap
;
476 rmIter(XmlIdMap_t
& i_rXmlIdMap
, XmlIdMap_t::iterator
const& i_rIter
,
477 OUString
const & i_rStream
, Metadatable
const& i_rObject
)
479 if (i_rIter
!= i_rXmlIdMap
.end())
481 XmlIdList_t
& rList( isContentFile(i_rStream
)
482 ? i_rIter
->second
.first
: i_rIter
->second
.second
);
483 rList
.remove(&const_cast<Metadatable
&>(i_rObject
));
484 if (i_rIter
->second
.first
.empty() && i_rIter
->second
.second
.empty())
486 i_rXmlIdMap
.erase(i_rIter
);
493 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElementList(
494 const OUString
& i_rStreamName
,
495 const OUString
& i_rIdref
) const
497 const XmlIdMap_t::const_iterator
iter( m_XmlIdMap
.find(i_rIdref
) );
498 if (iter
!= m_XmlIdMap
.end())
500 OSL_ENSURE(!iter
->second
.first
.empty() || !iter
->second
.second
.empty(),
501 "null entry in m_XmlIdMap");
502 return (isContentFile(i_rStreamName
))
503 ? &iter
->second
.first
504 : &iter
->second
.second
;
513 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElement(
514 const OUString
& i_rStreamName
,
515 const OUString
& i_rIdref
) const
517 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
519 throw lang::IllegalArgumentException(OUString(
520 "illegal XmlId"), nullptr, 0);
523 const XmlIdList_t
* pList( LookupElementList(i_rStreamName
, i_rIdref
) );
526 const XmlIdList_t::const_iterator
iter(
527 ::std::find_if(pList
->begin(), pList
->end(),
528 [](Metadatable
* item
)->bool {
529 return !(item
->IsInUndo() || item
->IsInClipboard());
531 if (iter
!= pList
->end())
540 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupXmlId(
541 const Metadatable
& i_rObject
,
542 OUString
& o_rStream
, OUString
& o_rIdref
) const
544 const XmlIdReverseMap_t::const_iterator
iter(
545 m_XmlIdReverseMap
.find(&i_rObject
) );
546 if (iter
!= m_XmlIdReverseMap
.end())
548 OSL_ENSURE(!iter
->second
.first
.isEmpty(),
549 "null stream in m_XmlIdReverseMap");
550 OSL_ENSURE(!iter
->second
.second
.isEmpty(),
551 "null id in m_XmlIdReverseMap");
552 o_rStream
= iter
->second
.first
;
553 o_rIdref
= iter
->second
.second
;
563 XmlIdRegistryDocument::XmlIdRegistry_Impl::TryInsertMetadatable(
564 Metadatable
& i_rObject
,
565 const OUString
& i_rStreamName
, const OUString
& i_rIdref
)
567 const bool bContent( isContentFile(i_rStreamName
) );
568 OSL_ENSURE(isContentFile(i_rStreamName
) || isStylesFile(i_rStreamName
),
571 XmlIdList_t
* pList( LookupElementList(i_rStreamName
, i_rIdref
) );
576 pList
->push_back( &i_rObject
);
581 // this is only called from TryRegister now, so check
582 // if all elements in the list are deleted (in undo) or
583 // placeholders, then "steal" the id from them
584 if ( pList
->end() == ::std::find_if(pList
->begin(), pList
->end(),
585 [](Metadatable
* item
)->bool {
586 return !(item
->IsInUndo() || item
->IsInClipboard());
589 pList
->push_front( &i_rObject
);
600 m_XmlIdMap
.insert(::std::make_pair(i_rIdref
, bContent
601 ? ::std::make_pair( XmlIdList_t( 1, &i_rObject
), XmlIdList_t() )
602 : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject
) )));
608 // Document XML ID Registry
611 XmlIdRegistryDocument::XmlIdRegistryDocument()
612 : m_pImpl( new XmlIdRegistry_Impl
)
617 removeLink(Metadatable
* i_pObject
)
619 OSL_ENSURE(i_pObject
, "null in list ???");
620 if (!i_pObject
) return;
621 if (i_pObject
->IsInClipboard())
623 MetadatableClipboard
* pLink(
624 dynamic_cast<MetadatableClipboard
*>( i_pObject
) );
625 OSL_ENSURE(pLink
, "IsInClipboard, but no MetadatableClipboard ?");
628 pLink
->OriginNoLongerInBusinessAnymore();
633 XmlIdRegistryDocument::~XmlIdRegistryDocument()
635 // notify all list elements that are actually in the clipboard
636 for (auto& aXmlId
: m_pImpl
->m_XmlIdMap
) {
637 for (auto aLink
: aXmlId
.second
.first
)
639 for (auto aLink
: aXmlId
.second
.second
)
645 XmlIdRegistryDocument::LookupXmlId(
646 const Metadatable
& i_rObject
,
647 OUString
& o_rStream
, OUString
& o_rIdref
) const
649 return m_pImpl
->LookupXmlId(i_rObject
, o_rStream
, o_rIdref
);
653 XmlIdRegistryDocument::LookupElement(
654 const OUString
& i_rStreamName
,
655 const OUString
& i_rIdref
) const
657 return m_pImpl
->LookupElement(i_rStreamName
, i_rIdref
);
661 XmlIdRegistryDocument::TryRegisterMetadatable(Metadatable
& i_rObject
,
662 OUString
const& i_rStreamName
, OUString
const& i_rIdref
)
664 OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject
,
665 OUStringToOString(i_rStreamName
, RTL_TEXTENCODING_UTF8
).getStr(),
666 OUStringToOString(i_rIdref
, RTL_TEXTENCODING_UTF8
).getStr());
668 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
669 "TryRegisterMetadatable called for MetadatableUndo?");
670 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
671 "TryRegisterMetadatable called for MetadatableClipboard?");
673 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
675 throw lang::IllegalArgumentException(OUString(
676 "illegal XmlId"), nullptr, 0);
678 if (i_rObject
.IsInContent()
679 ? !isContentFile(i_rStreamName
)
680 : !isStylesFile(i_rStreamName
))
682 throw lang::IllegalArgumentException(OUString(
683 "illegal XmlId: wrong stream"), nullptr, 0);
688 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
);
689 if (old_path
== i_rStreamName
&& old_idref
== i_rIdref
)
691 return (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
);
693 XmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
694 if (!old_idref
.isEmpty())
696 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
697 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
699 if (m_pImpl
->TryInsertMetadatable(i_rObject
, i_rStreamName
, i_rIdref
))
701 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
702 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] =
703 ::std::make_pair(i_rStreamName
, i_rIdref
);
713 XmlIdRegistryDocument::RegisterMetadatableAndCreateID(Metadatable
& i_rObject
)
715 OSL_TRACE("RegisterMetadatableAndCreateID: %p", &i_rObject
);
717 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
718 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
719 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
720 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
722 const bool isInContent( i_rObject
.IsInContent() );
723 const OUString
stream( OUString::createFromAscii(
724 isInContent
? s_content
: s_styles
) );
725 // check if we have a latent xmlid, and if yes, remove it
728 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
);
730 XmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
731 if (!old_idref
.isEmpty())
733 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
734 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
735 if (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
)
741 // remove latent xmlid
742 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
747 const OUString
id( create_id(m_pImpl
->m_XmlIdMap
) );
748 OSL_ENSURE(m_pImpl
->m_XmlIdMap
.find(id
) == m_pImpl
->m_XmlIdMap
.end(),
749 "created id is in use");
750 m_pImpl
->m_XmlIdMap
.insert(::std::make_pair(id
, isInContent
751 ? ::std::make_pair( XmlIdList_t( 1, &i_rObject
), XmlIdList_t() )
752 : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject
) )));
753 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] = ::std::make_pair(stream
, id
);
756 void XmlIdRegistryDocument::UnregisterMetadatable(const Metadatable
& i_rObject
)
758 OSL_TRACE("UnregisterMetadatable: %p", &i_rObject
);
762 if (!m_pImpl
->LookupXmlId(i_rObject
, path
, idref
))
764 OSL_FAIL("unregister: no xml id?");
767 const XmlIdMap_t::iterator
iter( m_pImpl
->m_XmlIdMap
.find(idref
) );
768 if (iter
!= m_pImpl
->m_XmlIdMap
.end())
770 rmIter(m_pImpl
->m_XmlIdMap
, iter
, path
, i_rObject
);
774 void XmlIdRegistryDocument::RemoveXmlIdForElement(const Metadatable
& i_rObject
)
776 OSL_TRACE("RemoveXmlIdForElement: %p", &i_rObject
);
778 const XmlIdReverseMap_t::iterator
iter(
779 m_pImpl
->m_XmlIdReverseMap
.find(&i_rObject
) );
780 if (iter
!= m_pImpl
->m_XmlIdReverseMap
.end())
782 OSL_ENSURE(!iter
->second
.second
.isEmpty(),
783 "null id in m_XmlIdReverseMap");
784 m_pImpl
->m_XmlIdReverseMap
.erase(iter
);
789 void XmlIdRegistryDocument::RegisterCopy(Metadatable
const& i_rSource
,
790 Metadatable
& i_rCopy
, const bool i_bCopyPrecedesSource
)
792 OSL_TRACE("RegisterCopy: %p -> %p (%d)\n",
793 &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 XmlIdList_t
* pList ( m_pImpl
->LookupElementList(path
, idref
) );
810 OSL_ENSURE( ::std::find( pList
->begin(), pList
->end(), &i_rCopy
)
811 == pList
->end(), "copy already registered???");
812 XmlIdList_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 OSL_TRACE("CreateUndo: %p", &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 OSL_TRACE("JoinMetadatables: %p <- %p", &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)
891 RMapEntry() : m_xLink() { }
892 RMapEntry(OUString
const& i_rStream
,
893 OUString
const& i_rXmlId
,
894 std::shared_ptr
<MetadatableClipboard
> const& i_pLink
895 = std::shared_ptr
<MetadatableClipboard
>())
896 : m_Stream(i_rStream
), m_XmlId(i_rXmlId
), m_xLink(i_pLink
)
900 // this would have been an auto_ptr, if only that would have compiled...
901 std::shared_ptr
<MetadatableClipboard
> m_xLink
;
904 /// element -> (stream name, idref, source)
905 typedef std::unordered_map
< const Metadatable
*,
907 PtrHash
<Metadatable
> >
908 ClipboardXmlIdReverseMap_t
;
910 /// Idref -> (content.xml element, styles.xml element)
911 typedef std::unordered_map
< OUString
,
912 ::std::pair
< Metadatable
*, Metadatable
* >, OUStringHash
>
915 struct XmlIdRegistryClipboard::XmlIdRegistry_Impl
918 : m_XmlIdMap(), m_XmlIdReverseMap() { }
920 bool TryInsertMetadatable(Metadatable
& i_xObject
,
921 const OUString
& i_rStream
, const OUString
& i_rIdref
);
923 bool LookupXmlId(const Metadatable
& i_xObject
,
924 OUString
& o_rStream
, OUString
& o_rIdref
,
925 MetadatableClipboard
const* &o_rpLink
) const;
927 Metadatable
* LookupElement(const OUString
& i_rStreamName
,
928 const OUString
& i_rIdref
) const;
930 Metadatable
* const* LookupEntry(const OUString
& i_rStreamName
,
931 const OUString
& i_rIdref
) const;
933 ClipboardXmlIdMap_t m_XmlIdMap
;
934 ClipboardXmlIdReverseMap_t m_XmlIdReverseMap
;
939 rmIter(ClipboardXmlIdMap_t
& i_rXmlIdMap
,
940 ClipboardXmlIdMap_t::iterator
const& i_rIter
,
941 OUString
const & i_rStream
, Metadatable
const& i_rObject
)
943 if (i_rIter
!= i_rXmlIdMap
.end())
945 Metadatable
*& rMeta
= isContentFile(i_rStream
)
946 ? i_rIter
->second
.first
: i_rIter
->second
.second
;
947 if (rMeta
== &i_rObject
)
951 if (!i_rIter
->second
.first
&& !i_rIter
->second
.second
)
953 i_rXmlIdMap
.erase(i_rIter
);
960 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupEntry(
961 const OUString
& i_rStreamName
,
962 const OUString
& i_rIdref
) const
964 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
966 throw lang::IllegalArgumentException(OUString(
967 "illegal XmlId"), nullptr, 0);
970 const ClipboardXmlIdMap_t::const_iterator
iter( m_XmlIdMap
.find(i_rIdref
) );
971 if (iter
!= m_XmlIdMap
.end())
973 OSL_ENSURE(iter
->second
.first
|| iter
->second
.second
,
974 "null entry in m_XmlIdMap");
975 return (isContentFile(i_rStreamName
))
976 ? &iter
->second
.first
977 : &iter
->second
.second
;
986 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupElement(
987 const OUString
& i_rStreamName
,
988 const OUString
& i_rIdref
) const
990 Metadatable
* const * ppEntry
= LookupEntry(i_rStreamName
, i_rIdref
);
991 return ppEntry
? *ppEntry
: nullptr;
995 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupXmlId(
996 const Metadatable
& i_rObject
,
997 OUString
& o_rStream
, OUString
& o_rIdref
,
998 MetadatableClipboard
const* &o_rpLink
) const
1000 const ClipboardXmlIdReverseMap_t::const_iterator
iter(
1001 m_XmlIdReverseMap
.find(&i_rObject
) );
1002 if (iter
!= m_XmlIdReverseMap
.end())
1004 OSL_ENSURE(!iter
->second
.m_Stream
.isEmpty(),
1005 "null stream in m_XmlIdReverseMap");
1006 OSL_ENSURE(!iter
->second
.m_XmlId
.isEmpty(),
1007 "null id in m_XmlIdReverseMap");
1008 o_rStream
= iter
->second
.m_Stream
;
1009 o_rIdref
= iter
->second
.m_XmlId
;
1010 o_rpLink
= iter
->second
.m_xLink
.get();
1020 XmlIdRegistryClipboard::XmlIdRegistry_Impl::TryInsertMetadatable(
1021 Metadatable
& i_rObject
,
1022 const OUString
& i_rStreamName
, const OUString
& i_rIdref
)
1024 bool bContent( isContentFile(i_rStreamName
) );
1025 OSL_ENSURE(isContentFile(i_rStreamName
) || isStylesFile(i_rStreamName
),
1028 Metadatable
** ppEntry
= const_cast<Metadatable
**>(LookupEntry(i_rStreamName
, i_rIdref
));
1037 *ppEntry
= &i_rObject
;
1043 m_XmlIdMap
.insert(::std::make_pair(i_rIdref
, bContent
1044 ? ::std::make_pair( &i_rObject
, static_cast<Metadatable
*>(nullptr) )
1045 : ::std::make_pair( static_cast<Metadatable
*>(nullptr), &i_rObject
)));
1051 // Clipboard XML ID Registry
1054 XmlIdRegistryClipboard::XmlIdRegistryClipboard()
1055 : m_pImpl( new XmlIdRegistry_Impl
)
1059 XmlIdRegistryClipboard::~XmlIdRegistryClipboard()
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 OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject
,
1085 OUStringToOString(i_rStreamName
, RTL_TEXTENCODING_UTF8
).getStr(),
1086 OUStringToOString(i_rIdref
, RTL_TEXTENCODING_UTF8
).getStr());
1088 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
1089 "TryRegisterMetadatable called for MetadatableUndo?");
1090 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
1091 "TryRegisterMetadatable called for MetadatableClipboard?");
1093 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
1095 throw lang::IllegalArgumentException(OUString(
1096 "illegal XmlId"), nullptr, 0);
1098 if (i_rObject
.IsInContent()
1099 ? !isContentFile(i_rStreamName
)
1100 : !isStylesFile(i_rStreamName
))
1102 throw lang::IllegalArgumentException(OUString(
1103 "illegal XmlId: wrong stream"), nullptr, 0);
1108 const MetadatableClipboard
* pLink
;
1109 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
, pLink
);
1110 if (old_path
== i_rStreamName
&& old_idref
== i_rIdref
)
1112 return (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
);
1114 ClipboardXmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
1115 if (!old_idref
.isEmpty())
1117 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
1118 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
1120 if (m_pImpl
->TryInsertMetadatable(i_rObject
, i_rStreamName
, i_rIdref
))
1122 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
1123 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] =
1124 RMapEntry(i_rStreamName
, i_rIdref
);
1134 XmlIdRegistryClipboard::RegisterMetadatableAndCreateID(Metadatable
& i_rObject
)
1136 OSL_TRACE("RegisterMetadatableAndCreateID: %p", &i_rObject
);
1138 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
1139 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
1140 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
1141 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
1143 bool isInContent( i_rObject
.IsInContent() );
1144 OUString
stream( OUString::createFromAscii(
1145 isInContent
? s_content
: s_styles
) );
1149 LookupXmlId(i_rObject
, old_path
, old_idref
);
1150 if (!old_idref
.isEmpty() &&
1151 (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
))
1157 const OUString
id( create_id(m_pImpl
->m_XmlIdMap
) );
1158 OSL_ENSURE(m_pImpl
->m_XmlIdMap
.find(id
) == m_pImpl
->m_XmlIdMap
.end(),
1159 "created id is in use");
1160 m_pImpl
->m_XmlIdMap
.insert(::std::make_pair(id
, isInContent
1161 ? ::std::make_pair( &i_rObject
, static_cast<Metadatable
*>(nullptr) )
1162 : ::std::make_pair( static_cast<Metadatable
*>(nullptr), &i_rObject
)));
1163 // N.B.: if i_rObject had a latent XmlId, then we implicitly delete the
1164 // MetadatableClipboard and thus the latent XmlId here
1165 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] = RMapEntry(stream
, id
);
1168 void XmlIdRegistryClipboard::UnregisterMetadatable(const Metadatable
& i_rObject
)
1170 OSL_TRACE("UnregisterMetadatable: %p", &i_rObject
);
1174 const MetadatableClipboard
* pLink
;
1175 if (!m_pImpl
->LookupXmlId(i_rObject
, path
, idref
, pLink
))
1177 OSL_FAIL("unregister: no xml id?");
1180 const ClipboardXmlIdMap_t::iterator
iter( m_pImpl
->m_XmlIdMap
.find(idref
) );
1181 if (iter
!= m_pImpl
->m_XmlIdMap
.end())
1183 rmIter(m_pImpl
->m_XmlIdMap
, iter
, path
, i_rObject
);
1188 void XmlIdRegistryClipboard::RemoveXmlIdForElement(const Metadatable
& i_rObject
)
1190 OSL_TRACE("RemoveXmlIdForElement: %p", &i_rObject
);
1192 ClipboardXmlIdReverseMap_t::iterator
iter(
1193 m_pImpl
->m_XmlIdReverseMap
.find(&i_rObject
) );
1194 if (iter
!= m_pImpl
->m_XmlIdReverseMap
.end())
1196 OSL_ENSURE(!iter
->second
.m_XmlId
.isEmpty(),
1197 "null id in m_XmlIdReverseMap");
1198 m_pImpl
->m_XmlIdReverseMap
.erase(iter
);
1203 std::shared_ptr
<MetadatableClipboard
>
1204 XmlIdRegistryClipboard::CreateClipboard(const bool i_isInContent
)
1206 OSL_TRACE("CreateClipboard:");
1208 return std::make_shared
<MetadatableClipboard
>(
1212 MetadatableClipboard
&
1213 XmlIdRegistryClipboard::RegisterCopyClipboard(Metadatable
& i_rCopy
,
1214 beans::StringPair
const & i_rReference
,
1215 const bool i_isLatent
)
1217 OSL_TRACE("RegisterCopyClipboard: %p -> "/*"%p"*/"(%s#%s) (%d)\n",
1218 /*&i_rSource,*/ &i_rCopy
,
1219 OUStringToOString(i_rReference
.First
,
1220 RTL_TEXTENCODING_UTF8
).getStr(),
1221 OUStringToOString(i_rReference
.Second
,
1222 RTL_TEXTENCODING_UTF8
).getStr(),
1225 // N.B.: when copying to the clipboard, the selection is always inserted
1226 // into the body, even if the source is a header/footer!
1227 // so we do not check whether the stream is right in this function
1229 if (!isValidXmlId(i_rReference
.First
, i_rReference
.Second
))
1231 throw lang::IllegalArgumentException(OUString(
1232 "illegal XmlId"), nullptr, 0);
1237 // this should succeed assuming clipboard has a single source document
1238 const bool success( m_pImpl
->TryInsertMetadatable(i_rCopy
,
1239 i_rReference
.First
, i_rReference
.Second
) );
1240 OSL_ENSURE(success
, "RegisterCopyClipboard: TryInsert failed?");
1243 const std::shared_ptr
<MetadatableClipboard
> xLink(
1244 CreateClipboard( isContentFile(i_rReference
.First
)) );
1245 m_pImpl
->m_XmlIdReverseMap
.insert(::std::make_pair(&i_rCopy
,
1246 RMapEntry(i_rReference
.First
, i_rReference
.Second
, xLink
)));
1247 return *xLink
.get();
1250 MetadatableClipboard
const*
1251 XmlIdRegistryClipboard::SourceLink(Metadatable
const& i_rObject
)
1255 const MetadatableClipboard
* pLink( nullptr );
1256 m_pImpl
->LookupXmlId(i_rObject
, path
, idref
, pLink
);
1261 // Metadatable mixin
1264 Metadatable::~Metadatable()
1266 RemoveMetadataReference();
1269 void Metadatable::RemoveMetadataReference()
1275 m_pReg
->UnregisterMetadatable( *this );
1276 m_pReg
->RemoveXmlIdForElement( *this );
1280 catch (const uno::Exception
&)
1282 OSL_FAIL("Metadatable::RemoveMetadataReference: exception");
1286 // css::rdf::XMetadatable:
1288 Metadatable::GetMetadataReference() const
1292 return m_pReg
->GetXmlIdForElement(*this);
1294 return beans::StringPair();
1297 void Metadatable::SetMetadataReference( const css::beans::StringPair
& i_rReference
)
1299 if (i_rReference
.Second
.isEmpty())
1301 RemoveMetadataReference();
1305 OUString
streamName( i_rReference
.First
);
1306 if (streamName
.isEmpty())
1308 // handle empty stream name as auto-detect.
1309 // necessary for importing flat file format.
1310 streamName
= OUString::createFromAscii(
1311 IsInContent() ? s_content
: s_styles
);
1313 XmlIdRegistry
& rReg( dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1314 if (rReg
.TryRegisterMetadatable(*this, streamName
, i_rReference
.Second
))
1320 throw lang::IllegalArgumentException(
1321 OUString("Metadatable::"
1322 "SetMetadataReference: argument is invalid"), /*this*/nullptr, 0);
1327 void Metadatable::EnsureMetadataReference()
1329 XmlIdRegistry
& rReg(
1330 m_pReg
? *m_pReg
: dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1331 rReg
.RegisterMetadatableAndCreateID( *this );
1335 const ::sfx2::IXmlIdRegistry
& GetRegistryConst(Metadatable
const& i_rObject
)
1337 return const_cast< Metadatable
& >( i_rObject
).GetRegistry();
1341 Metadatable::RegisterAsCopyOf(Metadatable
const & i_rSource
,
1342 const bool i_bCopyPrecedesSource
)
1344 OSL_ENSURE(typeid(*this) == typeid(i_rSource
)
1345 || typeid(i_rSource
) == typeid(MetadatableUndo
)
1346 || typeid(*this) == typeid(MetadatableUndo
)
1347 || typeid(i_rSource
) == typeid(MetadatableClipboard
)
1348 || typeid(*this) == typeid(MetadatableClipboard
),
1349 "RegisterAsCopyOf element with different class?");
1350 OSL_ENSURE(!this->m_pReg
, "RegisterAsCopyOf called on element with XmlId?");
1354 RemoveMetadataReference();
1359 if (i_rSource
.m_pReg
)
1361 XmlIdRegistry
& rReg(
1362 dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1363 if (i_rSource
.m_pReg
== &rReg
)
1365 OSL_ENSURE(!IsInClipboard(),
1366 "RegisterAsCopy: both in clipboard?");
1367 if (!IsInClipboard())
1369 XmlIdRegistryDocument
& rRegDoc(
1370 dynamic_cast<XmlIdRegistryDocument
&>( rReg
) );
1371 rRegDoc
.RegisterCopy(i_rSource
, *this,
1372 i_bCopyPrecedesSource
);
1373 this->m_pReg
= &rRegDoc
;
1377 // source is in different document
1378 XmlIdRegistryDocument
* pRegDoc(
1379 dynamic_cast<XmlIdRegistryDocument
*>(&rReg
) );
1380 XmlIdRegistryClipboard
* pRegClp(
1381 dynamic_cast<XmlIdRegistryClipboard
*>(&rReg
) );
1385 beans::StringPair
SourceRef(
1386 i_rSource
.m_pReg
->GetXmlIdForElement(i_rSource
) );
1387 bool isLatent( SourceRef
.Second
.isEmpty() );
1388 XmlIdRegistryDocument
* pSourceRegDoc(
1389 dynamic_cast<XmlIdRegistryDocument
*>(i_rSource
.m_pReg
) );
1390 OSL_ENSURE(pSourceRegDoc
, "RegisterAsCopyOf: 2 clipboards?");
1391 if (!pSourceRegDoc
) return;
1392 // this is a copy _to_ the clipboard
1395 pSourceRegDoc
->LookupXmlId(i_rSource
,
1396 SourceRef
.First
, SourceRef
.Second
);
1398 Metadatable
& rLink(
1399 pRegClp
->RegisterCopyClipboard(*this, SourceRef
, isLatent
));
1400 this->m_pReg
= pRegClp
;
1401 // register as copy in the non-clipboard registry
1402 pSourceRegDoc
->RegisterCopy(i_rSource
, rLink
,
1403 false); // i_bCopyPrecedesSource);
1404 rLink
.m_pReg
= pSourceRegDoc
;
1408 XmlIdRegistryClipboard
* pSourceRegClp(
1409 dynamic_cast<XmlIdRegistryClipboard
*>(i_rSource
.m_pReg
) );
1410 OSL_ENSURE(pSourceRegClp
,
1411 "RegisterAsCopyOf: 2 non-clipboards?");
1412 if (!pSourceRegClp
) return;
1413 const MetadatableClipboard
* pLink(
1414 pSourceRegClp
->SourceLink(i_rSource
) );
1415 // may happen if src got its id via UNO call
1417 // only register copy if clipboard content is from this SwDoc!
1418 if (pLink
&& (&GetRegistryConst(*pLink
) == pRegDoc
))
1420 // this is a copy _from_ the clipboard; check if the
1421 // element is still in the same stream
1422 // N.B.: we check the stream of pLink, not of i_rSource!
1423 bool srcInContent( pLink
->IsInContent() );
1424 bool tgtInContent( this->IsInContent() );
1425 if (srcInContent
== tgtInContent
)
1427 pRegDoc
->RegisterCopy(*pLink
, *this,
1428 true); // i_bCopyPrecedesSource);
1429 this->m_pReg
= pRegDoc
;
1431 // otherwise: stream change! do not register!
1436 OSL_FAIL("neither RegDoc nor RegClp cannot happen");
1440 catch (const uno::Exception
&)
1442 OSL_FAIL("Metadatable::RegisterAsCopyOf: exception");
1446 std::shared_ptr
<MetadatableUndo
> Metadatable::CreateUndo() const
1448 OSL_ENSURE(!IsInUndo(), "CreateUndo called for object in undo?");
1449 OSL_ENSURE(!IsInClipboard(), "CreateUndo called for object in clipboard?");
1452 if (!IsInClipboard() && !IsInUndo() && m_pReg
)
1454 XmlIdRegistryDocument
* pRegDoc(
1455 dynamic_cast<XmlIdRegistryDocument
*>( m_pReg
) );
1456 std::shared_ptr
<MetadatableUndo
> xUndo(
1457 sfx2::XmlIdRegistryDocument::CreateUndo(*this) );
1458 pRegDoc
->RegisterCopy(*this, *xUndo
, false);
1459 xUndo
->m_pReg
= pRegDoc
;
1463 catch (const uno::Exception
&)
1465 OSL_FAIL("Metadatable::CreateUndo: exception");
1467 return std::shared_ptr
<MetadatableUndo
>();
1470 std::shared_ptr
<MetadatableUndo
> Metadatable::CreateUndoForDelete()
1472 std::shared_ptr
<MetadatableUndo
> const xUndo( CreateUndo() );
1473 RemoveMetadataReference();
1477 void Metadatable::RestoreMetadata(
1478 std::shared_ptr
<MetadatableUndo
> const& i_pUndo
)
1480 OSL_ENSURE(!IsInUndo(), "RestoreMetadata called for object in undo?");
1481 OSL_ENSURE(!IsInClipboard(),
1482 "RestoreMetadata called for object in clipboard?");
1483 if (IsInClipboard() || IsInUndo()) return;
1484 RemoveMetadataReference();
1487 this->RegisterAsCopyOf(*i_pUndo
, true);
1492 Metadatable::JoinMetadatable(Metadatable
const & i_rOther
,
1493 const bool i_isMergedEmpty
, const bool i_isOtherEmpty
)
1495 OSL_ENSURE(!IsInUndo(), "JoinMetadatables called for object in undo?");
1496 OSL_ENSURE(!IsInClipboard(),
1497 "JoinMetadatables called for object in clipboard?");
1498 if (IsInClipboard() || IsInUndo()) return;
1500 if (i_isOtherEmpty
&& !i_isMergedEmpty
)
1502 // other is empty, thus loses => nothing to do
1505 if (i_isMergedEmpty
&& !i_isOtherEmpty
)
1507 this->RemoveMetadataReference();
1508 this->RegisterAsCopyOf(i_rOther
, true);
1512 if (!i_rOther
.m_pReg
)
1514 // other doesn't have xmlid, thus loses => nothing to do
1519 this->RegisterAsCopyOf(i_rOther
, true);
1520 // assumption: i_rOther will be deleted, so don't unregister it here
1525 XmlIdRegistryDocument
* pRegDoc(
1526 dynamic_cast<XmlIdRegistryDocument
*>( m_pReg
) );
1527 OSL_ENSURE(pRegDoc
, "JoinMetadatable: no pRegDoc?");
1530 pRegDoc
->JoinMetadatables(*this, i_rOther
);
1533 catch (const uno::Exception
&)
1535 OSL_FAIL("Metadatable::JoinMetadatable: exception");
1540 // XMetadatable mixin
1543 OUString SAL_CALL
MetadatableMixin::getStringValue()
1544 throw (css::uno::RuntimeException
, std::exception
)
1546 return getNamespace() + getLocalName();
1550 OUString SAL_CALL
MetadatableMixin::getLocalName()
1551 throw (css::uno::RuntimeException
, std::exception
)
1553 SolarMutexGuard aGuard
;
1554 beans::StringPair
mdref( getMetadataReference() );
1555 if (mdref
.Second
.isEmpty())
1557 ensureMetadataReference(); // N.B.: side effect!
1558 mdref
= getMetadataReference();
1561 buf
.append(mdref
.First
);
1563 buf
.append(mdref
.Second
);
1564 return buf
.makeStringAndClear();
1567 OUString SAL_CALL
MetadatableMixin::getNamespace()
1568 throw (css::uno::RuntimeException
, std::exception
)
1570 SolarMutexGuard aGuard
;
1571 const uno::Reference
< frame::XModel
> xModel( GetModel() );
1572 const uno::Reference
< rdf::XURI
> xDMA( xModel
, uno::UNO_QUERY_THROW
);
1573 return xDMA
->getStringValue();
1576 // css::rdf::XMetadatable:
1577 beans::StringPair SAL_CALL
1578 MetadatableMixin::getMetadataReference()
1579 throw (uno::RuntimeException
, std::exception
)
1581 SolarMutexGuard aGuard
;
1583 Metadatable
*const pObject( GetCoreObject() );
1586 throw uno::RuntimeException(
1588 "MetadatableMixin: cannot get core object; not inserted?"),
1591 return pObject
->GetMetadataReference();
1595 MetadatableMixin::setMetadataReference(
1596 const beans::StringPair
& i_rReference
)
1597 throw (uno::RuntimeException
, lang::IllegalArgumentException
, std::exception
)
1599 SolarMutexGuard aGuard
;
1601 Metadatable
*const pObject( GetCoreObject() );
1604 throw uno::RuntimeException(
1606 "MetadatableMixin: cannot get core object; not inserted?"),
1609 return pObject
->SetMetadataReference(i_rReference
);
1612 void SAL_CALL
MetadatableMixin::ensureMetadataReference()
1613 throw (uno::RuntimeException
, std::exception
)
1615 SolarMutexGuard aGuard
;
1617 Metadatable
*const pObject( GetCoreObject() );
1620 throw uno::RuntimeException(
1622 "MetadatableMixin: cannot get core object; not inserted?"),
1625 return pObject
->EnsureMetadataReference();
1630 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */