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>
24 #include <com/sun/star/container/NoSuchElementException.hpp>
26 #include <com/sun/star/container/XNameAccess.hpp>
28 #include <com/sun/star/beans/XPropertySet.hpp>
30 #include <com/sun/star/embed/ElementModes.hpp>
32 #include <com/sun/star/embed/XTransactedObject.hpp>
34 #include <com/sun/star/embed/XPackageStructureCreator.hpp>
36 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
38 #include <com/sun/star/io/XSeekable.hpp>
42 #define PATH_SEPARATOR_ASCII "/"
43 #define PATH_SEPARATOR_UNICODE ((sal_Unicode)'/')
44 #define PATH_SEPARATOR OUString(PATH_SEPARATOR_ASCII)
49 StorageHolder::StorageHolder()
53 StorageHolder::~StorageHolder()
56 // dispose/clear etcpp.
59 void StorageHolder::forgetCachedStorages()
61 osl::MutexGuard
g(m_mutex
);
62 TPath2StorageInfo::iterator pIt
;
63 for ( pIt
= m_lStorages
.begin();
64 pIt
!= m_lStorages
.end();
67 TStorageInfo
& rInfo
= pIt
->second
;
68 // TODO think about listener !
69 rInfo
.Storage
.clear();
74 void StorageHolder::setRootStorage(const css::uno::Reference
< css::embed::XStorage
>& xRoot
)
76 osl::MutexGuard
g(m_mutex
);
80 css::uno::Reference
< css::embed::XStorage
> StorageHolder::getRootStorage() const
82 osl::MutexGuard
g(m_mutex
);
86 css::uno::Reference
< css::embed::XStorage
> StorageHolder::openPath(const OUString
& sPath
,
89 OUString sNormedPath
= StorageHolder::impl_st_normPath(sPath
);
90 OUStringList lFolders
= StorageHolder::impl_st_parsePath(sNormedPath
);
92 // SAFE -> ----------------------------------
93 osl::ResettableMutexGuard
aReadLock(m_mutex
);
94 css::uno::Reference
< css::embed::XStorage
> xParent
= m_xRoot
;
96 // <- SAFE ----------------------------------
98 css::uno::Reference
< css::embed::XStorage
> xChild
;
100 OUStringList::const_iterator pIt
;
102 for ( pIt
= lFolders
.begin();
103 pIt
!= lFolders
.end();
106 const OUString
& sChild
= *pIt
;
107 OUString
sCheckPath (sRelPath
);
108 sCheckPath
+= sChild
;
109 sCheckPath
+= PATH_SEPARATOR
;
111 // SAFE -> ------------------------------
114 // If we found an already open storage ... we must increase
115 // its use count. Otherwhise it will may be closed to early :-)
116 TPath2StorageInfo::iterator pCheck
= m_lStorages
.find(sCheckPath
);
117 TStorageInfo
* pInfo
= 0;
118 if (pCheck
!= m_lStorages
.end())
120 pInfo
= &(pCheck
->second
);
122 xChild
= pInfo
->Storage
;
125 // <- SAFE ------------------------------
130 // <- SAFE ------------------------------
134 xChild
= StorageHolder::openSubStorageWithFallback(xParent
, sChild
, nOpenMode
, true); // TODO think about delegating fallback decision to our own calli!
136 catch(const css::uno::RuntimeException
&)
138 catch(const css::uno::Exception
&)
141 in case we found some "already existing storages" on the path before and increased its UseCount ...
142 and now we will get an exception on creating a new sub storage ...
143 we must decrease all UseCounts, which was touched before. Otherwise these storages can't be closed!
145 Idea: Using of another structure member "PossibleUseCount" as vector of unique numbers.
146 Every thread use another unique number to identify all "owned candidates".
147 A flush method with the same unique number force increasing of the "UseCount" variable then
148 inside a synchronized block ...
153 osl::MutexGuard
g(m_mutex
);
154 pInfo
= &(m_lStorages
[sCheckPath
]);
155 pInfo
->Storage
= xChild
;
161 sRelPath
+= PATH_SEPARATOR
;
164 // TODO think about return last storage as working storage ... but dont caching it inside this holder!
165 // => otherwise the same storage is may be commit more than once.
170 StorageHolder::TStorageList
StorageHolder::getAllPathStorages(const OUString
& sPath
)
172 OUString sNormedPath
= StorageHolder::impl_st_normPath(sPath
);
173 OUStringList lFolders
= StorageHolder::impl_st_parsePath(sNormedPath
);
175 StorageHolder::TStorageList lStoragesOfPath
;
177 OUStringList::const_iterator pIt
;
179 osl::MutexGuard
g(m_mutex
);
181 for ( pIt
= lFolders
.begin();
182 pIt
!= lFolders
.end();
185 const OUString
& sChild
= *pIt
;
186 OUString
sCheckPath (sRelPath
);
187 sCheckPath
+= sChild
;
188 sCheckPath
+= PATH_SEPARATOR
;
190 TPath2StorageInfo::iterator pCheck
= m_lStorages
.find(sCheckPath
);
191 if (pCheck
== m_lStorages
.end())
193 // at least one path element was not found
194 // Seems that this path isn't open ...
195 lStoragesOfPath
.clear();
196 return lStoragesOfPath
;
199 TStorageInfo
& rInfo
= pCheck
->second
;
200 lStoragesOfPath
.push_back(rInfo
.Storage
);
203 sRelPath
+= PATH_SEPARATOR
;
206 return lStoragesOfPath
;
209 void StorageHolder::commitPath(const OUString
& sPath
)
211 StorageHolder::TStorageList lStorages
= getAllPathStorages(sPath
);
213 css::uno::Reference
< css::embed::XTransactedObject
> xCommit
;
214 StorageHolder::TStorageList::reverse_iterator pIt
;
215 for ( pIt
= lStorages
.rbegin(); // order of commit is important ... otherwise changes are not recognized!
216 pIt
!= lStorages
.rend();
219 xCommit
= css::uno::Reference
< css::embed::XTransactedObject
>(*pIt
, css::uno::UNO_QUERY
);
225 // SAFE -> ------------------------------
226 osl::ClearableMutexGuard
aReadLock(m_mutex
);
227 xCommit
= css::uno::Reference
< css::embed::XTransactedObject
>(m_xRoot
, css::uno::UNO_QUERY
);
229 // <- SAFE ------------------------------
235 void StorageHolder::closePath(const OUString
& rPath
)
237 OUString sNormedPath
= StorageHolder::impl_st_normPath(rPath
);
238 OUStringList lFolders
= StorageHolder::impl_st_parsePath(sNormedPath
);
240 /* convert list of paths in the following way:
241 [0] = "path_1" => "path_1
242 [1] = "path_2" => "path_1/path_2"
243 [2] = "path_3" => "path_1/path_2/path_3"
245 OUStringList::iterator pIt1
;
246 OUString sParentPath
;
247 for ( pIt1
= lFolders
.begin();
248 pIt1
!= lFolders
.end();
251 OUString sCurrentRelPath
= sParentPath
;
252 sCurrentRelPath
+= *pIt1
;
253 sCurrentRelPath
+= PATH_SEPARATOR
;
254 *pIt1
= sCurrentRelPath
;
255 sParentPath
= sCurrentRelPath
;
258 osl::MutexGuard
g(m_mutex
);
260 OUStringList::reverse_iterator pIt2
;
261 for ( pIt2
= lFolders
.rbegin();
262 pIt2
!= lFolders
.rend();
265 OUString sPath
= *pIt2
;
266 TPath2StorageInfo::iterator pPath
= m_lStorages
.find(sPath
);
267 if (pPath
== m_lStorages
.end())
270 TStorageInfo
& rInfo
= pPath
->second
;
272 if (rInfo
.UseCount
< 1)
274 rInfo
.Storage
.clear();
275 m_lStorages
.erase(pPath
);
280 void StorageHolder::notifyPath(const OUString
& sPath
)
282 OUString sNormedPath
= StorageHolder::impl_st_normPath(sPath
);
284 osl::MutexGuard
g(m_mutex
);
286 TPath2StorageInfo::iterator pIt1
= m_lStorages
.find(sNormedPath
);
287 if (pIt1
== m_lStorages
.end())
290 TStorageInfo
& rInfo
= pIt1
->second
;
291 TStorageListenerList::iterator pIt2
;
292 for ( pIt2
= rInfo
.Listener
.begin();
293 pIt2
!= rInfo
.Listener
.end();
296 IStorageListener
* pListener
= *pIt2
;
298 pListener
->changesOccurred(sNormedPath
);
302 void StorageHolder::addStorageListener( IStorageListener
* pListener
,
303 const OUString
& sPath
)
305 OUString sNormedPath
= StorageHolder::impl_st_normPath(sPath
);
307 osl::MutexGuard
g(m_mutex
);
309 TPath2StorageInfo::iterator pIt1
= m_lStorages
.find(sNormedPath
);
310 if (pIt1
== m_lStorages
.end())
313 TStorageInfo
& rInfo
= pIt1
->second
;
314 TStorageListenerList::iterator pIt2
= ::std::find(rInfo
.Listener
.begin(), rInfo
.Listener
.end(), pListener
);
315 if (pIt2
== rInfo
.Listener
.end())
316 rInfo
.Listener
.push_back(pListener
);
319 void StorageHolder::removeStorageListener( IStorageListener
* pListener
,
320 const OUString
& sPath
)
322 OUString sNormedPath
= StorageHolder::impl_st_normPath(sPath
);
324 osl::MutexGuard
g(m_mutex
);
326 TPath2StorageInfo::iterator pIt1
= m_lStorages
.find(sNormedPath
);
327 if (pIt1
== m_lStorages
.end())
330 TStorageInfo
& rInfo
= pIt1
->second
;
331 TStorageListenerList::iterator pIt2
= ::std::find(rInfo
.Listener
.begin(), rInfo
.Listener
.end(), pListener
);
332 if (pIt2
!= rInfo
.Listener
.end())
333 rInfo
.Listener
.erase(pIt2
);
336 OUString
StorageHolder::getPathOfStorage(const css::uno::Reference
< css::embed::XStorage
>& xStorage
)
338 osl::MutexGuard
g(m_mutex
);
340 TPath2StorageInfo::const_iterator pIt
;
341 for ( pIt
= m_lStorages
.begin();
342 pIt
!= m_lStorages
.end();
345 const TStorageInfo
& rInfo
= pIt
->second
;
346 if (rInfo
.Storage
== xStorage
)
350 if (pIt
== m_lStorages
.end())
356 css::uno::Reference
< css::embed::XStorage
> StorageHolder::getParentStorage(const css::uno::Reference
< css::embed::XStorage
>& xChild
)
358 OUString sChildPath
= getPathOfStorage(xChild
);
359 return getParentStorage(sChildPath
);
362 css::uno::Reference
< css::embed::XStorage
> StorageHolder::getParentStorage(const OUString
& sChildPath
)
364 // normed path = "a/b/c/" ... we search for "a/b/"
365 OUString sNormedPath
= StorageHolder::impl_st_normPath(sChildPath
);
366 OUStringList lFolders
= StorageHolder::impl_st_parsePath(sNormedPath
);
367 sal_Int32 c
= lFolders
.size();
369 // a) "" => - => no parent
370 // b) "a/b/c/" => "a/b/" => return storage "a/b/"
371 // c) "a/" => "" => return root !
375 return css::uno::Reference
< css::embed::XStorage
>();
377 // SAFE -> ----------------------------------
378 osl::ClearableMutexGuard
aReadLock(m_mutex
);
385 OUString sParentPath
;
387 for (i
=0; i
<c
-1; ++i
)
389 sParentPath
+= lFolders
[i
];
390 sParentPath
+= PATH_SEPARATOR
;
393 TPath2StorageInfo::const_iterator pParent
= m_lStorages
.find(sParentPath
);
394 if (pParent
!= m_lStorages
.end())
395 return pParent
->second
.Storage
;
398 // <- SAFE ----------------------------------
401 SAL_INFO("fwk", "StorageHolder::getParentStorage(): Unexpected situation. Cached storage item seems to be wrong.");
402 return css::uno::Reference
< css::embed::XStorage
>();
405 void StorageHolder::operator=(const StorageHolder
& rCopy
)
407 osl::MutexGuard
g(m_mutex
);
408 m_xRoot
= rCopy
.m_xRoot
;
409 m_lStorages
= rCopy
.m_lStorages
;
412 css::uno::Reference
< css::embed::XStorage
> StorageHolder::openSubStorageWithFallback(const css::uno::Reference
< css::embed::XStorage
>& xBaseStorage
,
413 const OUString
& sSubStorage
,
414 sal_Int32 eOpenMode
,
417 // a) try it first with user specified open mode
418 // ignore errors ... but save it for later use!
419 css::uno::Exception exResult
;
422 css::uno::Reference
< css::embed::XStorage
> xSubStorage
= xBaseStorage
->openStorageElement(sSubStorage
, eOpenMode
);
423 if (xSubStorage
.is())
426 catch(const css::uno::RuntimeException
&)
428 catch(const css::uno::Exception
& ex
)
431 // b) readonly already tried? => forward last error!
433 (!bAllowFallback
) || // fallback allowed ?
434 ((eOpenMode
& css::embed::ElementModes::WRITE
) != css::embed::ElementModes::WRITE
) // fallback possible ?
438 // c) try it readonly
439 // dont catch exception here! Outside code wish to know, if operation failed or not.
440 // Otherwhise they work on NULL references ...
441 sal_Int32 eNewMode
= (eOpenMode
& ~css::embed::ElementModes::WRITE
);
442 css::uno::Reference
< css::embed::XStorage
> xSubStorage
= xBaseStorage
->openStorageElement(sSubStorage
, eNewMode
);
443 if (xSubStorage
.is())
447 SAL_INFO("fwk", "openSubStorageWithFallback(): Unexpected situation! Got no exception for missing storage ...");
448 return css::uno::Reference
< css::embed::XStorage
>();
451 css::uno::Reference
< css::io::XStream
> StorageHolder::openSubStreamWithFallback(const css::uno::Reference
< css::embed::XStorage
>& xBaseStorage
,
452 const OUString
& sSubStream
,
453 sal_Int32 eOpenMode
,
456 // a) try it first with user specified open mode
457 // ignore errors ... but save it for later use!
458 css::uno::Exception exResult
;
461 css::uno::Reference
< css::io::XStream
> xSubStream
= xBaseStorage
->openStreamElement(sSubStream
, eOpenMode
);
465 catch(const css::uno::RuntimeException
&)
467 catch(const css::uno::Exception
& ex
)
470 // b) readonly already tried? => forward last error!
472 (!bAllowFallback
) || // fallback allowed ?
473 ((eOpenMode
& css::embed::ElementModes::WRITE
) != css::embed::ElementModes::WRITE
) // fallback possible ?
477 // c) try it readonly
478 // dont catch exception here! Outside code wish to know, if operation failed or not.
479 // Otherwhise they work on NULL references ...
480 sal_Int32 eNewMode
= (eOpenMode
& ~css::embed::ElementModes::WRITE
);
481 css::uno::Reference
< css::io::XStream
> xSubStream
= xBaseStorage
->openStreamElement(sSubStream
, eNewMode
);
486 SAL_INFO("fwk", "openSubStreamWithFallbacks(): Unexpected situation! Got no exception for missing stream ...");
487 return css::uno::Reference
< css::io::XStream
>();
490 OUString
StorageHolder::impl_st_normPath(const OUString
& sPath
)
492 // path must start without "/" but end with "/"!
494 OUString sNormedPath
= sPath
;
496 // "/bla" => "bla" && "/" => "" (!)
497 if (sNormedPath
.startsWith(PATH_SEPARATOR
))
498 sNormedPath
+= sNormedPath
.copy(1);
500 // "/" => "" || "" => "" ?
501 if (sNormedPath
.isEmpty())
505 if (sNormedPath
.lastIndexOf(PATH_SEPARATOR
) != (sNormedPath
.getLength()-1))
506 sNormedPath
+= PATH_SEPARATOR
;
511 OUStringList
StorageHolder::impl_st_parsePath(const OUString
& sPath
)
517 OUString sToken
= sPath
.getToken(0, PATH_SEPARATOR_UNICODE
, i
);
520 lToken
.push_back(sToken
);
525 } // namespace framework
527 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */