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