LibreOffice Module framework (master) 1
storageholder.cxx
Go to the documentation of this file.
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
22#include <sal/log.hxx>
23
24#include <com/sun/star/embed/ElementModes.hpp>
25
26#include <com/sun/star/embed/XTransactedObject.hpp>
27
28#include <rtl/ustrbuf.hxx>
29#include <o3tl/string_view.hxx>
30
31#include <algorithm>
32
33constexpr OUStringLiteral PATH_SEPARATOR = u"/";
34#define PATH_SEPARATOR_UNICODE u'/'
35
36namespace framework
37{
38
40{
41}
42
44{
45 // TODO implement me
46 // dispose/clear etcpp.
47}
48
50{
51 std::unique_lock g(m_mutex);
52 for (auto & lStorage : m_lStorages)
53 {
54 TStorageInfo& rInfo = lStorage.second;
55 // TODO think about listener !
56 rInfo.Storage.clear();
57 }
58 m_lStorages.clear();
59}
60
61void StorageHolder::setRootStorage(const css::uno::Reference< css::embed::XStorage >& xRoot)
62{
63 std::unique_lock g(m_mutex);
64 m_xRoot = xRoot;
65}
66
67css::uno::Reference< css::embed::XStorage > StorageHolder::getRootStorage() const
68{
69 std::unique_lock g(m_mutex);
70 return m_xRoot;
71}
72
73css::uno::Reference< css::embed::XStorage > StorageHolder::openPath(const OUString& sPath ,
74 sal_Int32 nOpenMode)
75{
76 OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
77 std::vector<OUString> lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
78
79 // SAFE -> ----------------------------------
80 std::unique_lock aReadLock(m_mutex);
81 css::uno::Reference< css::embed::XStorage > xParent = m_xRoot;
82 aReadLock.unlock();
83 // <- SAFE ----------------------------------
84
85 css::uno::Reference< css::embed::XStorage > xChild;
86 OUString sRelPath;
87
88 for (auto const& lFolder : lFolders)
89 {
90 OUString sCheckPath (sRelPath + lFolder + PATH_SEPARATOR);
91
92 // SAFE -> ------------------------------
93 aReadLock.lock();
94
95 // If we found an already open storage ... we must increase
96 // its use count. Otherwise it will may be closed too early :-)
97 TPath2StorageInfo::iterator pCheck = m_lStorages.find(sCheckPath);
98 TStorageInfo* pInfo = nullptr;
99 if (pCheck != m_lStorages.end())
100 {
101 pInfo = &(pCheck->second);
102 ++(pInfo->UseCount);
103 xChild = pInfo->Storage;
104
105 aReadLock.unlock();
106 // <- SAFE ------------------------------
107 }
108 else
109 {
110 aReadLock.unlock();
111 // <- SAFE ------------------------------
112
113 try
114 {
115 xChild = StorageHolder::openSubStorageWithFallback(xParent, lFolder, nOpenMode); // TODO think about delegating fallback decision to our own caller!
116 }
117 catch(const css::uno::RuntimeException&)
118 { throw; }
119 catch(const css::uno::Exception&)
120 {
121 /* TODO URGENT!
122 in case we found some "already existing storages" on the path before and increased its UseCount ...
123 and now we will get an exception on creating a new sub storage ...
124 we must decrease all UseCounts, which was touched before. Otherwise these storages can't be closed!
125
126 Idea: Using of another structure member "PossibleUseCount" as vector of unique numbers.
127 Every thread use another unique number to identify all "owned candidates".
128 A flush method with the same unique number force increasing of the "UseCount" variable then
129 inside a synchronized block ...
130 */
131 throw;
132 }
133
134 std::unique_lock g(m_mutex);
135 pInfo = &(m_lStorages[sCheckPath]);
136 pInfo->Storage = xChild;
137 pInfo->UseCount = 1;
138 }
139
140 xParent = xChild;
141 sRelPath += lFolder + PATH_SEPARATOR;
142 }
143
144 // TODO think about return last storage as working storage ... but don't caching it inside this holder!
145 // => otherwise the same storage is may be commit more than once.
146
147 return xChild;
148}
149
151{
152 OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
153 std::vector<OUString> lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
154
155 StorageHolder::TStorageList lStoragesOfPath;
156 OUString sRelPath;
157
158 std::unique_lock g(m_mutex);
159
160 for (auto const& lFolder : lFolders)
161 {
162 OUString sCheckPath (sRelPath + lFolder + PATH_SEPARATOR);
163
164 TPath2StorageInfo::iterator pCheck = m_lStorages.find(sCheckPath);
165 if (pCheck == m_lStorages.end())
166 {
167 // at least one path element was not found
168 // Seems that this path isn't open ...
169 lStoragesOfPath.clear();
170 return lStoragesOfPath;
171 }
172
173 TStorageInfo& rInfo = pCheck->second;
174 lStoragesOfPath.push_back(rInfo.Storage);
175
176 sRelPath += lFolder + PATH_SEPARATOR;
177 }
178
179 return lStoragesOfPath;
180}
181
182void StorageHolder::commitPath(const OUString& sPath)
183{
185
186 css::uno::Reference< css::embed::XTransactedObject > xCommit;
187 StorageHolder::TStorageList::reverse_iterator pIt;
188 for ( pIt = lStorages.rbegin(); // order of commit is important ... otherwise changes are not recognized!
189 pIt != lStorages.rend();
190 ++pIt )
191 {
192 xCommit.set(*pIt, css::uno::UNO_QUERY);
193 if (!xCommit.is())
194 continue;
195 xCommit->commit();
196 }
197
198 // SAFE -> ------------------------------
199 {
200 std::unique_lock aReadLock(m_mutex);
201 xCommit.set(m_xRoot, css::uno::UNO_QUERY);
202 }
203 // <- SAFE ------------------------------
204
205 if (xCommit.is())
206 xCommit->commit();
207}
208
209void StorageHolder::closePath(const OUString& rPath)
210{
211 OUString sNormedPath = StorageHolder::impl_st_normPath(rPath);
212 std::vector<OUString> lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
213
214 /* convert list of paths in the following way:
215 [0] = "path_1" => "path_1
216 [1] = "path_2" => "path_1/path_2"
217 [2] = "path_3" => "path_1/path_2/path_3"
218 */
219 OUString sParentPath;
220 for (auto & lFolder : lFolders)
221 {
222 OUString sCurrentRelPath(sParentPath + lFolder + PATH_SEPARATOR);
223 lFolder = sCurrentRelPath;
224 sParentPath = sCurrentRelPath;
225 }
226
227 std::unique_lock g(m_mutex);
228
229 std::vector<OUString>::reverse_iterator pIt2;
230 for ( pIt2 = lFolders.rbegin();
231 pIt2 != lFolders.rend();
232 ++pIt2 )
233 {
234 OUString sPath = *pIt2;
235 TPath2StorageInfo::iterator pPath = m_lStorages.find(sPath);
236 if (pPath == m_lStorages.end())
237 continue; // ???
238
239 TStorageInfo& rInfo = pPath->second;
240 --rInfo.UseCount;
241 if (rInfo.UseCount < 1)
242 {
243 rInfo.Storage.clear();
244 m_lStorages.erase(pPath);
245 }
246 }
247}
248
249void StorageHolder::notifyPath(const OUString& sPath)
250{
251 OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
252
253 std::unique_lock g(m_mutex);
254
255 TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
256 if (pIt1 == m_lStorages.end())
257 return;
258
259 TStorageInfo& rInfo = pIt1->second;
260 for (auto const& listener : rInfo.Listener)
261 {
262 if (listener)
263 listener->changesOccurred();
264 }
265}
266
268 const OUString& sPath )
269{
270 OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
271
272 std::unique_lock g(m_mutex);
273
274 TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
275 if (pIt1 == m_lStorages.end())
276 return;
277
278 TStorageInfo& rInfo = pIt1->second;
279 TStorageListenerList::iterator pIt2 = ::std::find(rInfo.Listener.begin(), rInfo.Listener.end(), pListener);
280 if (pIt2 == rInfo.Listener.end())
281 rInfo.Listener.push_back(pListener);
282}
283
285 const OUString& sPath )
286{
287 OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
288
289 std::unique_lock g(m_mutex);
290
291 TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
292 if (pIt1 == m_lStorages.end())
293 return;
294
295 TStorageInfo& rInfo = pIt1->second;
296 TStorageListenerList::iterator pIt2 = ::std::find(rInfo.Listener.begin(), rInfo.Listener.end(), pListener);
297 if (pIt2 != rInfo.Listener.end())
298 rInfo.Listener.erase(pIt2);
299}
300
301OUString StorageHolder::getPathOfStorage(const css::uno::Reference< css::embed::XStorage >& xStorage)
302{
303 std::unique_lock g(m_mutex);
304
305 for (auto const& lStorage : m_lStorages)
306 {
307 const TStorageInfo& rInfo = lStorage.second;
308 if (rInfo.Storage == xStorage)
309 return lStorage.first;
310 }
311
312 return OUString();
313}
314
315css::uno::Reference< css::embed::XStorage > StorageHolder::getParentStorage(const css::uno::Reference< css::embed::XStorage >& xChild)
316{
317 OUString sChildPath = getPathOfStorage(xChild);
318 return getParentStorage(sChildPath);
319}
320
321css::uno::Reference< css::embed::XStorage > StorageHolder::getParentStorage(const OUString& sChildPath)
322{
323 // normed path = "a/b/c/" ... we search for "a/b/"
324 OUString sNormedPath = StorageHolder::impl_st_normPath(sChildPath);
325 std::vector<OUString> lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
326 sal_Int32 c = lFolders.size();
327
328 // a) "" => - => no parent
329 // b) "a/b/c/" => "a/b/" => return storage "a/b/"
330 // c) "a/" => "" => return root !
331
332 // a)
333 if (c < 1)
334 return css::uno::Reference< css::embed::XStorage >();
335
336 // SAFE -> ----------------------------------
337 {
338 std::unique_lock aReadLock(m_mutex);
339
340 // b)
341 if (c < 2)
342 return m_xRoot;
343
344 // c)
345 OUStringBuffer sParentPath(64);
346 sal_Int32 i = 0;
347 for (i = 0; i < c - 1; ++i)
348 {
349 sParentPath.append(lFolders[i] + PATH_SEPARATOR);
350 }
351
352 auto pParent = m_lStorages.find(sParentPath.makeStringAndClear());
353 if (pParent != m_lStorages.end())
354 return pParent->second.Storage;
355 }
356 // <- SAFE ----------------------------------
357
358 // ?
359 SAL_INFO("fwk", "StorageHolder::getParentStorage(): Unexpected situation. Cached storage item seems to be wrong.");
360 return css::uno::Reference< css::embed::XStorage >();
361}
362
364{
365 std::unique_lock g(m_mutex);
366 m_xRoot = rCopy.m_xRoot;
367 m_lStorages = rCopy.m_lStorages;
368 return *this;
369}
370
371css::uno::Reference< css::embed::XStorage > StorageHolder::openSubStorageWithFallback(const css::uno::Reference< css::embed::XStorage >& xBaseStorage ,
372 const OUString& sSubStorage ,
373 sal_Int32 eOpenMode)
374{
375 // a) try it first with user specified open mode
376 // ignore errors ... but save it for later use!
377 try
378 {
379 css::uno::Reference< css::embed::XStorage > xSubStorage = xBaseStorage->openStorageElement(sSubStorage, eOpenMode);
380 if (xSubStorage.is())
381 return xSubStorage;
382 }
383 catch(const css::uno::RuntimeException&)
384 {
385 throw;
386 }
387 catch(const css::uno::Exception&)
388 {
389 // b) readonly already tried? => forward last error!
390 if ((eOpenMode & css::embed::ElementModes::WRITE) != css::embed::ElementModes::WRITE) // fallback possible ?
391 throw;
392 }
393
394 // b) readonly already tried, throw error
395 if ((eOpenMode & css::embed::ElementModes::WRITE) != css::embed::ElementModes::WRITE) // fallback possible ?
396 throw css::uno::Exception();
397
398 // c) try it readonly
399 // don't catch exception here! Outside code wish to know, if operation failed or not.
400 // Otherwise they work on NULL references ...
401 sal_Int32 eNewMode = (eOpenMode & ~css::embed::ElementModes::WRITE);
402 css::uno::Reference< css::embed::XStorage > xSubStorage = xBaseStorage->openStorageElement(sSubStorage, eNewMode);
403 if (xSubStorage.is())
404 return xSubStorage;
405
406 // d) no chance!
407 SAL_INFO("fwk", "openSubStorageWithFallback(): Unexpected situation! Got no exception for missing storage ...");
408 return css::uno::Reference< css::embed::XStorage >();
409}
410
411OUString StorageHolder::impl_st_normPath(const OUString& sPath)
412{
413 // path must start without "/" but end with "/"!
414
415 OUString sNormedPath = sPath;
416
417 // "/bla" => "bla" && "/" => "" (!)
418 (void)sNormedPath.startsWith(PATH_SEPARATOR, &sNormedPath);
419
420 // "/" => "" || "" => "" ?
421 if (sNormedPath.isEmpty())
422 return OUString();
423
424 // "bla" => "bla/"
425 if (sNormedPath.lastIndexOf(PATH_SEPARATOR_UNICODE) != (sNormedPath.getLength()-1))
426 sNormedPath += PATH_SEPARATOR;
427
428 return sNormedPath;
429}
430
431std::vector<OUString> StorageHolder::impl_st_parsePath(std::u16string_view sPath)
432{
433 std::vector<OUString> lToken;
434 sal_Int32 i = 0;
435 while (true)
436 {
437 OUString sToken( o3tl::getToken(sPath, 0, PATH_SEPARATOR_UNICODE, i) );
438 if (i < 0)
439 break;
440 lToken.push_back(sToken);
441 }
442 return lToken;
443}
444
445} // namespace framework
446
447/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void removeStorageListener(XMLBasedAcceleratorConfiguration *pListener, const OUString &sPath)
TODO.
static std::vector< OUString > impl_st_parsePath(std::u16string_view sPath)
TODO.
StorageHolder & operator=(const StorageHolder &rCopy)
TODO.
void commitPath(const OUString &sPath)
TODO.
css::uno::Reference< css::embed::XStorage > getRootStorage() const
TODO.
OUString getPathOfStorage(const css::uno::Reference< css::embed::XStorage > &xStorage)
TODO.
void forgetCachedStorages()
TODO.
StorageHolder::TStorageList getAllPathStorages(const OUString &sPath)
TODO.
void setRootStorage(const css::uno::Reference< css::embed::XStorage > &xRoot)
TODO.
void closePath(const OUString &sPath)
TODO.
css::uno::Reference< css::embed::XStorage > getParentStorage(const css::uno::Reference< css::embed::XStorage > &xChild)
TODO.
css::uno::Reference< css::embed::XStorage > m_xRoot
TODO.
void notifyPath(const OUString &sPath)
TODO.
static css::uno::Reference< css::embed::XStorage > openSubStorageWithFallback(const css::uno::Reference< css::embed::XStorage > &xBaseStorage, const OUString &sSubStorage, sal_Int32 eOpenMode)
opens a sub element of the specified base storage.
void addStorageListener(XMLBasedAcceleratorConfiguration *pListener, const OUString &sPath)
TODO.
::std::vector< css::uno::Reference< css::embed::XStorage > > TStorageList
TODO.
TPath2StorageInfo m_lStorages
TODO.
css::uno::Reference< css::embed::XStorage > openPath(const OUString &sPath, sal_Int32 nOpenMode)
TODO open or get!
static OUString impl_st_normPath(const OUString &sPath)
TODO.
implements a read/write access to the accelerator configuration.
float u
#define SAL_INFO(area, stream)
int i
std::basic_string_view< charT, traits > getToken(std::basic_string_view< charT, traits > sv, charT delimiter, std::size_t &position)
#define PATH_SEPARATOR_UNICODE
constexpr OUStringLiteral PATH_SEPARATOR
css::uno::Reference< css::embed::XStorage > Storage
ElementOpenMode eOpenMode