LibreOffice Module sfx2 (master) 1
Metadatable.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21#include <sal/log.hxx>
22
23#include <osl/diagnose.h>
24#include <sfx2/Metadatable.hxx>
26
27#include <utility>
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>
33
34#include <algorithm>
35#include <memory>
36#include <string_view>
37#include <unordered_map>
38#if OSL_DEBUG_LEVEL > 0
39#include <typeinfo>
40#endif
41
42
109using namespace ::com::sun::star;
110
112
113
114namespace sfx2 {
115
116constexpr OUStringLiteral s_content = u"content.xml";
117constexpr OUStringLiteral s_styles = u"styles.xml";
118
119static bool isContentFile(std::u16string_view i_rPath)
120{
121 return i_rPath == s_content;
122}
123
124static bool isStylesFile (std::u16string_view i_rPath)
125{
126 return i_rPath == s_styles;
127}
128
129
130// XML ID handling ---------------------------------------------------
131
144{
145
146public:
148
150 virtual css::uno::Reference< css::rdf::XMetadatable >
152 const css::beans::StringPair & i_rReference) const
153 override;
154
162 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) = 0;
163
178 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
179 OUString const& i_rStreamName, OUString const& i_rIdref)
180 = 0;
181
191 virtual void UnregisterMetadatable(Metadatable const&) = 0;
192
194 css::beans::StringPair
195 GetXmlIdForElement(Metadatable const&) const;
196
198 virtual void RemoveXmlIdForElement(Metadatable const&) = 0;
199
200protected:
201
202 virtual bool LookupXmlId(const Metadatable& i_xObject,
203 OUString & o_rStream, OUString & o_rIdref) const = 0;
204
205 virtual Metadatable* LookupElement(const OUString & i_rStreamName,
206 const OUString & i_rIdref) const = 0;
207};
208
209// XmlIdRegistryDocument ---------------------------------------------
210
211namespace {
212
214class XmlIdRegistryDocument : public XmlIdRegistry
215{
216
217public:
218 XmlIdRegistryDocument();
219
220 virtual ~XmlIdRegistryDocument() override;
221
222 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) override;
223
224 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
225 OUString const& i_rStreamName, OUString const& i_rIdref) override;
226
227 virtual void UnregisterMetadatable(Metadatable const&) override;
228
229 virtual void RemoveXmlIdForElement(Metadatable const&) override;
230
233 void RegisterCopy(Metadatable const& i_rSource, Metadatable & i_rCopy,
234 const bool i_bCopyPrecedesSource);
235
237 static std::shared_ptr<MetadatableUndo> CreateUndo(
238 Metadatable const& i_rObject);
239
241 void JoinMetadatables(Metadatable & i_rMerged, Metadatable const& i_rOther);
242
243 // unfortunately public, Metadatable::RegisterAsCopyOf needs this
244 virtual bool LookupXmlId(const Metadatable& i_xObject,
245 OUString & o_rStream, OUString & o_rIdref) const override;
246
247private:
248
249 virtual Metadatable* LookupElement(const OUString & i_rStreamName,
250 const OUString & i_rIdref) const override;
251
252 struct XmlIdRegistry_Impl;
253 ::std::unique_ptr<XmlIdRegistry_Impl> m_pImpl;
254};
255
256}
257
258// MetadatableUndo ---------------------------------------------------
259
261class MetadatableUndo : public Metadatable
262{
264 const bool m_isInContent;
265public:
266 explicit MetadatableUndo(const bool i_isInContent)
267 : m_isInContent(i_isInContent) { }
268 virtual ::sfx2::XmlIdRegistry& GetRegistry() override
269 {
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 ?");
273 return *m_pReg;
274 }
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; }
280};
281
282// MetadatableClipboard ----------------------------------------------
283
285class MetadatableClipboard : public Metadatable
286{
288 const bool m_isInContent;
289public:
290 explicit MetadatableClipboard(const bool i_isInContent)
291 : m_isInContent(i_isInContent) { }
292 virtual ::sfx2::XmlIdRegistry& GetRegistry() override
293 {
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 ?");
297 return *m_pReg;
298 }
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; }
305};
306
307// XmlIdRegistryClipboard --------------------------------------------
308
309namespace {
310
311class XmlIdRegistryClipboard : public XmlIdRegistry
312{
313
314public:
315 XmlIdRegistryClipboard();
316
317 virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) override;
318
319 virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
320 OUString const& i_rStreamName, OUString const& i_rIdref) override;
321
322 virtual void UnregisterMetadatable(Metadatable const&) override;
323
324 virtual void RemoveXmlIdForElement(Metadatable const&) override;
325
327 MetadatableClipboard & RegisterCopyClipboard(Metadatable & i_rCopy,
328 beans::StringPair const & i_rReference,
329 const bool i_isLatent);
330
332 MetadatableClipboard const* SourceLink(Metadatable const& i_rObject);
333
334private:
335 virtual bool LookupXmlId(const Metadatable& i_xObject,
336 OUString & o_rStream, OUString & o_rIdref) const override;
337
338 virtual Metadatable* LookupElement(const OUString & i_rStreamName,
339 const OUString & i_rIdref) const override;
340
342 static std::shared_ptr<MetadatableClipboard> CreateClipboard(
343 const bool i_isInContent);
344
345 struct XmlIdRegistry_Impl;
346 ::std::unique_ptr<XmlIdRegistry_Impl> m_pImpl;
347};
348
349}
350
351// XmlIdRegistry
352
353::sfx2::IXmlIdRegistry * createXmlIdRegistry(const bool i_DocIsClipboard)
354{
355 return i_DocIsClipboard
356 ? static_cast<XmlIdRegistry*>( new XmlIdRegistryClipboard )
357 : static_cast<XmlIdRegistry*>( new XmlIdRegistryDocument );
358}
359
361{
362}
363
364css::uno::Reference< css::rdf::XMetadatable >
366 const beans::StringPair & i_rReference) const
367{
368 Metadatable* pObject( LookupElement(i_rReference.First,
369 i_rReference.Second) );
370 return pObject ? pObject->MakeUnoObject() : nullptr;
371}
372
373beans::StringPair
374XmlIdRegistry::GetXmlIdForElement(const Metadatable& i_rObject) const
375{
376 OUString path;
377 OUString idref;
378 if (LookupXmlId(i_rObject, path, idref))
379 {
380 if (LookupElement(path, idref) == &i_rObject)
381 {
382 return beans::StringPair(path, idref);
383 }
384 }
385 return beans::StringPair();
386}
387
388
390template< typename T >
391static OUString create_id(const
392 std::unordered_map< OUString, T > & i_rXmlIdMap)
393{
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;
398 OUString id;
399
400 if (bHack)
401 {
402 static sal_Int64 nIdCounter = SAL_CONST_INT64(4000000000);
403 do
404 {
405 id = prefix + OUString::number(nIdCounter++);
406 iter = i_rXmlIdMap.find(id);
407 }
408 while (iter != i_rXmlIdMap.end());
409 }
410 else
411 {
412 do
413 {
415 std::numeric_limits<unsigned int>::max()));
416 id = prefix + OUString::number(n);
417 iter = i_rXmlIdMap.find(id);
418 }
419 while (iter != i_rXmlIdMap.end());
420 }
421 return id;
422}
423
424
425// Document XML ID Registry (_Impl)
426
428typedef ::std::vector< Metadatable* > XmlIdVector_t;
429
431typedef std::unordered_map< OUString,
432 ::std::pair< XmlIdVector_t, XmlIdVector_t > > XmlIdMap_t;
433
434namespace {
435
437template<typename T> struct PtrHash
438{
439 size_t operator() (T const * i_pT) const
440 {
441 return reinterpret_cast<size_t>(i_pT);
442 }
443};
444
445}
446
448typedef std::unordered_map< const Metadatable*,
449 ::std::pair< OUString, OUString>, PtrHash<Metadatable> >
451
453{
455
456 bool TryInsertMetadatable(Metadatable& i_xObject,
457 std::u16string_view i_rStream, const OUString & i_rIdref);
458
459 bool LookupXmlId(const Metadatable& i_xObject,
460 OUString & o_rStream, OUString & o_rIdref) const;
461
462 Metadatable* LookupElement(std::u16string_view i_rStreamName,
463 const OUString & i_rIdref) const;
464
466 std::u16string_view i_rStreamName,
467 const OUString & i_rIdref) const;
468
470 std::u16string_view i_rStreamName,
471 const OUString & i_rIdref)
472 {
473 return const_cast<XmlIdVector_t*>(
474 const_cast<const XmlIdRegistry_Impl*>(this)
475 ->LookupElementVector(i_rStreamName, i_rIdref));
476 }
477
480};
481
482
483static void
484rmIter(XmlIdMap_t & i_rXmlIdMap, XmlIdMap_t::iterator const& i_rIter,
485 std::u16string_view i_rStream, Metadatable const& i_rObject)
486{
487 if (i_rIter != i_rXmlIdMap.end())
488 {
489 XmlIdVector_t & rVector( isContentFile(i_rStream)
490 ? i_rIter->second.first : i_rIter->second.second );
491 rVector.erase(std::remove(rVector.begin(), rVector.end(), &const_cast<Metadatable&>(i_rObject)));
492 if (i_rIter->second.first.empty() && i_rIter->second.second.empty())
493 {
494 i_rXmlIdMap.erase(i_rIter);
495 }
496 }
497}
498
499
500const XmlIdVector_t *
502 std::u16string_view i_rStreamName,
503 const OUString & i_rIdref) const
504{
505 const XmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
506 if (iter != m_XmlIdMap.end())
507 {
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;
513 }
514 else
515 {
516 return nullptr;
517 }
518}
519
520Metadatable*
522 std::u16string_view i_rStreamName,
523 const OUString & i_rIdref) const
524{
525 if (!isValidXmlId(i_rStreamName, i_rIdref))
526 {
527 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
528 }
529
530 const XmlIdVector_t * pList( LookupElementVector(i_rStreamName, i_rIdref) );
531 if (pList)
532 {
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());
537 } ) ) ;
538 if (iter != pList->end())
539 {
540 return *iter;
541 }
542 }
543 return nullptr;
544}
545
546bool
548 const Metadatable& i_rObject,
549 OUString & o_rStream, OUString & o_rIdref) const
550{
551 const XmlIdReverseMap_t::const_iterator iter(
552 m_XmlIdReverseMap.find(&i_rObject) );
553 if (iter != m_XmlIdReverseMap.end())
554 {
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;
561 return true;
562 }
563 else
564 {
565 return false;
566 }
567}
568
569bool
571 Metadatable & i_rObject,
572 std::u16string_view i_rStreamName, const OUString & i_rIdref)
573{
574 const bool bContent( isContentFile(i_rStreamName) );
575 OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName),
576 "invalid stream");
577
578 XmlIdVector_t * pList( LookupElementVector(i_rStreamName, i_rIdref) );
579 if (pList)
580 {
581 if (pList->empty())
582 {
583 pList->push_back( &i_rObject );
584 return true;
585 }
586 else
587 {
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());
594 } ) )
595 {
596 pList->insert(pList->begin(), &i_rObject );
597 return true;
598 }
599 else
600 {
601 return false;
602 }
603 }
604 }
605 else
606 {
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 ) )));
610 return true;
611 }
612}
613
614
615// Document XML ID Registry
616
617
618XmlIdRegistryDocument::XmlIdRegistryDocument()
620{
621}
622
623static void
624removeLink(Metadatable* i_pObject)
625{
626 OSL_ENSURE(i_pObject, "null in list ???");
627 if (!i_pObject) return;
628 if (i_pObject->IsInClipboard())
629 {
631 dynamic_cast<MetadatableClipboard*>( i_pObject ) );
632 OSL_ENSURE(pLink, "IsInClipboard, but no MetadatableClipboard ?");
633 if (pLink)
634 {
635 pLink->OriginNoLongerInBusinessAnymore();
636 }
637 }
638}
639
640XmlIdRegistryDocument::~XmlIdRegistryDocument()
641{
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)
645 removeLink(aLink);
646 for (auto aLink : aXmlId.second.second)
647 removeLink(aLink);
648 }
649}
650
651bool
652XmlIdRegistryDocument::LookupXmlId(
653 const Metadatable& i_rObject,
654 OUString & o_rStream, OUString & o_rIdref) const
655{
656 return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref);
657}
658
659Metadatable*
660XmlIdRegistryDocument::LookupElement(
661 const OUString & i_rStreamName,
662 const OUString & i_rIdref) const
663{
664 return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
665}
666
667bool
668XmlIdRegistryDocument::TryRegisterMetadatable(Metadatable & i_rObject,
669 OUString const& i_rStreamName, OUString const& i_rIdref)
670{
671 SAL_INFO("sfx", "TryRegisterMetadatable: " << &i_rObject << " (" << i_rStreamName << "#" << i_rIdref << ")");
672
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?");
677
678 if (!isValidXmlId(i_rStreamName, i_rIdref))
679 {
680 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
681 }
682 if (i_rObject.IsInContent()
683 ? !isContentFile(i_rStreamName)
684 : !isStylesFile(i_rStreamName))
685 {
686 throw lang::IllegalArgumentException("illegal XmlId: wrong stream", nullptr, 0);
687 }
688
689 OUString old_path;
690 OUString old_idref;
691 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
692 if (old_path == i_rStreamName && old_idref == i_rIdref)
693 {
694 return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
695 }
696 XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
697 if (!old_idref.isEmpty())
698 {
699 old_id = m_pImpl->m_XmlIdMap.find(old_idref);
700 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
701 }
702 if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
703 {
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);
707 return true;
708 }
709 else
710 {
711 return false;
712 }
713}
714
715void
716XmlIdRegistryDocument::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
717{
718 SAL_INFO("sfx", "RegisterMetadatableAndCreateID: " << &i_rObject);
719
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?");
724
725 const bool isInContent( i_rObject.IsInContent() );
726 const OUString stream(
727 isInContent ? OUString(s_content) : OUString(s_styles) );
728 // check if we have a latent xmlid, and if yes, remove it
729 OUString old_path;
730 OUString old_idref;
731 m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
732
733 XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
734 if (!old_idref.isEmpty())
735 {
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)
739 {
740 return;
741 }
742 else
743 {
744 // remove latent xmlid
745 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
746 }
747 }
748
749 // create id
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);
757}
758
759void XmlIdRegistryDocument::UnregisterMetadatable(const Metadatable& i_rObject)
760{
761 SAL_INFO("sfx", "UnregisterMetadatable: " << &i_rObject);
762
763 OUString path;
764 OUString idref;
765 if (!m_pImpl->LookupXmlId(i_rObject, path, idref))
766 {
767 OSL_FAIL("unregister: no xml id?");
768 return;
769 }
770 const XmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
771 if (iter != m_pImpl->m_XmlIdMap.end())
772 {
773 rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
774 }
775}
776
777void XmlIdRegistryDocument::RemoveXmlIdForElement(const Metadatable& i_rObject)
778{
779 SAL_INFO("sfx", "RemoveXmlIdForElement: " << &i_rObject);
780
781 const XmlIdReverseMap_t::iterator iter(
782 m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
783 if (iter != m_pImpl->m_XmlIdReverseMap.end())
784 {
785 OSL_ENSURE(!iter->second.second.isEmpty(),
786 "null id in m_XmlIdReverseMap");
787 m_pImpl->m_XmlIdReverseMap.erase(iter);
788 }
789}
790
791
792void XmlIdRegistryDocument::RegisterCopy(Metadatable const& i_rSource,
793 Metadatable & i_rCopy, const bool i_bCopyPrecedesSource)
794{
795 SAL_INFO("sfx", "RegisterCopy: " << &i_rSource << " -> " << &i_rCopy << " (" << i_bCopyPrecedesSource << ")");
796
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?");
803
804 OUString path;
805 OUString idref;
806 if (!m_pImpl->LookupXmlId( i_rSource, path, idref ))
807 {
808 OSL_FAIL("no xml id?");
809 return;
810 }
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())
818 {
819 return;
820 }
821 if (i_bCopyPrecedesSource)
822 {
823 pList->insert( srcpos, &i_rCopy );
824 }
825 else
826 {
827 // for undo push_back does not work! must insert right after source
828 pList->insert( ++srcpos, &i_rCopy );
829 }
830 m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy,
831 ::std::make_pair(path, idref)));
832}
833
834std::shared_ptr<MetadatableUndo>
835XmlIdRegistryDocument::CreateUndo(Metadatable const& i_rObject)
836{
837 SAL_INFO("sfx", "CreateUndo: " << &i_rObject);
838
839 return std::make_shared<MetadatableUndo>(
840 i_rObject.IsInContent() );
841}
842
843/*
844i_rMerged is both a source and the target node of the merge
845i_rOther is the other source, and will be deleted after the merge
846
847dimensions: none|latent|actual empty|nonempty
848i_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
854latent|nonempty none|nonempty => 1
855latent|nonempty latent|nonempty => 1|2
856 *|nonempty actual|nonempty => 2
857actual|nonempty *|nonempty => 1
858actual|nonempty actual|nonempty => 1|2
859*/
860void
861XmlIdRegistryDocument::JoinMetadatables(
862 Metadatable & i_rMerged, Metadatable const & i_rOther)
863{
864 SAL_INFO("sfx", "JoinMetadatables: " << &i_rMerged << " <- " << &i_rOther);
865
866 bool mergedOwnsRef;
867 OUString path;
868 OUString idref;
869 if (m_pImpl->LookupXmlId(i_rMerged, path, idref))
870 {
871 mergedOwnsRef = (m_pImpl->LookupElement(path, idref) == &i_rMerged);
872 }
873 else
874 {
875 OSL_FAIL("JoinMetadatables: no xmlid?");
876 return;
877 }
878 if (!mergedOwnsRef)
879 {
880 i_rMerged.RemoveMetadataReference();
881 i_rMerged.RegisterAsCopyOf(i_rOther, true);
882 return;
883 }
884 // other cases: merged has actual ref and is nonempty,
885 // other has latent/actual ref and is nonempty: other loses => nothing to do
886}
887
888
889// Clipboard XML ID Registry (_Impl)
890
891namespace {
892
893struct RMapEntry
894{
895 RMapEntry() {}
896 RMapEntry(OUString i_aStream,
897 OUString i_aXmlId,
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))
901 {}
902 OUString m_Stream;
903 OUString m_XmlId;
904 // this would have been an auto_ptr, if only that would have compiled...
905 std::shared_ptr<MetadatableClipboard> m_xLink;
906};
907
908}
909
911typedef std::unordered_map< const Metadatable*,
912 struct RMapEntry,
913 PtrHash<Metadatable> >
915
917typedef std::unordered_map< OUString,
918 ::std::pair< Metadatable*, Metadatable* > >
920
922{
924
925 bool TryInsertMetadatable(Metadatable& i_xObject,
926 std::u16string_view i_rStream, const OUString & i_rIdref);
927
928 bool LookupXmlId(const Metadatable& i_xObject,
929 OUString & o_rStream, OUString & o_rIdref,
930 MetadatableClipboard const* &o_rpLink) const;
931
932 Metadatable* LookupElement(std::u16string_view i_rStreamName,
933 const OUString & i_rIdref) const;
934
935 Metadatable* const* LookupEntry(std::u16string_view i_rStreamName,
936 const OUString & i_rIdref) const;
937
940};
941
942
943static void
945 ClipboardXmlIdMap_t::iterator const& i_rIter,
946 std::u16string_view i_rStream, Metadatable const& i_rObject)
947{
948 if (i_rIter == i_rXmlIdMap.end())
949 return;
950
951 Metadatable *& rMeta = isContentFile(i_rStream)
952 ? i_rIter->second.first : i_rIter->second.second;
953 if (rMeta == &i_rObject)
954 {
955 rMeta = nullptr;
956 }
957 if (!i_rIter->second.first && !i_rIter->second.second)
958 {
959 i_rXmlIdMap.erase(i_rIter);
960 }
961}
962
963
964Metadatable* const*
966 std::u16string_view i_rStreamName,
967 const OUString & i_rIdref) const
968{
969 if (!isValidXmlId(i_rStreamName, i_rIdref))
970 {
971 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
972 }
973
974 const ClipboardXmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
975 if (iter != m_XmlIdMap.end())
976 {
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;
982 }
983 else
984 {
985 return nullptr;
986 }
987}
988
989Metadatable*
991 std::u16string_view i_rStreamName,
992 const OUString & i_rIdref) const
993{
994 Metadatable * const * ppEntry = LookupEntry(i_rStreamName, i_rIdref);
995 return ppEntry ? *ppEntry : nullptr;
996}
997
998bool
1000 const Metadatable& i_rObject,
1001 OUString & o_rStream, OUString & o_rIdref,
1002 MetadatableClipboard const* &o_rpLink) const
1003{
1004 const ClipboardXmlIdReverseMap_t::const_iterator iter(
1005 m_XmlIdReverseMap.find(&i_rObject) );
1006 if (iter != m_XmlIdReverseMap.end())
1007 {
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();
1015 return true;
1016 }
1017 else
1018 {
1019 return false;
1020 }
1021}
1022
1023bool
1025 Metadatable & i_rObject,
1026 std::u16string_view i_rStreamName, const OUString & i_rIdref)
1027{
1028 bool bContent( isContentFile(i_rStreamName) );
1029 OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName),
1030 "invalid stream");
1031
1032 Metadatable ** ppEntry = const_cast<Metadatable**>(LookupEntry(i_rStreamName, i_rIdref));
1033 if (ppEntry)
1034 {
1035 if (*ppEntry)
1036 {
1037 return false;
1038 }
1039 else
1040 {
1041 *ppEntry = &i_rObject;
1042 return true;
1043 }
1044 }
1045 else
1046 {
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 )));
1050 return true;
1051 }
1052}
1053
1054
1055// Clipboard XML ID Registry
1056
1057
1058XmlIdRegistryClipboard::XmlIdRegistryClipboard()
1060{
1061}
1062
1063bool
1064XmlIdRegistryClipboard::LookupXmlId(
1065 const Metadatable& i_rObject,
1066 OUString & o_rStream, OUString & o_rIdref) const
1067{
1069 return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref, pLink);
1070}
1071
1072Metadatable*
1073XmlIdRegistryClipboard::LookupElement(
1074 const OUString & i_rStreamName,
1075 const OUString & i_rIdref) const
1076{
1077 return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
1078}
1079
1080bool
1081XmlIdRegistryClipboard::TryRegisterMetadatable(Metadatable & i_rObject,
1082 OUString const& i_rStreamName, OUString const& i_rIdref)
1083{
1084 SAL_INFO("sfx", "TryRegisterMetadatable: " << &i_rObject << " (" << i_rStreamName << "#" << i_rIdref <<")");
1085
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?");
1090
1091 if (!isValidXmlId(i_rStreamName, i_rIdref))
1092 {
1093 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
1094 }
1095 if (i_rObject.IsInContent()
1096 ? !isContentFile(i_rStreamName)
1097 : !isStylesFile(i_rStreamName))
1098 {
1099 throw lang::IllegalArgumentException("illegal XmlId: wrong stream", nullptr, 0);
1100 }
1101
1102 OUString old_path;
1103 OUString old_idref;
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)
1107 {
1108 return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
1109 }
1110 ClipboardXmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
1111 if (!old_idref.isEmpty())
1112 {
1113 old_id = m_pImpl->m_XmlIdMap.find(old_idref);
1114 OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
1115 }
1116 if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
1117 {
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);
1121 return true;
1122 }
1123 else
1124 {
1125 return false;
1126 }
1127}
1128
1129void
1130XmlIdRegistryClipboard::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
1131{
1132 SAL_INFO("sfx", "RegisterMetadatableAndCreateID: " << &i_rObject);
1133
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?");
1138
1139 bool isInContent( i_rObject.IsInContent() );
1140 OUString stream(
1141 isInContent ? OUString(s_content) : OUString(s_styles) );
1142
1143 OUString old_path;
1144 OUString old_idref;
1145 LookupXmlId(i_rObject, old_path, old_idref);
1146 if (!old_idref.isEmpty() &&
1147 (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject))
1148 {
1149 return;
1150 }
1151
1152 // create id
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);
1162}
1163
1164void XmlIdRegistryClipboard::UnregisterMetadatable(const Metadatable& i_rObject)
1165{
1166 SAL_INFO("sfx", "UnregisterMetadatable: " << &i_rObject);
1167
1168 OUString path;
1169 OUString idref;
1170 const MetadatableClipboard * pLink;
1171 if (!m_pImpl->LookupXmlId(i_rObject, path, idref, pLink))
1172 {
1173 OSL_FAIL("unregister: no xml id?");
1174 return;
1175 }
1176 const ClipboardXmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
1177 if (iter != m_pImpl->m_XmlIdMap.end())
1178 {
1179 rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
1180 }
1181}
1182
1183
1184void XmlIdRegistryClipboard::RemoveXmlIdForElement(const Metadatable& i_rObject)
1185{
1186 SAL_INFO("sfx", "RemoveXmlIdForElement: " << &i_rObject);
1187
1188 ClipboardXmlIdReverseMap_t::iterator iter(
1189 m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
1190 if (iter != m_pImpl->m_XmlIdReverseMap.end())
1191 {
1192 OSL_ENSURE(!iter->second.m_XmlId.isEmpty(),
1193 "null id in m_XmlIdReverseMap");
1194 m_pImpl->m_XmlIdReverseMap.erase(iter);
1195 }
1196}
1197
1198
1199std::shared_ptr<MetadatableClipboard>
1200XmlIdRegistryClipboard::CreateClipboard(const bool i_isInContent)
1201{
1202 SAL_INFO("sfx", "CreateClipboard:");
1203
1204 return std::make_shared<MetadatableClipboard>(
1205 i_isInContent );
1206}
1207
1208MetadatableClipboard &
1209XmlIdRegistryClipboard::RegisterCopyClipboard(Metadatable & i_rCopy,
1210 beans::StringPair const & i_rReference,
1211 const bool i_isLatent)
1212{
1213 SAL_INFO("sfx", "RegisterCopyClipboard: " << &i_rCopy
1214 << " -> (" << i_rReference.First << "#" << i_rReference.Second << ") (" << i_isLatent << ")");
1215
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
1219
1220 if (!isValidXmlId(i_rReference.First, i_rReference.Second))
1221 {
1222 throw lang::IllegalArgumentException("illegal XmlId", nullptr, 0);
1223 }
1224
1225 if (!i_isLatent)
1226 {
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?");
1231 }
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)));
1236 return *xLink;
1237}
1238
1239MetadatableClipboard const*
1240XmlIdRegistryClipboard::SourceLink(Metadatable const& i_rObject)
1241{
1242 OUString path;
1243 OUString idref;
1244 const MetadatableClipboard * pLink( nullptr );
1245 m_pImpl->LookupXmlId(i_rObject, path, idref, pLink);
1246 return pLink;
1247}
1248
1249
1250// Metadatable mixin
1251
1252
1253Metadatable::~Metadatable()
1254{
1255 RemoveMetadataReference();
1256}
1257
1258void Metadatable::RemoveMetadataReference()
1259{
1260 try
1261 {
1262 if (m_pReg)
1263 {
1264 m_pReg->UnregisterMetadatable( *this );
1265 m_pReg->RemoveXmlIdForElement( *this );
1266 m_pReg = nullptr;
1267 }
1268 }
1269 catch (const uno::Exception &)
1270 {
1271 TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::RemoveMetadataReference");
1272 }
1273}
1274
1275// css::rdf::XMetadatable:
1276beans::StringPair
1277Metadatable::GetMetadataReference() const
1278{
1279 if (m_pReg)
1280 {
1281 return m_pReg->GetXmlIdForElement(*this);
1282 }
1283 return beans::StringPair();
1284}
1285
1286void Metadatable::SetMetadataReference( const css::beans::StringPair & i_rReference)
1287{
1288 if (i_rReference.Second.isEmpty())
1289 {
1290 RemoveMetadataReference();
1291 }
1292 else
1293 {
1294 OUString streamName( i_rReference.First );
1295 if (streamName.isEmpty())
1296 {
1297 // handle empty stream name as auto-detect.
1298 // necessary for importing flat file format.
1299 streamName = IsInContent() ? OUString(s_content) : OUString(s_styles);
1300 }
1301 XmlIdRegistry & rReg( dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1302 if (!rReg.TryRegisterMetadatable(*this, streamName, i_rReference.Second))
1303 {
1304 throw lang::IllegalArgumentException(
1305 "Metadatable::SetMetadataReference: argument is invalid", /*this*/nullptr, 0);
1306 }
1307
1308 m_pReg = &rReg;
1309 }
1310}
1311
1312void Metadatable::EnsureMetadataReference()
1313{
1314 XmlIdRegistry& rReg(
1315 m_pReg ? *m_pReg : dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1316 rReg.RegisterMetadatableAndCreateID( *this );
1317 m_pReg = &rReg;
1318}
1319
1320static const ::sfx2::IXmlIdRegistry& GetRegistryConst(Metadatable const& i_rObject)
1321{
1322 return const_cast< Metadatable& >( i_rObject ).GetRegistry();
1323}
1324
1325void
1326Metadatable::RegisterAsCopyOf(Metadatable const & i_rSource,
1327 const bool i_bCopyPrecedesSource)
1328{
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?");
1336
1337 if (m_pReg)
1338 {
1339 RemoveMetadataReference();
1340 }
1341
1342 try
1343 {
1344 if (i_rSource.m_pReg)
1345 {
1346 XmlIdRegistry & rReg(
1347 dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1348 if (i_rSource.m_pReg == &rReg)
1349 {
1350 OSL_ENSURE(!IsInClipboard(),
1351 "RegisterAsCopy: both in clipboard?");
1352 if (!IsInClipboard())
1353 {
1354 XmlIdRegistryDocument & rRegDoc(
1355 dynamic_cast<XmlIdRegistryDocument&>( rReg ) );
1356 rRegDoc.RegisterCopy(i_rSource, *this,
1357 i_bCopyPrecedesSource);
1358 m_pReg = &rRegDoc;
1359 }
1360 return;
1361 }
1362 // source is in different document
1363 XmlIdRegistryDocument * pRegDoc(
1364 dynamic_cast<XmlIdRegistryDocument *>(&rReg) );
1365 XmlIdRegistryClipboard * pRegClp(
1366 dynamic_cast<XmlIdRegistryClipboard*>(&rReg) );
1367
1368 if (pRegClp)
1369 {
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
1378 if (isLatent)
1379 {
1380 pSourceRegDoc->LookupXmlId(i_rSource,
1381 SourceRef.First, SourceRef.Second);
1382 }
1383 Metadatable & rLink(
1384 pRegClp->RegisterCopyClipboard(*this, SourceRef, isLatent));
1385 m_pReg = pRegClp;
1386 // register as copy in the non-clipboard registry
1387 pSourceRegDoc->RegisterCopy(i_rSource, rLink,
1388 false); // i_bCopyPrecedesSource);
1389 rLink.m_pReg = pSourceRegDoc;
1390 }
1391 else if (pRegDoc)
1392 {
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
1401 if (!pLink) return;
1402 // only register copy if clipboard content is from this SwDoc!
1403 if (&GetRegistryConst(*pLink) == pRegDoc)
1404 {
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)
1411 {
1412 pRegDoc->RegisterCopy(*pLink, *this,
1413 true); // i_bCopyPrecedesSource);
1414 m_pReg = pRegDoc;
1415 }
1416 // otherwise: stream change! do not register!
1417 }
1418 }
1419 else
1420 {
1421 OSL_FAIL("neither RegDoc nor RegClp cannot happen");
1422 }
1423 }
1424 }
1425 catch (const uno::Exception &)
1426 {
1427 TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::RegisterAsCopyOf");
1428 }
1429}
1430
1431std::shared_ptr<MetadatableUndo> Metadatable::CreateUndo() const
1432{
1433 OSL_ENSURE(!IsInUndo(), "CreateUndo called for object in undo?");
1434 OSL_ENSURE(!IsInClipboard(), "CreateUndo called for object in clipboard?");
1435 try
1436 {
1437 if (!IsInClipboard() && !IsInUndo() && m_pReg)
1438 {
1439 XmlIdRegistryDocument * pRegDoc(
1440 dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
1441 assert(pRegDoc);
1442 std::shared_ptr<MetadatableUndo> xUndo(
1443 sfx2::XmlIdRegistryDocument::CreateUndo(*this) );
1444 pRegDoc->RegisterCopy(*this, *xUndo, false);
1445 xUndo->m_pReg = pRegDoc;
1446 return xUndo;
1447 }
1448 }
1449 catch (const uno::Exception &)
1450 {
1451 TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::CreateUndo");
1452 }
1453 return std::shared_ptr<MetadatableUndo>();
1454}
1455
1456std::shared_ptr<MetadatableUndo> Metadatable::CreateUndoForDelete()
1457{
1458 std::shared_ptr<MetadatableUndo> const xUndo( CreateUndo() );
1459 RemoveMetadataReference();
1460 return xUndo;
1461}
1462
1463void Metadatable::RestoreMetadata(
1464 std::shared_ptr<MetadatableUndo> const& i_pUndo)
1465{
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();
1471 if (i_pUndo)
1472 {
1473 RegisterAsCopyOf(*i_pUndo, true);
1474 }
1475}
1476
1477void
1478Metadatable::JoinMetadatable(Metadatable const & i_rOther,
1479 const bool i_isMergedEmpty, const bool i_isOtherEmpty)
1480{
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;
1485
1486 if (i_isOtherEmpty && !i_isMergedEmpty)
1487 {
1488 // other is empty, thus loses => nothing to do
1489 return;
1490 }
1491 if (i_isMergedEmpty && !i_isOtherEmpty)
1492 {
1493 RemoveMetadataReference();
1494 RegisterAsCopyOf(i_rOther, true);
1495 return;
1496 }
1497
1498 if (!i_rOther.m_pReg)
1499 {
1500 // other doesn't have xmlid, thus loses => nothing to do
1501 return;
1502 }
1503 if (!m_pReg)
1504 {
1505 RegisterAsCopyOf(i_rOther, true);
1506 // assumption: i_rOther will be deleted, so don't unregister it here
1507 return;
1508 }
1509 try
1510 {
1511 XmlIdRegistryDocument * pRegDoc(
1512 dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
1513 OSL_ENSURE(pRegDoc, "JoinMetadatable: no pRegDoc?");
1514 if (pRegDoc)
1515 {
1516 pRegDoc->JoinMetadatables(*this, i_rOther);
1517 }
1518 }
1519 catch (const uno::Exception &)
1520 {
1521 TOOLS_WARN_EXCEPTION( "sfx.doc", "Metadatable::JoinMetadatable");
1522 }
1523}
1524
1525
1526// XMetadatable mixin
1527
1528// css::rdf::XNode:
1530{
1531 return getNamespace() + getLocalName();
1532}
1533
1534// css::rdf::XURI:
1536{
1537 SolarMutexGuard aGuard;
1538 beans::StringPair mdref( getMetadataReference() );
1539 if (mdref.Second.isEmpty())
1540 {
1541 ensureMetadataReference(); // N.B.: side effect!
1542 mdref = getMetadataReference();
1543 }
1544 return mdref.First + "#" + mdref.Second;
1545}
1546
1548{
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();
1553}
1554
1555// css::rdf::XMetadatable:
1556beans::StringPair SAL_CALL
1558{
1559 SolarMutexGuard aGuard;
1560
1561 Metadatable *const pObject( GetCoreObject() );
1562 if (!pObject)
1563 {
1564 throw uno::RuntimeException(
1565 "MetadatableMixin: cannot get core object; not inserted?",
1566 *this);
1567 }
1568 return pObject->GetMetadataReference();
1569}
1570
1571void SAL_CALL
1573 const beans::StringPair & i_rReference)
1574{
1575 SolarMutexGuard aGuard;
1576
1577 Metadatable *const pObject( GetCoreObject() );
1578 if (!pObject)
1579 {
1580 throw uno::RuntimeException(
1581 "MetadatableMixin: cannot get core object; not inserted?",
1582 *this);
1583 }
1584 return pObject->SetMetadataReference(i_rReference);
1585}
1586
1588{
1589 SolarMutexGuard aGuard;
1590
1591 Metadatable *const pObject( GetCoreObject() );
1592 if (!pObject)
1593 {
1594 throw uno::RuntimeException(
1595 "MetadatableMixin: cannot get core object; not inserted?",
1596 *this);
1597 }
1598 return pObject->EnsureMetadataReference();
1599}
1600
1601} // namespace sfx2
1602
1603/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OUString m_Stream
::std::unique_ptr< XmlIdRegistry_Impl > m_pImpl
std::shared_ptr< MetadatableClipboard > m_xLink
OUString m_XmlId
interface for getElementByMetadataReference; for use by sfx2::DocumentMetadataAccess
the horrible Clipboard Metadatable: inserted into lists to track position
virtual bool IsInClipboard() const override
virtual ::sfx2::XmlIdRegistry & GetRegistry() override
virtual css::uno::Reference< css::rdf::XMetadatable > MakeUnoObject() override
const bool m_isInContent
as determined by the stream of the source in original document
virtual bool IsInUndo() const override
virtual bool IsInContent() const override
MetadatableClipboard(const bool i_isInContent)
virtual OUString SAL_CALL getLocalName() override
virtual OUString SAL_CALL getNamespace() override
virtual void SAL_CALL ensureMetadataReference() override
virtual css::beans::StringPair SAL_CALL getMetadataReference() override
virtual void SAL_CALL setMetadataReference(const css::beans::StringPair &i_rReference) override
virtual OUString SAL_CALL getStringValue() override
the horrible Undo Metadatable: is inserted into lists to track position
virtual bool IsInClipboard() const override
virtual ::sfx2::XmlIdRegistry & GetRegistry() override
MetadatableUndo(const bool i_isInContent)
virtual bool IsInUndo() const override
const bool m_isInContent
as determined by the stream of the source in original document
virtual bool IsInContent() const override
virtual css::uno::Reference< css::rdf::XMetadatable > MakeUnoObject() override
handles registration of XMetadatable.
virtual bool TryRegisterMetadatable(Metadatable &i_xObject, OUString const &i_rStreamName, OUString const &i_rIdref)=0
try to register an ODF element at a given XML ID, or update its registration to a different XML ID.
virtual bool LookupXmlId(const Metadatable &i_xObject, OUString &o_rStream, OUString &o_rIdref) const =0
virtual css::uno::Reference< css::rdf::XMetadatable > GetElementByMetadataReference(const css::beans::StringPair &i_rReference) const override
get the ODF element with the given metadata reference.
virtual void RegisterMetadatableAndCreateID(Metadatable &i_xObject)=0
register an ODF element at a newly generated, unique metadata reference.
css::beans::StringPair GetXmlIdForElement(Metadatable const &) const
get the metadata reference for the given element.
virtual void RemoveXmlIdForElement(Metadatable const &)=0
remove the metadata reference for the given element.
virtual void UnregisterMetadatable(Metadatable const &)=0
unregister an ODF element.
virtual Metadatable * LookupElement(const OUString &i_rStreamName, const OUString &i_rIdref) const =0
#define TOOLS_WARN_EXCEPTION(area, stream)
Reference< XOutputStream > stream
float u
EmbeddedObjectRef * pObject
sal_Int64 n
SvBaseLink * pLink
Definition: lnkbase2.cxx:81
#define SAL_INFO(area, stream)
unsigned int uniform_uint_distribution(unsigned int a, unsigned int b)
static OUString create_id(const std::unordered_map< OUString, T > &i_rXmlIdMap)
generate unique xml:id
constexpr OUStringLiteral s_styles
constexpr OUStringLiteral s_content
::std::vector< Metadatable * > XmlIdVector_t
element list
static void rmIter(XmlIdMap_t &i_rXmlIdMap, XmlIdMap_t::iterator const &i_rIter, std::u16string_view i_rStream, Metadatable const &i_rObject)
std::unordered_map< OUString, ::std::pair< Metadatable *, Metadatable * > > ClipboardXmlIdMap_t
Idref -> (content.xml element, styles.xml element)
bool isValidXmlId(std::u16string_view i_rStreamName, std::u16string_view i_rIdref)
std::unordered_map< const Metadatable *, ::std::pair< OUString, OUString >, PtrHash< Metadatable > > XmlIdReverseMap_t
element -> (stream name, idref)
static void removeLink(Metadatable *i_pObject)
static bool isStylesFile(std::u16string_view i_rPath)
::sfx2::IXmlIdRegistry * createXmlIdRegistry(const bool i_DocIsClipboard)
create a sfx2::XmlIdRegistryDocument or a sfx2::XmlIdRegistryClipboard
static const ::sfx2::IXmlIdRegistry & GetRegistryConst(Metadatable const &i_rObject)
std::unordered_map< const Metadatable *, struct RMapEntry, PtrHash< Metadatable > > ClipboardXmlIdReverseMap_t
element -> (stream name, idref, source)
std::unordered_map< OUString, ::std::pair< XmlIdVector_t, XmlIdVector_t > > XmlIdMap_t
Idref -> (content.xml element list, styles.xml element list)
static bool isContentFile(std::u16string_view i_rPath)
Metadatable * LookupElement(std::u16string_view i_rStreamName, const OUString &i_rIdref) const
Metadatable *const * LookupEntry(std::u16string_view i_rStreamName, const OUString &i_rIdref) const
bool TryInsertMetadatable(Metadatable &i_xObject, std::u16string_view i_rStream, const OUString &i_rIdref)
bool LookupXmlId(const Metadatable &i_xObject, OUString &o_rStream, OUString &o_rIdref, MetadatableClipboard const *&o_rpLink) const
Metadatable * LookupElement(std::u16string_view i_rStreamName, const OUString &i_rIdref) const
bool TryInsertMetadatable(Metadatable &i_xObject, std::u16string_view i_rStream, const OUString &i_rIdref)
XmlIdVector_t * LookupElementVector(std::u16string_view i_rStreamName, const OUString &i_rIdref)
const XmlIdVector_t * LookupElementVector(std::u16string_view i_rStreamName, const OUString &i_rIdref) const
bool LookupXmlId(const Metadatable &i_xObject, OUString &o_rStream, OUString &o_rIdref) const
Reference< XModel > xModel