1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <accelerators/storageholder.hxx>
21 #include <accelerators/acceleratorconfiguration.hxx>
22 #include <sal/log.hxx>
24 #include <com/sun/star/embed/ElementModes.hpp>
26 #include <com/sun/star/embed/XTransactedObject.hpp>
28 #include <rtl/ustrbuf.hxx>
29 #include <o3tl/string_view.hxx>
33 constexpr OUString PATH_SEPARATOR
= u
"/"_ustr
;
34 #define PATH_SEPARATOR_UNICODE u'/'
39 StorageHolder::StorageHolder()
43 StorageHolder::~StorageHolder()
46 // dispose/clear etcpp.
49 void StorageHolder::forgetCachedStorages()
51 std::unique_lock
g(m_mutex
);
52 for (auto & lStorage
: m_lStorages
)
54 TStorageInfo
& rInfo
= lStorage
.second
;
55 // TODO think about listener !
56 rInfo
.Storage
.clear();
61 void StorageHolder::setRootStorage(const css::uno::Reference
< css::embed::XStorage
>& xRoot
)
63 std::unique_lock
g(m_mutex
);
67 css::uno::Reference
< css::embed::XStorage
> StorageHolder::getRootStorage() const
69 std::unique_lock
g(m_mutex
);
73 css::uno::Reference
< css::embed::XStorage
> StorageHolder::openPath(const OUString
& sPath
,
76 OUString sNormedPath
= StorageHolder::impl_st_normPath(sPath
);
77 std::vector
<OUString
> lFolders
= StorageHolder::impl_st_parsePath(sNormedPath
);
79 // SAFE -> ----------------------------------
80 std::unique_lock
aReadLock(m_mutex
);
81 css::uno::Reference
< css::embed::XStorage
> xParent
= m_xRoot
;
83 // <- SAFE ----------------------------------
85 css::uno::Reference
< css::embed::XStorage
> xChild
;
88 for (auto const& lFolder
: lFolders
)
90 OUString
sCheckPath (sRelPath
+ lFolder
+ PATH_SEPARATOR
);
92 // SAFE -> ------------------------------
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())
101 pInfo
= &(pCheck
->second
);
103 xChild
= pInfo
->Storage
;
106 // <- SAFE ------------------------------
111 // <- SAFE ------------------------------
115 xChild
= StorageHolder::openSubStorageWithFallback(xParent
, lFolder
, nOpenMode
); // TODO think about delegating fallback decision to our own caller!
117 catch(const css::uno::RuntimeException
&)
119 catch(const css::uno::Exception
&)
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!
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 ...
134 std::unique_lock
g(m_mutex
);
135 pInfo
= &(m_lStorages
[sCheckPath
]);
136 pInfo
->Storage
= xChild
;
141 sRelPath
+= lFolder
+ PATH_SEPARATOR
;
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.
150 StorageHolder::TStorageList
StorageHolder::getAllPathStorages(const OUString
& sPath
)
152 OUString sNormedPath
= StorageHolder::impl_st_normPath(sPath
);
153 std::vector
<OUString
> lFolders
= StorageHolder::impl_st_parsePath(sNormedPath
);
155 StorageHolder::TStorageList lStoragesOfPath
;
158 std::unique_lock
g(m_mutex
);
160 for (auto const& lFolder
: lFolders
)
162 OUString
sCheckPath (sRelPath
+ lFolder
+ PATH_SEPARATOR
);
164 TPath2StorageInfo::iterator pCheck
= m_lStorages
.find(sCheckPath
);
165 if (pCheck
== m_lStorages
.end())
167 // at least one path element was not found
168 // Seems that this path isn't open ...
169 lStoragesOfPath
.clear();
170 return lStoragesOfPath
;
173 TStorageInfo
& rInfo
= pCheck
->second
;
174 lStoragesOfPath
.push_back(rInfo
.Storage
);
176 sRelPath
+= lFolder
+ PATH_SEPARATOR
;
179 return lStoragesOfPath
;
182 void StorageHolder::commitPath(const OUString
& sPath
)
184 StorageHolder::TStorageList lStorages
= getAllPathStorages(sPath
);
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();
192 xCommit
.set(*pIt
, css::uno::UNO_QUERY
);
198 // SAFE -> ------------------------------
200 std::unique_lock
aReadLock(m_mutex
);
201 xCommit
.set(m_xRoot
, css::uno::UNO_QUERY
);
203 // <- SAFE ------------------------------
209 void StorageHolder::closePath(const OUString
& rPath
)
211 OUString sNormedPath
= StorageHolder::impl_st_normPath(rPath
);
212 std::vector
<OUString
> lFolders
= StorageHolder::impl_st_parsePath(sNormedPath
);
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"
219 OUString sParentPath
;
220 for (auto & lFolder
: lFolders
)
222 OUString
sCurrentRelPath(sParentPath
+ lFolder
+ PATH_SEPARATOR
);
223 lFolder
= sCurrentRelPath
;
224 sParentPath
= sCurrentRelPath
;
227 std::unique_lock
g(m_mutex
);
229 std::vector
<OUString
>::reverse_iterator pIt2
;
230 for ( pIt2
= lFolders
.rbegin();
231 pIt2
!= lFolders
.rend();
234 OUString sPath
= *pIt2
;
235 TPath2StorageInfo::iterator pPath
= m_lStorages
.find(sPath
);
236 if (pPath
== m_lStorages
.end())
239 TStorageInfo
& rInfo
= pPath
->second
;
241 if (rInfo
.UseCount
< 1)
243 rInfo
.Storage
.clear();
244 m_lStorages
.erase(pPath
);
249 void StorageHolder::notifyPath(const OUString
& sPath
)
251 OUString sNormedPath
= StorageHolder::impl_st_normPath(sPath
);
253 std::unique_lock
g(m_mutex
);
255 TPath2StorageInfo::iterator pIt1
= m_lStorages
.find(sNormedPath
);
256 if (pIt1
== m_lStorages
.end())
259 TStorageInfo
& rInfo
= pIt1
->second
;
260 for (auto const& listener
: rInfo
.Listener
)
263 listener
->changesOccurred();
267 void StorageHolder::addStorageListener( XMLBasedAcceleratorConfiguration
* pListener
,
268 const OUString
& sPath
)
270 OUString sNormedPath
= StorageHolder::impl_st_normPath(sPath
);
272 std::unique_lock
g(m_mutex
);
274 TPath2StorageInfo::iterator pIt1
= m_lStorages
.find(sNormedPath
);
275 if (pIt1
== m_lStorages
.end())
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
);
284 void StorageHolder::removeStorageListener( XMLBasedAcceleratorConfiguration
* pListener
,
285 const OUString
& sPath
)
287 OUString sNormedPath
= StorageHolder::impl_st_normPath(sPath
);
289 std::unique_lock
g(m_mutex
);
291 TPath2StorageInfo::iterator pIt1
= m_lStorages
.find(sNormedPath
);
292 if (pIt1
== m_lStorages
.end())
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
);
301 OUString
StorageHolder::getPathOfStorage(const css::uno::Reference
< css::embed::XStorage
>& xStorage
)
303 std::unique_lock
g(m_mutex
);
305 for (auto const& lStorage
: m_lStorages
)
307 const TStorageInfo
& rInfo
= lStorage
.second
;
308 if (rInfo
.Storage
== xStorage
)
309 return lStorage
.first
;
315 css::uno::Reference
< css::embed::XStorage
> StorageHolder::getParentStorage(const css::uno::Reference
< css::embed::XStorage
>& xChild
)
317 OUString sChildPath
= getPathOfStorage(xChild
);
318 return getParentStorage(sChildPath
);
321 css::uno::Reference
< css::embed::XStorage
> StorageHolder::getParentStorage(const OUString
& sChildPath
)
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();
328 // a) "" => - => no parent
329 // b) "a/b/c/" => "a/b/" => return storage "a/b/"
330 // c) "a/" => "" => return root !
334 return css::uno::Reference
< css::embed::XStorage
>();
336 // SAFE -> ----------------------------------
338 std::unique_lock
aReadLock(m_mutex
);
345 OUStringBuffer
sParentPath(64);
347 for (i
= 0; i
< c
- 1; ++i
)
349 sParentPath
.append(lFolders
[i
] + PATH_SEPARATOR
);
352 auto pParent
= m_lStorages
.find(sParentPath
.makeStringAndClear());
353 if (pParent
!= m_lStorages
.end())
354 return pParent
->second
.Storage
;
356 // <- SAFE ----------------------------------
359 SAL_INFO("fwk", "StorageHolder::getParentStorage(): Unexpected situation. Cached storage item seems to be wrong.");
360 return css::uno::Reference
< css::embed::XStorage
>();
363 StorageHolder
& StorageHolder::operator=(const StorageHolder
& rCopy
)
365 std::unique_lock
g(m_mutex
);
366 m_xRoot
= rCopy
.m_xRoot
;
367 m_lStorages
= rCopy
.m_lStorages
;
371 css::uno::Reference
< css::embed::XStorage
> StorageHolder::openSubStorageWithFallback(const css::uno::Reference
< css::embed::XStorage
>& xBaseStorage
,
372 const OUString
& sSubStorage
,
375 // a) try it first with user specified open mode
376 // ignore errors ... but save it for later use!
379 css::uno::Reference
< css::embed::XStorage
> xSubStorage
= xBaseStorage
->openStorageElement(sSubStorage
, eOpenMode
);
380 if (xSubStorage
.is())
383 catch(const css::uno::RuntimeException
&)
387 catch(const css::uno::Exception
&)
389 // b) readonly already tried? => forward last error!
390 if ((eOpenMode
& css::embed::ElementModes::WRITE
) != css::embed::ElementModes::WRITE
) // fallback possible ?
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();
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())
407 SAL_INFO("fwk", "openSubStorageWithFallback(): Unexpected situation! Got no exception for missing storage ...");
408 return css::uno::Reference
< css::embed::XStorage
>();
411 OUString
StorageHolder::impl_st_normPath(const OUString
& sPath
)
413 // path must start without "/" but end with "/"!
415 OUString sNormedPath
= sPath
;
417 // "/bla" => "bla" && "/" => "" (!)
418 (void)sNormedPath
.startsWith(PATH_SEPARATOR
, &sNormedPath
);
420 // "/" => "" || "" => "" ?
421 if (sNormedPath
.isEmpty())
425 if (sNormedPath
.lastIndexOf(PATH_SEPARATOR_UNICODE
) != (sNormedPath
.getLength()-1))
426 sNormedPath
+= PATH_SEPARATOR
;
431 std::vector
<OUString
> StorageHolder::impl_st_parsePath(std::u16string_view sPath
)
433 std::vector
<OUString
> lToken
;
437 OUString
sToken( o3tl::getToken(sPath
, 0, PATH_SEPARATOR_UNICODE
, i
) );
440 lToken
.push_back(sToken
);
445 } // namespace framework
447 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */