xref: /aoo42x/main/sfx2/source/doc/Metadatable.cxx (revision 86e1cf34)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "precompiled_sfx2.hxx"
25 
26 #include <sfx2/Metadatable.hxx>
27 #include <sfx2/XmlIdRegistry.hxx>
28 
29 #include <vos/mutex.hxx>
30 #include <vcl/svapp.hxx> // solarmutex
31 
32 #include <rtl/random.h>
33 
34 #include <boost/bind.hpp>
35 
36 #include <memory>
37 #include <hash_map>
38 #include <list>
39 #include <algorithm>
40 #if OSL_DEBUG_LEVEL > 0
41 #include <typeinfo>
42 #endif
43 
44 
45 /** XML ID handling.
46 
47     There is an abstract base class <type>XmlIdRegistry</type>, with
48     2 subclasses <type>XmlIdRegistryDocument</type> for "normal" documents,
49     and <type>XmlIdRegistryClipboard</type> for clipboard documents.
50     These classes are responsible for managing XML IDs for all elements
51     of the model. Only the implementation of the <type>Metadatable</type>
52     base class needs to know the registries, so they are not in the header.
53 
54     The handling of XML IDs differs between clipboard and non-clipboard
55     documents in several aspects. Most importantly, non-clipboard documents
56     can have several elements associated with one XML ID.
57     This is necessary because of the weird undo implementation:
58     deleting a text node moves the deleted node to the undo array, but
59     executing undo will then create a <em>copy</em> of that node in the
60     document array. These 2 nodes must have the same XML ID, because
61     we cannot know whether the user will do a redo next, or something else.
62 
63     Because we need to have a mechanism for several objects per XML ID anyway,
64     we use that also to enable some usability features:
65     The document registry has a list of Metadatables per XML ID.
66     This list is sorted by priority, i.e., the first element has highest
67     priority. When inserting copies, care must be taken that they are inserted
68     at the right position: either before or after the source.
69     This is done by <method>Metadatable::RegisterAsCopyOf</method>.
70     When a text node is split, then both resulting text nodes are inserted
71     into the list. If the user then deletes one text node, the other one
72     will have the XML ID.
73     Also, when a Metadatable is copied to the clipboard and then pasted,
74     the copy is inserted into the list. If the user then deletes the source,
75     the XML ID is not lost.
76     The goal is that it should be hard to lose an XML ID by accident, which
77     is especially important as long as we do not have an UI that displays them.
78 
79     There are two subclasses of <type>Metadatable</type>:
80     <ul><li><type>MetadatableClipboard</type>: for copies in the clipboard</li>
81         <li><type>MetadatableUndo</type>: for undo, because a Metadatable
82         may be destroyed on delete and a new one created on undo.</li></ul>
83     These serve only to track the position in an XML ID list in a document
84     registry, so that future actions can insert objects at the right position.
85     Unfortunately, inserting dummy objects seems to be necessary:
86     <ul><li>it is not sufficient to just remember the saved id, because then
87             the relative priorities might change when executing the undo</li>
88         <li>it is not sufficient to record the position as an integer, because
89             if we delete a text node and then undo, the node will be copied(!),
90             and we will have one more node in the list.<li>
91         <li>it is not sufficient to record the pointer of the previous/next
92             Metadatable, because if we delete a text node, undo, and then
93             do something to clear the redo array, the original text node is
94             destroyed, and is replaced by the copy created by undo</li></ul>
95 
96     If content from a non-clipboard document is copied into a clipboard
97     document, a dummy <type>MetadatableClipboard</type> is inserted into the
98     non-clipboard document registry in order to track the position of the
99     source element.  When the clipboard content is pasted back into the source
100     document, this dummy object is used to associate the pasted element with
101     that same XML ID.
102 
103     If a <type>Metadatable</type> is deleted or merged,
104     <method>Metadatable::CreateUndo</method> is called, and returns a
105     <type>MetadatableUndo<type> instance, which can be used to undo the action
106     by passing it to <method>Metadatable::RestoreMetadata</method>.
107 
108     @author mst
109  */
110 
111 
112 using namespace ::com::sun::star;
113 
114 using ::sfx2::isValidXmlId;
115 
116 
117 namespace sfx2 {
118 
119 static const char s_content [] = "content.xml";
120 static const char s_styles  [] = "styles.xml";
121 static const char s_prefix  [] = "id";  // prefix for generated xml:id
122 
isContentFile(::rtl::OUString const & i_rPath)123 static bool isContentFile(::rtl::OUString const & i_rPath)
124 {
125     return i_rPath.equalsAscii(s_content);
126 }
127 
isStylesFile(::rtl::OUString const & i_rPath)128 static bool isStylesFile (::rtl::OUString const & i_rPath)
129 {
130     return i_rPath.equalsAscii(s_styles);
131 }
132 
133 
134 //=============================================================================
135 // XML ID handling ---------------------------------------------------
136 
137 /** handles registration of XMetadatable.
138 
139     This class is responsible for guaranteeing that XMetadatable objects
140     always have XML IDs that are unique within a stream.
141 
142     This is an abstract base class; see subclasses XmlIdRegistryDocument and
143     XmlIdRegistryClipboard.
144 
145     @see SwDoc::GetXmlIdRegistry
146     @see SwDocShell::GetXmlIdRegistry
147  */
148 class XmlIdRegistry : public sfx2::IXmlIdRegistry
149 {
150 
151 public:
152     XmlIdRegistry();
153 
154     virtual ~XmlIdRegistry();
155 
156     /** get the ODF element with the given metadata reference. */
157     virtual ::com::sun::star::uno::Reference<
158             ::com::sun::star::rdf::XMetadatable > SAL_CALL
159         GetElementByMetadataReference(
160             const ::com::sun::star::beans::StringPair & i_rReference) const;
161 
162     /** register an ODF element at a newly generated, unique metadata reference.
163 
164         <p>
165         Find a fresh XML ID, and register it for the element.
166         The generated ID does not occur in any stream of the document.
167         </p>
168      */
169     virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject) = 0;
170 
171     /** try to register an ODF element at a given XML ID, or update its
172         registation to a different XML ID.
173 
174         <p>
175         If the given new metadata reference is not already occupied in the
176         document, unregister the element at its old metadata reference if
177         it has one, and register the new metadata reference for the element.
178         Note that this method only ensures that XML IDs are unique per stream,
179         so using the same XML ID in both content.xml and styles.xml is allowed.
180         </p>
181 
182         @returns
183             true iff the element has successfully been registered
184      */
185     virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
186         ::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref)
187         = 0;
188 
189     /** unregister an ODF element.
190 
191         <p>
192         Unregister the element at its metadata reference.
193         Does not remove the metadata reference from the element.
194         </p>
195 
196         @see RemoveXmlIdForElement
197      */
198     virtual void UnregisterMetadatable(Metadatable const&) = 0;
199 
200     /** get the metadata reference for the given element. */
201     ::com::sun::star::beans::StringPair
202         GetXmlIdForElement(Metadatable const&) const;
203 
204     /** remove the metadata reference for the given element. */
205     virtual void RemoveXmlIdForElement(Metadatable const&) = 0;
206 
207 protected:
208 
209     virtual bool LookupXmlId(const Metadatable& i_xObject,
210         ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const = 0;
211 
212     virtual Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName,
213         const ::rtl::OUString & i_rIdref) const = 0;
214 };
215 
216 // XmlIdRegistryDocument ---------------------------------------------
217 
218 /** non-clipboard documents */
219 class XmlIdRegistryDocument : public XmlIdRegistry
220 {
221 
222 public:
223     XmlIdRegistryDocument();
224 
225     virtual ~XmlIdRegistryDocument();
226 
227     virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject);
228 
229     virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
230         ::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref);
231 
232     virtual void UnregisterMetadatable(Metadatable const&);
233 
234     virtual void RemoveXmlIdForElement(Metadatable const&);
235 
236     /** register i_rCopy as a copy of i_rSource,
237         with precedence iff i_bCopyPrecedesSource is true */
238     void RegisterCopy(Metadatable const& i_rSource, Metadatable & i_rCopy,
239         const bool i_bCopyPrecedesSource);
240 
241     /** create a Undo Metadatable for i_rObject. */
242     ::boost::shared_ptr<MetadatableUndo> CreateUndo(
243         Metadatable const& i_rObject);
244 
245     /** merge i_rMerged and i_rOther into i_rMerged. */
246     void JoinMetadatables(Metadatable & i_rMerged, Metadatable const& i_rOther);
247 
248     // unfortunately public, Metadatable::RegisterAsCopyOf needs this
249     virtual bool LookupXmlId(const Metadatable& i_xObject,
250         ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const;
251 
252 private:
253 
254     virtual Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName,
255         const ::rtl::OUString & i_rIdref) const;
256 
257     struct XmlIdRegistry_Impl;
258     ::std::auto_ptr<XmlIdRegistry_Impl> m_pImpl;
259 };
260 
261 // MetadatableUndo ---------------------------------------------------
262 
263 /** the horrible Undo Metadatable: is inserted into lists to track position */
264 class MetadatableUndo : public Metadatable
265 {
266     /// as determined by the stream of the source in original document
267     const bool m_isInContent;
268 public:
MetadatableUndo(const bool i_isInContent)269     MetadatableUndo(const bool i_isInContent)
270         : m_isInContent(i_isInContent) { }
GetRegistry()271     virtual ::sfx2::XmlIdRegistry& GetRegistry()
272     {
273         // N.B. for Undo, m_pReg is initialized by registering this as copy in
274         // CreateUndo; it is never cleared
275         OSL_ENSURE(m_pReg, "no m_pReg in MetadatableUndo ?");
276         return *m_pReg;
277     }
IsInClipboard() const278     virtual bool IsInClipboard() const { return false; }
IsInUndo() const279     virtual bool IsInUndo() const { return true; }
IsInContent() const280     virtual bool IsInContent() const { return m_isInContent; }
281     virtual ::com::sun::star::uno::Reference<
MakeUnoObject()282         ::com::sun::star::rdf::XMetadatable > MakeUnoObject()
283     { OSL_ENSURE(false, "MetadatableUndo::MakeUnoObject"); throw; }
284 };
285 
286 // MetadatableClipboard ----------------------------------------------
287 
288 /** the horrible Clipboard Metadatable: inserted into lists to track position */
289 class MetadatableClipboard : public Metadatable
290 {
291     /// as determined by the stream of the source in original document
292     const bool m_isInContent;
293 public:
MetadatableClipboard(const bool i_isInContent)294     MetadatableClipboard(const bool i_isInContent)
295         : m_isInContent(i_isInContent) { }
GetRegistry()296     virtual ::sfx2::XmlIdRegistry& GetRegistry()
297     {
298     // N.B. for Clipboard, m_pReg is initialized by registering this as copy in
299     // RegisterAsCopyOf; it is only cleared by OriginNoLongerInBusinessAnymore
300         OSL_ENSURE(m_pReg, "no m_pReg in MetadatableClipboard ?");
301         return *m_pReg;
302     }
IsInClipboard() const303     virtual bool IsInClipboard() const { return true; }
IsInUndo() const304     virtual bool IsInUndo() const { return false; }
IsInContent() const305     virtual bool IsInContent() const { return m_isInContent; }
306     virtual ::com::sun::star::uno::Reference<
MakeUnoObject()307         ::com::sun::star::rdf::XMetadatable > MakeUnoObject()
308     { OSL_ENSURE(false, "MetadatableClipboard::MakeUnoObject"); throw; }
OriginNoLongerInBusinessAnymore()309     void OriginNoLongerInBusinessAnymore() { m_pReg = 0; }
310 };
311 
312 // XmlIdRegistryClipboard --------------------------------------------
313 
314 class XmlIdRegistryClipboard : public XmlIdRegistry
315 {
316 
317 public:
318     XmlIdRegistryClipboard();
319     virtual ~XmlIdRegistryClipboard();
320 
321     virtual void RegisterMetadatableAndCreateID(Metadatable& i_xObject);
322 
323     virtual bool TryRegisterMetadatable(Metadatable& i_xObject,
324         ::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref);
325 
326     virtual void UnregisterMetadatable(Metadatable const&);
327 
328     virtual void RemoveXmlIdForElement(Metadatable const&);
329 
330     /** register i_rCopy as a copy of i_rSource */
331     MetadatableClipboard & RegisterCopyClipboard(Metadatable & i_rCopy,
332         beans::StringPair const & i_rReference,
333         const bool i_isLatent);
334 
335     /** get the Metadatable that links i_rObject to its origin registry */
336     MetadatableClipboard const* SourceLink(Metadatable const& i_rObject);
337 
338 private:
339     virtual bool LookupXmlId(const Metadatable& i_xObject,
340         ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const;
341 
342     virtual Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName,
343         const ::rtl::OUString & i_rIdref) const;
344 
345     /** create a Clipboard Metadatable for i_rObject. */
346     ::boost::shared_ptr<MetadatableClipboard> CreateClipboard(
347         const bool i_isInContent);
348 
349     struct XmlIdRegistry_Impl;
350     ::std::auto_ptr<XmlIdRegistry_Impl> m_pImpl;
351 };
352 
353 
354 //=============================================================================
355 // XmlIdRegistry
356 
createXmlIdRegistry(const bool i_DocIsClipboard)357 ::sfx2::IXmlIdRegistry * createXmlIdRegistry(const bool i_DocIsClipboard)
358 {
359     return i_DocIsClipboard
360         ? static_cast<XmlIdRegistry*>( new XmlIdRegistryClipboard )
361         : static_cast<XmlIdRegistry*>( new XmlIdRegistryDocument );
362 }
363 
XmlIdRegistry()364 XmlIdRegistry::XmlIdRegistry()
365 {
366 }
367 
~XmlIdRegistry()368 XmlIdRegistry::~XmlIdRegistry()
369 {
370 }
371 
372 ::com::sun::star::uno::Reference< ::com::sun::star::rdf::XMetadatable > SAL_CALL
GetElementByMetadataReference(const beans::StringPair & i_rReference) const373 XmlIdRegistry::GetElementByMetadataReference(
374     const beans::StringPair & i_rReference) const
375 {
376     Metadatable* pObject( LookupElement(i_rReference.First,
377         i_rReference.Second) );
378     return pObject ? pObject->MakeUnoObject() : 0;
379 }
380 
381 beans::StringPair
GetXmlIdForElement(const Metadatable & i_rObject) const382 XmlIdRegistry::GetXmlIdForElement(const Metadatable& i_rObject) const
383 {
384     ::rtl::OUString path;
385     ::rtl::OUString idref;
386     if (LookupXmlId(i_rObject, path, idref))
387     {
388         if (LookupElement(path, idref) == &i_rObject)
389         {
390             return beans::StringPair(path, idref);
391         }
392     }
393     return beans::StringPair();
394 }
395 
396 
397 /// generate unique xml:id
398 template< typename T >
create_id(const::std::hash_map<::rtl::OUString,T,::rtl::OUStringHash> & i_rXmlIdMap)399 /*static*/ ::rtl::OUString create_id(const
400     ::std::hash_map< ::rtl::OUString, T, ::rtl::OUStringHash > & i_rXmlIdMap)
401 {
402     static rtlRandomPool s_Pool( rtl_random_createPool() );
403     const ::rtl::OUString prefix( ::rtl::OUString::createFromAscii(s_prefix) );
404     typename ::std::hash_map< ::rtl::OUString, T, ::rtl::OUStringHash >
405         ::const_iterator iter;
406     ::rtl::OUString id;
407     do
408     {
409         sal_Int32 n;
410         rtl_random_getBytes(s_Pool, & n, sizeof(n));
411         id = prefix + ::rtl::OUString::valueOf(static_cast<sal_Int32>(abs(n)));
412         iter = i_rXmlIdMap.find(id);
413     }
414     while (iter != i_rXmlIdMap.end());
415     return id;
416 }
417 
418 //=============================================================================
419 // Document XML ID Registry (_Impl)
420 
421 /// element list
422 typedef ::std::list< Metadatable* > XmlIdList_t;
423 
424 /// Idref -> (content.xml element list, styles.xml element list)
425 typedef ::std::hash_map< ::rtl::OUString,
426     ::std::pair< XmlIdList_t, XmlIdList_t >, ::rtl::OUStringHash > XmlIdMap_t;
427 
428 /// pointer hash template
429 template<typename T> struct PtrHash
430 {
operator ()sfx2::PtrHash431     size_t operator() (T const * i_pT) const
432     {
433         return reinterpret_cast<size_t>(i_pT);
434     }
435 };
436 
437 /// element -> (stream name, idref)
438 typedef ::std::hash_map< const Metadatable*,
439     ::std::pair< ::rtl::OUString, ::rtl::OUString>, PtrHash<Metadatable> >
440     XmlIdReverseMap_t;
441 
442 struct XmlIdRegistryDocument::XmlIdRegistry_Impl
443 {
XmlIdRegistry_Implsfx2::XmlIdRegistryDocument::XmlIdRegistry_Impl444     XmlIdRegistry_Impl()
445         : m_XmlIdMap(), m_XmlIdReverseMap() { }
446 
447     bool TryInsertMetadatable(Metadatable& i_xObject,
448         const ::rtl::OUString & i_rStream, const ::rtl::OUString & i_rIdref);
449 
450     bool LookupXmlId(const Metadatable& i_xObject,
451         ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const;
452 
453     Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName,
454         const ::rtl::OUString & i_rIdref) const;
455 
456     const XmlIdList_t * LookupElementList(
457         const ::rtl::OUString & i_rStreamName,
458         const ::rtl::OUString & i_rIdref) const;
459 
LookupElementListsfx2::XmlIdRegistryDocument::XmlIdRegistry_Impl460           XmlIdList_t * LookupElementList(
461         const ::rtl::OUString & i_rStreamName,
462         const ::rtl::OUString & i_rIdref)
463     {
464         return const_cast<XmlIdList_t*>(
465             const_cast<const XmlIdRegistry_Impl*>(this)
466                 ->LookupElementList(i_rStreamName, i_rIdref));
467     }
468 
469     XmlIdMap_t m_XmlIdMap;
470     XmlIdReverseMap_t m_XmlIdReverseMap;
471 };
472 
473 // -------------------------------------------------------------------
474 
475 static void
rmIter(XmlIdMap_t & i_rXmlIdMap,XmlIdMap_t::iterator const & i_rIter,::rtl::OUString const & i_rStream,Metadatable const & i_rObject)476 rmIter(XmlIdMap_t & i_rXmlIdMap, XmlIdMap_t::iterator const& i_rIter,
477     ::rtl::OUString const & i_rStream, Metadatable const& i_rObject)
478 {
479     if (i_rIter != i_rXmlIdMap.end())
480     {
481         XmlIdList_t & rList( isContentFile(i_rStream)
482             ? i_rIter->second.first : i_rIter->second.second );
483         rList.remove(&const_cast<Metadatable&>(i_rObject));
484         if (i_rIter->second.first.empty() && i_rIter->second.second.empty())
485         {
486             i_rXmlIdMap.erase(i_rIter);
487         }
488     }
489 }
490 
491 // -------------------------------------------------------------------
492 
493 const XmlIdList_t *
LookupElementList(const::rtl::OUString & i_rStreamName,const::rtl::OUString & i_rIdref) const494 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElementList(
495     const ::rtl::OUString & i_rStreamName,
496     const ::rtl::OUString & i_rIdref) const
497 {
498     const XmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
499     if (iter != m_XmlIdMap.end())
500     {
501         OSL_ENSURE(!iter->second.first.empty() || !iter->second.second.empty(),
502             "null entry in m_XmlIdMap");
503         return (isContentFile(i_rStreamName))
504             ?  &iter->second.first
505             :  &iter->second.second;
506     }
507     else
508     {
509         return 0;
510     }
511 }
512 
513 Metadatable*
LookupElement(const::rtl::OUString & i_rStreamName,const::rtl::OUString & i_rIdref) const514 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupElement(
515     const ::rtl::OUString & i_rStreamName,
516     const ::rtl::OUString & i_rIdref) const
517 {
518     if (!isValidXmlId(i_rStreamName, i_rIdref))
519     {
520         throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
521             "illegal XmlId"), 0, 0);
522     }
523 
524     const XmlIdList_t * pList( LookupElementList(i_rStreamName, i_rIdref) );
525     if (pList)
526     {
527         const XmlIdList_t::const_iterator iter(
528             ::std::find_if(pList->begin(), pList->end(),
529                 ::boost::bind(
530                     ::std::logical_not<bool>(),
531                         ::boost::bind(
532                             ::std::logical_or<bool>(),
533                                 ::boost::bind( &Metadatable::IsInUndo, _1 ),
534                                 ::boost::bind( &Metadatable::IsInClipboard, _1 )
535             ) ) ) );
536         if (iter != pList->end())
537         {
538             return *iter;
539         }
540     }
541     return 0;
542 }
543 
544 bool
LookupXmlId(const Metadatable & i_rObject,::rtl::OUString & o_rStream,::rtl::OUString & o_rIdref) const545 XmlIdRegistryDocument::XmlIdRegistry_Impl::LookupXmlId(
546     const Metadatable& i_rObject,
547     ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const
548 {
549     const XmlIdReverseMap_t::const_iterator iter(
550         m_XmlIdReverseMap.find(&i_rObject) );
551     if (iter != m_XmlIdReverseMap.end())
552     {
553         OSL_ENSURE(!iter->second.first.equalsAscii(""),
554             "null stream in m_XmlIdReverseMap");
555         OSL_ENSURE(!iter->second.second.equalsAscii(""),
556             "null id in m_XmlIdReverseMap");
557         o_rStream = iter->second.first;
558         o_rIdref  = iter->second.second;
559         return true;
560     }
561     else
562     {
563         return false;
564     }
565 }
566 
567 bool
TryInsertMetadatable(Metadatable & i_rObject,const::rtl::OUString & i_rStreamName,const::rtl::OUString & i_rIdref)568 XmlIdRegistryDocument::XmlIdRegistry_Impl::TryInsertMetadatable(
569     Metadatable & i_rObject,
570     const ::rtl::OUString & i_rStreamName, const ::rtl::OUString & i_rIdref)
571 {
572     const bool bContent( isContentFile(i_rStreamName) );
573     OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName),
574         "invalid stream");
575 
576     XmlIdList_t * pList( LookupElementList(i_rStreamName, i_rIdref) );
577     if (pList)
578     {
579         if (pList->empty())
580         {
581             pList->push_back( &i_rObject );
582             return true;
583         }
584         else
585         {
586             // this is only called from TryRegister now, so check
587             // if all elements in the list are deleted (in undo) or
588             // placeholders, then "steal" the id from them
589             if ( pList->end() == ::std::find_if(pList->begin(), pList->end(),
590                 ::boost::bind(
591                     ::std::logical_not<bool>(),
592                         ::boost::bind(
593                             ::std::logical_or<bool>(),
594                                 ::boost::bind( &Metadatable::IsInUndo, _1 ),
595                                 ::boost::bind( &Metadatable::IsInClipboard, _1 )
596                 ) ) ) )
597             {
598 // ???  this is not undoable
599 //                pList->clear();
600 //                pList->push_back( &i_rObject );
601                 pList->push_front( &i_rObject );
602                 return true;
603             }
604             else
605             {
606                 return false;
607             }
608         }
609     }
610     else
611     {
612         m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent
613             ? ::std::make_pair( XmlIdList_t( 1, &i_rObject ), XmlIdList_t() )
614             : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject ) )));
615         return true;
616     }
617 }
618 
619 //=============================================================================
620 // Document XML ID Registry
621 
622 
XmlIdRegistryDocument()623 XmlIdRegistryDocument::XmlIdRegistryDocument()
624     :   m_pImpl( new XmlIdRegistry_Impl )
625 {
626 }
627 
628 static void
removeLink(Metadatable * i_pObject)629 removeLink(Metadatable* i_pObject)
630 {
631     OSL_ENSURE(i_pObject, "null in list ???");
632     if (!i_pObject) return;
633     if (i_pObject->IsInClipboard())
634     {
635         MetadatableClipboard* pLink(
636             dynamic_cast<MetadatableClipboard*>( i_pObject ) );
637         OSL_ENSURE(pLink, "IsInClipboard, but no MetadatableClipboard ?");
638         if (pLink)
639         {
640             pLink->OriginNoLongerInBusinessAnymore();
641         }
642     }
643 }
644 
~XmlIdRegistryDocument()645 XmlIdRegistryDocument::~XmlIdRegistryDocument()
646 {
647     // notify all list elements that are actually in the clipboard
648     for (XmlIdMap_t::iterator iter(m_pImpl->m_XmlIdMap.begin());
649         iter != m_pImpl->m_XmlIdMap.end(); ++iter)
650     {
651         ::std::for_each(iter->second.first.begin(), iter->second.first.end(),
652             removeLink);
653         ::std::for_each(iter->second.second.begin(), iter->second.second.end(),
654             removeLink);
655     }
656 }
657 
658 bool
LookupXmlId(const Metadatable & i_rObject,::rtl::OUString & o_rStream,::rtl::OUString & o_rIdref) const659 XmlIdRegistryDocument::LookupXmlId(
660     const Metadatable& i_rObject,
661     ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const
662 {
663     return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref);
664 }
665 
666 Metadatable*
LookupElement(const::rtl::OUString & i_rStreamName,const::rtl::OUString & i_rIdref) const667 XmlIdRegistryDocument::LookupElement(
668     const ::rtl::OUString & i_rStreamName,
669     const ::rtl::OUString & i_rIdref) const
670 {
671     return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
672 }
673 
674 bool
TryRegisterMetadatable(Metadatable & i_rObject,::rtl::OUString const & i_rStreamName,::rtl::OUString const & i_rIdref)675 XmlIdRegistryDocument::TryRegisterMetadatable(Metadatable & i_rObject,
676     ::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref)
677 {
678     OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject,
679         ::rtl::OUStringToOString(i_rStreamName, RTL_TEXTENCODING_UTF8).getStr(),
680         ::rtl::OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8).getStr());
681 
682     OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
683         "TryRegisterMetadatable called for MetadatableUndo?");
684     OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
685         "TryRegisterMetadatable called for MetadatableClipboard?");
686 
687     if (!isValidXmlId(i_rStreamName, i_rIdref))
688     {
689         throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
690             "illegal XmlId"), 0, 0);
691     }
692     if (i_rObject.IsInContent()
693         ?   !isContentFile(i_rStreamName)
694         :   !isStylesFile(i_rStreamName))
695     {
696         throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
697             "illegal XmlId: wrong stream"), 0, 0);
698     }
699 
700     ::rtl::OUString old_path;
701     ::rtl::OUString old_idref;
702     m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
703     if (old_path  == i_rStreamName && old_idref == i_rIdref)
704     {
705         return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
706     }
707     XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
708     if (!old_idref.equalsAscii(""))
709     {
710         old_id = m_pImpl->m_XmlIdMap.find(old_idref);
711         OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
712     }
713     if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
714     {
715         rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
716         m_pImpl->m_XmlIdReverseMap[&i_rObject] =
717             ::std::make_pair(i_rStreamName, i_rIdref);
718         return true;
719     }
720     else
721     {
722         return false;
723     }
724 }
725 
726 void
RegisterMetadatableAndCreateID(Metadatable & i_rObject)727 XmlIdRegistryDocument::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
728 {
729     OSL_TRACE("RegisterMetadatableAndCreateID: %p\n", &i_rObject);
730 
731     OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
732         "RegisterMetadatableAndCreateID called for MetadatableUndo?");
733     OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
734         "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
735 
736     const bool isInContent( i_rObject.IsInContent() );
737     const ::rtl::OUString stream( ::rtl::OUString::createFromAscii(
738         isInContent ? s_content : s_styles ) );
739     // check if we have a latent xmlid, and if yes, remove it
740     ::rtl::OUString old_path;
741     ::rtl::OUString old_idref;
742     m_pImpl->LookupXmlId(i_rObject, old_path, old_idref);
743 
744     XmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
745     if (!old_idref.equalsAscii(""))
746     {
747         old_id = m_pImpl->m_XmlIdMap.find(old_idref);
748         OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
749         if (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject)
750         {
751             return;
752         }
753         else
754         {
755             // remove latent xmlid
756             rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
757         }
758     }
759 
760     // create id
761     const ::rtl::OUString id( create_id(m_pImpl->m_XmlIdMap) );
762     OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(),
763         "created id is in use");
764     m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent
765         ? ::std::make_pair( XmlIdList_t( 1, &i_rObject ), XmlIdList_t() )
766         : ::std::make_pair( XmlIdList_t(), XmlIdList_t( 1, &i_rObject ) )));
767     m_pImpl->m_XmlIdReverseMap[&i_rObject] = ::std::make_pair(stream, id);
768 }
769 
UnregisterMetadatable(const Metadatable & i_rObject)770 void XmlIdRegistryDocument::UnregisterMetadatable(const Metadatable& i_rObject)
771 {
772     OSL_TRACE("UnregisterMetadatable: %p\n", &i_rObject);
773 
774     ::rtl::OUString path;
775     ::rtl::OUString idref;
776     if (!m_pImpl->LookupXmlId(i_rObject, path, idref))
777     {
778         OSL_ENSURE(false, "unregister: no xml id?");
779         return;
780     }
781     const XmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
782     if (iter != m_pImpl->m_XmlIdMap.end())
783     {
784         rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
785     }
786 }
787 
RemoveXmlIdForElement(const Metadatable & i_rObject)788 void XmlIdRegistryDocument::RemoveXmlIdForElement(const Metadatable& i_rObject)
789 {
790     OSL_TRACE("RemoveXmlIdForElement: %p\n", &i_rObject);
791 
792     const XmlIdReverseMap_t::iterator iter(
793         m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
794     if (iter != m_pImpl->m_XmlIdReverseMap.end())
795     {
796         OSL_ENSURE(!iter->second.second.equalsAscii(""),
797             "null id in m_XmlIdReverseMap");
798         m_pImpl->m_XmlIdReverseMap.erase(iter);
799     }
800 }
801 
802 // -------------------------------------------------------------------
803 
RegisterCopy(Metadatable const & i_rSource,Metadatable & i_rCopy,const bool i_bCopyPrecedesSource)804 void XmlIdRegistryDocument::RegisterCopy(Metadatable const& i_rSource,
805     Metadatable & i_rCopy, const bool i_bCopyPrecedesSource)
806 {
807     OSL_TRACE("RegisterCopy: %p -> %p (%d)\n",
808         &i_rSource, &i_rCopy, i_bCopyPrecedesSource);
809 
810     // potential sources: clipboard, undo array, splitNode
811     // assumption: stream change can only happen via clipboard, and is handled
812     // by Metadatable::RegisterAsCopyOf
813     OSL_ENSURE(i_rSource.IsInUndo() || i_rCopy.IsInUndo() ||
814         (i_rSource.IsInContent() == i_rCopy.IsInContent()),
815         "RegisterCopy: not in same stream?");
816 
817     ::rtl::OUString path;
818     ::rtl::OUString idref;
819     if (!m_pImpl->LookupXmlId( i_rSource, path, idref ))
820     {
821         OSL_ENSURE(false, "no xml id?");
822         return;
823     }
824     XmlIdList_t * pList ( m_pImpl->LookupElementList(path, idref) );
825     OSL_ENSURE( ::std::find( pList->begin(), pList->end(), &i_rCopy )
826         == pList->end(), "copy already registered???");
827     XmlIdList_t::iterator srcpos(
828         ::std::find( pList->begin(), pList->end(), &i_rSource ) );
829     OSL_ENSURE(srcpos != pList->end(), "source not in list???");
830     if (srcpos == pList->end())
831     {
832         return;
833     }
834     if (i_bCopyPrecedesSource)
835     {
836         pList->insert( srcpos, &i_rCopy );
837     }
838     else
839     {
840         // for undo push_back does not work! must insert right after source
841         pList->insert( ++srcpos, &i_rCopy );
842     }
843     m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy,
844         ::std::make_pair(path, idref)));
845 }
846 
847 ::boost::shared_ptr<MetadatableUndo>
CreateUndo(Metadatable const & i_rObject)848 XmlIdRegistryDocument::CreateUndo(Metadatable const& i_rObject)
849 {
850     OSL_TRACE("CreateUndo: %p\n", &i_rObject);
851 
852     return ::boost::shared_ptr<MetadatableUndo>(
853                 new MetadatableUndo(i_rObject.IsInContent()) );
854 }
855 
856 /*
857 i_rMerged is both a source and the target node of the merge
858 i_rOther is the other source, and will be deleted after the merge
859 
860 dimensions: none|latent|actual empty|nonempty
861 i_rMerged(1)    i_rOther(2)        result
862      *|empty         *|empty    => 1|2 (arbitrary)
863      *|empty         *|nonempty => 2
864      *|nonempty      *|empty    => 1
865   none|nonempty   none|nonempty => none
866   none|nonempty latent|nonempty => 2
867 latent|nonempty   none|nonempty => 1
868 latent|nonempty latent|nonempty => 1|2
869      *|nonempty actual|nonempty => 2
870 actual|nonempty      *|nonempty => 1
871 actual|nonempty actual|nonempty => 1|2
872 */
873 void
JoinMetadatables(Metadatable & i_rMerged,Metadatable const & i_rOther)874 XmlIdRegistryDocument::JoinMetadatables(
875     Metadatable & i_rMerged, Metadatable const & i_rOther)
876 {
877     OSL_TRACE("JoinMetadatables: %p <- %p\n", &i_rMerged, &i_rOther);
878 
879     bool mergedOwnsRef;
880     ::rtl::OUString path;
881     ::rtl::OUString idref;
882     if (m_pImpl->LookupXmlId(i_rMerged, path, idref))
883     {
884         mergedOwnsRef = (m_pImpl->LookupElement(path, idref) == &i_rMerged);
885     }
886     else
887     {
888         OSL_ENSURE(false, "JoinMetadatables: no xmlid?");
889         return;
890     }
891     if (!mergedOwnsRef)
892     {
893         i_rMerged.RemoveMetadataReference();
894         i_rMerged.RegisterAsCopyOf(i_rOther, true);
895         return;
896     }
897     // other cases: merged has actual ref and is nonempty,
898     // other has latent/actual ref and is nonempty: other loses => nothing to do
899 }
900 
901 
902 //=============================================================================
903 // Clipboard XML ID Registry (_Impl)
904 
905 struct RMapEntry
906 {
RMapEntrysfx2::RMapEntry907     RMapEntry() : m_pLink() { }
RMapEntrysfx2::RMapEntry908     RMapEntry(::rtl::OUString const& i_rStream,
909             ::rtl::OUString const& i_rXmlId,
910             ::boost::shared_ptr<MetadatableClipboard> const& i_pLink
911                 = ::boost::shared_ptr<MetadatableClipboard>())
912         :   m_Stream(i_rStream), m_XmlId(i_rXmlId), m_pLink(i_pLink)
913         {}
914     ::rtl::OUString m_Stream;
915     ::rtl::OUString m_XmlId;
916     // this would have been an auto_ptr, if only that would have compiled...
917     ::boost::shared_ptr<MetadatableClipboard> m_pLink;
918 };
919 
920 /// element -> (stream name, idref, source)
921 typedef ::std::hash_map< const Metadatable*,
922     struct RMapEntry,
923     PtrHash<Metadatable> >
924     ClipboardXmlIdReverseMap_t;
925 
926 /// Idref -> (content.xml element, styles.xml element)
927 typedef ::std::hash_map< ::rtl::OUString,
928     ::std::pair< Metadatable*, Metadatable* >, ::rtl::OUStringHash >
929     ClipboardXmlIdMap_t;
930 
931 struct XmlIdRegistryClipboard::XmlIdRegistry_Impl
932 {
XmlIdRegistry_Implsfx2::XmlIdRegistryClipboard::XmlIdRegistry_Impl933     XmlIdRegistry_Impl()
934         : m_XmlIdMap(), m_XmlIdReverseMap() { }
935 
936     bool TryInsertMetadatable(Metadatable& i_xObject,
937         const ::rtl::OUString & i_rStream, const ::rtl::OUString & i_rIdref);
938 
939     bool LookupXmlId(const Metadatable& i_xObject,
940         ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref,
941         MetadatableClipboard const* &o_rpLink) const;
942 
943     Metadatable* LookupElement(const ::rtl::OUString & i_rStreamName,
944         const ::rtl::OUString & i_rIdref) const;
945 
946     Metadatable* const* LookupEntry(const ::rtl::OUString & i_rStreamName,
947         const ::rtl::OUString & i_rIdref) const;
948 
LookupEntrysfx2::XmlIdRegistryClipboard::XmlIdRegistry_Impl949     Metadatable*      * LookupEntry(const ::rtl::OUString & i_rStreamName,
950         const ::rtl::OUString & i_rIdref)
951     {
952         return const_cast<Metadatable**>(
953             const_cast<const XmlIdRegistry_Impl*>(this)
954                 ->LookupEntry(i_rStreamName, i_rIdref));
955     }
956 
957     ClipboardXmlIdMap_t m_XmlIdMap;
958     ClipboardXmlIdReverseMap_t m_XmlIdReverseMap;
959 };
960 
961 // -------------------------------------------------------------------
962 
963 static void
rmIter(ClipboardXmlIdMap_t & i_rXmlIdMap,ClipboardXmlIdMap_t::iterator const & i_rIter,::rtl::OUString const & i_rStream,Metadatable const & i_rObject)964 rmIter(ClipboardXmlIdMap_t & i_rXmlIdMap,
965     ClipboardXmlIdMap_t::iterator const& i_rIter,
966     ::rtl::OUString const & i_rStream, Metadatable const& i_rObject)
967 {
968     if (i_rIter != i_rXmlIdMap.end())
969     {
970         Metadatable *& rMeta = isContentFile(i_rStream)
971             ? i_rIter->second.first : i_rIter->second.second;
972         if (rMeta == &i_rObject)
973         {
974             rMeta = 0;
975         }
976         if (!i_rIter->second.first && !i_rIter->second.second)
977         {
978             i_rXmlIdMap.erase(i_rIter);
979         }
980     }
981 }
982 
983 // -------------------------------------------------------------------
984 
985 Metadatable* const*
LookupEntry(const::rtl::OUString & i_rStreamName,const::rtl::OUString & i_rIdref) const986 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupEntry(
987     const ::rtl::OUString & i_rStreamName,
988     const ::rtl::OUString & i_rIdref) const
989 {
990     if (!isValidXmlId(i_rStreamName, i_rIdref))
991     {
992         throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
993             "illegal XmlId"), 0, 0);
994     }
995 
996     const ClipboardXmlIdMap_t::const_iterator iter( m_XmlIdMap.find(i_rIdref) );
997     if (iter != m_XmlIdMap.end())
998     {
999         OSL_ENSURE(iter->second.first || iter->second.second,
1000             "null entry in m_XmlIdMap");
1001         return (isContentFile(i_rStreamName))
1002             ?  &iter->second.first
1003             :  &iter->second.second;
1004     }
1005     else
1006     {
1007         return 0;
1008     }
1009 }
1010 
1011 Metadatable*
LookupElement(const::rtl::OUString & i_rStreamName,const::rtl::OUString & i_rIdref) const1012 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupElement(
1013     const ::rtl::OUString & i_rStreamName,
1014     const ::rtl::OUString & i_rIdref) const
1015 {
1016     Metadatable * const * ppEntry = LookupEntry(i_rStreamName, i_rIdref);
1017     return ppEntry ? *ppEntry : 0;
1018 }
1019 
1020 bool
LookupXmlId(const Metadatable & i_rObject,::rtl::OUString & o_rStream,::rtl::OUString & o_rIdref,MetadatableClipboard const * & o_rpLink) const1021 XmlIdRegistryClipboard::XmlIdRegistry_Impl::LookupXmlId(
1022     const Metadatable& i_rObject,
1023     ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref,
1024     MetadatableClipboard const* &o_rpLink) const
1025 {
1026     const ClipboardXmlIdReverseMap_t::const_iterator iter(
1027         m_XmlIdReverseMap.find(&i_rObject) );
1028     if (iter != m_XmlIdReverseMap.end())
1029     {
1030         OSL_ENSURE(!iter->second.m_Stream.equalsAscii(""),
1031             "null stream in m_XmlIdReverseMap");
1032         OSL_ENSURE(!iter->second.m_XmlId.equalsAscii(""),
1033             "null id in m_XmlIdReverseMap");
1034         o_rStream = iter->second.m_Stream;
1035         o_rIdref  = iter->second.m_XmlId;
1036         o_rpLink  = iter->second.m_pLink.get();
1037         return true;
1038     }
1039     else
1040     {
1041         return false;
1042     }
1043 }
1044 
1045 bool
TryInsertMetadatable(Metadatable & i_rObject,const::rtl::OUString & i_rStreamName,const::rtl::OUString & i_rIdref)1046 XmlIdRegistryClipboard::XmlIdRegistry_Impl::TryInsertMetadatable(
1047     Metadatable & i_rObject,
1048     const ::rtl::OUString & i_rStreamName, const ::rtl::OUString & i_rIdref)
1049 {
1050     bool bContent( isContentFile(i_rStreamName) );
1051     OSL_ENSURE(isContentFile(i_rStreamName) || isStylesFile(i_rStreamName),
1052         "invalid stream");
1053 
1054     //wntmsci12 won't parse this:
1055 //    Metadatable ** ppEntry( LookupEntry(i_rStreamName, i_rIdref) );
1056     Metadatable ** ppEntry = LookupEntry(i_rStreamName, i_rIdref);
1057     if (ppEntry)
1058     {
1059         if (*ppEntry)
1060         {
1061             return false;
1062         }
1063         else
1064         {
1065             *ppEntry = &i_rObject;
1066             return true;
1067         }
1068     }
1069     else
1070     {
1071         m_XmlIdMap.insert(::std::make_pair(i_rIdref, bContent
1072             ? ::std::make_pair( &i_rObject, static_cast<Metadatable*>(0) )
1073             : ::std::make_pair( static_cast<Metadatable*>(0), &i_rObject )));
1074         return true;
1075     }
1076 }
1077 
1078 //=============================================================================
1079 // Clipboard XML ID Registry
1080 
1081 
XmlIdRegistryClipboard()1082 XmlIdRegistryClipboard::XmlIdRegistryClipboard()
1083     :   m_pImpl( new XmlIdRegistry_Impl )
1084 {
1085 }
1086 
~XmlIdRegistryClipboard()1087 XmlIdRegistryClipboard::~XmlIdRegistryClipboard()
1088 {
1089 }
1090 
1091 bool
LookupXmlId(const Metadatable & i_rObject,::rtl::OUString & o_rStream,::rtl::OUString & o_rIdref) const1092 XmlIdRegistryClipboard::LookupXmlId(
1093     const Metadatable& i_rObject,
1094     ::rtl::OUString & o_rStream, ::rtl::OUString & o_rIdref) const
1095 {
1096     const MetadatableClipboard * pLink;
1097     return m_pImpl->LookupXmlId(i_rObject, o_rStream, o_rIdref, pLink);
1098 }
1099 
1100 Metadatable*
LookupElement(const::rtl::OUString & i_rStreamName,const::rtl::OUString & i_rIdref) const1101 XmlIdRegistryClipboard::LookupElement(
1102     const ::rtl::OUString & i_rStreamName,
1103     const ::rtl::OUString & i_rIdref) const
1104 {
1105     return m_pImpl->LookupElement(i_rStreamName, i_rIdref);
1106 }
1107 
1108 bool
TryRegisterMetadatable(Metadatable & i_rObject,::rtl::OUString const & i_rStreamName,::rtl::OUString const & i_rIdref)1109 XmlIdRegistryClipboard::TryRegisterMetadatable(Metadatable & i_rObject,
1110     ::rtl::OUString const& i_rStreamName, ::rtl::OUString const& i_rIdref)
1111 {
1112     OSL_TRACE("TryRegisterMetadatable: %p (%s#%s)\n", &i_rObject,
1113         ::rtl::OUStringToOString(i_rStreamName, RTL_TEXTENCODING_UTF8).getStr(),
1114         ::rtl::OUStringToOString(i_rIdref, RTL_TEXTENCODING_UTF8).getStr());
1115 
1116     OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
1117         "TryRegisterMetadatable called for MetadatableUndo?");
1118     OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
1119         "TryRegisterMetadatable called for MetadatableClipboard?");
1120 
1121     if (!isValidXmlId(i_rStreamName, i_rIdref))
1122     {
1123         throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
1124             "illegal XmlId"), 0, 0);
1125     }
1126     if (i_rObject.IsInContent()
1127         ?   !isContentFile(i_rStreamName)
1128         :   !isStylesFile(i_rStreamName))
1129     {
1130         throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
1131             "illegal XmlId: wrong stream"), 0, 0);
1132     }
1133 
1134     ::rtl::OUString old_path;
1135     ::rtl::OUString old_idref;
1136     const MetadatableClipboard * pLink;
1137     m_pImpl->LookupXmlId(i_rObject, old_path, old_idref, pLink);
1138     if (old_path  == i_rStreamName && old_idref == i_rIdref)
1139     {
1140         return (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject);
1141     }
1142     ClipboardXmlIdMap_t::iterator old_id( m_pImpl->m_XmlIdMap.end() );
1143     if (!old_idref.equalsAscii(""))
1144     {
1145         old_id = m_pImpl->m_XmlIdMap.find(old_idref);
1146         OSL_ENSURE(old_id != m_pImpl->m_XmlIdMap.end(), "old id not found");
1147     }
1148     if (m_pImpl->TryInsertMetadatable(i_rObject, i_rStreamName, i_rIdref))
1149     {
1150         rmIter(m_pImpl->m_XmlIdMap, old_id, old_path, i_rObject);
1151         m_pImpl->m_XmlIdReverseMap[&i_rObject] =
1152             RMapEntry(i_rStreamName, i_rIdref);
1153         return true;
1154     }
1155     else
1156     {
1157         return false;
1158     }
1159 }
1160 
1161 void
RegisterMetadatableAndCreateID(Metadatable & i_rObject)1162 XmlIdRegistryClipboard::RegisterMetadatableAndCreateID(Metadatable & i_rObject)
1163 {
1164     OSL_TRACE("RegisterMetadatableAndCreateID: %p\n", &i_rObject);
1165 
1166     OSL_ENSURE(!dynamic_cast<MetadatableUndo*>(&i_rObject),
1167         "RegisterMetadatableAndCreateID called for MetadatableUndo?");
1168     OSL_ENSURE(!dynamic_cast<MetadatableClipboard*>(&i_rObject),
1169         "RegisterMetadatableAndCreateID called for MetadatableClipboard?");
1170 
1171     bool isInContent( i_rObject.IsInContent() );
1172     ::rtl::OUString stream( ::rtl::OUString::createFromAscii(
1173         isInContent ? s_content : s_styles ) );
1174 
1175     ::rtl::OUString old_path;
1176     ::rtl::OUString old_idref;
1177     LookupXmlId(i_rObject, old_path, old_idref);
1178     if (!old_idref.equalsAscii("") &&
1179         (m_pImpl->LookupElement(old_path, old_idref) == &i_rObject))
1180     {
1181         return;
1182     }
1183 
1184     // create id
1185     const ::rtl::OUString id( create_id(m_pImpl->m_XmlIdMap) );
1186     OSL_ENSURE(m_pImpl->m_XmlIdMap.find(id) == m_pImpl->m_XmlIdMap.end(),
1187         "created id is in use");
1188     m_pImpl->m_XmlIdMap.insert(::std::make_pair(id, isInContent
1189         ? ::std::make_pair( &i_rObject, static_cast<Metadatable*>(0) )
1190         : ::std::make_pair( static_cast<Metadatable*>(0), &i_rObject )));
1191     // N.B.: if i_rObject had a latent XmlId, then we implicitly delete the
1192     // MetadatableClipboard and thus the latent XmlId here
1193     m_pImpl->m_XmlIdReverseMap[&i_rObject] = RMapEntry(stream, id);
1194 }
1195 
UnregisterMetadatable(const Metadatable & i_rObject)1196 void XmlIdRegistryClipboard::UnregisterMetadatable(const Metadatable& i_rObject)
1197 {
1198     OSL_TRACE("UnregisterMetadatable: %p\n", &i_rObject);
1199 
1200     ::rtl::OUString path;
1201     ::rtl::OUString idref;
1202     const MetadatableClipboard * pLink;
1203     if (!m_pImpl->LookupXmlId(i_rObject, path, idref, pLink))
1204     {
1205         OSL_ENSURE(false, "unregister: no xml id?");
1206         return;
1207     }
1208     const ClipboardXmlIdMap_t::iterator iter( m_pImpl->m_XmlIdMap.find(idref) );
1209     if (iter != m_pImpl->m_XmlIdMap.end())
1210     {
1211         rmIter(m_pImpl->m_XmlIdMap, iter, path, i_rObject);
1212     }
1213 }
1214 
1215 
RemoveXmlIdForElement(const Metadatable & i_rObject)1216 void XmlIdRegistryClipboard::RemoveXmlIdForElement(const Metadatable& i_rObject)
1217 {
1218     OSL_TRACE("RemoveXmlIdForElement: %p\n", &i_rObject);
1219 
1220     ClipboardXmlIdReverseMap_t::iterator iter(
1221         m_pImpl->m_XmlIdReverseMap.find(&i_rObject) );
1222     if (iter != m_pImpl->m_XmlIdReverseMap.end())
1223     {
1224         OSL_ENSURE(!iter->second.m_XmlId.equalsAscii(""),
1225             "null id in m_XmlIdReverseMap");
1226         m_pImpl->m_XmlIdReverseMap.erase(iter);
1227     }
1228 }
1229 
1230 // -------------------------------------------------------------------
1231 
1232 ::boost::shared_ptr<MetadatableClipboard>
CreateClipboard(const bool i_isInContent)1233 XmlIdRegistryClipboard::CreateClipboard(const bool i_isInContent)
1234 {
1235     OSL_TRACE("CreateClipboard: \n");
1236 
1237     return ::boost::shared_ptr<MetadatableClipboard>(
1238         new MetadatableClipboard(i_isInContent) );
1239 }
1240 
1241 MetadatableClipboard &
RegisterCopyClipboard(Metadatable & i_rCopy,beans::StringPair const & i_rReference,const bool i_isLatent)1242 XmlIdRegistryClipboard::RegisterCopyClipboard(Metadatable & i_rCopy,
1243     beans::StringPair const & i_rReference,
1244     const bool i_isLatent)
1245 {
1246     OSL_TRACE("RegisterCopyClipboard: %p -> "/*"%p"*/"(%s#%s) (%d)\n",
1247         /*&i_rSource,*/ &i_rCopy,
1248         ::rtl::OUStringToOString(i_rReference.First,
1249             RTL_TEXTENCODING_UTF8).getStr(),
1250         ::rtl::OUStringToOString(i_rReference.Second,
1251             RTL_TEXTENCODING_UTF8).getStr(),
1252         i_isLatent);
1253 
1254     // N.B.: when copying to the clipboard, the selection is always inserted
1255     //       into the body, even if the source is a header/footer!
1256     //       so we do not check whether the stream is right in this function
1257 
1258     if (!isValidXmlId(i_rReference.First, i_rReference.Second))
1259     {
1260         throw lang::IllegalArgumentException(::rtl::OUString::createFromAscii(
1261             "illegal XmlId"), 0, 0);
1262     }
1263 
1264     if (!i_isLatent)
1265     {
1266         // this should succeed assuming clipboard has a single source document
1267         const bool success( m_pImpl->TryInsertMetadatable(i_rCopy,
1268                 i_rReference.First, i_rReference.Second) );
1269         OSL_ENSURE(success, "RegisterCopyClipboard: TryInsert failed?");
1270         (void) success;
1271     }
1272     const ::boost::shared_ptr<MetadatableClipboard> pLink(
1273         CreateClipboard( isContentFile(i_rReference.First)) );
1274     m_pImpl->m_XmlIdReverseMap.insert(::std::make_pair(&i_rCopy,
1275         RMapEntry(i_rReference.First, i_rReference.Second, pLink)));
1276     return *pLink.get();
1277 }
1278 
1279 MetadatableClipboard const*
SourceLink(Metadatable const & i_rObject)1280 XmlIdRegistryClipboard::SourceLink(Metadatable const& i_rObject)
1281 {
1282     ::rtl::OUString path;
1283     ::rtl::OUString idref;
1284     const MetadatableClipboard * pLink( 0 );
1285     m_pImpl->LookupXmlId(i_rObject, path, idref, pLink);
1286     return pLink;
1287 }
1288 
1289 
1290 //=============================================================================
1291 // Metadatable mixin
1292 
1293 
~Metadatable()1294 Metadatable::~Metadatable()
1295 {
1296     RemoveMetadataReference();
1297 }
1298 
RemoveMetadataReference()1299 void Metadatable::RemoveMetadataReference()
1300 {
1301     try
1302     {
1303         if (m_pReg)
1304         {
1305             m_pReg->UnregisterMetadatable( *this );
1306             m_pReg->RemoveXmlIdForElement( *this );
1307             m_pReg = 0;
1308         }
1309     }
1310     catch (uno::Exception &)
1311     {
1312         OSL_ENSURE(false, "Metadatable::RemoveMetadataReference: exception");
1313     }
1314 }
1315 
1316 // ::com::sun::star::rdf::XMetadatable:
1317 beans::StringPair
GetMetadataReference() const1318 Metadatable::GetMetadataReference() const
1319 {
1320     if (m_pReg)
1321     {
1322         return m_pReg->GetXmlIdForElement(*this);
1323     }
1324     return beans::StringPair();
1325 }
1326 
1327 void
SetMetadataReference(const::com::sun::star::beans::StringPair & i_rReference)1328 Metadatable::SetMetadataReference(
1329     const ::com::sun::star::beans::StringPair & i_rReference)
1330 {
1331     if (i_rReference.Second.equalsAscii(""))
1332     {
1333         RemoveMetadataReference();
1334     }
1335     else
1336     {
1337         ::rtl::OUString streamName( i_rReference.First );
1338         if (streamName.equalsAscii(""))
1339         {
1340             // handle empty stream name as auto-detect.
1341             // necessary for importing flat file format.
1342             streamName = ::rtl::OUString::createFromAscii(
1343                             IsInContent() ? s_content : s_styles );
1344         }
1345         XmlIdRegistry & rReg( dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1346         if (rReg.TryRegisterMetadatable(*this, streamName, i_rReference.Second))
1347         {
1348             m_pReg = &rReg;
1349         }
1350         else
1351         {
1352             throw lang::IllegalArgumentException(
1353                 ::rtl::OUString::createFromAscii("Metadatable::"
1354                     "SetMetadataReference: argument is invalid"), /*this*/0, 0);
1355         }
1356     }
1357 }
1358 
EnsureMetadataReference()1359 void Metadatable::EnsureMetadataReference()
1360 {
1361     XmlIdRegistry& rReg(
1362         m_pReg ? *m_pReg : dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1363     rReg.RegisterMetadatableAndCreateID( *this );
1364     m_pReg = &rReg;
1365 }
1366 
GetRegistryConst(Metadatable const & i_rObject)1367 const ::sfx2::IXmlIdRegistry& GetRegistryConst(Metadatable const& i_rObject)
1368 {
1369     return const_cast< Metadatable& >( i_rObject ).GetRegistry();
1370 }
1371 
1372 void
RegisterAsCopyOf(Metadatable const & i_rSource,const bool i_bCopyPrecedesSource)1373 Metadatable::RegisterAsCopyOf(Metadatable const & i_rSource,
1374     const bool i_bCopyPrecedesSource)
1375 {
1376     OSL_ENSURE(typeid(*this) == typeid(i_rSource)
1377         || typeid(i_rSource) == typeid(MetadatableUndo)
1378         || typeid(*this)     == typeid(MetadatableUndo)
1379         || typeid(i_rSource) == typeid(MetadatableClipboard)
1380         || typeid(*this)     == typeid(MetadatableClipboard),
1381         "RegisterAsCopyOf element with different class?");
1382     OSL_ENSURE(!this->m_pReg, "RegisterAsCopyOf called on element with XmlId?");
1383 
1384     if (this->m_pReg)
1385     {
1386         RemoveMetadataReference();
1387     }
1388 
1389     try
1390     {
1391         if (i_rSource.m_pReg)
1392         {
1393             XmlIdRegistry & rReg(
1394                 dynamic_cast<XmlIdRegistry&>( GetRegistry() ) );
1395             if (i_rSource.m_pReg == &rReg)
1396             {
1397                 OSL_ENSURE(!IsInClipboard(),
1398                     "RegisterAsCopy: both in clipboard?");
1399                 if (!IsInClipboard())
1400                 {
1401                     XmlIdRegistryDocument & rRegDoc(
1402                         dynamic_cast<XmlIdRegistryDocument&>( rReg ) );
1403                     rRegDoc.RegisterCopy(i_rSource, *this,
1404                         i_bCopyPrecedesSource);
1405                     this->m_pReg = &rRegDoc;
1406                 }
1407                 return;
1408             }
1409             // source is in different document
1410             XmlIdRegistryDocument  * pRegDoc(
1411                 dynamic_cast<XmlIdRegistryDocument *>(&rReg) );
1412             XmlIdRegistryClipboard * pRegClp(
1413                 dynamic_cast<XmlIdRegistryClipboard*>(&rReg) );
1414 
1415             if (pRegClp)
1416             {
1417                 beans::StringPair SourceRef(
1418                     i_rSource.m_pReg->GetXmlIdForElement(i_rSource) );
1419                 bool isLatent( SourceRef.Second.equalsAscii("") );
1420                 XmlIdRegistryDocument * pSourceRegDoc(
1421                     dynamic_cast<XmlIdRegistryDocument*>(i_rSource.m_pReg) );
1422                 OSL_ENSURE(pSourceRegDoc, "RegisterAsCopyOf: 2 clipboards?");
1423                 if (!pSourceRegDoc) return;
1424                 // this is a copy _to_ the clipboard
1425                 if (isLatent)
1426                 {
1427                     pSourceRegDoc->LookupXmlId(i_rSource,
1428                         SourceRef.First, SourceRef.Second);
1429                 }
1430                 Metadatable & rLink(
1431                     pRegClp->RegisterCopyClipboard(*this, SourceRef, isLatent));
1432                 this->m_pReg = pRegClp;
1433                 // register as copy in the non-clipboard registry
1434                 pSourceRegDoc->RegisterCopy(i_rSource, rLink,
1435                     false); // i_bCopyPrecedesSource);
1436                 rLink.m_pReg = pSourceRegDoc;
1437             }
1438             else if (pRegDoc)
1439             {
1440                 XmlIdRegistryClipboard * pSourceRegClp(
1441                     dynamic_cast<XmlIdRegistryClipboard*>(i_rSource.m_pReg) );
1442                 OSL_ENSURE(pSourceRegClp,
1443                     "RegisterAsCopyOf: 2 non-clipboards?");
1444                 if (!pSourceRegClp) return;
1445                 const MetadatableClipboard * pLink(
1446                     pSourceRegClp->SourceLink(i_rSource) );
1447                 // may happen if src got its id via UNO call
1448                 if (!pLink) return;
1449                 // only register copy if clipboard content is from this SwDoc!
1450                 if (pLink && (&GetRegistryConst(*pLink) == pRegDoc))
1451                 {
1452                     // this is a copy _from_ the clipboard; check if the
1453                     // element is still in the same stream
1454                     // N.B.: we check the stream of pLink, not of i_rSource!
1455                     bool srcInContent( pLink->IsInContent() );
1456                     bool tgtInContent( this->IsInContent() );
1457                     if (srcInContent == tgtInContent)
1458                     {
1459                         pRegDoc->RegisterCopy(*pLink, *this,
1460                             true); // i_bCopyPrecedesSource);
1461                         this->m_pReg = pRegDoc;
1462                     }
1463                     // otherwise: stream change! do not register!
1464                 }
1465             }
1466             else
1467             {
1468                 OSL_ENSURE(false, "neither RegDoc nor RegClp cannot happen");
1469             }
1470 #if 0
1471                 {
1472                     //FIXME: do we need this at all???
1473                     XmlIdRegistryDocument & rRegDoc(
1474                         dynamic_cast<XmlIdRegistryDocument&>( rReg ) );
1475                     {
1476                         if (rRegDoc.TryRegisterMetadatable(*this, SourceRef))
1477                         {
1478                             this->m_pReg = &rRegDoc;
1479                         }
1480                     }
1481                 }
1482 #endif
1483         }
1484     }
1485     catch (uno::Exception &)
1486     {
1487         OSL_ENSURE(false, "Metadatable::RegisterAsCopyOf: exception");
1488     }
1489 }
1490 
CreateUndo() const1491 ::boost::shared_ptr<MetadatableUndo> Metadatable::CreateUndo() const
1492 {
1493     OSL_ENSURE(!IsInUndo(), "CreateUndo called for object in undo?");
1494     OSL_ENSURE(!IsInClipboard(), "CreateUndo called for object in clipboard?");
1495     try
1496     {
1497         if (!IsInClipboard() && !IsInUndo() && m_pReg)
1498         {
1499             XmlIdRegistryDocument * pRegDoc(
1500                 dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
1501             ::boost::shared_ptr<MetadatableUndo> pUndo(
1502                 pRegDoc->CreateUndo(*this) );
1503             pRegDoc->RegisterCopy(*this, *pUndo, false);
1504             pUndo->m_pReg = pRegDoc;
1505             return pUndo;
1506         }
1507     }
1508     catch (uno::Exception &)
1509     {
1510         OSL_ENSURE(false, "Metadatable::CreateUndo: exception");
1511     }
1512     return ::boost::shared_ptr<MetadatableUndo>();
1513 }
1514 
CreateUndoForDelete()1515 ::boost::shared_ptr<MetadatableUndo> Metadatable::CreateUndoForDelete()
1516 {
1517     ::boost::shared_ptr<MetadatableUndo> const pUndo( CreateUndo() );
1518     RemoveMetadataReference();
1519     return pUndo;
1520 }
1521 
RestoreMetadata(::boost::shared_ptr<MetadatableUndo> const & i_pUndo)1522 void Metadatable::RestoreMetadata(
1523     ::boost::shared_ptr<MetadatableUndo> const& i_pUndo)
1524 {
1525     OSL_ENSURE(!IsInUndo(), "RestoreMetadata called for object in undo?");
1526     OSL_ENSURE(!IsInClipboard(),
1527         "RestoreMetadata called for object in clipboard?");
1528     if (IsInClipboard() || IsInUndo()) return;
1529     RemoveMetadataReference();
1530     if (i_pUndo)
1531     {
1532         this->RegisterAsCopyOf(*i_pUndo, true);
1533     }
1534 }
1535 
1536 void
JoinMetadatable(Metadatable const & i_rOther,const bool i_isMergedEmpty,const bool i_isOtherEmpty)1537 Metadatable::JoinMetadatable(Metadatable const & i_rOther,
1538     const bool i_isMergedEmpty, const bool i_isOtherEmpty)
1539 {
1540     OSL_ENSURE(!IsInUndo(), "JoinMetadatables called for object in undo?");
1541     OSL_ENSURE(!IsInClipboard(),
1542         "JoinMetadatables called for object in clipboard?");
1543     if (IsInClipboard() || IsInUndo()) return;
1544 
1545     if (i_isOtherEmpty && !i_isMergedEmpty)
1546     {
1547         // other is empty, thus loses => nothing to do
1548         return;
1549     }
1550     if (i_isMergedEmpty && !i_isOtherEmpty)
1551     {
1552         this->RemoveMetadataReference();
1553         this->RegisterAsCopyOf(i_rOther, true);
1554         return;
1555     }
1556 
1557     if (!i_rOther.m_pReg)
1558     {
1559         // other doesn't have xmlid, thus loses => nothing to do
1560         return;
1561     }
1562     if (!m_pReg)
1563     {
1564         this->RegisterAsCopyOf(i_rOther, true);
1565         // assumption: i_rOther will be deleted, so don't unregister it here
1566         return;
1567     }
1568     try
1569     {
1570         XmlIdRegistryDocument * pRegDoc(
1571             dynamic_cast<XmlIdRegistryDocument*>( m_pReg ) );
1572         OSL_ENSURE(pRegDoc, "JoinMetadatable: no pRegDoc?");
1573         if (pRegDoc)
1574         {
1575             pRegDoc->JoinMetadatables(*this, i_rOther);
1576         }
1577     }
1578     catch (uno::Exception &)
1579     {
1580         OSL_ENSURE(false, "Metadatable::JoinMetadatable: exception");
1581     }
1582 }
1583 
1584 
1585 //=============================================================================
1586 // XMetadatable mixin
1587 
1588 // ::com::sun::star::rdf::XNode:
getStringValue()1589 ::rtl::OUString SAL_CALL MetadatableMixin::getStringValue()
1590     throw (::com::sun::star::uno::RuntimeException)
1591 {
1592     return getNamespace() + getLocalName();
1593 }
1594 
1595 // ::com::sun::star::rdf::XURI:
getLocalName()1596 ::rtl::OUString SAL_CALL MetadatableMixin::getLocalName()
1597     throw (::com::sun::star::uno::RuntimeException)
1598 {
1599     ::vos::OGuard aGuard( Application::GetSolarMutex() );
1600     beans::StringPair mdref( getMetadataReference() );
1601     if (!mdref.Second.getLength())
1602     {
1603         ensureMetadataReference(); // N.B.: side effect!
1604         mdref = getMetadataReference();
1605     }
1606     ::rtl::OUStringBuffer buf;
1607     buf.append(mdref.First);
1608     buf.append(static_cast<sal_Unicode>('#'));
1609     buf.append(mdref.Second);
1610     return buf.makeStringAndClear();
1611 }
1612 
getNamespace()1613 ::rtl::OUString SAL_CALL MetadatableMixin::getNamespace()
1614     throw (::com::sun::star::uno::RuntimeException)
1615 {
1616     ::vos::OGuard aGuard( Application::GetSolarMutex() );
1617     const uno::Reference< frame::XModel > xModel( GetModel() );
1618     const uno::Reference< rdf::XURI > xDMA( xModel, uno::UNO_QUERY_THROW );
1619     return xDMA->getStringValue();
1620 }
1621 
1622 // ::com::sun::star::rdf::XMetadatable:
1623 beans::StringPair SAL_CALL
getMetadataReference()1624 MetadatableMixin::getMetadataReference()
1625 throw (uno::RuntimeException)
1626 {
1627     ::vos::OGuard aGuard( Application::GetSolarMutex() );
1628 
1629     Metadatable *const pObject( GetCoreObject() );
1630     if (!pObject)
1631     {
1632         throw uno::RuntimeException(
1633             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
1634                 "MetadatableMixin: cannot get core object; not inserted?")),
1635             *this);
1636     }
1637     return pObject->GetMetadataReference();
1638 }
1639 
1640 void SAL_CALL
setMetadataReference(const beans::StringPair & i_rReference)1641 MetadatableMixin::setMetadataReference(
1642     const beans::StringPair & i_rReference)
1643 throw (uno::RuntimeException, lang::IllegalArgumentException)
1644 {
1645     ::vos::OGuard aGuard( Application::GetSolarMutex() );
1646 
1647     Metadatable *const pObject( GetCoreObject() );
1648     if (!pObject)
1649     {
1650         throw uno::RuntimeException(
1651             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
1652                 "MetadatableMixin: cannot get core object; not inserted?")),
1653             *this);
1654     }
1655     return pObject->SetMetadataReference(i_rReference);
1656 }
1657 
ensureMetadataReference()1658 void SAL_CALL MetadatableMixin::ensureMetadataReference()
1659 throw (uno::RuntimeException)
1660 {
1661     ::vos::OGuard aGuard( Application::GetSolarMutex() );
1662 
1663     Metadatable *const pObject( GetCoreObject() );
1664     if (!pObject)
1665     {
1666         throw uno::RuntimeException(
1667             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
1668                 "MetadatableMixin: cannot get core object; not inserted?")),
1669             *this);
1670     }
1671     return pObject->EnsureMetadataReference();
1672 }
1673 
1674 } // namespace sfx2
1675 
1676 
1677 //=============================================================================
1678 
1679 #if OSL_DEBUG_LEVEL > 1
1680 
1681 #include <stdio.h>
1682 
1683 static void dump(sfx2::XmlIdList_t * pList)
1684 #ifdef GCC
1685 __attribute__ ((unused))
1686 #endif
1687 ;
dump(sfx2::XmlIdList_t * pList)1688 static void dump(sfx2::XmlIdList_t * pList)
1689 {
1690     fprintf(stderr, "\nXmlIdList(%p):  ", pList);
1691     for (sfx2::XmlIdList_t::iterator i = pList->begin(); i != pList->end(); ++i)
1692     {
1693         fprintf(stderr, "%p  ", *i);
1694     }
1695     fprintf(stderr, "\n");
1696 }
1697 
1698 #endif
1699 
1700