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