1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: SwMetadatable.cxx,v $
10 * $Revision: 1.1.2.8 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 #include "precompiled_sfx2.hxx"
33 #include <sfx2/Metadatable.hxx>
34 #include <sfx2/XmlIdRegistry.hxx>
36 #include <vos/mutex.hxx>
37 #include <vcl/svapp.hxx> // solarmutex
39 #include <boost/bind.hpp>
45 #if OSL_DEBUG_LEVEL > 0
52 There is an abstract base class <type>XmlIdRegistry</type>, with
53 2 subclasses <type>XmlIdRegistryDocument</type> for "normal" documents,
54 and <type>XmlIdRegistryClipboard</type> for clipboard documents.
55 These classes are responsible for managing XML IDs for all elements
56 of the model. Only the implementation of the <type>Metadatable</type>
57 base class needs to know the registries, so they are not in the header.
59 The handling of XML IDs differs between clipboard and non-clipboard
60 documents in several aspects. Most importantly, non-clipboard documents
61 can have several elements associated with one XML ID.
62 This is necessary because of the weird undo implementation:
63 deleting a text node moves the deleted node to the undo array, but
64 executing undo will then create a <em>copy</em> of that node in the
65 document array. These 2 nodes must have the same XML ID, because
66 we cannot know whether the user will do a redo next, or something else.
68 Because we need to have a mechanism for several objects per XML ID anyway,
69 we use that also to enable some usability features:
70 The document registry has a list of Metadatables per XML ID.
71 This list is sorted by priority, i.e., the first element has highest
72 priority. When inserting copies, care must be taken that they are inserted
73 at the right position: either before or after the source.
74 This is done by <method>Metadatable::RegisterAsCopyOf</method>.
75 When a text node is split, then both resulting text nodes are inserted
76 into the list. If the user then deletes one text node, the other one
78 Also, when a Metadatable is copied to the clipboard and then pasted,
79 the copy is inserted into the list. If the user then deletes the source,
80 the XML ID is not lost.
81 The goal is that it should be hard to lose an XML ID by accident, which
82 is especially important as long as we do not have an UI that displays them.
84 There are two subclasses of <type>Metadatable</type>:
85 <ul><li><type>MetadatableClipboard</type>: for copies in the clipboard</li>
86 <li><type>MetadatableUndo</type>: for undo, because a Metadatable
87 may be destroyed on delete and a new one created on undo.</li></ul>
88 These serve only to track the position in an XML ID list in a document
89 registry, so that future actions can insert objects at the right position.
90 Unfortunately, inserting dummy objects seems to be necessary:
91 <ul><li>it is not sufficent to just remember the saved id, because then
92 the relative priorities might change when executing the undo</li>
93 <li>it is not sufficient to record the position as an integer, because
94 if we delete a text node and then undo, the node will be copied(!),
95 and we will have one more node in the list.<li>
96 <li>it is not sufficient to record the pointer of the previous/next
97 Metadatable, because if we delete a text node, undo, and then
98 do something to clear the redo array, the original text node is
99 destroyed, and is replaced by the copy created by undo</li></ul>
101 If content from a non-clipboard document is copied into a clipboard
102 document, a dummy <type>MetadatableClipboard</type> is inserted into the
103 non-clipboard document registry in order to track the position of the
104 source element. When the clipboard content is pasted back into the source
105 document, this dummy object is used to associate the pasted element with
108 If a <type>Metadatable</type> is deleted or merged,
109 <method>Metadatable::CreateUndo</method> is called, and returns a
110 <type>MetadatableUndo<type> instance, which can be used to undo the action
111 by passing it to <method>Metadatable::RestoreMetadata</method>.
117 using namespace ::com::sun::star
;
119 using ::sfx2::isValidXmlId
;
124 static const char s_content
[] = "content.xml";
125 static const char s_styles
[] = "styles.xml";
126 static const char s_prefix
[] = "id"; // prefix for generated xml:id
128 static bool isContentFile(::rtl::OUString
const & i_rPath
)
130 return i_rPath
.equalsAscii(s_content
);
133 static bool isStylesFile (::rtl::OUString
const & i_rPath
)
135 return i_rPath
.equalsAscii(s_styles
);
139 //=============================================================================
140 // XML ID handling ---------------------------------------------------
142 /** handles registration of XMetadatable.
144 This class is responsible for guaranteeing that XMetadatable objects
145 always have XML IDs that are unique within a stream.
147 This is an abstract base class; see subclasses XmlIdRegistryDocument and
148 XmlIdRegistryClipboard.
150 @see SwDoc::GetXmlIdRegistry
151 @see SwDocShell::GetXmlIdRegistry
153 class XmlIdRegistry
: public sfx2::IXmlIdRegistry
159 virtual ~XmlIdRegistry();
161 /** get the ODF element with the given metadata reference. */
162 virtual ::com::sun::star::uno::Reference
<
163 ::com::sun::star::rdf::XMetadatable
> SAL_CALL
164 GetElementByMetadataReference(
165 const ::com::sun::star::beans::StringPair
& i_rReference
) const;
167 /** register an ODF element at a newly generated, unique metadata reference.
170 Find a fresh XML ID, and register it for the element.
171 The generated ID does not occur in any stream of the document.
174 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
) = 0;
176 /** try to register an ODF element at a given XML ID, or update its
177 registation to a different XML ID.
180 If the given new metadata reference is not already occupied in the
181 document, unregister the element at its old metadata reference if
182 it has one, and register the new metadata reference for the element.
183 Note that this method only ensures that XML IDs are unique per stream,
184 so using the same XML ID in both content.xml and styles.xml is allowed.
188 true iff the element has successfully been registered
190 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
191 ::rtl::OUString
const& i_rStreamName
, ::rtl::OUString
const& i_rIdref
)
194 /** unregister an ODF element.
197 Unregister the element at its metadata reference.
198 Does not remove the metadata reference from the element.
201 @see RemoveXmlIdForElement
203 virtual void UnregisterMetadatable(Metadatable
const&) = 0;
205 /** get the metadata reference for the given element. */
206 ::com::sun::star::beans::StringPair
207 GetXmlIdForElement(Metadatable
const&) const;
209 /** remove the metadata reference for the given element. */
210 virtual void RemoveXmlIdForElement(Metadatable
const&) = 0;
214 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
215 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
) const = 0;
217 virtual Metadatable
* LookupElement(const ::rtl::OUString
& i_rStreamName
,
218 const ::rtl::OUString
& i_rIdref
) const = 0;
221 // XmlIdRegistryDocument ---------------------------------------------
223 /** non-clipboard documents */
224 class XmlIdRegistryDocument
: public XmlIdRegistry
228 XmlIdRegistryDocument();
230 virtual ~XmlIdRegistryDocument();
232 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
);
234 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
235 ::rtl::OUString
const& i_rStreamName
, ::rtl::OUString
const& i_rIdref
);
237 virtual void UnregisterMetadatable(Metadatable
const&);
239 virtual void RemoveXmlIdForElement(Metadatable
const&);
241 /** register i_rCopy as a copy of i_rSource,
242 with precedence iff i_bCopyPrecedesSource is true */
243 void RegisterCopy(Metadatable
const& i_rSource
, Metadatable
& i_rCopy
,
244 const bool i_bCopyPrecedesSource
);
246 /** create a Undo Metadatable for i_rObject. */
247 ::boost::shared_ptr
<MetadatableUndo
> CreateUndo(
248 Metadatable
const& i_rObject
);
250 /** merge i_rMerged and i_rOther into i_rMerged. */
251 void JoinMetadatables(Metadatable
& i_rMerged
, Metadatable
const& i_rOther
);
253 // unfortunately public, Metadatable::RegisterAsCopyOf needs this
254 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
255 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
) const;
259 virtual Metadatable
* LookupElement(const ::rtl::OUString
& i_rStreamName
,
260 const ::rtl::OUString
& i_rIdref
) const;
262 struct XmlIdRegistry_Impl
;
263 ::std::auto_ptr
<XmlIdRegistry_Impl
> m_pImpl
;
266 // MetadatableUndo ---------------------------------------------------
268 /** the horrible Undo Metadatable: is inserted into lists to track position */
269 class MetadatableUndo
: public Metadatable
271 /// as determined by the stream of the source in original document
272 const bool m_isInContent
;
274 MetadatableUndo(const bool i_isInContent
)
275 : m_isInContent(i_isInContent
) { }
276 virtual ::sfx2::XmlIdRegistry
& GetRegistry()
278 // N.B. for Undo, m_pReg is initialized by registering this as copy in
279 // CreateUndo; it is never cleared
280 OSL_ENSURE(m_pReg
, "no m_pReg in MetadatableUndo ?");
283 virtual bool IsInClipboard() const { return false; }
284 virtual bool IsInUndo() const { return true; }
285 virtual bool IsInContent() const { return m_isInContent
; }
286 virtual ::com::sun::star::uno::Reference
<
287 ::com::sun::star::rdf::XMetadatable
> MakeUnoObject()
288 { OSL_ENSURE(false, "MetadatableUndo::MakeUnoObject"); throw; }
291 // MetadatableClipboard ----------------------------------------------
293 /** the horrible Clipboard Metadatable: inserted into lists to track position */
294 class MetadatableClipboard
: public Metadatable
296 /// as determined by the stream of the source in original document
297 const bool m_isInContent
;
299 MetadatableClipboard(const bool i_isInContent
)
300 : m_isInContent(i_isInContent
) { }
301 virtual ::sfx2::XmlIdRegistry
& GetRegistry()
303 // N.B. for Clipboard, m_pReg is initialized by registering this as copy in
304 // RegisterAsCopyOf; it is only cleared by OriginNoLongerInBusinessAnymore
305 OSL_ENSURE(m_pReg
, "no m_pReg in MetadatableClipboard ?");
308 virtual bool IsInClipboard() const { return true; }
309 virtual bool IsInUndo() const { return false; }
310 virtual bool IsInContent() const { return m_isInContent
; }
311 virtual ::com::sun::star::uno::Reference
<
312 ::com::sun::star::rdf::XMetadatable
> MakeUnoObject()
313 { OSL_ENSURE(false, "MetadatableClipboard::MakeUnoObject"); throw; }
314 void OriginNoLongerInBusinessAnymore() { m_pReg
= 0; }
317 // XmlIdRegistryClipboard --------------------------------------------
319 class XmlIdRegistryClipboard
: public XmlIdRegistry
323 XmlIdRegistryClipboard();
324 virtual ~XmlIdRegistryClipboard();
326 virtual void RegisterMetadatableAndCreateID(Metadatable
& i_xObject
);
328 virtual bool TryRegisterMetadatable(Metadatable
& i_xObject
,
329 ::rtl::OUString
const& i_rStreamName
, ::rtl::OUString
const& i_rIdref
);
331 virtual void UnregisterMetadatable(Metadatable
const&);
333 virtual void RemoveXmlIdForElement(Metadatable
const&);
335 /** register i_rCopy as a copy of i_rSource */
336 MetadatableClipboard
& RegisterCopyClipboard(Metadatable
& i_rCopy
,
337 beans::StringPair
const & i_rReference
,
338 const bool i_isLatent
);
340 /** get the Metadatable that links i_rObject to its origin registry */
341 MetadatableClipboard
const* SourceLink(Metadatable
const& i_rObject
);
344 virtual bool LookupXmlId(const Metadatable
& i_xObject
,
345 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
) const;
347 virtual Metadatable
* LookupElement(const ::rtl::OUString
& i_rStreamName
,
348 const ::rtl::OUString
& i_rIdref
) const;
350 /** create a Clipboard Metadatable for i_rObject. */
351 ::boost::shared_ptr
<MetadatableClipboard
> CreateClipboard(
352 const bool i_isInContent
);
354 struct XmlIdRegistry_Impl
;
355 ::std::auto_ptr
<XmlIdRegistry_Impl
> m_pImpl
;
359 //=============================================================================
362 ::sfx2::IXmlIdRegistry
* createXmlIdRegistry(const bool i_DocIsClipboard
)
364 return i_DocIsClipboard
365 ? static_cast<XmlIdRegistry
*>( new XmlIdRegistryClipboard
)
366 : static_cast<XmlIdRegistry
*>( new XmlIdRegistryDocument
);
369 XmlIdRegistry::XmlIdRegistry()
373 XmlIdRegistry::~XmlIdRegistry()
377 ::com::sun::star::uno::Reference
< ::com::sun::star::rdf::XMetadatable
> SAL_CALL
378 XmlIdRegistry::GetElementByMetadataReference(
379 const beans::StringPair
& i_rReference
) const
381 Metadatable
* pObject( LookupElement(i_rReference
.First
,
382 i_rReference
.Second
) );
383 return pObject
? pObject
->MakeUnoObject() : 0;
387 XmlIdRegistry::GetXmlIdForElement(const Metadatable
& i_rObject
) const
389 ::rtl::OUString path
;
390 ::rtl::OUString idref
;
391 if (LookupXmlId(i_rObject
, path
, idref
))
393 if (LookupElement(path
, idref
) == &i_rObject
)
395 return beans::StringPair(path
, idref
);
398 return beans::StringPair();
402 /// generate unique xml:id
403 template< typename T
>
404 /*static*/ ::rtl::OUString
create_id(const
405 ::std::hash_map
< ::rtl::OUString
, T
, ::rtl::OUStringHash
> & i_rXmlIdMap
)
407 const ::rtl::OUString
prefix( ::rtl::OUString::createFromAscii(s_prefix
) );
408 typename ::std::hash_map
< ::rtl::OUString
, T
, ::rtl::OUStringHash
>
409 ::const_iterator iter
;
413 const int n( rand() );
414 id
= prefix
+ ::rtl::OUString::valueOf(static_cast<sal_Int64
>(n
));
415 iter
= i_rXmlIdMap
.find(id
);
417 while (iter
!= i_rXmlIdMap
.end());
421 //=============================================================================
422 // Document XML ID Registry (_Impl)
425 typedef ::std::list
< Metadatable
* > XmlIdList_t
;
427 /// Idref -> (content.xml element list, styles.xml element list)
428 typedef ::std::hash_map
< ::rtl::OUString
,
429 ::std::pair
< XmlIdList_t
, XmlIdList_t
>, ::rtl::OUStringHash
> XmlIdMap_t
;
431 /// pointer hash template
432 template<typename T
> struct PtrHash
434 size_t operator() (T
const * i_pT
) const
436 return reinterpret_cast<size_t>(i_pT
);
440 /// element -> (stream name, idref)
441 typedef ::std::hash_map
< const Metadatable
*,
442 ::std::pair
< ::rtl::OUString
, ::rtl::OUString
>, PtrHash
<Metadatable
> >
445 struct XmlIdRegistryDocument::XmlIdRegistry_Impl
448 : m_XmlIdMap(), m_XmlIdReverseMap() { }
450 bool TryInsertMetadatable(Metadatable
& i_xObject
,
451 const ::rtl::OUString
& i_rStream
, const ::rtl::OUString
& i_rIdref
);
453 bool LookupXmlId(const Metadatable
& i_xObject
,
454 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
) const;
456 Metadatable
* LookupElement(const ::rtl::OUString
& i_rStreamName
,
457 const ::rtl::OUString
& i_rIdref
) const;
459 const XmlIdList_t
* LookupElementList(
460 const ::rtl::OUString
& i_rStreamName
,
461 const ::rtl::OUString
& i_rIdref
) const;
463 XmlIdList_t
* LookupElementList(
464 const ::rtl::OUString
& i_rStreamName
,
465 const ::rtl::OUString
& i_rIdref
)
467 return const_cast<XmlIdList_t
*>(
468 const_cast<const XmlIdRegistry_Impl
*>(this)
469 ->LookupElementList(i_rStreamName
, i_rIdref
));
472 XmlIdMap_t m_XmlIdMap
;
473 XmlIdReverseMap_t m_XmlIdReverseMap
;
476 // -------------------------------------------------------------------
479 rmIter(XmlIdMap_t
& i_rXmlIdMap
, XmlIdMap_t::iterator
const& i_rIter
,
480 ::rtl::OUString
const & i_rStream
, Metadatable
const& i_rObject
)
482 if (i_rIter
!= i_rXmlIdMap
.end())
484 XmlIdList_t
& rList( isContentFile(i_rStream
)
485 ? i_rIter
->second
.first
: i_rIter
->second
.second
);
486 rList
.remove(&const_cast<Metadatable
&>(i_rObject
));
487 if (i_rIter
->second
.first
.empty() && i_rIter
->second
.second
.empty())
489 i_rXmlIdMap
.erase(i_rIter
);
494 // -------------------------------------------------------------------
497 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElementList(
498 const ::rtl::OUString
& i_rStreamName
,
499 const ::rtl::OUString
& i_rIdref
) const
501 const XmlIdMap_t::const_iterator
iter( m_XmlIdMap
.find(i_rIdref
) );
502 if (iter
!= m_XmlIdMap
.end())
504 OSL_ENSURE(!iter
->second
.first
.empty() || !iter
->second
.second
.empty(),
505 "null entry in m_XmlIdMap");
506 return (isContentFile(i_rStreamName
))
507 ? &iter
->second
.first
508 : &iter
->second
.second
;
517 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElement(
518 const ::rtl::OUString
& i_rStreamName
,
519 const ::rtl::OUString
& i_rIdref
) const
521 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
523 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
524 "illegal XmlId"), 0, 0);
527 const XmlIdList_t
* pList( LookupElementList(i_rStreamName
, i_rIdref
) );
530 const XmlIdList_t::const_iterator
iter(
531 ::std::find_if(pList
->begin(), pList
->end(),
533 ::std::logical_not
<bool>(),
535 ::std::logical_or
<bool>(),
536 ::boost::bind( &Metadatable::IsInUndo
, _1
),
537 ::boost::bind( &Metadatable::IsInClipboard
, _1
)
539 if (iter
!= pList
->end())
548 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupXmlId(
549 const Metadatable
& i_rObject
,
550 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
) const
552 const XmlIdReverseMap_t::const_iterator
iter(
553 m_XmlIdReverseMap
.find(&i_rObject
) );
554 if (iter
!= m_XmlIdReverseMap
.end())
556 OSL_ENSURE(!iter
->second
.first
.equalsAscii(""),
557 "null stream in m_XmlIdReverseMap");
558 OSL_ENSURE(!iter
->second
.second
.equalsAscii(""),
559 "null id in m_XmlIdReverseMap");
560 o_rStream
= iter
->second
.first
;
561 o_rIdref
= iter
->second
.second
;
571 XmlIdRegistryDocument::XmlIdRegistry_Impl::TryInsertMetadatable(
572 Metadatable
& i_rObject
,
573 const ::rtl::OUString
& i_rStreamName
, const ::rtl::OUString
& i_rIdref
)
575 const bool bContent( isContentFile(i_rStreamName
) );
576 OSL_ENSURE(isContentFile(i_rStreamName
) || isStylesFile(i_rStreamName
),
579 XmlIdList_t
* pList( LookupElementList(i_rStreamName
, i_rIdref
) );
584 pList
->push_back( &i_rObject
);
589 // this is only called from TryRegister now, so check
590 // if all elements in the list are deleted (in undo) or
591 // placeholders, then "steal" the id from them
592 if ( pList
->end() == ::std::find_if(pList
->begin(), pList
->end(),
594 ::std::logical_not
<bool>(),
596 ::std::logical_or
<bool>(),
597 ::boost::bind( &Metadatable::IsInUndo
, _1
),
598 ::boost::bind( &Metadatable::IsInClipboard
, _1
)
601 // ??? this is not undoable
603 // pList->push_back( &i_rObject );
604 pList
->push_front( &i_rObject
);
615 m_XmlIdMap
.insert(::std::make_pair(i_rIdref
, bContent
616 ? ::std::make_pair( XmlIdList_t( 1, &i_rObject
), XmlIdList_t() )
617 : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject
) )));
622 //=============================================================================
623 // Document XML ID Registry
626 XmlIdRegistryDocument::XmlIdRegistryDocument()
627 : m_pImpl( new XmlIdRegistry_Impl
)
632 removeLink(Metadatable
* i_pObject
)
634 OSL_ENSURE(i_pObject
, "null in list ???");
635 if (!i_pObject
) return;
636 if (i_pObject
->IsInClipboard())
638 MetadatableClipboard
* pLink(
639 dynamic_cast<MetadatableClipboard
*>( i_pObject
) );
640 OSL_ENSURE(pLink
, "IsInClipboard, but no MetadatableClipboard ?");
643 pLink
->OriginNoLongerInBusinessAnymore();
648 XmlIdRegistryDocument::~XmlIdRegistryDocument()
650 // notify all list elements that are actually in the clipboard
651 for (XmlIdMap_t::iterator
iter(m_pImpl
->m_XmlIdMap
.begin());
652 iter
!= m_pImpl
->m_XmlIdMap
.end(); ++iter
)
654 ::std::for_each(iter
->second
.first
.begin(), iter
->second
.first
.end(),
656 ::std::for_each(iter
->second
.second
.begin(), iter
->second
.second
.end(),
662 XmlIdRegistryDocument::LookupXmlId(
663 const Metadatable
& i_rObject
,
664 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
) const
666 return m_pImpl
->LookupXmlId(i_rObject
, o_rStream
, o_rIdref
);
670 XmlIdRegistryDocument::LookupElement(
671 const ::rtl::OUString
& i_rStreamName
,
672 const ::rtl::OUString
& i_rIdref
) const
674 return m_pImpl
->LookupElement(i_rStreamName
, i_rIdref
);
678 XmlIdRegistryDocument::TryRegisterMetadatable(Metadatable
& i_rObject
,
679 ::rtl::OUString
const& i_rStreamName
, ::rtl::OUString
const& i_rIdref
)
681 OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject
,
682 ::rtl::OUStringToOString(i_rStreamName
, RTL_TEXTENCODING_UTF8
).getStr(),
683 ::rtl::OUStringToOString(i_rIdref
, RTL_TEXTENCODING_UTF8
).getStr());
685 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
686 "TryRegisterMetadatable called for MetadatableUndo?");
687 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
688 "TryRegisterMetadatable called for MetadatableClipboard?");
690 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
692 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
693 "illegal XmlId"), 0, 0);
695 if (i_rObject
.IsInContent()
696 ? !isContentFile(i_rStreamName
)
697 : !isStylesFile(i_rStreamName
))
699 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
700 "illegal XmlId: wrong stream"), 0, 0);
703 ::rtl::OUString old_path
;
704 ::rtl::OUString old_idref
;
705 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
);
706 if (old_path
== i_rStreamName
&& old_idref
== i_rIdref
)
708 return (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
);
710 XmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
711 if (!old_idref
.equalsAscii(""))
713 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
714 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
716 if (m_pImpl
->TryInsertMetadatable(i_rObject
, i_rStreamName
, i_rIdref
))
718 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
719 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] =
720 ::std::make_pair(i_rStreamName
, i_rIdref
);
730 XmlIdRegistryDocument::RegisterMetadatableAndCreateID(Metadatable
& i_rObject
)
732 OSL_TRACE("RegisterMetadatableAndCreateID: %p\n", &i_rObject
);
734 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
735 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
736 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
737 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
739 const bool isInContent( i_rObject
.IsInContent() );
740 const ::rtl::OUString
stream( ::rtl::OUString::createFromAscii(
741 isInContent
? s_content
: s_styles
) );
742 // check if we have a latent xmlid, and if yes, remove it
743 ::rtl::OUString old_path
;
744 ::rtl::OUString old_idref
;
745 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
);
747 XmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
748 if (!old_idref
.equalsAscii(""))
750 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
751 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
752 if (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
)
758 // remove latent xmlid
759 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
764 const ::rtl::OUString
id( create_id(m_pImpl
->m_XmlIdMap
) );
765 OSL_ENSURE(m_pImpl
->m_XmlIdMap
.find(id
) == m_pImpl
->m_XmlIdMap
.end(),
766 "created id is in use");
767 m_pImpl
->m_XmlIdMap
.insert(::std::make_pair(id
, isInContent
768 ? ::std::make_pair( XmlIdList_t( 1, &i_rObject
), XmlIdList_t() )
769 : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject
) )));
770 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] = ::std::make_pair(stream
, id
);
773 void XmlIdRegistryDocument::UnregisterMetadatable(const Metadatable
& i_rObject
)
775 OSL_TRACE("UnregisterMetadatable: %p\n", &i_rObject
);
777 ::rtl::OUString path
;
778 ::rtl::OUString idref
;
779 if (!m_pImpl
->LookupXmlId(i_rObject
, path
, idref
))
781 OSL_ENSURE(false, "unregister: no xml id?");
784 const XmlIdMap_t::iterator
iter( m_pImpl
->m_XmlIdMap
.find(idref
) );
785 if (iter
!= m_pImpl
->m_XmlIdMap
.end())
787 rmIter(m_pImpl
->m_XmlIdMap
, iter
, path
, i_rObject
);
791 void XmlIdRegistryDocument::RemoveXmlIdForElement(const Metadatable
& i_rObject
)
793 OSL_TRACE("RemoveXmlIdForElement: %p\n", &i_rObject
);
795 const XmlIdReverseMap_t::iterator
iter(
796 m_pImpl
->m_XmlIdReverseMap
.find(&i_rObject
) );
797 if (iter
!= m_pImpl
->m_XmlIdReverseMap
.end())
799 OSL_ENSURE(!iter
->second
.second
.equalsAscii(""),
800 "null id in m_XmlIdReverseMap");
801 m_pImpl
->m_XmlIdReverseMap
.erase(iter
);
805 // -------------------------------------------------------------------
807 void XmlIdRegistryDocument::RegisterCopy(Metadatable
const& i_rSource
,
808 Metadatable
& i_rCopy
, const bool i_bCopyPrecedesSource
)
810 OSL_TRACE("RegisterCopy: %p -> %p (%d)\n",
811 &i_rSource
, &i_rCopy
, i_bCopyPrecedesSource
);
813 // potential sources: clipboard, undo array, splitNode
814 // assumption: stream change can only happen via clipboard, and is handled
815 // by Metadatable::RegisterAsCopyOf
816 OSL_ENSURE(i_rSource
.IsInUndo() || i_rCopy
.IsInUndo() ||
817 (i_rSource
.IsInContent() == i_rCopy
.IsInContent()),
818 "RegisterCopy: not in same stream?");
820 ::rtl::OUString path
;
821 ::rtl::OUString idref
;
822 if (!m_pImpl
->LookupXmlId( i_rSource
, path
, idref
))
824 OSL_ENSURE(false, "no xml id?");
827 XmlIdList_t
* pList ( m_pImpl
->LookupElementList(path
, idref
) );
828 OSL_ENSURE( ::std::find( pList
->begin(), pList
->end(), &i_rCopy
)
829 == pList
->end(), "copy already registered???");
830 XmlIdList_t::iterator
srcpos(
831 ::std::find( pList
->begin(), pList
->end(), &i_rSource
) );
832 OSL_ENSURE(srcpos
!= pList
->end(), "source not in list???");
833 if (srcpos
== pList
->end())
837 if (i_bCopyPrecedesSource
)
839 pList
->insert( srcpos
, &i_rCopy
);
843 // for undo push_back does not work! must insert right after source
844 pList
->insert( ++srcpos
, &i_rCopy
);
846 m_pImpl
->m_XmlIdReverseMap
.insert(::std::make_pair(&i_rCopy
,
847 ::std::make_pair(path
, idref
)));
850 ::boost::shared_ptr
<MetadatableUndo
>
851 XmlIdRegistryDocument::CreateUndo(Metadatable
const& i_rObject
)
853 OSL_TRACE("CreateUndo: %p\n", &i_rObject
);
855 return ::boost::shared_ptr
<MetadatableUndo
>(
856 new MetadatableUndo(i_rObject
.IsInContent()) );
860 i_rMerged is both a source and the target node of the merge
861 i_rOther is the other source, and will be deleted after the merge
863 dimensions: none|latent|actual empty|nonempty
864 i_rMerged(1) i_rOther(2) result
865 *|empty *|empty => 1|2 (arbitrary)
866 *|empty *|nonempty => 2
867 *|nonempty *|empty => 1
868 none|nonempty none|nonempty => none
869 none|nonempty latent|nonempty => 2
870 latent|nonempty none|nonempty => 1
871 latent|nonempty latent|nonempty => 1|2
872 *|nonempty actual|nonempty => 2
873 actual|nonempty *|nonempty => 1
874 actual|nonempty actual|nonempty => 1|2
877 XmlIdRegistryDocument::JoinMetadatables(
878 Metadatable
& i_rMerged
, Metadatable
const & i_rOther
)
880 OSL_TRACE("JoinMetadatables: %p <- %p\n", &i_rMerged
, &i_rOther
);
883 ::rtl::OUString path
;
884 ::rtl::OUString idref
;
885 if (m_pImpl
->LookupXmlId(i_rMerged
, path
, idref
))
887 mergedOwnsRef
= (m_pImpl
->LookupElement(path
, idref
) == &i_rMerged
);
891 OSL_ENSURE(false, "JoinMetadatables: no xmlid?");
896 i_rMerged
.RemoveMetadataReference();
897 i_rMerged
.RegisterAsCopyOf(i_rOther
, true);
900 // other cases: merged has actual ref and is nonempty,
901 // other has latent/actual ref and is nonempty: other loses => nothing to do
905 //=============================================================================
906 // Clipboard XML ID Registry (_Impl)
910 RMapEntry() : m_pLink() { }
911 RMapEntry(::rtl::OUString
const& i_rStream
,
912 ::rtl::OUString
const& i_rXmlId
,
913 ::boost::shared_ptr
<MetadatableClipboard
> const& i_pLink
914 = ::boost::shared_ptr
<MetadatableClipboard
>())
915 : m_Stream(i_rStream
), m_XmlId(i_rXmlId
), m_pLink(i_pLink
)
917 ::rtl::OUString m_Stream
;
918 ::rtl::OUString m_XmlId
;
919 // this would have been an auto_ptr, if only that would have compiled...
920 ::boost::shared_ptr
<MetadatableClipboard
> m_pLink
;
923 /// element -> (stream name, idref, source)
924 typedef ::std::hash_map
< const Metadatable
*,
926 PtrHash
<Metadatable
> >
927 ClipboardXmlIdReverseMap_t
;
929 /// Idref -> (content.xml element, styles.xml element)
930 typedef ::std::hash_map
< ::rtl::OUString
,
931 ::std::pair
< Metadatable
*, Metadatable
* >, ::rtl::OUStringHash
>
934 struct XmlIdRegistryClipboard::XmlIdRegistry_Impl
937 : m_XmlIdMap(), m_XmlIdReverseMap() { }
939 bool TryInsertMetadatable(Metadatable
& i_xObject
,
940 const ::rtl::OUString
& i_rStream
, const ::rtl::OUString
& i_rIdref
);
942 bool LookupXmlId(const Metadatable
& i_xObject
,
943 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
,
944 MetadatableClipboard
const* &o_rpLink
) const;
946 Metadatable
* LookupElement(const ::rtl::OUString
& i_rStreamName
,
947 const ::rtl::OUString
& i_rIdref
) const;
949 Metadatable
* const* LookupEntry(const ::rtl::OUString
& i_rStreamName
,
950 const ::rtl::OUString
& i_rIdref
) const;
952 Metadatable
* * LookupEntry(const ::rtl::OUString
& i_rStreamName
,
953 const ::rtl::OUString
& i_rIdref
)
955 return const_cast<Metadatable
**>(
956 const_cast<const XmlIdRegistry_Impl
*>(this)
957 ->LookupEntry(i_rStreamName
, i_rIdref
));
960 ClipboardXmlIdMap_t m_XmlIdMap
;
961 ClipboardXmlIdReverseMap_t m_XmlIdReverseMap
;
964 // -------------------------------------------------------------------
967 rmIter(ClipboardXmlIdMap_t
& i_rXmlIdMap
,
968 ClipboardXmlIdMap_t::iterator
const& i_rIter
,
969 ::rtl::OUString
const & i_rStream
, Metadatable
const& i_rObject
)
971 if (i_rIter
!= i_rXmlIdMap
.end())
973 Metadatable
*& rMeta
= isContentFile(i_rStream
)
974 ? i_rIter
->second
.first
: i_rIter
->second
.second
;
975 if (rMeta
== &i_rObject
)
979 if (!i_rIter
->second
.first
&& !i_rIter
->second
.second
)
981 i_rXmlIdMap
.erase(i_rIter
);
986 // -------------------------------------------------------------------
989 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupEntry(
990 const ::rtl::OUString
& i_rStreamName
,
991 const ::rtl::OUString
& i_rIdref
) const
993 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
995 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
996 "illegal XmlId"), 0, 0);
999 const ClipboardXmlIdMap_t::const_iterator
iter( m_XmlIdMap
.find(i_rIdref
) );
1000 if (iter
!= m_XmlIdMap
.end())
1002 OSL_ENSURE(iter
->second
.first
|| iter
->second
.second
,
1003 "null entry in m_XmlIdMap");
1004 return (isContentFile(i_rStreamName
))
1005 ? &iter
->second
.first
1006 : &iter
->second
.second
;
1015 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupElement(
1016 const ::rtl::OUString
& i_rStreamName
,
1017 const ::rtl::OUString
& i_rIdref
) const
1019 Metadatable
* const * ppEntry
= LookupEntry(i_rStreamName
, i_rIdref
);
1020 return ppEntry
? *ppEntry
: 0;
1024 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupXmlId(
1025 const Metadatable
& i_rObject
,
1026 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
,
1027 MetadatableClipboard
const* &o_rpLink
) const
1029 const ClipboardXmlIdReverseMap_t::const_iterator
iter(
1030 m_XmlIdReverseMap
.find(&i_rObject
) );
1031 if (iter
!= m_XmlIdReverseMap
.end())
1033 OSL_ENSURE(!iter
->second
.m_Stream
.equalsAscii(""),
1034 "null stream in m_XmlIdReverseMap");
1035 OSL_ENSURE(!iter
->second
.m_XmlId
.equalsAscii(""),
1036 "null id in m_XmlIdReverseMap");
1037 o_rStream
= iter
->second
.m_Stream
;
1038 o_rIdref
= iter
->second
.m_XmlId
;
1039 o_rpLink
= iter
->second
.m_pLink
.get();
1049 XmlIdRegistryClipboard::XmlIdRegistry_Impl::TryInsertMetadatable(
1050 Metadatable
& i_rObject
,
1051 const ::rtl::OUString
& i_rStreamName
, const ::rtl::OUString
& i_rIdref
)
1053 bool bContent( isContentFile(i_rStreamName
) );
1054 OSL_ENSURE(isContentFile(i_rStreamName
) || isStylesFile(i_rStreamName
),
1057 //wntmsci12 won't parse this:
1058 // Metadatable ** ppEntry( LookupEntry(i_rStreamName, i_rIdref) );
1059 Metadatable
** ppEntry
= LookupEntry(i_rStreamName
, i_rIdref
);
1068 *ppEntry
= &i_rObject
;
1074 m_XmlIdMap
.insert(::std::make_pair(i_rIdref
, bContent
1075 ? ::std::make_pair( &i_rObject
, static_cast<Metadatable
*>(0) )
1076 : ::std::make_pair( static_cast<Metadatable
*>(0), &i_rObject
)));
1081 //=============================================================================
1082 // Clipboard XML ID Registry
1085 XmlIdRegistryClipboard::XmlIdRegistryClipboard()
1086 : m_pImpl( new XmlIdRegistry_Impl
)
1090 XmlIdRegistryClipboard::~XmlIdRegistryClipboard()
1095 XmlIdRegistryClipboard::LookupXmlId(
1096 const Metadatable
& i_rObject
,
1097 ::rtl::OUString
& o_rStream
, ::rtl::OUString
& o_rIdref
) const
1099 const MetadatableClipboard
* pLink
;
1100 return m_pImpl
->LookupXmlId(i_rObject
, o_rStream
, o_rIdref
, pLink
);
1104 XmlIdRegistryClipboard::LookupElement(
1105 const ::rtl::OUString
& i_rStreamName
,
1106 const ::rtl::OUString
& i_rIdref
) const
1108 return m_pImpl
->LookupElement(i_rStreamName
, i_rIdref
);
1112 XmlIdRegistryClipboard::TryRegisterMetadatable(Metadatable
& i_rObject
,
1113 ::rtl::OUString
const& i_rStreamName
, ::rtl::OUString
const& i_rIdref
)
1115 OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject
,
1116 ::rtl::OUStringToOString(i_rStreamName
, RTL_TEXTENCODING_UTF8
).getStr(),
1117 ::rtl::OUStringToOString(i_rIdref
, RTL_TEXTENCODING_UTF8
).getStr());
1119 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
1120 "TryRegisterMetadatable called for MetadatableUndo?");
1121 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
1122 "TryRegisterMetadatable called for MetadatableClipboard?");
1124 if (!isValidXmlId(i_rStreamName
, i_rIdref
))
1126 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
1127 "illegal XmlId"), 0, 0);
1129 if (i_rObject
.IsInContent()
1130 ? !isContentFile(i_rStreamName
)
1131 : !isStylesFile(i_rStreamName
))
1133 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
1134 "illegal XmlId: wrong stream"), 0, 0);
1137 ::rtl::OUString old_path
;
1138 ::rtl::OUString old_idref
;
1139 const MetadatableClipboard
* pLink
;
1140 m_pImpl
->LookupXmlId(i_rObject
, old_path
, old_idref
, pLink
);
1141 if (old_path
== i_rStreamName
&& old_idref
== i_rIdref
)
1143 return (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
);
1145 ClipboardXmlIdMap_t::iterator
old_id( m_pImpl
->m_XmlIdMap
.end() );
1146 if (!old_idref
.equalsAscii(""))
1148 old_id
= m_pImpl
->m_XmlIdMap
.find(old_idref
);
1149 OSL_ENSURE(old_id
!= m_pImpl
->m_XmlIdMap
.end(), "old id not found");
1151 if (m_pImpl
->TryInsertMetadatable(i_rObject
, i_rStreamName
, i_rIdref
))
1153 rmIter(m_pImpl
->m_XmlIdMap
, old_id
, old_path
, i_rObject
);
1154 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] =
1155 RMapEntry(i_rStreamName
, i_rIdref
);
1165 XmlIdRegistryClipboard::RegisterMetadatableAndCreateID(Metadatable
& i_rObject
)
1167 OSL_TRACE("RegisterMetadatableAndCreateID: %p\n", &i_rObject
);
1169 OSL_ENSURE(!dynamic_cast<MetadatableUndo
*>(&i_rObject
),
1170 "RegisterMetadatableAndCreateID called for MetadatableUndo?");
1171 OSL_ENSURE(!dynamic_cast<MetadatableClipboard
*>(&i_rObject
),
1172 "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
1174 bool isInContent( i_rObject
.IsInContent() );
1175 ::rtl::OUString
stream( ::rtl::OUString::createFromAscii(
1176 isInContent
? s_content
: s_styles
) );
1178 ::rtl::OUString old_path
;
1179 ::rtl::OUString old_idref
;
1180 LookupXmlId(i_rObject
, old_path
, old_idref
);
1181 if (!old_idref
.equalsAscii("") &&
1182 (m_pImpl
->LookupElement(old_path
, old_idref
) == &i_rObject
))
1188 const ::rtl::OUString
id( create_id(m_pImpl
->m_XmlIdMap
) );
1189 OSL_ENSURE(m_pImpl
->m_XmlIdMap
.find(id
) == m_pImpl
->m_XmlIdMap
.end(),
1190 "created id is in use");
1191 m_pImpl
->m_XmlIdMap
.insert(::std::make_pair(id
, isInContent
1192 ? ::std::make_pair( &i_rObject
, static_cast<Metadatable
*>(0) )
1193 : ::std::make_pair( static_cast<Metadatable
*>(0), &i_rObject
)));
1194 // N.B.: if i_rObject had a latent XmlId, then we implicitly delete the
1195 // MetadatableClipboard and thus the latent XmlId here
1196 m_pImpl
->m_XmlIdReverseMap
[&i_rObject
] = RMapEntry(stream
, id
);
1199 void XmlIdRegistryClipboard::UnregisterMetadatable(const Metadatable
& i_rObject
)
1201 OSL_TRACE("UnregisterMetadatable: %p\n", &i_rObject
);
1203 ::rtl::OUString path
;
1204 ::rtl::OUString idref
;
1205 const MetadatableClipboard
* pLink
;
1206 if (!m_pImpl
->LookupXmlId(i_rObject
, path
, idref
, pLink
))
1208 OSL_ENSURE(false, "unregister: no xml id?");
1211 const ClipboardXmlIdMap_t::iterator
iter( m_pImpl
->m_XmlIdMap
.find(idref
) );
1212 if (iter
!= m_pImpl
->m_XmlIdMap
.end())
1214 rmIter(m_pImpl
->m_XmlIdMap
, iter
, path
, i_rObject
);
1219 void XmlIdRegistryClipboard::RemoveXmlIdForElement(const Metadatable
& i_rObject
)
1221 OSL_TRACE("RemoveXmlIdForElement: %p\n", &i_rObject
);
1223 ClipboardXmlIdReverseMap_t::iterator
iter(
1224 m_pImpl
->m_XmlIdReverseMap
.find(&i_rObject
) );
1225 if (iter
!= m_pImpl
->m_XmlIdReverseMap
.end())
1227 OSL_ENSURE(!iter
->second
.m_XmlId
.equalsAscii(""),
1228 "null id in m_XmlIdReverseMap");
1229 m_pImpl
->m_XmlIdReverseMap
.erase(iter
);
1233 // -------------------------------------------------------------------
1235 ::boost::shared_ptr
<MetadatableClipboard
>
1236 XmlIdRegistryClipboard::CreateClipboard(const bool i_isInContent
)
1238 OSL_TRACE("CreateClipboard: \n");
1240 return ::boost::shared_ptr
<MetadatableClipboard
>(
1241 new MetadatableClipboard(i_isInContent
) );
1244 MetadatableClipboard
&
1245 XmlIdRegistryClipboard::RegisterCopyClipboard(Metadatable
& i_rCopy
,
1246 beans::StringPair
const & i_rReference
,
1247 const bool i_isLatent
)
1249 OSL_TRACE("RegisterCopyClipboard: %p -> "/*"%p"*/"(%s#%s) (%d)\n",
1250 /*&i_rSource,*/ &i_rCopy
,
1251 ::rtl::OUStringToOString(i_rReference
.First
,
1252 RTL_TEXTENCODING_UTF8
).getStr(),
1253 ::rtl::OUStringToOString(i_rReference
.Second
,
1254 RTL_TEXTENCODING_UTF8
).getStr(),
1257 // N.B.: when copying to the clipboard, the selection is always inserted
1258 // into the body, even if the source is a header/footer!
1259 // so we do not check whether the stream is right in this function
1261 if (!isValidXmlId(i_rReference
.First
, i_rReference
.Second
))
1263 throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
1264 "illegal XmlId"), 0, 0);
1269 // this should succeed assuming clipboard has a single source document
1270 const bool success( m_pImpl
->TryInsertMetadatable(i_rCopy
,
1271 i_rReference
.First
, i_rReference
.Second
) );
1272 OSL_ENSURE(success
, "RegisterCopyClipboard: TryInsert failed?");
1275 const ::boost::shared_ptr
<MetadatableClipboard
> pLink(
1276 CreateClipboard( isContentFile(i_rReference
.First
)) );
1277 m_pImpl
->m_XmlIdReverseMap
.insert(::std::make_pair(&i_rCopy
,
1278 RMapEntry(i_rReference
.First
, i_rReference
.Second
, pLink
)));
1279 return *pLink
.get();
1282 MetadatableClipboard
const*
1283 XmlIdRegistryClipboard::SourceLink(Metadatable
const& i_rObject
)
1285 ::rtl::OUString path
;
1286 ::rtl::OUString idref
;
1287 const MetadatableClipboard
* pLink( 0 );
1288 m_pImpl
->LookupXmlId(i_rObject
, path
, idref
, pLink
);
1293 //=============================================================================
1294 // Metadatable mixin
1297 Metadatable::~Metadatable()
1299 RemoveMetadataReference();
1302 void Metadatable::RemoveMetadataReference()
1308 m_pReg
->UnregisterMetadatable( *this );
1309 m_pReg
->RemoveXmlIdForElement( *this );
1313 catch (uno::Exception
&)
1315 OSL_ENSURE(false, "Metadatable::RemoveMetadataReference: exception");
1319 // ::com::sun::star::rdf::XMetadatable:
1321 Metadatable::GetMetadataReference() const
1325 return m_pReg
->GetXmlIdForElement(*this);
1327 return beans::StringPair();
1331 Metadatable::SetMetadataReference(
1332 const ::com::sun::star::beans::StringPair
& i_rReference
)
1334 if (i_rReference
.Second
.equalsAscii(""))
1336 RemoveMetadataReference();
1340 ::rtl::OUString
streamName( i_rReference
.First
);
1341 if (streamName
.equalsAscii(""))
1343 // handle empty stream name as auto-detect.
1344 // necessary for importing flat file format.
1345 streamName
= ::rtl::OUString::createFromAscii(
1346 IsInContent() ? s_content
: s_styles
);
1348 XmlIdRegistry
& rReg( dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1349 if (rReg
.TryRegisterMetadatable(*this, streamName
, i_rReference
.Second
))
1355 throw lang::IllegalArgumentException(
1356 ::rtl::OUString::createFromAscii("Metadatable::"
1357 "SetMetadataReference: argument is invalid"), /*this*/0, 0);
1362 void Metadatable::EnsureMetadataReference()
1364 XmlIdRegistry
& rReg(
1365 m_pReg
? *m_pReg
: dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1366 rReg
.RegisterMetadatableAndCreateID( *this );
1370 const ::sfx2::IXmlIdRegistry
& GetRegistryConst(Metadatable
const& i_rObject
)
1372 return const_cast< Metadatable
& >( i_rObject
).GetRegistry();
1376 Metadatable::RegisterAsCopyOf(Metadatable
const & i_rSource
,
1377 const bool i_bCopyPrecedesSource
)
1379 OSL_ENSURE(typeid(*this) == typeid(i_rSource
)
1380 || typeid(i_rSource
) == typeid(MetadatableUndo
)
1381 || typeid(*this) == typeid(MetadatableUndo
)
1382 || typeid(i_rSource
) == typeid(MetadatableClipboard
)
1383 || typeid(*this) == typeid(MetadatableClipboard
),
1384 "RegisterAsCopyOf element with different class?");
1385 OSL_ENSURE(!this->m_pReg
, "RegisterAsCopyOf called on element with XmlId?");
1389 RemoveMetadataReference();
1394 if (i_rSource
.m_pReg
)
1396 XmlIdRegistry
& rReg(
1397 dynamic_cast<XmlIdRegistry
&>( GetRegistry() ) );
1398 if (i_rSource
.m_pReg
== &rReg
)
1400 OSL_ENSURE(!IsInClipboard(),
1401 "RegisterAsCopy: both in clipboard?");
1402 if (!IsInClipboard())
1404 XmlIdRegistryDocument
& rRegDoc(
1405 dynamic_cast<XmlIdRegistryDocument
&>( rReg
) );
1406 rRegDoc
.RegisterCopy(i_rSource
, *this,
1407 i_bCopyPrecedesSource
);
1408 this->m_pReg
= &rRegDoc
;
1412 // source is in different document
1413 XmlIdRegistryDocument
* pRegDoc(
1414 dynamic_cast<XmlIdRegistryDocument
*>(&rReg
) );
1415 XmlIdRegistryClipboard
* pRegClp(
1416 dynamic_cast<XmlIdRegistryClipboard
*>(&rReg
) );
1420 beans::StringPair
SourceRef(
1421 i_rSource
.m_pReg
->GetXmlIdForElement(i_rSource
) );
1422 bool isLatent( SourceRef
.Second
.equalsAscii("") );
1423 XmlIdRegistryDocument
* pSourceRegDoc(
1424 dynamic_cast<XmlIdRegistryDocument
*>(i_rSource
.m_pReg
) );
1425 OSL_ENSURE(pSourceRegDoc
, "RegisterAsCopyOf: 2 clipboards?");
1426 if (!pSourceRegDoc
) return;
1427 // this is a copy _to_ the clipboard
1430 pSourceRegDoc
->LookupXmlId(i_rSource
,
1431 SourceRef
.First
, SourceRef
.Second
);
1433 Metadatable
& rLink(
1434 pRegClp
->RegisterCopyClipboard(*this, SourceRef
, isLatent
));
1435 this->m_pReg
= pRegClp
;
1436 // register as copy in the non-clipboard registry
1437 pSourceRegDoc
->RegisterCopy(i_rSource
, rLink
,
1438 false); // i_bCopyPrecedesSource);
1439 rLink
.m_pReg
= pSourceRegDoc
;
1443 XmlIdRegistryClipboard
* pSourceRegClp(
1444 dynamic_cast<XmlIdRegistryClipboard
*>(i_rSource
.m_pReg
) );
1445 OSL_ENSURE(pSourceRegClp
,
1446 "RegisterAsCopyOf: 2 non-clipboards?");
1447 if (!pSourceRegClp
) return;
1448 const MetadatableClipboard
* pLink(
1449 pSourceRegClp
->SourceLink(i_rSource
) );
1450 // may happen if src got its id via UNO call
1452 // only register copy if clipboard content is from this SwDoc!
1453 if (pLink
&& (&GetRegistryConst(*pLink
) == pRegDoc
))
1455 // this is a copy _from_ the clipboard; check if the
1456 // element is still in the same stream
1457 // N.B.: we check the stream of pLink, not of i_rSource!
1458 bool srcInContent( pLink
->IsInContent() );
1459 bool tgtInContent( this->IsInContent() );
1460 if (srcInContent
== tgtInContent
)
1462 pRegDoc
->RegisterCopy(*pLink
, *this,
1463 true); // i_bCopyPrecedesSource);
1464 this->m_pReg
= pRegDoc
;
1466 // otherwise: stream change! do not register!
1471 OSL_ENSURE(false, "neither RegDoc nor RegClp cannot happen");
1475 //FIXME: do we need this at all???
1476 XmlIdRegistryDocument
& rRegDoc(
1477 dynamic_cast<XmlIdRegistryDocument
&>( rReg
) );
1479 if (rRegDoc
.TryRegisterMetadatable(*this, SourceRef
))
1481 this->m_pReg
= &rRegDoc
;
1488 catch (uno::Exception
&)
1490 OSL_ENSURE(false, "Metadatable::RegisterAsCopyOf: exception");
1494 ::boost::shared_ptr
<MetadatableUndo
> Metadatable::CreateUndo(
1495 const bool i_isDelete
)
1497 OSL_ENSURE(!IsInUndo(), "CreateUndo called for object in undo?");
1498 OSL_ENSURE(!IsInClipboard(), "CreateUndo called for object in clipboard?");
1501 if (!IsInClipboard() && !IsInUndo() && m_pReg
)
1503 XmlIdRegistryDocument
* pRegDoc(
1504 dynamic_cast<XmlIdRegistryDocument
*>( m_pReg
) );
1505 ::boost::shared_ptr
<MetadatableUndo
> pUndo(
1506 pRegDoc
->CreateUndo(*this) );
1507 pRegDoc
->RegisterCopy(*this, *pUndo
, false);
1508 pUndo
->m_pReg
= pRegDoc
;
1512 RemoveMetadataReference();
1517 catch (uno::Exception
&)
1519 OSL_ENSURE(false, "Metadatable::CreateUndo: exception");
1521 return ::boost::shared_ptr
<MetadatableUndo
>();
1524 void Metadatable::RestoreMetadata(
1525 ::boost::shared_ptr
<MetadatableUndo
> const& i_pUndo
)
1527 OSL_ENSURE(!IsInUndo(), "RestoreMetadata called for object in undo?");
1528 OSL_ENSURE(!IsInClipboard(),
1529 "RestoreMetadata called for object in clipboard?");
1530 if (IsInClipboard() || IsInUndo()) return;
1531 RemoveMetadataReference();
1534 this->RegisterAsCopyOf(*i_pUndo
, true);
1539 Metadatable::JoinMetadatable(Metadatable
const & i_rOther
,
1540 const bool i_isMergedEmpty
, const bool i_isOtherEmpty
)
1542 OSL_ENSURE(!IsInUndo(), "JoinMetadatables called for object in undo?");
1543 OSL_ENSURE(!IsInClipboard(),
1544 "JoinMetadatables called for object in clipboard?");
1545 if (IsInClipboard() || IsInUndo()) return;
1547 if (i_isOtherEmpty
&& !i_isMergedEmpty
)
1549 // other is empty, thus loses => nothing to do
1552 if (i_isMergedEmpty
&& !i_isOtherEmpty
)
1554 this->RemoveMetadataReference();
1555 this->RegisterAsCopyOf(i_rOther
, true);
1559 if (!i_rOther
.m_pReg
)
1561 // other doesn't have xmlid, thus loses => nothing to do
1566 this->RegisterAsCopyOf(i_rOther
, true);
1567 // assumption: i_rOther will be deleted, so don't unregister it here
1572 XmlIdRegistryDocument
* pRegDoc(
1573 dynamic_cast<XmlIdRegistryDocument
*>( m_pReg
) );
1574 OSL_ENSURE(pRegDoc
, "JoinMetadatable: no pRegDoc?");
1577 pRegDoc
->JoinMetadatables(*this, i_rOther
);
1580 catch (uno::Exception
&)
1582 OSL_ENSURE(false, "Metadatable::JoinMetadatable: exception");
1587 //=============================================================================
1588 // XMetadatable mixin
1590 // ::com::sun::star::rdf::XNode:
1591 ::rtl::OUString SAL_CALL
MetadatableMixin::getStringValue()
1592 throw (::com::sun::star::uno::RuntimeException
)
1594 return getNamespace() + getLocalName();
1597 // ::com::sun::star::rdf::XURI:
1598 ::rtl::OUString SAL_CALL
MetadatableMixin::getLocalName()
1599 throw (::com::sun::star::uno::RuntimeException
)
1601 ::vos::OGuard
aGuard( Application::GetSolarMutex() );
1602 beans::StringPair
mdref( getMetadataReference() );
1603 if (!mdref
.Second
.getLength())
1605 ensureMetadataReference(); // N.B.: side effect!
1606 mdref
= getMetadataReference();
1608 ::rtl::OUStringBuffer buf
;
1609 buf
.append(mdref
.First
);
1610 buf
.append(static_cast<sal_Unicode
>('#'));
1611 buf
.append(mdref
.Second
);
1612 return buf
.makeStringAndClear();
1615 ::rtl::OUString SAL_CALL
MetadatableMixin::getNamespace()
1616 throw (::com::sun::star::uno::RuntimeException
)
1618 ::vos::OGuard
aGuard( Application::GetSolarMutex() );
1619 const uno::Reference
< frame::XModel
> xModel( GetModel() );
1620 const uno::Reference
< rdf::XURI
> xDMA( xModel
, uno::UNO_QUERY_THROW
);
1621 return xDMA
->getStringValue();
1624 // ::com::sun::star::rdf::XMetadatable:
1625 beans::StringPair SAL_CALL
1626 MetadatableMixin::getMetadataReference()
1627 throw (uno::RuntimeException
)
1629 ::vos::OGuard
aGuard( Application::GetSolarMutex() );
1630 Metadatable
* pObject( GetCoreObject() );
1633 return pObject
->GetMetadataReference();
1637 throw uno::RuntimeException();
1642 MetadatableMixin::setMetadataReference(
1643 const beans::StringPair
& i_rReference
)
1644 throw (uno::RuntimeException
, lang::IllegalArgumentException
)
1646 ::vos::OGuard
aGuard( Application::GetSolarMutex() );
1647 Metadatable
* pObject( GetCoreObject() );
1650 return pObject
->SetMetadataReference(i_rReference
);
1654 throw uno::RuntimeException();
1658 void SAL_CALL
MetadatableMixin::ensureMetadataReference()
1659 throw (uno::RuntimeException
)
1661 ::vos::OGuard
aGuard( Application::GetSolarMutex() );
1662 Metadatable
* pObject( GetCoreObject() );
1665 return pObject
->EnsureMetadataReference();
1669 throw uno::RuntimeException();
1676 //=============================================================================
1678 #if OSL_DEBUG_LEVEL > 1
1680 static ::sfx2::XmlIdRegistryDocument s_Reg
;
1681 static ::sfx2::XmlIdRegistryClipboard s_RegClip
;
1683 class MockMetadatable
: public ::sfx2::Metadatable
1686 MockMetadatable(bool i_isInClip
= false) :
1687 m_bInClipboard(i_isInClip
), m_bInUndo(false), m_bInContent(true) {}
1688 bool m_bInClipboard
;
1691 virtual bool IsInClipboard() const { return m_bInClipboard
; }
1692 virtual bool IsInUndo() const { return m_bInUndo
; }
1693 virtual bool IsInContent() const { return m_bInContent
; }
1694 virtual ::sfx2::XmlIdRegistry
& GetRegistry() { return m_bInClipboard
? static_cast< ::sfx2::XmlIdRegistry
&>(s_RegClip
) : static_cast< ::sfx2::XmlIdRegistry
&>(s_Reg
); }
1695 virtual ::com::sun::star::uno::Reference
<
1696 ::com::sun::star::rdf::XMetadatable
> MakeUnoObject() { return 0; }
1699 bool operator==(beans::StringPair p1
, beans::StringPair p2
)
1701 return p1
.First
== p2
.First
&& p1
.Second
== p2
.Second
;
1706 OSL_TRACE("SwMetadatable test(): start\n");
1712 ::rtl::OUString empty
;
1713 ::rtl::OUString
content( ::rtl::OUString::createFromAscii("content.xml") );
1714 ::rtl::OUString
styles ( ::rtl::OUString::createFromAscii("styles.xml") );
1715 ::rtl::OUString
sid1( ::rtl::OUString::createFromAscii("id1") );
1716 ::rtl::OUString
sid2( ::rtl::OUString::createFromAscii("id2") );
1717 ::rtl::OUString
sid3( ::rtl::OUString::createFromAscii("id3") );
1718 ::rtl::OUString
sid4( ::rtl::OUString::createFromAscii("id4") );
1719 beans::StringPair
id1(content
, sid1
);
1720 beans::StringPair
id2(content
, sid2
);
1721 beans::StringPair
id3(content
, sid3
);
1722 beans::StringPair
id4(styles
, sid4
);
1723 beans::StringPair
id3e(empty
, sid3
);
1724 beans::StringPair
id4e(empty
, sid4
);
1725 m1
.SetMetadataReference(id1
);
1726 OSL_ENSURE(m1
.GetMetadataReference() == id1
, "set failed");
1728 m2
.SetMetadataReference(id1
);
1729 OSL_ENSURE(false, "set duplicate succeeded");
1730 } catch (lang::IllegalArgumentException
) { }
1731 m1
.SetMetadataReference(id1
);
1732 OSL_ENSURE(m1
.GetMetadataReference() == id1
, "set failed (existing)");
1733 m1
.EnsureMetadataReference();
1734 OSL_ENSURE(m1
.GetMetadataReference() == id1
, "ensure failed (existing)");
1736 m2
.EnsureMetadataReference();
1737 beans::StringPair
m2id(m2
.GetMetadataReference());
1738 OSL_ENSURE(m2id
.Second
.getLength(), "ensure failed");
1739 m2
.EnsureMetadataReference();
1740 OSL_ENSURE(m2
.GetMetadataReference() == m2id
, "ensure failed (idempotent)");
1742 m1
.m_bInUndo
= true;
1743 OSL_ENSURE(!m1
.GetMetadataReference().Second
.getLength(), "move to undo failed");
1745 m1
.m_bInUndo
= false;
1746 OSL_ENSURE(m1
.GetMetadataReference() == id1
, "move from undo failed");
1748 m1
.m_bInUndo
= true;
1750 m2
.SetMetadataReference(id1
); // steal!
1751 } catch (lang::IllegalArgumentException
&) {
1752 OSL_ENSURE(false, "set duplicate to undo failed");
1754 m1
.m_bInUndo
= false;
1755 OSL_ENSURE(!m1
.GetMetadataReference().Second
.getLength(), "move from undo: duplicate");
1757 m3
.RegisterAsCopyOf(m2
);
1758 OSL_ENSURE(m2
.GetMetadataReference() == id1
, "copy: source");
1759 OSL_ENSURE(!m3
.GetMetadataReference().Second
.getLength(), "copy: duplicate");
1760 m4
.RegisterAsCopyOf(m3
);
1761 OSL_ENSURE(m2
.GetMetadataReference() == id1
, "copy: source");
1762 OSL_ENSURE(!m3
.GetMetadataReference().Second
.getLength(), "copy: duplicate");
1763 OSL_ENSURE(!m4
.GetMetadataReference().Second
.getLength(), "copy: duplicate");
1764 m2
.m_bInUndo
= true;
1765 OSL_ENSURE(m3
.GetMetadataReference() == id1
, "duplicate to undo");
1766 OSL_ENSURE(!m2
.GetMetadataReference().Second
.getLength(), "duplicate to undo");
1767 m2
.m_bInUndo
= false;
1768 OSL_ENSURE(m2
.GetMetadataReference() == id1
, "duplicate from undo");
1769 OSL_ENSURE(!m3
.GetMetadataReference().Second
.getLength(), "duplicate from undo");
1771 m4
.EnsureMetadataReference(); // new!
1772 beans::StringPair
m4id(m4
.GetMetadataReference());
1773 OSL_ENSURE(m4id
.Second
.getLength() && !(m4id
== id1
), "ensure on duplicate");
1775 MockMetadatable
mc1(true); // in clipboard
1776 MockMetadatable
mc2(true);
1777 MockMetadatable
mc3(true);
1778 MockMetadatable
mc4(true);
1779 MockMetadatable m2p
;
1780 MockMetadatable m3p
;
1782 mc1
.SetMetadataReference(id2
);
1783 OSL_ENSURE(mc1
.GetMetadataReference() == id2
, "set failed");
1785 mc2
.SetMetadataReference(id2
);
1786 OSL_ENSURE(false, "set duplicate succeeded");
1787 } catch (lang::IllegalArgumentException
) { }
1788 mc1
.SetMetadataReference(id2
);
1789 OSL_ENSURE(mc1
.GetMetadataReference() == id2
, "set failed (existing)");
1790 mc1
.EnsureMetadataReference();
1791 OSL_ENSURE(mc1
.GetMetadataReference() == id2
, "ensure failed (existing)");
1792 mc2
.EnsureMetadataReference();
1793 beans::StringPair
mc2id(mc2
.GetMetadataReference());
1794 OSL_ENSURE(mc2id
.Second
.getLength(), "ensure failed");
1795 mc2
.EnsureMetadataReference();
1796 OSL_ENSURE(mc2
.GetMetadataReference() == mc2id
, "ensure failed (idempotent)");
1797 mc2
.RemoveMetadataReference();
1798 OSL_ENSURE(!mc2
.GetMetadataReference().Second
.getLength(), "remove failed");
1800 // set up mc2 as copy of m2 and mc3 as copy of m3
1801 mc3
.RegisterAsCopyOf(m3
);
1802 OSL_ENSURE(!mc3
.GetMetadataReference().Second
.getLength() , "copy to clipboard (latent)");
1803 mc2
.RegisterAsCopyOf(m2
);
1804 OSL_ENSURE(mc2
.GetMetadataReference() == id1
, "copy to clipboard (non-latent)");
1805 // paste mc2 to m2p and mc3 to m3p
1806 m2p
.RegisterAsCopyOf(mc2
);
1807 OSL_ENSURE(!m2p
.GetMetadataReference().Second
.getLength() , "paste from clipboard (non-latent)");
1808 m3p
.RegisterAsCopyOf(mc3
);
1809 OSL_ENSURE(!m3p
.GetMetadataReference().Second
.getLength() , "paste from clipboard (latent)");
1810 // delete m2, m2p, m3
1811 m2
.RemoveMetadataReference();
1812 OSL_ENSURE(!m2
.GetMetadataReference().Second
.getLength(), "remove failed");
1813 OSL_ENSURE(m2p
.GetMetadataReference() == id1
, "paste-remove (non-latent)");
1814 m2p
.RemoveMetadataReference();
1815 OSL_ENSURE(!m2p
.GetMetadataReference().Second
.getLength(), "remove failed");
1816 OSL_ENSURE(m3
.GetMetadataReference() == id1
, "paste-remove2 (non-latent)");
1817 m3
.RemoveMetadataReference();
1818 OSL_ENSURE(!m3
.GetMetadataReference().Second
.getLength(), "remove failed");
1819 OSL_ENSURE(m3p
.GetMetadataReference() == id1
, "paste-remove (latent)");
1821 mc2
.SetMetadataReference(beans::StringPair());
1822 OSL_ENSURE(!mc3
.GetMetadataReference().Second
.getLength() , "in clipboard becomes non-latent");
1824 m2p
.RegisterAsCopyOf(mc2
);
1825 OSL_ENSURE(!m2p
.GetMetadataReference().Second
.getLength(), "remove-paste");
1826 OSL_ENSURE(m3p
.GetMetadataReference() == id1
, "remove-paste (stolen)");
1828 // auto-detect stream
1829 m5
.SetMetadataReference(id3e
);
1830 OSL_ENSURE(m5
.GetMetadataReference() == id3
, "auto-detect (content)");
1831 m5
.m_bInContent
= false;
1832 m5
.SetMetadataReference(id4e
);
1833 OSL_ENSURE(m5
.GetMetadataReference() == id4
, "auto-detect (styles)");
1835 OSL_TRACE("sfx2::Metadatable test(): finished\n");
1838 struct Test
{ Test() { test(); } };
1844 static void dump(sfx2::XmlIdList_t
* pList
)
1846 __attribute__ ((unused
))
1849 static void dump(sfx2::XmlIdList_t
* pList
)
1851 fprintf(stderr
, "\nXmlIdList(%p): ", pList
);
1852 for (sfx2::XmlIdList_t::iterator i
= pList
->begin(); i
!= pList
->end(); ++i
)
1854 fprintf(stderr
, "%p ", *i
);
1856 fprintf(stderr
, "\n");