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