Update ooo320-m1
[ooovba.git] / sfx2 / source / doc / Metadatable.cxx
blob433b2512df871c4e166cc602ad01e11948477474
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>
41 #include <memory>
42 #include <hash_map>
43 #include <list>
44 #include <algorithm>
45 #if OSL_DEBUG_LEVEL > 0
46 #include <typeinfo>
47 #endif
50 /** XML ID handling.
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
77 will have the XML ID.
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
106 that same XML ID.
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>.
113 @author mst
117 using namespace ::com::sun::star;
119 using ::sfx2::isValidXmlId;
122 namespace sfx2 {
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
156 public:
157 XmlIdRegistry();
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.
172 </p>
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.
185 </p>
187 @returns
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)
192 = 0;
194 /** unregister an ODF element.
197 Unregister the element at its metadata reference.
198 Does not remove the metadata reference from the element.
199 </p>
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;
212 protected:
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
227 public:
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;
257 private:
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;
273 public:
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 ?");
281 return *m_pReg;
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;
298 public:
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 ?");
306 return *m_pReg;
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
322 public:
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);
343 private:
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 //=============================================================================
360 // XmlIdRegistry
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;
386 beans::StringPair
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;
410 ::rtl::OUString id;
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());
418 return id;
421 //=============================================================================
422 // Document XML ID Registry (_Impl)
424 /// element list
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> >
443 XmlIdReverseMap_t;
445 struct XmlIdRegistryDocument::XmlIdRegistry_Impl
447 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 // -------------------------------------------------------------------
478 static void
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 // -------------------------------------------------------------------
496 const XmlIdList_t *
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;
510 else
512 return 0;
516 Metadatable*
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) );
528 if (pList)
530 const XmlIdList_t::const_iterator iter(
531 ::std::find_if(pList->begin(), pList->end(),
532 ::boost::bind(
533 ::std::logical_not<bool>(),
534 ::boost::bind(
535 ::std::logical_or<bool>(),
536 ::boost::bind( &Metadatable::IsInUndo, _1 ),
537 ::boost::bind( &Metadatable::IsInClipboard, _1 )
538 ) ) ) );
539 if (iter != pList->end())
541 return *iter;
544 return 0;
547 bool
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;
562 return true;
564 else
566 return false;
570 bool
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),
577 "invalid stream");
579 XmlIdList_t * pList( LookupElementList(i_rStreamName, i_rIdref) );
580 if (pList)
582 if (pList->empty())
584 pList->push_back( &i_rObject );
585 return true;
587 else
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(),
593 ::boost::bind(
594 ::std::logical_not<bool>(),
595 ::boost::bind(
596 ::std::logical_or<bool>(),
597 ::boost::bind( &Metadatable::IsInUndo, _1 ),
598 ::boost::bind( &Metadatable::IsInClipboard, _1 )
599 ) ) ) )
601 // ??? this is not undoable
602 // pList->clear();
603 // pList->push_back( &i_rObject );
604 pList->push_front( &i_rObject );
605 return true;
607 else
609 return false;
613 else
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 ) )));
618 return true;
622 //=============================================================================
623 // Document XML ID Registry
626 XmlIdRegistryDocument::XmlIdRegistryDocument()
627 : m_pImpl( new XmlIdRegistry_Impl )
631 static void
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 ?");
641 if (pLink)
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(),
655 removeLink);
656 ::std::for_each(iter->second.second.begin(), iter->second.second.end(),
657 removeLink);
661 bool
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);
669 Metadatable*
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);
677 bool
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);
721 return true;
723 else
725 return false;
729 void
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)
754 return;
756 else
758 // remove latent xmlid
759 rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
763 // create id
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?");
782 return;
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?");
825 return;
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())
835 return;
837 if (i_bCopyPrecedesSource)
839 pList->insert( srcpos, &i_rCopy );
841 else
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
876 void
877 XmlIdRegistryDocument::JoinMetadatables(
878 Metadatable & i_rMerged, Metadatable const & i_rOther)
880 OSL_TRACE("JoinMetadatables: %p <- %p\n", &i_rMerged, &i_rOther);
882 bool mergedOwnsRef;
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);
889 else
891 OSL_ENSURE(false, "JoinMetadatables: no xmlid?");
892 return;
894 if (!mergedOwnsRef)
896 i_rMerged.RemoveMetadataReference();
897 i_rMerged.RegisterAsCopyOf(i_rOther, true);
898 return;
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)
908 struct RMapEntry
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*,
925 struct RMapEntry,
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 >
932 ClipboardXmlIdMap_t;
934 struct XmlIdRegistryClipboard::XmlIdRegistry_Impl
936 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 // -------------------------------------------------------------------
966 static void
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)
977 rMeta = 0;
979 if (!i_rIter->second.first && !i_rIter->second.second)
981 i_rXmlIdMap.erase(i_rIter);
986 // -------------------------------------------------------------------
988 Metadatable* const*
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;
1008 else
1010 return 0;
1014 Metadatable*
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;
1023 bool
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();
1040 return true;
1042 else
1044 return false;
1048 bool
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),
1055 "invalid stream");
1057 //wntmsci12 won't parse this:
1058 // Metadatable ** ppEntry( LookupEntry(i_rStreamName, i_rIdref) );
1059 Metadatable ** ppEntry = LookupEntry(i_rStreamName, i_rIdref);
1060 if (ppEntry)
1062 if (*ppEntry)
1064 return false;
1066 else
1068 *ppEntry = &i_rObject;
1069 return true;
1072 else
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 )));
1077 return true;
1081 //=============================================================================
1082 // Clipboard XML ID Registry
1085 XmlIdRegistryClipboard::XmlIdRegistryClipboard()
1086 : m_pImpl( new XmlIdRegistry_Impl )
1090 XmlIdRegistryClipboard::~XmlIdRegistryClipboard()
1094 bool
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);
1103 Metadatable*
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);
1111 bool
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);
1156 return true;
1158 else
1160 return false;
1164 void
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))
1184 return;
1187 // create id
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?");
1209 return;
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(),
1255 i_isLatent);
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);
1267 if (!i_isLatent)
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?");
1273 (void) success;
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);
1289 return pLink;
1293 //=============================================================================
1294 // Metadatable mixin
1297 Metadatable::~Metadatable()
1299 RemoveMetadataReference();
1302 void Metadatable::RemoveMetadataReference()
1306 if (m_pReg)
1308 m_pReg->UnregisterMetadatable( *this );
1309 m_pReg->RemoveXmlIdForElement( *this );
1310 m_pReg = 0;
1313 catch (uno::Exception &)
1315 OSL_ENSURE(false, "Metadatable::RemoveMetadataReference: exception");
1319 // ::com::sun::star::rdf::XMetadatable:
1320 beans::StringPair
1321 Metadatable::GetMetadataReference() const
1323 if (m_pReg)
1325 return m_pReg->GetXmlIdForElement(*this);
1327 return beans::StringPair();
1330 void
1331 Metadatable::SetMetadataReference(
1332 const ::com::sun::star::beans::StringPair & i_rReference)
1334 if (i_rReference.Second.equalsAscii(""))
1336 RemoveMetadataReference();
1338 else
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))
1351 m_pReg = &rReg;
1353 else
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 );
1367 m_pReg = &rReg;
1370 const ::sfx2::IXmlIdRegistry& GetRegistryConst(Metadatable const& i_rObject)
1372 return const_cast< Metadatable& >( i_rObject ).GetRegistry();
1375 void
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?");
1387 if (this->m_pReg)
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;
1410 return;
1412 // source is in different document
1413 XmlIdRegistryDocument * pRegDoc(
1414 dynamic_cast<XmlIdRegistryDocument *>(&rReg) );
1415 XmlIdRegistryClipboard * pRegClp(
1416 dynamic_cast<XmlIdRegistryClipboard*>(&rReg) );
1418 if (pRegClp)
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
1428 if (isLatent)
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;
1441 else if (pRegDoc)
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
1451 if (!pLink) return;
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!
1469 else
1471 OSL_ENSURE(false, "neither RegDoc nor RegClp cannot happen");
1473 #if 0
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;
1485 #endif
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;
1510 if (i_isDelete)
1512 RemoveMetadataReference();
1514 return pUndo;
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();
1532 if (i_pUndo)
1534 this->RegisterAsCopyOf(*i_pUndo, true);
1538 void
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
1550 return;
1552 if (i_isMergedEmpty && !i_isOtherEmpty)
1554 this->RemoveMetadataReference();
1555 this->RegisterAsCopyOf(i_rOther, true);
1556 return;
1559 if (!i_rOther.m_pReg)
1561 // other doesn't have xmlid, thus loses => nothing to do
1562 return;
1564 if (!m_pReg)
1566 this->RegisterAsCopyOf(i_rOther, true);
1567 // assumption: i_rOther will be deleted, so don't unregister it here
1568 return;
1572 XmlIdRegistryDocument * pRegDoc(
1573 dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
1574 OSL_ENSURE(pRegDoc, "JoinMetadatable: no pRegDoc?");
1575 if (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() );
1631 if (pObject)
1633 return pObject->GetMetadataReference();
1635 else
1637 throw uno::RuntimeException();
1641 void SAL_CALL
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() );
1648 if (pObject)
1650 return pObject->SetMetadataReference(i_rReference);
1652 else
1654 throw uno::RuntimeException();
1658 void SAL_CALL MetadatableMixin::ensureMetadataReference()
1659 throw (uno::RuntimeException)
1661 ::vos::OGuard aGuard( Application::GetSolarMutex() );
1662 Metadatable* pObject( GetCoreObject() );
1663 if (pObject)
1665 return pObject->EnsureMetadataReference();
1667 else
1669 throw uno::RuntimeException();
1673 } // namespace sfx2
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
1685 public:
1686 MockMetadatable(bool i_isInClip = false) :
1687 m_bInClipboard(i_isInClip), m_bInUndo(false), m_bInContent(true) {}
1688 bool m_bInClipboard;
1689 bool m_bInUndo;
1690 bool m_bInContent;
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;
1704 void test()
1706 OSL_TRACE("SwMetadatable test(): start\n");
1707 MockMetadatable m1;
1708 MockMetadatable m2;
1709 MockMetadatable m3;
1710 MockMetadatable m4;
1711 MockMetadatable m5;
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");
1727 try {
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;
1749 try {
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");
1784 try {
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)");
1820 // delete mc2
1821 mc2.SetMetadataReference(beans::StringPair());
1822 OSL_ENSURE(!mc3.GetMetadataReference().Second.getLength() , "in clipboard becomes non-latent");
1823 // paste mc2
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(); } };
1839 static Test s_test;
1842 #include <stdio.h>
1844 static void dump(sfx2::XmlIdList_t * pList)
1845 #ifdef GCC
1846 __attribute__ ((unused))
1847 #endif
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");
1859 #endif