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> // solarmutex
27 #include <rtl/random.h>
29 #include <boost/bind.hpp>
32 #include <boost/unordered_map.hpp>
35 #if OSL_DEBUG_LEVEL > 0
42 There is an abstract base class <type>XmlIdRegistry</type>, with
43 2 subclasses <type>XmlIdRegistryDocument</type> for "normal" documents,
44 and <type>XmlIdRegistryClipboard</type> for clipboard documents.
45 These classes are responsible for managing XML IDs for all elements
46 of the model. Only the implementation of the <type>Metadatable</type>
47 base class needs to know the registries, so they are not in the header.
49 The handling of XML IDs differs between clipboard and non-clipboard
50 documents in several aspects. Most importantly, non-clipboard documents
51 can have several elements associated with one XML ID.
52 This is necessary because of the weird undo implementation:
53 deleting a text node moves the deleted node to the undo array, but
54 executing undo will then create a <em>copy</em> of that node in the
55 document array. These 2 nodes must have the same XML ID, because
56 we cannot know whether the user will do a redo next, or something else.
58 Because we need to have a mechanism for several objects per XML ID anyway,
59 we use that also to enable some usability features:
60 The document registry has a list of Metadatables per XML ID.
61 This list is sorted by priority, i.e., the first element has highest
62 priority. When inserting copies, care must be taken that they are inserted
63 at the right position: either before or after the source.
64 This is done by <method>Metadatable::RegisterAsCopyOf</method>.
65 When a text node is split, then both resulting text nodes are inserted
66 into the list. If the user then deletes one text node, the other one
68 Also, when a Metadatable is copied to the clipboard and then pasted,
69 the copy is inserted into the list. If the user then deletes the source,
70 the XML ID is not lost.
71 The goal is that it should be hard to lose an XML ID by accident, which
72 is especially important as long as we do not have an UI that displays them.
74 There are two subclasses of <type>Metadatable</type>:
75 <ul><li><type>MetadatableClipboard</type>: for copies in the clipboard</li>
76 <li><type>MetadatableUndo</type>: for undo, because a Metadatable
77 may be destroyed on delete and a new one created on undo.</li></ul>
78 These serve only to track the position in an XML ID list in a document
79 registry, so that future actions can insert objects at the right position.
80 Unfortunately, inserting dummy objects seems to be necessary:
81 <ul><li>it is not sufficent to just remember the saved id, because then
82 the relative priorities might change when executing the undo</li>
83 <li>it is not sufficient to record the position as an integer, because
84 if we delete a text node and then undo, the node will be copied(!),
85 and we will have one more node in the list.<li>
86 <li>it is not sufficient to record the pointer of the previous/next
87 Metadatable, because if we delete a text node, undo, and then
88 do something to clear the redo array, the original text node is
89 destroyed, and is replaced by the copy created by undo</li></ul>
91 If content from a non-clipboard document is copied into a clipboard
92 document, a dummy <type>MetadatableClipboard</type> is inserted into the
93 non-clipboard document registry in order to track the position of the
94 source element. When the clipboard content is pasted back into the source
95 document, this dummy object is used to associate the pasted element with
98 If a <type>Metadatable</type> is deleted or merged,
99 <method>Metadatable::CreateUndo</method> is called, and returns a
100 <type>MetadatableUndo<type> instance, which can be used to undo the action
101 by passing it to <method>Metadatable::RestoreMetadata</method>.
107 using namespace ::com::sun::star
;
109 using ::sfx2::isValidXmlId
;
114 static const char s_content
[] = "content.xml";
115 static const char s_styles
[] = "styles.xml";
116 static const char s_prefix
[] = "id"; // prefix for generated xml:id
118 static bool isContentFile(OUString
const & i_rPath
)
120 return i_rPath
== s_content
;
123 static bool isStylesFile (OUString
const & i_rPath
)
125 return i_rPath
== s_styles
;
129 //=============================================================================
130 // XML ID handling ---------------------------------------------------
132 /** handles registration of XMetadatable.
134 This class is responsible for guaranteeing that XMetadatable objects
135 always have XML IDs that are unique within a stream.
137 This is an abstract base class; see subclasses XmlIdRegistryDocument and
138 XmlIdRegistryClipboard.
140 @see SwDoc::GetXmlIdRegistry
141 @see SwDocShell::GetXmlIdRegistry
143 class XmlIdRegistry
: public sfx2::IXmlIdRegistry
149 virtual ~XmlIdRegistry();
151 /** get the ODF element with the given metadata reference. */
152 virtual ::com::sun::star::uno::Reference
<
153 ::com::sun::star::rdf::XMetadatable
> SAL_CALL
154 GetElementByMetadataReference(
155 const ::com::sun::star::beans::StringPair
& i_rReference
) const;
157 /** register an ODF element at a newly generated, unique metadata reference.
160 Find a fresh XML ID, and register it for the element.
161 The generated ID does not occur in any stream of the document.
164 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
) = 0;
166 /** try to register an ODF element at a given XML ID, or update its
167 registation to a different XML ID.
170 If the given new metadata reference is not already occupied in the
171 document, unregister the element at its old metadata reference if
172 it has one, and register the new metadata reference for the element.
173 Note that this method only ensures that XML IDs are unique per stream,
174 so using the same XML ID in both content.xml and styles.xml is allowed.
178 true iff the element has successfully been registered
180 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
181 OUString
const& i_rStreamName
, OUString
const& i_rIdref
)
184 /** unregister an ODF element.
187 Unregister the element at its metadata reference.
188 Does not remove the metadata reference from the element.
191 @see RemoveXmlIdForElement
193 virtual void UnregisterMetadatable(Metadatable
const&) = 0;
195 /** get the metadata reference for the given element. */
196 ::com::sun::star::beans::StringPair
197 GetXmlIdForElement(Metadatable
const&) const;
199 /** remove the metadata reference for the given element. */
200 virtual void RemoveXmlIdForElement(Metadatable
const&) = 0;
204 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
205 OUString
& o_rStream
, OUString
& o_rIdref
) const = 0;
207 virtual Metadatable
* LookupElement(const OUString
& i_rStreamName
,
208 const OUString
& i_rIdref
) const = 0;
211 // XmlIdRegistryDocument ---------------------------------------------
213 /** non-clipboard documents */
214 class XmlIdRegistryDocument
: public XmlIdRegistry
218 XmlIdRegistryDocument();
220 virtual ~XmlIdRegistryDocument();
222 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
);
224 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
225 OUString
const& i_rStreamName
, OUString
const& i_rIdref
);
227 virtual void UnregisterMetadatable(Metadatable
const&);
229 virtual void RemoveXmlIdForElement(Metadatable
const&);
231 /** register i_rCopy as a copy of i_rSource,
232 with precedence iff i_bCopyPrecedesSource is true */
233 void RegisterCopy(Metadatable
const& i_rSource
, Metadatable
& i_rCopy
,
234 const bool i_bCopyPrecedesSource
);
236 /** create a Undo Metadatable for i_rObject. */
237 ::boost::shared_ptr
<MetadatableUndo
> CreateUndo(
238 Metadatable
const& i_rObject
);
240 /** merge i_rMerged and i_rOther into i_rMerged. */
241 void JoinMetadatables(Metadatable
& i_rMerged
, Metadatable
const& i_rOther
);
243 // unfortunately public, Metadatable::RegisterAsCopyOf needs this
244 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
245 OUString
& o_rStream
, OUString
& o_rIdref
) const;
249 virtual Metadatable
* LookupElement(const OUString
& i_rStreamName
,
250 const OUString
& i_rIdref
) const;
252 struct XmlIdRegistry_Impl
;
253 ::std::auto_ptr
<XmlIdRegistry_Impl
> m_pImpl
;
256 // MetadatableUndo ---------------------------------------------------
258 /** the horrible Undo Metadatable: is inserted into lists to track position */
259 class MetadatableUndo
: public Metadatable
261 /// as determined by the stream of the source in original document
262 const bool m_isInContent
;
264 MetadatableUndo(const bool i_isInContent
)
265 : m_isInContent(i_isInContent
) { }
266 virtual ::sfx2::XmlIdRegistry
& GetRegistry()
268 // N.B. for Undo, m_pReg is initialized by registering this as copy in
269 // CreateUndo; it is never cleared
270 OSL_ENSURE(m_pReg
, "no m_pReg in MetadatableUndo ?");
273 virtual bool IsInClipboard() const { return false; }
274 virtual bool IsInUndo() const { return true; }
275 virtual bool IsInContent() const { return m_isInContent
; }
276 virtual ::com::sun::star::uno::Reference
<
277 ::com::sun::star::rdf::XMetadatable
> MakeUnoObject()
278 { OSL_FAIL("MetadatableUndo::MakeUnoObject"); throw; }
281 // MetadatableClipboard ----------------------------------------------
283 /** the horrible Clipboard Metadatable: inserted into lists to track position */
284 class MetadatableClipboard
: public Metadatable
286 /// as determined by the stream of the source in original document
287 const bool m_isInContent
;
289 MetadatableClipboard(const bool i_isInContent
)
290 : m_isInContent(i_isInContent
) { }
291 virtual ::sfx2::XmlIdRegistry
& GetRegistry()
293 // N.B. for Clipboard, m_pReg is initialized by registering this as copy in
294 // RegisterAsCopyOf; it is only cleared by OriginNoLongerInBusinessAnymore
295 OSL_ENSURE(m_pReg
, "no m_pReg in MetadatableClipboard ?");
298 virtual bool IsInClipboard() const { return true; }
299 virtual bool IsInUndo() const { return false; }
300 virtual bool IsInContent() const { return m_isInContent
; }
301 virtual ::com::sun::star::uno::Reference
<
302 ::com::sun::star::rdf::XMetadatable
> MakeUnoObject()
303 { OSL_FAIL("MetadatableClipboard::MakeUnoObject"); throw; }
304 void OriginNoLongerInBusinessAnymore() { m_pReg
= 0; }
307 // XmlIdRegistryClipboard --------------------------------------------
309 class XmlIdRegistryClipboard
: public XmlIdRegistry
313 XmlIdRegistryClipboard();
314 virtual ~XmlIdRegistryClipboard();
316 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
);
318 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
319 OUString
const& i_rStreamName
, OUString
const& i_rIdref
);
321 virtual void UnregisterMetadatable(Metadatable
const&);
323 virtual void RemoveXmlIdForElement(Metadatable
const&);
325 /** register i_rCopy as a copy of i_rSource */
326 MetadatableClipboard
& RegisterCopyClipboard(Metadatable
& i_rCopy
,
327 beans::StringPair
const & i_rReference
,
328 const bool i_isLatent
);
330 /** get the Metadatable that links i_rObject to its origin registry */
331 MetadatableClipboard
const* SourceLink(Metadatable
const& i_rObject
);
334 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
335 OUString
& o_rStream
, OUString
& o_rIdref
) const;
337 virtual Metadatable
* LookupElement(const OUString
& i_rStreamName
,
338 const OUString
& i_rIdref
) const;
340 /** create a Clipboard Metadatable for i_rObject. */
341 ::boost::shared_ptr
<MetadatableClipboard
> CreateClipboard(
342 const bool i_isInContent
);
344 struct XmlIdRegistry_Impl
;
345 ::std::auto_ptr
<XmlIdRegistry_Impl
> m_pImpl
;
349 //=============================================================================
352 ::sfx2::IXmlIdRegistry
* createXmlIdRegistry(const bool i_DocIsClipboard
)
354 return i_DocIsClipboard
355 ? static_cast<XmlIdRegistry
*>( new XmlIdRegistryClipboard
)
356 : static_cast<XmlIdRegistry
*>( new XmlIdRegistryDocument
);
359 XmlIdRegistry::XmlIdRegistry()
363 XmlIdRegistry::~XmlIdRegistry()
367 ::com::sun::star::uno::Reference
< ::com::sun::star::rdf::XMetadatable
> SAL_CALL
368 XmlIdRegistry::GetElementByMetadataReference(
369 const beans::StringPair
& i_rReference
) const
371 Metadatable
* pObject( LookupElement(i_rReference
.First
,
372 i_rReference
.Second
) );
373 return pObject
? pObject
->MakeUnoObject() : 0;
377 XmlIdRegistry::GetXmlIdForElement(const Metadatable
& i_rObject
) const
381 if (LookupXmlId(i_rObject
, path
, idref
))
383 if (LookupElement(path
, idref
) == &i_rObject
)
385 return beans::StringPair(path
, idref
);
388 return beans::StringPair();
392 /// generate unique xml:id
393 template< typename T
>
394 /*static*/ OUString
create_id(const
395 ::boost::unordered_map
< OUString
, T
, OUStringHash
> & i_rXmlIdMap
)
397 static rtlRandomPool
s_Pool( rtl_random_createPool() );
398 const OUString
prefix(s_prefix
);
399 typename ::boost::unordered_map
< OUString
, T
, OUStringHash
>
400 ::const_iterator iter
;
405 rtl_random_getBytes(s_Pool
, & n
, sizeof(n
));
406 id
= prefix
+ OUString::valueOf(static_cast<sal_Int32
>(abs(n
)));
407 iter
= i_rXmlIdMap
.find(id
);
409 while (iter
!= i_rXmlIdMap
.end());
413 //=============================================================================
414 // Document XML ID Registry (_Impl)
417 typedef ::std::list
< Metadatable
* > XmlIdList_t
;
419 /// Idref -> (content.xml element list, styles.xml element list)
420 typedef ::boost::unordered_map
< OUString
,
421 ::std::pair
< XmlIdList_t
, XmlIdList_t
>, OUStringHash
> XmlIdMap_t
;
423 /// pointer hash template
424 template<typename T
> struct PtrHash
426 size_t operator() (T
const * i_pT
) const
428 return reinterpret_cast<size_t>(i_pT
);
432 /// element -> (stream name, idref)
433 typedef ::boost::unordered_map
< const Metadatable
*,
434 ::std::pair
< OUString
, OUString
>, PtrHash
<Metadatable
> >
437 struct XmlIdRegistryDocument::XmlIdRegistry_Impl
440 : m_XmlIdMap(), m_XmlIdReverseMap() { }
442 bool TryInsertMetadatable(Metadatable
& i_xObject
,
443 const OUString
& i_rStream
, const OUString
& i_rIdref
);
445 bool LookupXmlId(const Metadatable
& i_xObject
,
446 OUString
& o_rStream
, OUString
& o_rIdref
) const;
448 Metadatable
* LookupElement(const OUString
& i_rStreamName
,
449 const OUString
& i_rIdref
) const;
451 const XmlIdList_t
* LookupElementList(
452 const OUString
& i_rStreamName
,
453 const OUString
& i_rIdref
) const;
455 XmlIdList_t
* LookupElementList(
456 const OUString
& i_rStreamName
,
457 const OUString
& i_rIdref
)
459 return const_cast<XmlIdList_t
*>(
460 const_cast<const XmlIdRegistry_Impl
*>(this)
461 ->LookupElementList(i_rStreamName
, i_rIdref
));
464 XmlIdMap_t m_XmlIdMap
;
465 XmlIdReverseMap_t m_XmlIdReverseMap
;
468 // -------------------------------------------------------------------
471 rmIter(XmlIdMap_t
& i_rXmlIdMap
, XmlIdMap_t::iterator
const& i_rIter
,
472 OUString
const & i_rStream
, Metadatable
const& i_rObject
)
474 if (i_rIter
!= i_rXmlIdMap
.end())
476 XmlIdList_t
& rList( isContentFile(i_rStream
)
477 ? i_rIter
->second
.first
: i_rIter
->second
.second
);
478 rList
.remove(&const_cast<Metadatable
&>(i_rObject
));
479 if (i_rIter
->second
.first
.empty() && i_rIter
->second
.second
.empty())
481 i_rXmlIdMap
.erase(i_rIter
);
486 // -------------------------------------------------------------------
489 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElementList(
490 const OUString
& i_rStreamName
,
491 const OUString
& i_rIdref
) const
493 const XmlIdMap_t::const_iterator
iter( m_XmlIdMap
.find(i_rIdref
) );
494 if (iter
!= m_XmlIdMap
.end())
496 OSL_ENSURE(!iter
->second
.first
.empty() || !iter
->second
.second
.empty(),
497 "null entry in m_XmlIdMap");
498 return (isContentFile(i_rStreamName
))
499 ? &iter
->second
.first
500 : &iter
->second
.second
;
509 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElement(
510 const OUString
& i_rStreamName
,
511 const OUString
& i_rIdref
) const
513 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
515 throw lang::IllegalArgumentException(OUString(
516 "illegal XmlId"), 0, 0);
519 const XmlIdList_t
* pList( LookupElementList(i_rStreamName
, i_rIdref
) );
522 const XmlIdList_t::const_iterator
iter(
523 ::std::find_if(pList
->begin(), pList
->end(),
525 ::std::logical_not
<bool>(),
527 ::std::logical_or
<bool>(),
528 ::boost::bind( &Metadatable::IsInUndo
, _1
),
529 ::boost::bind( &Metadatable::IsInClipboard
, _1
)
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(),
586 ::std::logical_not
<bool>(),
588 ::std::logical_or
<bool>(),
589 ::boost::bind( &Metadatable::IsInUndo
, _1
),
590 ::boost::bind( &Metadatable::IsInClipboard
, _1
)
593 pList
->push_front( &i_rObject
);
604 m_XmlIdMap
.insert(::std::make_pair(i_rIdref
, bContent
605 ? ::std::make_pair( XmlIdList_t( 1, &i_rObject
), XmlIdList_t() )
606 : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject
) )));
611 //=============================================================================
612 // Document XML ID Registry
615 XmlIdRegistryDocument::XmlIdRegistryDocument()
616 : m_pImpl( new XmlIdRegistry_Impl
)
621 removeLink(Metadatable
* i_pObject
)
623 OSL_ENSURE(i_pObject
, "null in list ???");
624 if (!i_pObject
) return;
625 if (i_pObject
->IsInClipboard())
627 MetadatableClipboard
* pLink(
628 dynamic_cast<MetadatableClipboard
*>( i_pObject
) );
629 OSL_ENSURE(pLink
, "IsInClipboard, but no MetadatableClipboard ?");
632 pLink
->OriginNoLongerInBusinessAnymore();
637 XmlIdRegistryDocument::~XmlIdRegistryDocument()
639 // notify all list elements that are actually in the clipboard
640 for (XmlIdMap_t::iterator
iter(m_pImpl
->m_XmlIdMap
.begin());
641 iter
!= m_pImpl
->m_XmlIdMap
.end(); ++iter
)
643 ::std::for_each(iter
->second
.first
.begin(), iter
->second
.first
.end(),
645 ::std::for_each(iter
->second
.second
.begin(), iter
->second
.second
.end(),
651 XmlIdRegistryDocument::LookupXmlId(
652 const Metadatable
& i_rObject
,
653 OUString
& o_rStream
, OUString
& o_rIdref
) const
655 return m_pImpl
->LookupXmlId(i_rObject
, o_rStream
, o_rIdref
);
659 XmlIdRegistryDocument::LookupElement(
660 const OUString
& i_rStreamName
,
661 const OUString
& i_rIdref
) const
663 return m_pImpl
->LookupElement(i_rStreamName
, i_rIdref
);
667 XmlIdRegistryDocument::TryRegisterMetadatable(Metadatable
& i_rObject
,
668 OUString
const& i_rStreamName
, OUString
const& i_rIdref
)
670 OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject
,
671 OUStringToOString(i_rStreamName
, RTL_TEXTENCODING_UTF8
).getStr(),
672 OUStringToOString(i_rIdref
, RTL_TEXTENCODING_UTF8
).getStr());
674 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
675 "TryRegisterMetadatable called for MetadatableUndo?");
676 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
677 "TryRegisterMetadatable called for MetadatableClipboard?");
679 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
681 throw lang::IllegalArgumentException(OUString(
682 "illegal XmlId"), 0, 0);
684 if (i_rObject
.IsInContent()
685 ? !isContentFile(i_rStreamName
)
686 : !isStylesFile(i_rStreamName
))
688 throw lang::IllegalArgumentException(OUString(
689 "illegal XmlId: wrong stream"), 0, 0);
694 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
);
695 if (old_path
== i_rStreamName
&& old_idref
== i_rIdref
)
697 return (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
);
699 XmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
700 if (!old_idref
.isEmpty())
702 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
703 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
705 if (m_pImpl
->TryInsertMetadatable(i_rObject
, i_rStreamName
, i_rIdref
))
707 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
708 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] =
709 ::std::make_pair(i_rStreamName
, i_rIdref
);
719 XmlIdRegistryDocument::RegisterMetadatableAndCreateID(Metadatable
& i_rObject
)
721 OSL_TRACE("RegisterMetadatableAndCreateID: %p", &i_rObject
);
723 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
724 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
725 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
726 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
728 const bool isInContent( i_rObject
.IsInContent() );
729 const OUString
stream( OUString::createFromAscii(
730 isInContent
? s_content
: s_styles
) );
731 // check if we have a latent xmlid, and if yes, remove it
734 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
);
736 XmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
737 if (!old_idref
.isEmpty())
739 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
740 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
741 if (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
)
747 // remove latent xmlid
748 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
753 const OUString
id( create_id(m_pImpl
->m_XmlIdMap
) );
754 OSL_ENSURE(m_pImpl
->m_XmlIdMap
.find(id
) == m_pImpl
->m_XmlIdMap
.end(),
755 "created id is in use");
756 m_pImpl
->m_XmlIdMap
.insert(::std::make_pair(id
, isInContent
757 ? ::std::make_pair( XmlIdList_t( 1, &i_rObject
), XmlIdList_t() )
758 : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject
) )));
759 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] = ::std::make_pair(stream
, id
);
762 void XmlIdRegistryDocument::UnregisterMetadatable(const Metadatable
& i_rObject
)
764 OSL_TRACE("UnregisterMetadatable: %p", &i_rObject
);
768 if (!m_pImpl
->LookupXmlId(i_rObject
, path
, idref
))
770 OSL_FAIL("unregister: no xml id?");
773 const XmlIdMap_t::iterator
iter( m_pImpl
->m_XmlIdMap
.find(idref
) );
774 if (iter
!= m_pImpl
->m_XmlIdMap
.end())
776 rmIter(m_pImpl
->m_XmlIdMap
, iter
, path
, i_rObject
);
780 void XmlIdRegistryDocument::RemoveXmlIdForElement(const Metadatable
& i_rObject
)
782 OSL_TRACE("RemoveXmlIdForElement: %p", &i_rObject
);
784 const XmlIdReverseMap_t::iterator
iter(
785 m_pImpl
->m_XmlIdReverseMap
.find(&i_rObject
) );
786 if (iter
!= m_pImpl
->m_XmlIdReverseMap
.end())
788 OSL_ENSURE(!iter
->second
.second
.isEmpty(),
789 "null id in m_XmlIdReverseMap");
790 m_pImpl
->m_XmlIdReverseMap
.erase(iter
);
794 // -------------------------------------------------------------------
796 void XmlIdRegistryDocument::RegisterCopy(Metadatable
const& i_rSource
,
797 Metadatable
& i_rCopy
, const bool i_bCopyPrecedesSource
)
799 OSL_TRACE("RegisterCopy: %p -> %p (%d)\n",
800 &i_rSource
, &i_rCopy
, i_bCopyPrecedesSource
);
802 // potential sources: clipboard, undo array, splitNode
803 // assumption: stream change can only happen via clipboard, and is handled
804 // by Metadatable::RegisterAsCopyOf
805 OSL_ENSURE(i_rSource
.IsInUndo() || i_rCopy
.IsInUndo() ||
806 (i_rSource
.IsInContent() == i_rCopy
.IsInContent()),
807 "RegisterCopy: not in same stream?");
811 if (!m_pImpl
->LookupXmlId( i_rSource
, path
, idref
))
813 OSL_FAIL("no xml id?");
816 XmlIdList_t
* pList ( m_pImpl
->LookupElementList(path
, idref
) );
817 OSL_ENSURE( ::std::find( pList
->begin(), pList
->end(), &i_rCopy
)
818 == pList
->end(), "copy already registered???");
819 XmlIdList_t::iterator
srcpos(
820 ::std::find( pList
->begin(), pList
->end(), &i_rSource
) );
821 OSL_ENSURE(srcpos
!= pList
->end(), "source not in list???");
822 if (srcpos
== pList
->end())
826 if (i_bCopyPrecedesSource
)
828 pList
->insert( srcpos
, &i_rCopy
);
832 // for undo push_back does not work! must insert right after source
833 pList
->insert( ++srcpos
, &i_rCopy
);
835 m_pImpl
->m_XmlIdReverseMap
.insert(::std::make_pair(&i_rCopy
,
836 ::std::make_pair(path
, idref
)));
839 ::boost::shared_ptr
<MetadatableUndo
>
840 XmlIdRegistryDocument::CreateUndo(Metadatable
const& i_rObject
)
842 OSL_TRACE("CreateUndo: %p", &i_rObject
);
844 return ::boost::shared_ptr
<MetadatableUndo
>(
845 new MetadatableUndo(i_rObject
.IsInContent()) );
849 i_rMerged is both a source and the target node of the merge
850 i_rOther is the other source, and will be deleted after the merge
852 dimensions: none|latent|actual empty|nonempty
853 i_rMerged(1) i_rOther(2) result
854 *|empty *|empty => 1|2 (arbitrary)
855 *|empty *|nonempty => 2
856 *|nonempty *|empty => 1
857 none|nonempty none|nonempty => none
858 none|nonempty latent|nonempty => 2
859 latent|nonempty none|nonempty => 1
860 latent|nonempty latent|nonempty => 1|2
861 *|nonempty actual|nonempty => 2
862 actual|nonempty *|nonempty => 1
863 actual|nonempty actual|nonempty => 1|2
866 XmlIdRegistryDocument::JoinMetadatables(
867 Metadatable
& i_rMerged
, Metadatable
const & i_rOther
)
869 OSL_TRACE("JoinMetadatables: %p <- %p", &i_rMerged
, &i_rOther
);
874 if (m_pImpl
->LookupXmlId(i_rMerged
, path
, idref
))
876 mergedOwnsRef
= (m_pImpl
->LookupElement(path
, idref
) == &i_rMerged
);
880 OSL_FAIL("JoinMetadatables: no xmlid?");
885 i_rMerged
.RemoveMetadataReference();
886 i_rMerged
.RegisterAsCopyOf(i_rOther
, true);
889 // other cases: merged has actual ref and is nonempty,
890 // other has latent/actual ref and is nonempty: other loses => nothing to do
894 //=============================================================================
895 // Clipboard XML ID Registry (_Impl)
899 RMapEntry() : m_pLink() { }
900 RMapEntry(OUString
const& i_rStream
,
901 OUString
const& i_rXmlId
,
902 ::boost::shared_ptr
<MetadatableClipboard
> const& i_pLink
903 = ::boost::shared_ptr
<MetadatableClipboard
>())
904 : m_Stream(i_rStream
), m_XmlId(i_rXmlId
), m_pLink(i_pLink
)
908 // this would have been an auto_ptr, if only that would have compiled...
909 ::boost::shared_ptr
<MetadatableClipboard
> m_pLink
;
912 /// element -> (stream name, idref, source)
913 typedef ::boost::unordered_map
< const Metadatable
*,
915 PtrHash
<Metadatable
> >
916 ClipboardXmlIdReverseMap_t
;
918 /// Idref -> (content.xml element, styles.xml element)
919 typedef ::boost::unordered_map
< OUString
,
920 ::std::pair
< Metadatable
*, Metadatable
* >, OUStringHash
>
923 struct XmlIdRegistryClipboard::XmlIdRegistry_Impl
926 : m_XmlIdMap(), m_XmlIdReverseMap() { }
928 bool TryInsertMetadatable(Metadatable
& i_xObject
,
929 const OUString
& i_rStream
, const OUString
& i_rIdref
);
931 bool LookupXmlId(const Metadatable
& i_xObject
,
932 OUString
& o_rStream
, OUString
& o_rIdref
,
933 MetadatableClipboard
const* &o_rpLink
) const;
935 Metadatable
* LookupElement(const OUString
& i_rStreamName
,
936 const OUString
& i_rIdref
) const;
938 Metadatable
* const* LookupEntry(const OUString
& i_rStreamName
,
939 const OUString
& i_rIdref
) const;
941 Metadatable
* * LookupEntry(const OUString
& i_rStreamName
,
942 const OUString
& i_rIdref
)
944 return const_cast<Metadatable
**>(
945 const_cast<const XmlIdRegistry_Impl
*>(this)
946 ->LookupEntry(i_rStreamName
, i_rIdref
));
949 ClipboardXmlIdMap_t m_XmlIdMap
;
950 ClipboardXmlIdReverseMap_t m_XmlIdReverseMap
;
953 // -------------------------------------------------------------------
956 rmIter(ClipboardXmlIdMap_t
& i_rXmlIdMap
,
957 ClipboardXmlIdMap_t::iterator
const& i_rIter
,
958 OUString
const & i_rStream
, Metadatable
const& i_rObject
)
960 if (i_rIter
!= i_rXmlIdMap
.end())
962 Metadatable
*& rMeta
= isContentFile(i_rStream
)
963 ? i_rIter
->second
.first
: i_rIter
->second
.second
;
964 if (rMeta
== &i_rObject
)
968 if (!i_rIter
->second
.first
&& !i_rIter
->second
.second
)
970 i_rXmlIdMap
.erase(i_rIter
);
975 // -------------------------------------------------------------------
978 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupEntry(
979 const OUString
& i_rStreamName
,
980 const OUString
& i_rIdref
) const
982 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
984 throw lang::IllegalArgumentException(OUString(
985 "illegal XmlId"), 0, 0);
988 const ClipboardXmlIdMap_t::const_iterator
iter( m_XmlIdMap
.find(i_rIdref
) );
989 if (iter
!= m_XmlIdMap
.end())
991 OSL_ENSURE(iter
->second
.first
|| iter
->second
.second
,
992 "null entry in m_XmlIdMap");
993 return (isContentFile(i_rStreamName
))
994 ? &iter
->second
.first
995 : &iter
->second
.second
;
1004 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupElement(
1005 const OUString
& i_rStreamName
,
1006 const OUString
& i_rIdref
) const
1008 Metadatable
* const * ppEntry
= LookupEntry(i_rStreamName
, i_rIdref
);
1009 return ppEntry
? *ppEntry
: 0;
1013 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupXmlId(
1014 const Metadatable
& i_rObject
,
1015 OUString
& o_rStream
, OUString
& o_rIdref
,
1016 MetadatableClipboard
const* &o_rpLink
) const
1018 const ClipboardXmlIdReverseMap_t::const_iterator
iter(
1019 m_XmlIdReverseMap
.find(&i_rObject
) );
1020 if (iter
!= m_XmlIdReverseMap
.end())
1022 OSL_ENSURE(!iter
->second
.m_Stream
.isEmpty(),
1023 "null stream in m_XmlIdReverseMap");
1024 OSL_ENSURE(!iter
->second
.m_XmlId
.isEmpty(),
1025 "null id in m_XmlIdReverseMap");
1026 o_rStream
= iter
->second
.m_Stream
;
1027 o_rIdref
= iter
->second
.m_XmlId
;
1028 o_rpLink
= iter
->second
.m_pLink
.get();
1038 XmlIdRegistryClipboard::XmlIdRegistry_Impl::TryInsertMetadatable(
1039 Metadatable
& i_rObject
,
1040 const OUString
& i_rStreamName
, const OUString
& i_rIdref
)
1042 bool bContent( isContentFile(i_rStreamName
) );
1043 OSL_ENSURE(isContentFile(i_rStreamName
) || isStylesFile(i_rStreamName
),
1046 Metadatable
** ppEntry
= LookupEntry(i_rStreamName
, i_rIdref
);
1055 *ppEntry
= &i_rObject
;
1061 m_XmlIdMap
.insert(::std::make_pair(i_rIdref
, bContent
1062 ? ::std::make_pair( &i_rObject
, static_cast<Metadatable
*>(0) )
1063 : ::std::make_pair( static_cast<Metadatable
*>(0), &i_rObject
)));
1068 //=============================================================================
1069 // Clipboard XML ID Registry
1072 XmlIdRegistryClipboard::XmlIdRegistryClipboard()
1073 : m_pImpl( new XmlIdRegistry_Impl
)
1077 XmlIdRegistryClipboard::~XmlIdRegistryClipboard()
1082 XmlIdRegistryClipboard::LookupXmlId(
1083 const Metadatable
& i_rObject
,
1084 OUString
& o_rStream
, OUString
& o_rIdref
) const
1086 const MetadatableClipboard
* pLink
;
1087 return m_pImpl
->LookupXmlId(i_rObject
, o_rStream
, o_rIdref
, pLink
);
1091 XmlIdRegistryClipboard::LookupElement(
1092 const OUString
& i_rStreamName
,
1093 const OUString
& i_rIdref
) const
1095 return m_pImpl
->LookupElement(i_rStreamName
, i_rIdref
);
1099 XmlIdRegistryClipboard::TryRegisterMetadatable(Metadatable
& i_rObject
,
1100 OUString
const& i_rStreamName
, OUString
const& i_rIdref
)
1102 OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject
,
1103 OUStringToOString(i_rStreamName
, RTL_TEXTENCODING_UTF8
).getStr(),
1104 OUStringToOString(i_rIdref
, RTL_TEXTENCODING_UTF8
).getStr());
1106 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
1107 "TryRegisterMetadatable called for MetadatableUndo?");
1108 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
1109 "TryRegisterMetadatable called for MetadatableClipboard?");
1111 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
1113 throw lang::IllegalArgumentException(OUString(
1114 "illegal XmlId"), 0, 0);
1116 if (i_rObject
.IsInContent()
1117 ? !isContentFile(i_rStreamName
)
1118 : !isStylesFile(i_rStreamName
))
1120 throw lang::IllegalArgumentException(OUString(
1121 "illegal XmlId: wrong stream"), 0, 0);
1126 const MetadatableClipboard
* pLink
;
1127 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
, pLink
);
1128 if (old_path
== i_rStreamName
&& old_idref
== i_rIdref
)
1130 return (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
);
1132 ClipboardXmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
1133 if (!old_idref
.isEmpty())
1135 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
1136 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
1138 if (m_pImpl
->TryInsertMetadatable(i_rObject
, i_rStreamName
, i_rIdref
))
1140 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
1141 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] =
1142 RMapEntry(i_rStreamName
, i_rIdref
);
1152 XmlIdRegistryClipboard::RegisterMetadatableAndCreateID(Metadatable
& i_rObject
)
1154 OSL_TRACE("RegisterMetadatableAndCreateID: %p", &i_rObject
);
1156 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
1157 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
1158 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
1159 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
1161 bool isInContent( i_rObject
.IsInContent() );
1162 OUString
stream( OUString::createFromAscii(
1163 isInContent
? s_content
: s_styles
) );
1167 LookupXmlId(i_rObject
, old_path
, old_idref
);
1168 if (!old_idref
.isEmpty() &&
1169 (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
))
1175 const OUString
id( create_id(m_pImpl
->m_XmlIdMap
) );
1176 OSL_ENSURE(m_pImpl
->m_XmlIdMap
.find(id
) == m_pImpl
->m_XmlIdMap
.end(),
1177 "created id is in use");
1178 m_pImpl
->m_XmlIdMap
.insert(::std::make_pair(id
, isInContent
1179 ? ::std::make_pair( &i_rObject
, static_cast<Metadatable
*>(0) )
1180 : ::std::make_pair( static_cast<Metadatable
*>(0), &i_rObject
)));
1181 // N.B.: if i_rObject had a latent XmlId, then we implicitly delete the
1182 // MetadatableClipboard and thus the latent XmlId here
1183 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] = RMapEntry(stream
, id
);
1186 void XmlIdRegistryClipboard::UnregisterMetadatable(const Metadatable
& i_rObject
)
1188 OSL_TRACE("UnregisterMetadatable: %p", &i_rObject
);
1192 const MetadatableClipboard
* pLink
;
1193 if (!m_pImpl
->LookupXmlId(i_rObject
, path
, idref
, pLink
))
1195 OSL_FAIL("unregister: no xml id?");
1198 const ClipboardXmlIdMap_t::iterator
iter( m_pImpl
->m_XmlIdMap
.find(idref
) );
1199 if (iter
!= m_pImpl
->m_XmlIdMap
.end())
1201 rmIter(m_pImpl
->m_XmlIdMap
, iter
, path
, i_rObject
);
1206 void XmlIdRegistryClipboard::RemoveXmlIdForElement(const Metadatable
& i_rObject
)
1208 OSL_TRACE("RemoveXmlIdForElement: %p", &i_rObject
);
1210 ClipboardXmlIdReverseMap_t::iterator
iter(
1211 m_pImpl
->m_XmlIdReverseMap
.find(&i_rObject
) );
1212 if (iter
!= m_pImpl
->m_XmlIdReverseMap
.end())
1214 OSL_ENSURE(!iter
->second
.m_XmlId
.isEmpty(),
1215 "null id in m_XmlIdReverseMap");
1216 m_pImpl
->m_XmlIdReverseMap
.erase(iter
);
1220 // -------------------------------------------------------------------
1222 ::boost::shared_ptr
<MetadatableClipboard
>
1223 XmlIdRegistryClipboard::CreateClipboard(const bool i_isInContent
)
1225 OSL_TRACE("CreateClipboard:");
1227 return ::boost::shared_ptr
<MetadatableClipboard
>(
1228 new MetadatableClipboard(i_isInContent
) );
1231 MetadatableClipboard
&
1232 XmlIdRegistryClipboard::RegisterCopyClipboard(Metadatable
& i_rCopy
,
1233 beans::StringPair
const & i_rReference
,
1234 const bool i_isLatent
)
1236 OSL_TRACE("RegisterCopyClipboard: %p -> "/*"%p"*/"(%s#%s) (%d)\n",
1237 /*&i_rSource,*/ &i_rCopy
,
1238 OUStringToOString(i_rReference
.First
,
1239 RTL_TEXTENCODING_UTF8
).getStr(),
1240 OUStringToOString(i_rReference
.Second
,
1241 RTL_TEXTENCODING_UTF8
).getStr(),
1244 // N.B.: when copying to the clipboard, the selection is always inserted
1245 // into the body, even if the source is a header/footer!
1246 // so we do not check whether the stream is right in this function
1248 if (!isValidXmlId(i_rReference
.First
, i_rReference
.Second
))
1250 throw lang::IllegalArgumentException(OUString(
1251 "illegal XmlId"), 0, 0);
1256 // this should succeed assuming clipboard has a single source document
1257 const bool success( m_pImpl
->TryInsertMetadatable(i_rCopy
,
1258 i_rReference
.First
, i_rReference
.Second
) );
1259 OSL_ENSURE(success
, "RegisterCopyClipboard: TryInsert failed?");
1262 const ::boost::shared_ptr
<MetadatableClipboard
> pLink(
1263 CreateClipboard( isContentFile(i_rReference
.First
)) );
1264 m_pImpl
->m_XmlIdReverseMap
.insert(::std::make_pair(&i_rCopy
,
1265 RMapEntry(i_rReference
.First
, i_rReference
.Second
, pLink
)));
1266 return *pLink
.get();
1269 MetadatableClipboard
const*
1270 XmlIdRegistryClipboard::SourceLink(Metadatable
const& i_rObject
)
1274 const MetadatableClipboard
* pLink( 0 );
1275 m_pImpl
->LookupXmlId(i_rObject
, path
, idref
, pLink
);
1280 //=============================================================================
1281 // Metadatable mixin
1284 Metadatable::~Metadatable()
1286 RemoveMetadataReference();
1289 void Metadatable::RemoveMetadataReference()
1295 m_pReg
->UnregisterMetadatable( *this );
1296 m_pReg
->RemoveXmlIdForElement( *this );
1300 catch (const uno::Exception
&)
1302 OSL_FAIL("Metadatable::RemoveMetadataReference: exception");
1306 // ::com::sun::star::rdf::XMetadatable:
1308 Metadatable::GetMetadataReference() const
1312 return m_pReg
->GetXmlIdForElement(*this);
1314 return beans::StringPair();
1318 Metadatable::SetMetadataReference(
1319 const ::com::sun::star::beans::StringPair
& i_rReference
)
1321 if (i_rReference
.Second
.isEmpty())
1323 RemoveMetadataReference();
1327 OUString
streamName( i_rReference
.First
);
1328 if (streamName
.isEmpty())
1330 // handle empty stream name as auto-detect.
1331 // necessary for importing flat file format.
1332 streamName
= OUString::createFromAscii(
1333 IsInContent() ? s_content
: s_styles
);
1335 XmlIdRegistry
& rReg( dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1336 if (rReg
.TryRegisterMetadatable(*this, streamName
, i_rReference
.Second
))
1342 throw lang::IllegalArgumentException(
1343 OUString("Metadatable::"
1344 "SetMetadataReference: argument is invalid"), /*this*/0, 0);
1349 void Metadatable::EnsureMetadataReference()
1351 XmlIdRegistry
& rReg(
1352 m_pReg
? *m_pReg
: dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1353 rReg
.RegisterMetadatableAndCreateID( *this );
1357 const ::sfx2::IXmlIdRegistry
& GetRegistryConst(Metadatable
const& i_rObject
)
1359 return const_cast< Metadatable
& >( i_rObject
).GetRegistry();
1363 Metadatable::RegisterAsCopyOf(Metadatable
const & i_rSource
,
1364 const bool i_bCopyPrecedesSource
)
1366 OSL_ENSURE(typeid(*this) == typeid(i_rSource
)
1367 || typeid(i_rSource
) == typeid(MetadatableUndo
)
1368 || typeid(*this) == typeid(MetadatableUndo
)
1369 || typeid(i_rSource
) == typeid(MetadatableClipboard
)
1370 || typeid(*this) == typeid(MetadatableClipboard
),
1371 "RegisterAsCopyOf element with different class?");
1372 OSL_ENSURE(!this->m_pReg
, "RegisterAsCopyOf called on element with XmlId?");
1376 RemoveMetadataReference();
1381 if (i_rSource
.m_pReg
)
1383 XmlIdRegistry
& rReg(
1384 dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1385 if (i_rSource
.m_pReg
== &rReg
)
1387 OSL_ENSURE(!IsInClipboard(),
1388 "RegisterAsCopy: both in clipboard?");
1389 if (!IsInClipboard())
1391 XmlIdRegistryDocument
& rRegDoc(
1392 dynamic_cast<XmlIdRegistryDocument
&>( rReg
) );
1393 rRegDoc
.RegisterCopy(i_rSource
, *this,
1394 i_bCopyPrecedesSource
);
1395 this->m_pReg
= &rRegDoc
;
1399 // source is in different document
1400 XmlIdRegistryDocument
* pRegDoc(
1401 dynamic_cast<XmlIdRegistryDocument
*>(&rReg
) );
1402 XmlIdRegistryClipboard
* pRegClp(
1403 dynamic_cast<XmlIdRegistryClipboard
*>(&rReg
) );
1407 beans::StringPair
SourceRef(
1408 i_rSource
.m_pReg
->GetXmlIdForElement(i_rSource
) );
1409 bool isLatent( SourceRef
.Second
.isEmpty() );
1410 XmlIdRegistryDocument
* pSourceRegDoc(
1411 dynamic_cast<XmlIdRegistryDocument
*>(i_rSource
.m_pReg
) );
1412 OSL_ENSURE(pSourceRegDoc
, "RegisterAsCopyOf: 2 clipboards?");
1413 if (!pSourceRegDoc
) return;
1414 // this is a copy _to_ the clipboard
1417 pSourceRegDoc
->LookupXmlId(i_rSource
,
1418 SourceRef
.First
, SourceRef
.Second
);
1420 Metadatable
& rLink(
1421 pRegClp
->RegisterCopyClipboard(*this, SourceRef
, isLatent
));
1422 this->m_pReg
= pRegClp
;
1423 // register as copy in the non-clipboard registry
1424 pSourceRegDoc
->RegisterCopy(i_rSource
, rLink
,
1425 false); // i_bCopyPrecedesSource);
1426 rLink
.m_pReg
= pSourceRegDoc
;
1430 XmlIdRegistryClipboard
* pSourceRegClp(
1431 dynamic_cast<XmlIdRegistryClipboard
*>(i_rSource
.m_pReg
) );
1432 OSL_ENSURE(pSourceRegClp
,
1433 "RegisterAsCopyOf: 2 non-clipboards?");
1434 if (!pSourceRegClp
) return;
1435 const MetadatableClipboard
* pLink(
1436 pSourceRegClp
->SourceLink(i_rSource
) );
1437 // may happen if src got its id via UNO call
1439 // only register copy if clipboard content is from this SwDoc!
1440 if (pLink
&& (&GetRegistryConst(*pLink
) == pRegDoc
))
1442 // this is a copy _from_ the clipboard; check if the
1443 // element is still in the same stream
1444 // N.B.: we check the stream of pLink, not of i_rSource!
1445 bool srcInContent( pLink
->IsInContent() );
1446 bool tgtInContent( this->IsInContent() );
1447 if (srcInContent
== tgtInContent
)
1449 pRegDoc
->RegisterCopy(*pLink
, *this,
1450 true); // i_bCopyPrecedesSource);
1451 this->m_pReg
= pRegDoc
;
1453 // otherwise: stream change! do not register!
1458 OSL_FAIL("neither RegDoc nor RegClp cannot happen");
1462 catch (const uno::Exception
&)
1464 OSL_FAIL("Metadatable::RegisterAsCopyOf: exception");
1468 ::boost::shared_ptr
<MetadatableUndo
> Metadatable::CreateUndo() const
1470 OSL_ENSURE(!IsInUndo(), "CreateUndo called for object in undo?");
1471 OSL_ENSURE(!IsInClipboard(), "CreateUndo called for object in clipboard?");
1474 if (!IsInClipboard() && !IsInUndo() && m_pReg
)
1476 XmlIdRegistryDocument
* pRegDoc(
1477 dynamic_cast<XmlIdRegistryDocument
*>( m_pReg
) );
1478 ::boost::shared_ptr
<MetadatableUndo
> pUndo(
1479 pRegDoc
->CreateUndo(*this) );
1480 pRegDoc
->RegisterCopy(*this, *pUndo
, false);
1481 pUndo
->m_pReg
= pRegDoc
;
1485 catch (const uno::Exception
&)
1487 OSL_FAIL("Metadatable::CreateUndo: exception");
1489 return ::boost::shared_ptr
<MetadatableUndo
>();
1492 ::boost::shared_ptr
<MetadatableUndo
> Metadatable::CreateUndoForDelete()
1494 ::boost::shared_ptr
<MetadatableUndo
> const pUndo( CreateUndo() );
1495 RemoveMetadataReference();
1499 void Metadatable::RestoreMetadata(
1500 ::boost::shared_ptr
<MetadatableUndo
> const& i_pUndo
)
1502 OSL_ENSURE(!IsInUndo(), "RestoreMetadata called for object in undo?");
1503 OSL_ENSURE(!IsInClipboard(),
1504 "RestoreMetadata called for object in clipboard?");
1505 if (IsInClipboard() || IsInUndo()) return;
1506 RemoveMetadataReference();
1509 this->RegisterAsCopyOf(*i_pUndo
, true);
1514 Metadatable::JoinMetadatable(Metadatable
const & i_rOther
,
1515 const bool i_isMergedEmpty
, const bool i_isOtherEmpty
)
1517 OSL_ENSURE(!IsInUndo(), "JoinMetadatables called for object in undo?");
1518 OSL_ENSURE(!IsInClipboard(),
1519 "JoinMetadatables called for object in clipboard?");
1520 if (IsInClipboard() || IsInUndo()) return;
1522 if (i_isOtherEmpty
&& !i_isMergedEmpty
)
1524 // other is empty, thus loses => nothing to do
1527 if (i_isMergedEmpty
&& !i_isOtherEmpty
)
1529 this->RemoveMetadataReference();
1530 this->RegisterAsCopyOf(i_rOther
, true);
1534 if (!i_rOther
.m_pReg
)
1536 // other doesn't have xmlid, thus loses => nothing to do
1541 this->RegisterAsCopyOf(i_rOther
, true);
1542 // assumption: i_rOther will be deleted, so don't unregister it here
1547 XmlIdRegistryDocument
* pRegDoc(
1548 dynamic_cast<XmlIdRegistryDocument
*>( m_pReg
) );
1549 OSL_ENSURE(pRegDoc
, "JoinMetadatable: no pRegDoc?");
1552 pRegDoc
->JoinMetadatables(*this, i_rOther
);
1555 catch (const uno::Exception
&)
1557 OSL_FAIL("Metadatable::JoinMetadatable: exception");
1562 //=============================================================================
1563 // XMetadatable mixin
1565 // ::com::sun::star::rdf::XNode:
1566 OUString SAL_CALL
MetadatableMixin::getStringValue()
1567 throw (::com::sun::star::uno::RuntimeException
)
1569 return getNamespace() + getLocalName();
1572 // ::com::sun::star::rdf::XURI:
1573 OUString SAL_CALL
MetadatableMixin::getLocalName()
1574 throw (::com::sun::star::uno::RuntimeException
)
1576 SolarMutexGuard aGuard
;
1577 beans::StringPair
mdref( getMetadataReference() );
1578 if (mdref
.Second
.isEmpty())
1580 ensureMetadataReference(); // N.B.: side effect!
1581 mdref
= getMetadataReference();
1584 buf
.append(mdref
.First
);
1585 buf
.append(static_cast<sal_Unicode
>('#'));
1586 buf
.append(mdref
.Second
);
1587 return buf
.makeStringAndClear();
1590 OUString SAL_CALL
MetadatableMixin::getNamespace()
1591 throw (::com::sun::star::uno::RuntimeException
)
1593 SolarMutexGuard aGuard
;
1594 const uno::Reference
< frame::XModel
> xModel( GetModel() );
1595 const uno::Reference
< rdf::XURI
> xDMA( xModel
, uno::UNO_QUERY_THROW
);
1596 return xDMA
->getStringValue();
1599 // ::com::sun::star::rdf::XMetadatable:
1600 beans::StringPair SAL_CALL
1601 MetadatableMixin::getMetadataReference()
1602 throw (uno::RuntimeException
)
1604 SolarMutexGuard aGuard
;
1606 Metadatable
*const pObject( GetCoreObject() );
1609 throw uno::RuntimeException(
1611 "MetadatableMixin: cannot get core object; not inserted?"),
1614 return pObject
->GetMetadataReference();
1618 MetadatableMixin::setMetadataReference(
1619 const beans::StringPair
& i_rReference
)
1620 throw (uno::RuntimeException
, lang::IllegalArgumentException
)
1622 SolarMutexGuard aGuard
;
1624 Metadatable
*const pObject( GetCoreObject() );
1627 throw uno::RuntimeException(
1629 "MetadatableMixin: cannot get core object; not inserted?"),
1632 return pObject
->SetMetadataReference(i_rReference
);
1635 void SAL_CALL
MetadatableMixin::ensureMetadataReference()
1636 throw (uno::RuntimeException
)
1638 SolarMutexGuard aGuard
;
1640 Metadatable
*const pObject( GetCoreObject() );
1643 throw uno::RuntimeException(
1645 "MetadatableMixin: cannot get core object; not inserted?"),
1648 return pObject
->EnsureMetadataReference();
1654 //=============================================================================
1656 #if OSL_DEBUG_LEVEL > 1
1660 static void dump(sfx2::XmlIdList_t
* pList
)
1662 __attribute__ ((unused
))
1665 static void dump(sfx2::XmlIdList_t
* pList
)
1667 fprintf(stderr
, "\nXmlIdList(%p): ", pList
);
1668 for (sfx2::XmlIdList_t::iterator i
= pList
->begin(); i
!= pList
->end(); ++i
)
1670 fprintf(stderr
, "%p ", *i
);
1672 fprintf(stderr
, "\n");
1677 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */