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