1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 #include <sfx2/Metadatable.hxx>
31 #include <sfx2/XmlIdRegistry.hxx>
33 #include <osl/mutex.hxx>
34 #include <vcl/svapp.hxx> // solarmutex
36 #include <rtl/random.h>
38 #include <boost/bind.hpp>
41 #include <boost/unordered_map.hpp>
44 #if OSL_DEBUG_LEVEL > 0
51 There is an abstract base class <type>XmlIdRegistry</type>, with
52 2 subclasses <type>XmlIdRegistryDocument</type> for "normal" documents,
53 and <type>XmlIdRegistryClipboard</type> for clipboard documents.
54 These classes are responsible for managing XML IDs for all elements
55 of the model. Only the implementation of the <type>Metadatable</type>
56 base class needs to know the registries, so they are not in the header.
58 The handling of XML IDs differs between clipboard and non-clipboard
59 documents in several aspects. Most importantly, non-clipboard documents
60 can have several elements associated with one XML ID.
61 This is necessary because of the weird undo implementation:
62 deleting a text node moves the deleted node to the undo array, but
63 executing undo will then create a <em>copy</em> of that node in the
64 document array. These 2 nodes must have the same XML ID, because
65 we cannot know whether the user will do a redo next, or something else.
67 Because we need to have a mechanism for several objects per XML ID anyway,
68 we use that also to enable some usability features:
69 The document registry has a list of Metadatables per XML ID.
70 This list is sorted by priority, i.e., the first element has highest
71 priority. When inserting copies, care must be taken that they are inserted
72 at the right position: either before or after the source.
73 This is done by <method>Metadatable::RegisterAsCopyOf</method>.
74 When a text node is split, then both resulting text nodes are inserted
75 into the list. If the user then deletes one text node, the other one
77 Also, when a Metadatable is copied to the clipboard and then pasted,
78 the copy is inserted into the list. If the user then deletes the source,
79 the XML ID is not lost.
80 The goal is that it should be hard to lose an XML ID by accident, which
81 is especially important as long as we do not have an UI that displays them.
83 There are two subclasses of <type>Metadatable</type>:
84 <ul><li><type>MetadatableClipboard</type>: for copies in the clipboard</li>
85 <li><type>MetadatableUndo</type>: for undo, because a Metadatable
86 may be destroyed on delete and a new one created on undo.</li></ul>
87 These serve only to track the position in an XML ID list in a document
88 registry, so that future actions can insert objects at the right position.
89 Unfortunately, inserting dummy objects seems to be necessary:
90 <ul><li>it is not sufficent to just remember the saved id, because then
91 the relative priorities might change when executing the undo</li>
92 <li>it is not sufficient to record the position as an integer, because
93 if we delete a text node and then undo, the node will be copied(!),
94 and we will have one more node in the list.<li>
95 <li>it is not sufficient to record the pointer of the previous/next
96 Metadatable, because if we delete a text node, undo, and then
97 do something to clear the redo array, the original text node is
98 destroyed, and is replaced by the copy created by undo</li></ul>
100 If content from a non-clipboard document is copied into a clipboard
101 document, a dummy <type>MetadatableClipboard</type> is inserted into the
102 non-clipboard document registry in order to track the position of the
103 source element. When the clipboard content is pasted back into the source
104 document, this dummy object is used to associate the pasted element with
107 If a <type>Metadatable</type> is deleted or merged,
108 <method>Metadatable::CreateUndo</method> is called, and returns a
109 <type>MetadatableUndo<type> instance, which can be used to undo the action
110 by passing it to <method>Metadatable::RestoreMetadata</method>.
116 using namespace ::com::sun::star
;
118 using ::sfx2::isValidXmlId
;
123 static const char s_content
[] = "content.xml";
124 static const char s_styles
[] = "styles.xml";
125 static const char s_prefix
[] = "id"; // prefix for generated xml:id
127 static bool isContentFile(::rtl::OUString
const & i_rPath
)
129 return i_rPath
== s_content
;
132 static bool isStylesFile (::rtl::OUString
const & i_rPath
)
134 return i_rPath
== s_styles
;
138 //=============================================================================
139 // XML ID handling ---------------------------------------------------
141 /** handles registration of XMetadatable.
143 This class is responsible for guaranteeing that XMetadatable objects
144 always have XML IDs that are unique within a stream.
146 This is an abstract base class; see subclasses XmlIdRegistryDocument and
147 XmlIdRegistryClipboard.
149 @see SwDoc::GetXmlIdRegistry
150 @see SwDocShell::GetXmlIdRegistry
152 class XmlIdRegistry
: public sfx2::IXmlIdRegistry
158 virtual ~XmlIdRegistry();
160 /** get the ODF element with the given metadata reference. */
161 virtual ::com::sun::star::uno::Reference
<
162 ::com::sun::star::rdf::XMetadatable
> SAL_CALL
163 GetElementByMetadataReference(
164 const ::com::sun::star::beans::StringPair
& i_rReference
) const;
166 /** register an ODF element at a newly generated, unique metadata reference.
169 Find a fresh XML ID, and register it for the element.
170 The generated ID does not occur in any stream of the document.
173 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
) = 0;
175 /** try to register an ODF element at a given XML ID, or update its
176 registation to a different XML ID.
179 If the given new metadata reference is not already occupied in the
180 document, unregister the element at its old metadata reference if
181 it has one, and register the new metadata reference for the element.
182 Note that this method only ensures that XML IDs are unique per stream,
183 so using the same XML ID in both content.xml and styles.xml is allowed.
187 true iff the element has successfully been registered
189 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
190 ::rtl::OUString
const& i_rStreamName
, ::rtl::OUString
const& i_rIdref
)
193 /** unregister an ODF element.
196 Unregister the element at its metadata reference.
197 Does not remove the metadata reference from the element.
200 @see RemoveXmlIdForElement
202 virtual void UnregisterMetadatable(Metadatable
const&) = 0;
204 /** get the metadata reference for the given element. */
205 ::com::sun::star::beans::StringPair
206 GetXmlIdForElement(Metadatable
const&) const;
208 /** remove the metadata reference for the given element. */
209 virtual void RemoveXmlIdForElement(Metadatable
const&) = 0;
213 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
214 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
) const = 0;
216 virtual Metadatable
* LookupElement(const ::rtl::OUString
& i_rStreamName
,
217 const ::rtl::OUString
& i_rIdref
) const = 0;
220 // XmlIdRegistryDocument ---------------------------------------------
222 /** non-clipboard documents */
223 class XmlIdRegistryDocument
: public XmlIdRegistry
227 XmlIdRegistryDocument();
229 virtual ~XmlIdRegistryDocument();
231 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
);
233 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
234 ::rtl::OUString
const& i_rStreamName
, ::rtl::OUString
const& i_rIdref
);
236 virtual void UnregisterMetadatable(Metadatable
const&);
238 virtual void RemoveXmlIdForElement(Metadatable
const&);
240 /** register i_rCopy as a copy of i_rSource,
241 with precedence iff i_bCopyPrecedesSource is true */
242 void RegisterCopy(Metadatable
const& i_rSource
, Metadatable
& i_rCopy
,
243 const bool i_bCopyPrecedesSource
);
245 /** create a Undo Metadatable for i_rObject. */
246 ::boost::shared_ptr
<MetadatableUndo
> CreateUndo(
247 Metadatable
const& i_rObject
);
249 /** merge i_rMerged and i_rOther into i_rMerged. */
250 void JoinMetadatables(Metadatable
& i_rMerged
, Metadatable
const& i_rOther
);
252 // unfortunately public, Metadatable::RegisterAsCopyOf needs this
253 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
254 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
) const;
258 virtual Metadatable
* LookupElement(const ::rtl::OUString
& i_rStreamName
,
259 const ::rtl::OUString
& i_rIdref
) const;
261 struct XmlIdRegistry_Impl
;
262 ::std::auto_ptr
<XmlIdRegistry_Impl
> m_pImpl
;
265 // MetadatableUndo ---------------------------------------------------
267 /** the horrible Undo Metadatable: is inserted into lists to track position */
268 class MetadatableUndo
: public Metadatable
270 /// as determined by the stream of the source in original document
271 const bool m_isInContent
;
273 MetadatableUndo(const bool i_isInContent
)
274 : m_isInContent(i_isInContent
) { }
275 virtual ::sfx2::XmlIdRegistry
& GetRegistry()
277 // N.B. for Undo, m_pReg is initialized by registering this as copy in
278 // CreateUndo; it is never cleared
279 OSL_ENSURE(m_pReg
, "no m_pReg in MetadatableUndo ?");
282 virtual bool IsInClipboard() const { return false; }
283 virtual bool IsInUndo() const { return true; }
284 virtual bool IsInContent() const { return m_isInContent
; }
285 virtual ::com::sun::star::uno::Reference
<
286 ::com::sun::star::rdf::XMetadatable
> MakeUnoObject()
287 { OSL_FAIL("MetadatableUndo::MakeUnoObject"); throw; }
290 // MetadatableClipboard ----------------------------------------------
292 /** the horrible Clipboard Metadatable: inserted into lists to track position */
293 class MetadatableClipboard
: public Metadatable
295 /// as determined by the stream of the source in original document
296 const bool m_isInContent
;
298 MetadatableClipboard(const bool i_isInContent
)
299 : m_isInContent(i_isInContent
) { }
300 virtual ::sfx2::XmlIdRegistry
& GetRegistry()
302 // N.B. for Clipboard, m_pReg is initialized by registering this as copy in
303 // RegisterAsCopyOf; it is only cleared by OriginNoLongerInBusinessAnymore
304 OSL_ENSURE(m_pReg
, "no m_pReg in MetadatableClipboard ?");
307 virtual bool IsInClipboard() const { return true; }
308 virtual bool IsInUndo() const { return false; }
309 virtual bool IsInContent() const { return m_isInContent
; }
310 virtual ::com::sun::star::uno::Reference
<
311 ::com::sun::star::rdf::XMetadatable
> MakeUnoObject()
312 { OSL_FAIL("MetadatableClipboard::MakeUnoObject"); throw; }
313 void OriginNoLongerInBusinessAnymore() { m_pReg
= 0; }
316 // XmlIdRegistryClipboard --------------------------------------------
318 class XmlIdRegistryClipboard
: public XmlIdRegistry
322 XmlIdRegistryClipboard();
323 virtual ~XmlIdRegistryClipboard();
325 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
);
327 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
328 ::rtl::OUString
const& i_rStreamName
, ::rtl::OUString
const& i_rIdref
);
330 virtual void UnregisterMetadatable(Metadatable
const&);
332 virtual void RemoveXmlIdForElement(Metadatable
const&);
334 /** register i_rCopy as a copy of i_rSource */
335 MetadatableClipboard
& RegisterCopyClipboard(Metadatable
& i_rCopy
,
336 beans::StringPair
const & i_rReference
,
337 const bool i_isLatent
);
339 /** get the Metadatable that links i_rObject to its origin registry */
340 MetadatableClipboard
const* SourceLink(Metadatable
const& i_rObject
);
343 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
344 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
) const;
346 virtual Metadatable
* LookupElement(const ::rtl::OUString
& i_rStreamName
,
347 const ::rtl::OUString
& i_rIdref
) const;
349 /** create a Clipboard Metadatable for i_rObject. */
350 ::boost::shared_ptr
<MetadatableClipboard
> CreateClipboard(
351 const bool i_isInContent
);
353 struct XmlIdRegistry_Impl
;
354 ::std::auto_ptr
<XmlIdRegistry_Impl
> m_pImpl
;
358 //=============================================================================
361 ::sfx2::IXmlIdRegistry
* createXmlIdRegistry(const bool i_DocIsClipboard
)
363 return i_DocIsClipboard
364 ? static_cast<XmlIdRegistry
*>( new XmlIdRegistryClipboard
)
365 : static_cast<XmlIdRegistry
*>( new XmlIdRegistryDocument
);
368 XmlIdRegistry::XmlIdRegistry()
372 XmlIdRegistry::~XmlIdRegistry()
376 ::com::sun::star::uno::Reference
< ::com::sun::star::rdf::XMetadatable
> SAL_CALL
377 XmlIdRegistry::GetElementByMetadataReference(
378 const beans::StringPair
& i_rReference
) const
380 Metadatable
* pObject( LookupElement(i_rReference
.First
,
381 i_rReference
.Second
) );
382 return pObject
? pObject
->MakeUnoObject() : 0;
386 XmlIdRegistry::GetXmlIdForElement(const Metadatable
& i_rObject
) const
388 ::rtl::OUString path
;
389 ::rtl::OUString idref
;
390 if (LookupXmlId(i_rObject
, path
, idref
))
392 if (LookupElement(path
, idref
) == &i_rObject
)
394 return beans::StringPair(path
, idref
);
397 return beans::StringPair();
401 /// generate unique xml:id
402 template< typename T
>
403 /*static*/ ::rtl::OUString
create_id(const
404 ::boost::unordered_map
< ::rtl::OUString
, T
, ::rtl::OUStringHash
> & i_rXmlIdMap
)
406 static rtlRandomPool
s_Pool( rtl_random_createPool() );
407 const ::rtl::OUString
prefix(s_prefix
);
408 typename ::boost::unordered_map
< ::rtl::OUString
, T
, ::rtl::OUStringHash
>
409 ::const_iterator iter
;
414 rtl_random_getBytes(s_Pool
, & n
, sizeof(n
));
415 id
= prefix
+ ::rtl::OUString::valueOf(static_cast<sal_Int32
>(abs(n
)));
416 iter
= i_rXmlIdMap
.find(id
);
418 while (iter
!= i_rXmlIdMap
.end());
422 //=============================================================================
423 // Document XML ID Registry (_Impl)
426 typedef ::std::list
< Metadatable
* > XmlIdList_t
;
428 /// Idref -> (content.xml element list, styles.xml element list)
429 typedef ::boost::unordered_map
< ::rtl::OUString
,
430 ::std::pair
< XmlIdList_t
, XmlIdList_t
>, ::rtl::OUStringHash
> XmlIdMap_t
;
432 /// pointer hash template
433 template<typename T
> struct PtrHash
435 size_t operator() (T
const * i_pT
) const
437 return reinterpret_cast<size_t>(i_pT
);
441 /// element -> (stream name, idref)
442 typedef ::boost::unordered_map
< const Metadatable
*,
443 ::std::pair
< ::rtl::OUString
, ::rtl::OUString
>, PtrHash
<Metadatable
> >
446 struct XmlIdRegistryDocument::XmlIdRegistry_Impl
449 : m_XmlIdMap(), m_XmlIdReverseMap() { }
451 bool TryInsertMetadatable(Metadatable
& i_xObject
,
452 const ::rtl::OUString
& i_rStream
, const ::rtl::OUString
& i_rIdref
);
454 bool LookupXmlId(const Metadatable
& i_xObject
,
455 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
) const;
457 Metadatable
* LookupElement(const ::rtl::OUString
& i_rStreamName
,
458 const ::rtl::OUString
& i_rIdref
) const;
460 const XmlIdList_t
* LookupElementList(
461 const ::rtl::OUString
& i_rStreamName
,
462 const ::rtl::OUString
& i_rIdref
) const;
464 XmlIdList_t
* LookupElementList(
465 const ::rtl::OUString
& i_rStreamName
,
466 const ::rtl::OUString
& i_rIdref
)
468 return const_cast<XmlIdList_t
*>(
469 const_cast<const XmlIdRegistry_Impl
*>(this)
470 ->LookupElementList(i_rStreamName
, i_rIdref
));
473 XmlIdMap_t m_XmlIdMap
;
474 XmlIdReverseMap_t m_XmlIdReverseMap
;
477 // -------------------------------------------------------------------
480 rmIter(XmlIdMap_t
& i_rXmlIdMap
, XmlIdMap_t::iterator
const& i_rIter
,
481 ::rtl::OUString
const & i_rStream
, Metadatable
const& i_rObject
)
483 if (i_rIter
!= i_rXmlIdMap
.end())
485 XmlIdList_t
& rList( isContentFile(i_rStream
)
486 ? i_rIter
->second
.first
: i_rIter
->second
.second
);
487 rList
.remove(&const_cast<Metadatable
&>(i_rObject
));
488 if (i_rIter
->second
.first
.empty() && i_rIter
->second
.second
.empty())
490 i_rXmlIdMap
.erase(i_rIter
);
495 // -------------------------------------------------------------------
498 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElementList(
499 const ::rtl::OUString
& i_rStreamName
,
500 const ::rtl::OUString
& i_rIdref
) const
502 const XmlIdMap_t::const_iterator
iter( m_XmlIdMap
.find(i_rIdref
) );
503 if (iter
!= m_XmlIdMap
.end())
505 OSL_ENSURE(!iter
->second
.first
.empty() || !iter
->second
.second
.empty(),
506 "null entry in m_XmlIdMap");
507 return (isContentFile(i_rStreamName
))
508 ? &iter
->second
.first
509 : &iter
->second
.second
;
518 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElement(
519 const ::rtl::OUString
& i_rStreamName
,
520 const ::rtl::OUString
& i_rIdref
) const
522 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
524 throw lang::IllegalArgumentException(::rtl::OUString(
525 "illegal XmlId"), 0, 0);
528 const XmlIdList_t
* pList( LookupElementList(i_rStreamName
, i_rIdref
) );
531 const XmlIdList_t::const_iterator
iter(
532 ::std::find_if(pList
->begin(), pList
->end(),
534 ::std::logical_not
<bool>(),
536 ::std::logical_or
<bool>(),
537 ::boost::bind( &Metadatable::IsInUndo
, _1
),
538 ::boost::bind( &Metadatable::IsInClipboard
, _1
)
540 if (iter
!= pList
->end())
549 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupXmlId(
550 const Metadatable
& i_rObject
,
551 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
) const
553 const XmlIdReverseMap_t::const_iterator
iter(
554 m_XmlIdReverseMap
.find(&i_rObject
) );
555 if (iter
!= m_XmlIdReverseMap
.end())
557 OSL_ENSURE(!iter
->second
.first
.isEmpty(),
558 "null stream in m_XmlIdReverseMap");
559 OSL_ENSURE(!iter
->second
.second
.isEmpty(),
560 "null id in m_XmlIdReverseMap");
561 o_rStream
= iter
->second
.first
;
562 o_rIdref
= iter
->second
.second
;
572 XmlIdRegistryDocument::XmlIdRegistry_Impl::TryInsertMetadatable(
573 Metadatable
& i_rObject
,
574 const ::rtl::OUString
& i_rStreamName
, const ::rtl::OUString
& i_rIdref
)
576 const bool bContent( isContentFile(i_rStreamName
) );
577 OSL_ENSURE(isContentFile(i_rStreamName
) || isStylesFile(i_rStreamName
),
580 XmlIdList_t
* pList( LookupElementList(i_rStreamName
, i_rIdref
) );
585 pList
->push_back( &i_rObject
);
590 // this is only called from TryRegister now, so check
591 // if all elements in the list are deleted (in undo) or
592 // placeholders, then "steal" the id from them
593 if ( pList
->end() == ::std::find_if(pList
->begin(), pList
->end(),
595 ::std::logical_not
<bool>(),
597 ::std::logical_or
<bool>(),
598 ::boost::bind( &Metadatable::IsInUndo
, _1
),
599 ::boost::bind( &Metadatable::IsInClipboard
, _1
)
602 pList
->push_front( &i_rObject
);
613 m_XmlIdMap
.insert(::std::make_pair(i_rIdref
, bContent
614 ? ::std::make_pair( XmlIdList_t( 1, &i_rObject
), XmlIdList_t() )
615 : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject
) )));
620 //=============================================================================
621 // Document XML ID Registry
624 XmlIdRegistryDocument::XmlIdRegistryDocument()
625 : m_pImpl( new XmlIdRegistry_Impl
)
630 removeLink(Metadatable
* i_pObject
)
632 OSL_ENSURE(i_pObject
, "null in list ???");
633 if (!i_pObject
) return;
634 if (i_pObject
->IsInClipboard())
636 MetadatableClipboard
* pLink(
637 dynamic_cast<MetadatableClipboard
*>( i_pObject
) );
638 OSL_ENSURE(pLink
, "IsInClipboard, but no MetadatableClipboard ?");
641 pLink
->OriginNoLongerInBusinessAnymore();
646 XmlIdRegistryDocument::~XmlIdRegistryDocument()
648 // notify all list elements that are actually in the clipboard
649 for (XmlIdMap_t::iterator
iter(m_pImpl
->m_XmlIdMap
.begin());
650 iter
!= m_pImpl
->m_XmlIdMap
.end(); ++iter
)
652 ::std::for_each(iter
->second
.first
.begin(), iter
->second
.first
.end(),
654 ::std::for_each(iter
->second
.second
.begin(), iter
->second
.second
.end(),
660 XmlIdRegistryDocument::LookupXmlId(
661 const Metadatable
& i_rObject
,
662 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
) const
664 return m_pImpl
->LookupXmlId(i_rObject
, o_rStream
, o_rIdref
);
668 XmlIdRegistryDocument::LookupElement(
669 const ::rtl::OUString
& i_rStreamName
,
670 const ::rtl::OUString
& i_rIdref
) const
672 return m_pImpl
->LookupElement(i_rStreamName
, i_rIdref
);
676 XmlIdRegistryDocument::TryRegisterMetadatable(Metadatable
& i_rObject
,
677 ::rtl::OUString
const& i_rStreamName
, ::rtl::OUString
const& i_rIdref
)
679 OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject
,
680 ::rtl::OUStringToOString(i_rStreamName
, RTL_TEXTENCODING_UTF8
).getStr(),
681 ::rtl::OUStringToOString(i_rIdref
, RTL_TEXTENCODING_UTF8
).getStr());
683 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
684 "TryRegisterMetadatable called for MetadatableUndo?");
685 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
686 "TryRegisterMetadatable called for MetadatableClipboard?");
688 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
690 throw lang::IllegalArgumentException(::rtl::OUString(
691 "illegal XmlId"), 0, 0);
693 if (i_rObject
.IsInContent()
694 ? !isContentFile(i_rStreamName
)
695 : !isStylesFile(i_rStreamName
))
697 throw lang::IllegalArgumentException(::rtl::OUString(
698 "illegal XmlId: wrong stream"), 0, 0);
701 ::rtl::OUString old_path
;
702 ::rtl::OUString old_idref
;
703 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
);
704 if (old_path
== i_rStreamName
&& old_idref
== i_rIdref
)
706 return (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
);
708 XmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
709 if (!old_idref
.isEmpty())
711 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
712 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
714 if (m_pImpl
->TryInsertMetadatable(i_rObject
, i_rStreamName
, i_rIdref
))
716 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
717 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] =
718 ::std::make_pair(i_rStreamName
, i_rIdref
);
728 XmlIdRegistryDocument::RegisterMetadatableAndCreateID(Metadatable
& i_rObject
)
730 OSL_TRACE("RegisterMetadatableAndCreateID: %p", &i_rObject
);
732 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
733 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
734 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
735 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
737 const bool isInContent( i_rObject
.IsInContent() );
738 const ::rtl::OUString
stream( ::rtl::OUString::createFromAscii(
739 isInContent
? s_content
: s_styles
) );
740 // check if we have a latent xmlid, and if yes, remove it
741 ::rtl::OUString old_path
;
742 ::rtl::OUString old_idref
;
743 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
);
745 XmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
746 if (!old_idref
.isEmpty())
748 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
749 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
750 if (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
)
756 // remove latent xmlid
757 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
762 const ::rtl::OUString
id( create_id(m_pImpl
->m_XmlIdMap
) );
763 OSL_ENSURE(m_pImpl
->m_XmlIdMap
.find(id
) == m_pImpl
->m_XmlIdMap
.end(),
764 "created id is in use");
765 m_pImpl
->m_XmlIdMap
.insert(::std::make_pair(id
, isInContent
766 ? ::std::make_pair( XmlIdList_t( 1, &i_rObject
), XmlIdList_t() )
767 : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject
) )));
768 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] = ::std::make_pair(stream
, id
);
771 void XmlIdRegistryDocument::UnregisterMetadatable(const Metadatable
& i_rObject
)
773 OSL_TRACE("UnregisterMetadatable: %p", &i_rObject
);
775 ::rtl::OUString path
;
776 ::rtl::OUString idref
;
777 if (!m_pImpl
->LookupXmlId(i_rObject
, path
, idref
))
779 OSL_FAIL("unregister: no xml id?");
782 const XmlIdMap_t::iterator
iter( m_pImpl
->m_XmlIdMap
.find(idref
) );
783 if (iter
!= m_pImpl
->m_XmlIdMap
.end())
785 rmIter(m_pImpl
->m_XmlIdMap
, iter
, path
, i_rObject
);
789 void XmlIdRegistryDocument::RemoveXmlIdForElement(const Metadatable
& i_rObject
)
791 OSL_TRACE("RemoveXmlIdForElement: %p", &i_rObject
);
793 const XmlIdReverseMap_t::iterator
iter(
794 m_pImpl
->m_XmlIdReverseMap
.find(&i_rObject
) );
795 if (iter
!= m_pImpl
->m_XmlIdReverseMap
.end())
797 OSL_ENSURE(!iter
->second
.second
.isEmpty(),
798 "null id in m_XmlIdReverseMap");
799 m_pImpl
->m_XmlIdReverseMap
.erase(iter
);
803 // -------------------------------------------------------------------
805 void XmlIdRegistryDocument::RegisterCopy(Metadatable
const& i_rSource
,
806 Metadatable
& i_rCopy
, const bool i_bCopyPrecedesSource
)
808 OSL_TRACE("RegisterCopy: %p -> %p (%d)\n",
809 &i_rSource
, &i_rCopy
, i_bCopyPrecedesSource
);
811 // potential sources: clipboard, undo array, splitNode
812 // assumption: stream change can only happen via clipboard, and is handled
813 // by Metadatable::RegisterAsCopyOf
814 OSL_ENSURE(i_rSource
.IsInUndo() || i_rCopy
.IsInUndo() ||
815 (i_rSource
.IsInContent() == i_rCopy
.IsInContent()),
816 "RegisterCopy: not in same stream?");
818 ::rtl::OUString path
;
819 ::rtl::OUString idref
;
820 if (!m_pImpl
->LookupXmlId( i_rSource
, path
, idref
))
822 OSL_FAIL("no xml id?");
825 XmlIdList_t
* pList ( m_pImpl
->LookupElementList(path
, idref
) );
826 OSL_ENSURE( ::std::find( pList
->begin(), pList
->end(), &i_rCopy
)
827 == pList
->end(), "copy already registered???");
828 XmlIdList_t::iterator
srcpos(
829 ::std::find( pList
->begin(), pList
->end(), &i_rSource
) );
830 OSL_ENSURE(srcpos
!= pList
->end(), "source not in list???");
831 if (srcpos
== pList
->end())
835 if (i_bCopyPrecedesSource
)
837 pList
->insert( srcpos
, &i_rCopy
);
841 // for undo push_back does not work! must insert right after source
842 pList
->insert( ++srcpos
, &i_rCopy
);
844 m_pImpl
->m_XmlIdReverseMap
.insert(::std::make_pair(&i_rCopy
,
845 ::std::make_pair(path
, idref
)));
848 ::boost::shared_ptr
<MetadatableUndo
>
849 XmlIdRegistryDocument::CreateUndo(Metadatable
const& i_rObject
)
851 OSL_TRACE("CreateUndo: %p", &i_rObject
);
853 return ::boost::shared_ptr
<MetadatableUndo
>(
854 new MetadatableUndo(i_rObject
.IsInContent()) );
858 i_rMerged is both a source and the target node of the merge
859 i_rOther is the other source, and will be deleted after the merge
861 dimensions: none|latent|actual empty|nonempty
862 i_rMerged(1) i_rOther(2) result
863 *|empty *|empty => 1|2 (arbitrary)
864 *|empty *|nonempty => 2
865 *|nonempty *|empty => 1
866 none|nonempty none|nonempty => none
867 none|nonempty latent|nonempty => 2
868 latent|nonempty none|nonempty => 1
869 latent|nonempty latent|nonempty => 1|2
870 *|nonempty actual|nonempty => 2
871 actual|nonempty *|nonempty => 1
872 actual|nonempty actual|nonempty => 1|2
875 XmlIdRegistryDocument::JoinMetadatables(
876 Metadatable
& i_rMerged
, Metadatable
const & i_rOther
)
878 OSL_TRACE("JoinMetadatables: %p <- %p", &i_rMerged
, &i_rOther
);
881 ::rtl::OUString path
;
882 ::rtl::OUString idref
;
883 if (m_pImpl
->LookupXmlId(i_rMerged
, path
, idref
))
885 mergedOwnsRef
= (m_pImpl
->LookupElement(path
, idref
) == &i_rMerged
);
889 OSL_FAIL("JoinMetadatables: no xmlid?");
894 i_rMerged
.RemoveMetadataReference();
895 i_rMerged
.RegisterAsCopyOf(i_rOther
, true);
898 // other cases: merged has actual ref and is nonempty,
899 // other has latent/actual ref and is nonempty: other loses => nothing to do
903 //=============================================================================
904 // Clipboard XML ID Registry (_Impl)
908 RMapEntry() : m_pLink() { }
909 RMapEntry(::rtl::OUString
const& i_rStream
,
910 ::rtl::OUString
const& i_rXmlId
,
911 ::boost::shared_ptr
<MetadatableClipboard
> const& i_pLink
912 = ::boost::shared_ptr
<MetadatableClipboard
>())
913 : m_Stream(i_rStream
), m_XmlId(i_rXmlId
), m_pLink(i_pLink
)
915 ::rtl::OUString m_Stream
;
916 ::rtl::OUString m_XmlId
;
917 // this would have been an auto_ptr, if only that would have compiled...
918 ::boost::shared_ptr
<MetadatableClipboard
> m_pLink
;
921 /// element -> (stream name, idref, source)
922 typedef ::boost::unordered_map
< const Metadatable
*,
924 PtrHash
<Metadatable
> >
925 ClipboardXmlIdReverseMap_t
;
927 /// Idref -> (content.xml element, styles.xml element)
928 typedef ::boost::unordered_map
< ::rtl::OUString
,
929 ::std::pair
< Metadatable
*, Metadatable
* >, ::rtl::OUStringHash
>
932 struct XmlIdRegistryClipboard::XmlIdRegistry_Impl
935 : m_XmlIdMap(), m_XmlIdReverseMap() { }
937 bool TryInsertMetadatable(Metadatable
& i_xObject
,
938 const ::rtl::OUString
& i_rStream
, const ::rtl::OUString
& i_rIdref
);
940 bool LookupXmlId(const Metadatable
& i_xObject
,
941 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
,
942 MetadatableClipboard
const* &o_rpLink
) const;
944 Metadatable
* LookupElement(const ::rtl::OUString
& i_rStreamName
,
945 const ::rtl::OUString
& i_rIdref
) const;
947 Metadatable
* const* LookupEntry(const ::rtl::OUString
& i_rStreamName
,
948 const ::rtl::OUString
& i_rIdref
) const;
950 Metadatable
* * LookupEntry(const ::rtl::OUString
& i_rStreamName
,
951 const ::rtl::OUString
& i_rIdref
)
953 return const_cast<Metadatable
**>(
954 const_cast<const XmlIdRegistry_Impl
*>(this)
955 ->LookupEntry(i_rStreamName
, i_rIdref
));
958 ClipboardXmlIdMap_t m_XmlIdMap
;
959 ClipboardXmlIdReverseMap_t m_XmlIdReverseMap
;
962 // -------------------------------------------------------------------
965 rmIter(ClipboardXmlIdMap_t
& i_rXmlIdMap
,
966 ClipboardXmlIdMap_t::iterator
const& i_rIter
,
967 ::rtl::OUString
const & i_rStream
, Metadatable
const& i_rObject
)
969 if (i_rIter
!= i_rXmlIdMap
.end())
971 Metadatable
*& rMeta
= isContentFile(i_rStream
)
972 ? i_rIter
->second
.first
: i_rIter
->second
.second
;
973 if (rMeta
== &i_rObject
)
977 if (!i_rIter
->second
.first
&& !i_rIter
->second
.second
)
979 i_rXmlIdMap
.erase(i_rIter
);
984 // -------------------------------------------------------------------
987 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupEntry(
988 const ::rtl::OUString
& i_rStreamName
,
989 const ::rtl::OUString
& i_rIdref
) const
991 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
993 throw lang::IllegalArgumentException(::rtl::OUString(
994 "illegal XmlId"), 0, 0);
997 const ClipboardXmlIdMap_t::const_iterator
iter( m_XmlIdMap
.find(i_rIdref
) );
998 if (iter
!= m_XmlIdMap
.end())
1000 OSL_ENSURE(iter
->second
.first
|| iter
->second
.second
,
1001 "null entry in m_XmlIdMap");
1002 return (isContentFile(i_rStreamName
))
1003 ? &iter
->second
.first
1004 : &iter
->second
.second
;
1013 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupElement(
1014 const ::rtl::OUString
& i_rStreamName
,
1015 const ::rtl::OUString
& i_rIdref
) const
1017 Metadatable
* const * ppEntry
= LookupEntry(i_rStreamName
, i_rIdref
);
1018 return ppEntry
? *ppEntry
: 0;
1022 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupXmlId(
1023 const Metadatable
& i_rObject
,
1024 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
,
1025 MetadatableClipboard
const* &o_rpLink
) const
1027 const ClipboardXmlIdReverseMap_t::const_iterator
iter(
1028 m_XmlIdReverseMap
.find(&i_rObject
) );
1029 if (iter
!= m_XmlIdReverseMap
.end())
1031 OSL_ENSURE(!iter
->second
.m_Stream
.isEmpty(),
1032 "null stream in m_XmlIdReverseMap");
1033 OSL_ENSURE(!iter
->second
.m_XmlId
.isEmpty(),
1034 "null id in m_XmlIdReverseMap");
1035 o_rStream
= iter
->second
.m_Stream
;
1036 o_rIdref
= iter
->second
.m_XmlId
;
1037 o_rpLink
= iter
->second
.m_pLink
.get();
1047 XmlIdRegistryClipboard::XmlIdRegistry_Impl::TryInsertMetadatable(
1048 Metadatable
& i_rObject
,
1049 const ::rtl::OUString
& i_rStreamName
, const ::rtl::OUString
& i_rIdref
)
1051 bool bContent( isContentFile(i_rStreamName
) );
1052 OSL_ENSURE(isContentFile(i_rStreamName
) || isStylesFile(i_rStreamName
),
1055 Metadatable
** ppEntry
= LookupEntry(i_rStreamName
, i_rIdref
);
1064 *ppEntry
= &i_rObject
;
1070 m_XmlIdMap
.insert(::std::make_pair(i_rIdref
, bContent
1071 ? ::std::make_pair( &i_rObject
, static_cast<Metadatable
*>(0) )
1072 : ::std::make_pair( static_cast<Metadatable
*>(0), &i_rObject
)));
1077 //=============================================================================
1078 // Clipboard XML ID Registry
1081 XmlIdRegistryClipboard::XmlIdRegistryClipboard()
1082 : m_pImpl( new XmlIdRegistry_Impl
)
1086 XmlIdRegistryClipboard::~XmlIdRegistryClipboard()
1091 XmlIdRegistryClipboard::LookupXmlId(
1092 const Metadatable
& i_rObject
,
1093 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
) const
1095 const MetadatableClipboard
* pLink
;
1096 return m_pImpl
->LookupXmlId(i_rObject
, o_rStream
, o_rIdref
, pLink
);
1100 XmlIdRegistryClipboard::LookupElement(
1101 const ::rtl::OUString
& i_rStreamName
,
1102 const ::rtl::OUString
& i_rIdref
) const
1104 return m_pImpl
->LookupElement(i_rStreamName
, i_rIdref
);
1108 XmlIdRegistryClipboard::TryRegisterMetadatable(Metadatable
& i_rObject
,
1109 ::rtl::OUString
const& i_rStreamName
, ::rtl::OUString
const& i_rIdref
)
1111 OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject
,
1112 ::rtl::OUStringToOString(i_rStreamName
, RTL_TEXTENCODING_UTF8
).getStr(),
1113 ::rtl::OUStringToOString(i_rIdref
, RTL_TEXTENCODING_UTF8
).getStr());
1115 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
1116 "TryRegisterMetadatable called for MetadatableUndo?");
1117 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
1118 "TryRegisterMetadatable called for MetadatableClipboard?");
1120 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
1122 throw lang::IllegalArgumentException(::rtl::OUString(
1123 "illegal XmlId"), 0, 0);
1125 if (i_rObject
.IsInContent()
1126 ? !isContentFile(i_rStreamName
)
1127 : !isStylesFile(i_rStreamName
))
1129 throw lang::IllegalArgumentException(::rtl::OUString(
1130 "illegal XmlId: wrong stream"), 0, 0);
1133 ::rtl::OUString old_path
;
1134 ::rtl::OUString old_idref
;
1135 const MetadatableClipboard
* pLink
;
1136 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
, pLink
);
1137 if (old_path
== i_rStreamName
&& old_idref
== i_rIdref
)
1139 return (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
);
1141 ClipboardXmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
1142 if (!old_idref
.isEmpty())
1144 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
1145 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
1147 if (m_pImpl
->TryInsertMetadatable(i_rObject
, i_rStreamName
, i_rIdref
))
1149 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
1150 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] =
1151 RMapEntry(i_rStreamName
, i_rIdref
);
1161 XmlIdRegistryClipboard::RegisterMetadatableAndCreateID(Metadatable
& i_rObject
)
1163 OSL_TRACE("RegisterMetadatableAndCreateID: %p", &i_rObject
);
1165 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
1166 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
1167 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
1168 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
1170 bool isInContent( i_rObject
.IsInContent() );
1171 ::rtl::OUString
stream( ::rtl::OUString::createFromAscii(
1172 isInContent
? s_content
: s_styles
) );
1174 ::rtl::OUString old_path
;
1175 ::rtl::OUString old_idref
;
1176 LookupXmlId(i_rObject
, old_path
, old_idref
);
1177 if (!old_idref
.isEmpty() &&
1178 (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
))
1184 const ::rtl::OUString
id( create_id(m_pImpl
->m_XmlIdMap
) );
1185 OSL_ENSURE(m_pImpl
->m_XmlIdMap
.find(id
) == m_pImpl
->m_XmlIdMap
.end(),
1186 "created id is in use");
1187 m_pImpl
->m_XmlIdMap
.insert(::std::make_pair(id
, isInContent
1188 ? ::std::make_pair( &i_rObject
, static_cast<Metadatable
*>(0) )
1189 : ::std::make_pair( static_cast<Metadatable
*>(0), &i_rObject
)));
1190 // N.B.: if i_rObject had a latent XmlId, then we implicitly delete the
1191 // MetadatableClipboard and thus the latent XmlId here
1192 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] = RMapEntry(stream
, id
);
1195 void XmlIdRegistryClipboard::UnregisterMetadatable(const Metadatable
& i_rObject
)
1197 OSL_TRACE("UnregisterMetadatable: %p", &i_rObject
);
1199 ::rtl::OUString path
;
1200 ::rtl::OUString idref
;
1201 const MetadatableClipboard
* pLink
;
1202 if (!m_pImpl
->LookupXmlId(i_rObject
, path
, idref
, pLink
))
1204 OSL_FAIL("unregister: no xml id?");
1207 const ClipboardXmlIdMap_t::iterator
iter( m_pImpl
->m_XmlIdMap
.find(idref
) );
1208 if (iter
!= m_pImpl
->m_XmlIdMap
.end())
1210 rmIter(m_pImpl
->m_XmlIdMap
, iter
, path
, i_rObject
);
1215 void XmlIdRegistryClipboard::RemoveXmlIdForElement(const Metadatable
& i_rObject
)
1217 OSL_TRACE("RemoveXmlIdForElement: %p", &i_rObject
);
1219 ClipboardXmlIdReverseMap_t::iterator
iter(
1220 m_pImpl
->m_XmlIdReverseMap
.find(&i_rObject
) );
1221 if (iter
!= m_pImpl
->m_XmlIdReverseMap
.end())
1223 OSL_ENSURE(!iter
->second
.m_XmlId
.isEmpty(),
1224 "null id in m_XmlIdReverseMap");
1225 m_pImpl
->m_XmlIdReverseMap
.erase(iter
);
1229 // -------------------------------------------------------------------
1231 ::boost::shared_ptr
<MetadatableClipboard
>
1232 XmlIdRegistryClipboard::CreateClipboard(const bool i_isInContent
)
1234 OSL_TRACE("CreateClipboard:");
1236 return ::boost::shared_ptr
<MetadatableClipboard
>(
1237 new MetadatableClipboard(i_isInContent
) );
1240 MetadatableClipboard
&
1241 XmlIdRegistryClipboard::RegisterCopyClipboard(Metadatable
& i_rCopy
,
1242 beans::StringPair
const & i_rReference
,
1243 const bool i_isLatent
)
1245 OSL_TRACE("RegisterCopyClipboard: %p -> "/*"%p"*/"(%s#%s) (%d)\n",
1246 /*&i_rSource,*/ &i_rCopy
,
1247 ::rtl::OUStringToOString(i_rReference
.First
,
1248 RTL_TEXTENCODING_UTF8
).getStr(),
1249 ::rtl::OUStringToOString(i_rReference
.Second
,
1250 RTL_TEXTENCODING_UTF8
).getStr(),
1253 // N.B.: when copying to the clipboard, the selection is always inserted
1254 // into the body, even if the source is a header/footer!
1255 // so we do not check whether the stream is right in this function
1257 if (!isValidXmlId(i_rReference
.First
, i_rReference
.Second
))
1259 throw lang::IllegalArgumentException(::rtl::OUString(
1260 "illegal XmlId"), 0, 0);
1265 // this should succeed assuming clipboard has a single source document
1266 const bool success( m_pImpl
->TryInsertMetadatable(i_rCopy
,
1267 i_rReference
.First
, i_rReference
.Second
) );
1268 OSL_ENSURE(success
, "RegisterCopyClipboard: TryInsert failed?");
1271 const ::boost::shared_ptr
<MetadatableClipboard
> pLink(
1272 CreateClipboard( isContentFile(i_rReference
.First
)) );
1273 m_pImpl
->m_XmlIdReverseMap
.insert(::std::make_pair(&i_rCopy
,
1274 RMapEntry(i_rReference
.First
, i_rReference
.Second
, pLink
)));
1275 return *pLink
.get();
1278 MetadatableClipboard
const*
1279 XmlIdRegistryClipboard::SourceLink(Metadatable
const& i_rObject
)
1281 ::rtl::OUString path
;
1282 ::rtl::OUString idref
;
1283 const MetadatableClipboard
* pLink( 0 );
1284 m_pImpl
->LookupXmlId(i_rObject
, path
, idref
, pLink
);
1289 //=============================================================================
1290 // Metadatable mixin
1293 Metadatable::~Metadatable()
1295 RemoveMetadataReference();
1298 void Metadatable::RemoveMetadataReference()
1304 m_pReg
->UnregisterMetadatable( *this );
1305 m_pReg
->RemoveXmlIdForElement( *this );
1309 catch (const uno::Exception
&)
1311 OSL_FAIL("Metadatable::RemoveMetadataReference: exception");
1315 // ::com::sun::star::rdf::XMetadatable:
1317 Metadatable::GetMetadataReference() const
1321 return m_pReg
->GetXmlIdForElement(*this);
1323 return beans::StringPair();
1327 Metadatable::SetMetadataReference(
1328 const ::com::sun::star::beans::StringPair
& i_rReference
)
1330 if (i_rReference
.Second
.isEmpty())
1332 RemoveMetadataReference();
1336 ::rtl::OUString
streamName( i_rReference
.First
);
1337 if (streamName
.isEmpty())
1339 // handle empty stream name as auto-detect.
1340 // necessary for importing flat file format.
1341 streamName
= ::rtl::OUString::createFromAscii(
1342 IsInContent() ? s_content
: s_styles
);
1344 XmlIdRegistry
& rReg( dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1345 if (rReg
.TryRegisterMetadatable(*this, streamName
, i_rReference
.Second
))
1351 throw lang::IllegalArgumentException(
1352 ::rtl::OUString("Metadatable::"
1353 "SetMetadataReference: argument is invalid"), /*this*/0, 0);
1358 void Metadatable::EnsureMetadataReference()
1360 XmlIdRegistry
& rReg(
1361 m_pReg
? *m_pReg
: dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1362 rReg
.RegisterMetadatableAndCreateID( *this );
1366 const ::sfx2::IXmlIdRegistry
& GetRegistryConst(Metadatable
const& i_rObject
)
1368 return const_cast< Metadatable
& >( i_rObject
).GetRegistry();
1372 Metadatable::RegisterAsCopyOf(Metadatable
const & i_rSource
,
1373 const bool i_bCopyPrecedesSource
)
1375 OSL_ENSURE(typeid(*this) == typeid(i_rSource
)
1376 || typeid(i_rSource
) == typeid(MetadatableUndo
)
1377 || typeid(*this) == typeid(MetadatableUndo
)
1378 || typeid(i_rSource
) == typeid(MetadatableClipboard
)
1379 || typeid(*this) == typeid(MetadatableClipboard
),
1380 "RegisterAsCopyOf element with different class?");
1381 OSL_ENSURE(!this->m_pReg
, "RegisterAsCopyOf called on element with XmlId?");
1385 RemoveMetadataReference();
1390 if (i_rSource
.m_pReg
)
1392 XmlIdRegistry
& rReg(
1393 dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1394 if (i_rSource
.m_pReg
== &rReg
)
1396 OSL_ENSURE(!IsInClipboard(),
1397 "RegisterAsCopy: both in clipboard?");
1398 if (!IsInClipboard())
1400 XmlIdRegistryDocument
& rRegDoc(
1401 dynamic_cast<XmlIdRegistryDocument
&>( rReg
) );
1402 rRegDoc
.RegisterCopy(i_rSource
, *this,
1403 i_bCopyPrecedesSource
);
1404 this->m_pReg
= &rRegDoc
;
1408 // source is in different document
1409 XmlIdRegistryDocument
* pRegDoc(
1410 dynamic_cast<XmlIdRegistryDocument
*>(&rReg
) );
1411 XmlIdRegistryClipboard
* pRegClp(
1412 dynamic_cast<XmlIdRegistryClipboard
*>(&rReg
) );
1416 beans::StringPair
SourceRef(
1417 i_rSource
.m_pReg
->GetXmlIdForElement(i_rSource
) );
1418 bool isLatent( SourceRef
.Second
.isEmpty() );
1419 XmlIdRegistryDocument
* pSourceRegDoc(
1420 dynamic_cast<XmlIdRegistryDocument
*>(i_rSource
.m_pReg
) );
1421 OSL_ENSURE(pSourceRegDoc
, "RegisterAsCopyOf: 2 clipboards?");
1422 if (!pSourceRegDoc
) return;
1423 // this is a copy _to_ the clipboard
1426 pSourceRegDoc
->LookupXmlId(i_rSource
,
1427 SourceRef
.First
, SourceRef
.Second
);
1429 Metadatable
& rLink(
1430 pRegClp
->RegisterCopyClipboard(*this, SourceRef
, isLatent
));
1431 this->m_pReg
= pRegClp
;
1432 // register as copy in the non-clipboard registry
1433 pSourceRegDoc
->RegisterCopy(i_rSource
, rLink
,
1434 false); // i_bCopyPrecedesSource);
1435 rLink
.m_pReg
= pSourceRegDoc
;
1439 XmlIdRegistryClipboard
* pSourceRegClp(
1440 dynamic_cast<XmlIdRegistryClipboard
*>(i_rSource
.m_pReg
) );
1441 OSL_ENSURE(pSourceRegClp
,
1442 "RegisterAsCopyOf: 2 non-clipboards?");
1443 if (!pSourceRegClp
) return;
1444 const MetadatableClipboard
* pLink(
1445 pSourceRegClp
->SourceLink(i_rSource
) );
1446 // may happen if src got its id via UNO call
1448 // only register copy if clipboard content is from this SwDoc!
1449 if (pLink
&& (&GetRegistryConst(*pLink
) == pRegDoc
))
1451 // this is a copy _from_ the clipboard; check if the
1452 // element is still in the same stream
1453 // N.B.: we check the stream of pLink, not of i_rSource!
1454 bool srcInContent( pLink
->IsInContent() );
1455 bool tgtInContent( this->IsInContent() );
1456 if (srcInContent
== tgtInContent
)
1458 pRegDoc
->RegisterCopy(*pLink
, *this,
1459 true); // i_bCopyPrecedesSource);
1460 this->m_pReg
= pRegDoc
;
1462 // otherwise: stream change! do not register!
1467 OSL_FAIL("neither RegDoc nor RegClp cannot happen");
1471 catch (const uno::Exception
&)
1473 OSL_FAIL("Metadatable::RegisterAsCopyOf: exception");
1477 ::boost::shared_ptr
<MetadatableUndo
> Metadatable::CreateUndo() const
1479 OSL_ENSURE(!IsInUndo(), "CreateUndo called for object in undo?");
1480 OSL_ENSURE(!IsInClipboard(), "CreateUndo called for object in clipboard?");
1483 if (!IsInClipboard() && !IsInUndo() && m_pReg
)
1485 XmlIdRegistryDocument
* pRegDoc(
1486 dynamic_cast<XmlIdRegistryDocument
*>( m_pReg
) );
1487 ::boost::shared_ptr
<MetadatableUndo
> pUndo(
1488 pRegDoc
->CreateUndo(*this) );
1489 pRegDoc
->RegisterCopy(*this, *pUndo
, false);
1490 pUndo
->m_pReg
= pRegDoc
;
1494 catch (const uno::Exception
&)
1496 OSL_FAIL("Metadatable::CreateUndo: exception");
1498 return ::boost::shared_ptr
<MetadatableUndo
>();
1501 ::boost::shared_ptr
<MetadatableUndo
> Metadatable::CreateUndoForDelete()
1503 ::boost::shared_ptr
<MetadatableUndo
> const pUndo( CreateUndo() );
1504 RemoveMetadataReference();
1508 void Metadatable::RestoreMetadata(
1509 ::boost::shared_ptr
<MetadatableUndo
> const& i_pUndo
)
1511 OSL_ENSURE(!IsInUndo(), "RestoreMetadata called for object in undo?");
1512 OSL_ENSURE(!IsInClipboard(),
1513 "RestoreMetadata called for object in clipboard?");
1514 if (IsInClipboard() || IsInUndo()) return;
1515 RemoveMetadataReference();
1518 this->RegisterAsCopyOf(*i_pUndo
, true);
1523 Metadatable::JoinMetadatable(Metadatable
const & i_rOther
,
1524 const bool i_isMergedEmpty
, const bool i_isOtherEmpty
)
1526 OSL_ENSURE(!IsInUndo(), "JoinMetadatables called for object in undo?");
1527 OSL_ENSURE(!IsInClipboard(),
1528 "JoinMetadatables called for object in clipboard?");
1529 if (IsInClipboard() || IsInUndo()) return;
1531 if (i_isOtherEmpty
&& !i_isMergedEmpty
)
1533 // other is empty, thus loses => nothing to do
1536 if (i_isMergedEmpty
&& !i_isOtherEmpty
)
1538 this->RemoveMetadataReference();
1539 this->RegisterAsCopyOf(i_rOther
, true);
1543 if (!i_rOther
.m_pReg
)
1545 // other doesn't have xmlid, thus loses => nothing to do
1550 this->RegisterAsCopyOf(i_rOther
, true);
1551 // assumption: i_rOther will be deleted, so don't unregister it here
1556 XmlIdRegistryDocument
* pRegDoc(
1557 dynamic_cast<XmlIdRegistryDocument
*>( m_pReg
) );
1558 OSL_ENSURE(pRegDoc
, "JoinMetadatable: no pRegDoc?");
1561 pRegDoc
->JoinMetadatables(*this, i_rOther
);
1564 catch (const uno::Exception
&)
1566 OSL_FAIL("Metadatable::JoinMetadatable: exception");
1571 //=============================================================================
1572 // XMetadatable mixin
1574 // ::com::sun::star::rdf::XNode:
1575 ::rtl::OUString SAL_CALL
MetadatableMixin::getStringValue()
1576 throw (::com::sun::star::uno::RuntimeException
)
1578 return getNamespace() + getLocalName();
1581 // ::com::sun::star::rdf::XURI:
1582 ::rtl::OUString SAL_CALL
MetadatableMixin::getLocalName()
1583 throw (::com::sun::star::uno::RuntimeException
)
1585 SolarMutexGuard aGuard
;
1586 beans::StringPair
mdref( getMetadataReference() );
1587 if (mdref
.Second
.isEmpty())
1589 ensureMetadataReference(); // N.B.: side effect!
1590 mdref
= getMetadataReference();
1592 ::rtl::OUStringBuffer buf
;
1593 buf
.append(mdref
.First
);
1594 buf
.append(static_cast<sal_Unicode
>('#'));
1595 buf
.append(mdref
.Second
);
1596 return buf
.makeStringAndClear();
1599 ::rtl::OUString SAL_CALL
MetadatableMixin::getNamespace()
1600 throw (::com::sun::star::uno::RuntimeException
)
1602 SolarMutexGuard aGuard
;
1603 const uno::Reference
< frame::XModel
> xModel( GetModel() );
1604 const uno::Reference
< rdf::XURI
> xDMA( xModel
, uno::UNO_QUERY_THROW
);
1605 return xDMA
->getStringValue();
1608 // ::com::sun::star::rdf::XMetadatable:
1609 beans::StringPair SAL_CALL
1610 MetadatableMixin::getMetadataReference()
1611 throw (uno::RuntimeException
)
1613 SolarMutexGuard aGuard
;
1615 Metadatable
*const pObject( GetCoreObject() );
1618 throw uno::RuntimeException(
1620 "MetadatableMixin: cannot get core object; not inserted?"),
1623 return pObject
->GetMetadataReference();
1627 MetadatableMixin::setMetadataReference(
1628 const beans::StringPair
& i_rReference
)
1629 throw (uno::RuntimeException
, lang::IllegalArgumentException
)
1631 SolarMutexGuard aGuard
;
1633 Metadatable
*const pObject( GetCoreObject() );
1636 throw uno::RuntimeException(
1638 "MetadatableMixin: cannot get core object; not inserted?"),
1641 return pObject
->SetMetadataReference(i_rReference
);
1644 void SAL_CALL
MetadatableMixin::ensureMetadataReference()
1645 throw (uno::RuntimeException
)
1647 SolarMutexGuard aGuard
;
1649 Metadatable
*const pObject( GetCoreObject() );
1652 throw uno::RuntimeException(
1654 "MetadatableMixin: cannot get core object; not inserted?"),
1657 return pObject
->EnsureMetadataReference();
1663 //=============================================================================
1665 #if OSL_DEBUG_LEVEL > 1
1669 static void dump(sfx2::XmlIdList_t
* pList
)
1671 __attribute__ ((unused
))
1674 static void dump(sfx2::XmlIdList_t
* pList
)
1676 fprintf(stderr
, "\nXmlIdList(%p): ", pList
);
1677 for (sfx2::XmlIdList_t::iterator i
= pList
->begin(); i
!= pList
->end(); ++i
)
1679 fprintf(stderr
, "%p ", *i
);
1681 fprintf(stderr
, "\n");
1686 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */