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