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>
22 #include <threadhelp/readguard.hxx>
23 #include <threadhelp/writeguard.hxx>
26 #include <com/sun/star/container/NoSuchElementException.hpp>
28 #include <com/sun/star/container/XNameAccess.hpp>
30 #include <com/sun/star/beans/XPropertySet.hpp>
32 #include <com/sun/star/embed/ElementModes.hpp>
34 #include <com/sun/star/embed/XTransactedObject.hpp>
36 #include <com/sun/star/embed/XPackageStructureCreator.hpp>
38 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
40 #include <com/sun/star/io/XSeekable.hpp>
43 #define PATH_SEPARATOR_ASCII "/"
44 #define PATH_SEPARATOR_UNICODE ((sal_Unicode)'/')
45 #define PATH_SEPARATOR OUString(PATH_SEPARATOR_ASCII)
51 //-----------------------------------------------
52 StorageHolder::StorageHolder()
57 //-----------------------------------------------
58 StorageHolder::~StorageHolder()
61 // dispose/clear etcpp.
64 //-----------------------------------------------
65 void StorageHolder::forgetCachedStorages()
67 // SAFE -> ----------------------------------
68 WriteGuard
aWriteLock(m_aLock
);
70 TPath2StorageInfo::iterator pIt
;
71 for ( pIt
= m_lStorages
.begin();
72 pIt
!= m_lStorages
.end() ;
75 TStorageInfo
& rInfo
= pIt
->second
;
76 // TODO think about listener !
77 rInfo
.Storage
.clear();
82 // <- SAFE ----------------------------------
85 //-----------------------------------------------
86 void StorageHolder::setRootStorage(const css::uno::Reference
< css::embed::XStorage
>& xRoot
)
88 // SAFE -> ----------------------------------
89 WriteGuard
aWriteLock(m_aLock
);
92 // <- SAFE ----------------------------------
95 //-----------------------------------------------
96 css::uno::Reference
< css::embed::XStorage
> StorageHolder::getRootStorage() const
98 // SAFE -> ----------------------------------
99 ReadGuard
aReadLock(m_aLock
);
101 // <- SAFE ----------------------------------
104 //-----------------------------------------------
105 css::uno::Reference
< css::embed::XStorage
> StorageHolder::openPath(const OUString
& sPath
,
108 OUString sNormedPath
= StorageHolder::impl_st_normPath(sPath
);
109 OUStringList lFolders
= StorageHolder::impl_st_parsePath(sNormedPath
);
111 // SAFE -> ----------------------------------
112 ReadGuard
aReadLock(m_aLock
);
113 css::uno::Reference
< css::embed::XStorage
> xParent
= m_xRoot
;
115 // <- SAFE ----------------------------------
117 css::uno::Reference
< css::embed::XStorage
> xChild
;
119 OUStringList::const_iterator pIt
;
121 for ( pIt
= lFolders
.begin();
122 pIt
!= lFolders
.end() ;
125 const OUString
& sChild
= *pIt
;
126 OUString
sCheckPath (sRelPath
);
127 sCheckPath
+= sChild
;
128 sCheckPath
+= PATH_SEPARATOR
;
130 // SAFE -> ------------------------------
133 // If we found an already open storage ... we must increase
134 // its use count. Otherwhise it will may be closed to early :-)
135 TPath2StorageInfo::iterator pCheck
= m_lStorages
.find(sCheckPath
);
136 TStorageInfo
* pInfo
= 0;
137 if (pCheck
!= m_lStorages
.end())
139 pInfo
= &(pCheck
->second
);
141 xChild
= pInfo
->Storage
;
146 // <- SAFE ------------------------------
150 xChild
= StorageHolder::openSubStorageWithFallback(xParent
, sChild
, nOpenMode
, sal_True
); // TODO think about delegating fallback decision to our own calli!
152 catch(const css::uno::RuntimeException
&)
154 catch(const css::uno::Exception
&)
157 in case we found some "already existing storages" on the path before and increased its UseCount ...
158 and now we will get an exception on creating a new sub storage ...
159 we must decrease all UseCounts, which was touched before. Otherwise these storages cant be closed!
161 Idea: Using of another structure member "PossibleUseCount" as vector of unique numbers.
162 Every thread use another unique number to identify all "owned candidates".
163 A flush method with the same unique number force increasing of the "UseCount" variable then
164 inside a synchronized block ...
169 // SAFE -> ------------------------------
170 WriteGuard
aWriteLock(m_aLock
);
171 pInfo
= &(m_lStorages
[sCheckPath
]);
172 pInfo
->Storage
= xChild
;
175 // <- SAFE ------------------------------
180 sRelPath
+= PATH_SEPARATOR
;
183 // TODO think about return last storage as working storage ... but dont caching it inside this holder!
184 // => otherwise the same storage is may be commit more then once.
189 //-----------------------------------------------
190 StorageHolder::TStorageList
StorageHolder::getAllPathStorages(const OUString
& sPath
)
192 OUString sNormedPath
= StorageHolder::impl_st_normPath(sPath
);
193 OUStringList lFolders
= StorageHolder::impl_st_parsePath(sNormedPath
);
195 StorageHolder::TStorageList lStoragesOfPath
;
197 OUStringList::const_iterator pIt
;
199 // SAFE -> ----------------------------------
200 ReadGuard
aReadLock(m_aLock
);
202 for ( pIt
= lFolders
.begin();
203 pIt
!= lFolders
.end() ;
206 const OUString
& sChild
= *pIt
;
207 OUString
sCheckPath (sRelPath
);
208 sCheckPath
+= sChild
;
209 sCheckPath
+= PATH_SEPARATOR
;
211 TPath2StorageInfo::iterator pCheck
= m_lStorages
.find(sCheckPath
);
212 if (pCheck
== m_lStorages
.end())
214 // at least one path element was not found
215 // Seems that this path isnt open ...
216 lStoragesOfPath
.clear();
217 return lStoragesOfPath
;
220 TStorageInfo
& rInfo
= pCheck
->second
;
221 lStoragesOfPath
.push_back(rInfo
.Storage
);
224 sRelPath
+= PATH_SEPARATOR
;
228 // <- SAFE ----------------------------------
230 return lStoragesOfPath
;
233 //-----------------------------------------------
234 void StorageHolder::commitPath(const OUString
& sPath
)
236 StorageHolder::TStorageList lStorages
= getAllPathStorages(sPath
);
238 css::uno::Reference
< css::embed::XTransactedObject
> xCommit
;
239 StorageHolder::TStorageList::reverse_iterator pIt
;
240 for ( pIt
= lStorages
.rbegin(); // order of commit is important ... otherwise changes are not recognized!
241 pIt
!= lStorages
.rend() ;
244 xCommit
= css::uno::Reference
< css::embed::XTransactedObject
>(*pIt
, css::uno::UNO_QUERY
);
250 // SAFE -> ------------------------------
251 ReadGuard
aReadLock(m_aLock
);
252 xCommit
= css::uno::Reference
< css::embed::XTransactedObject
>(m_xRoot
, css::uno::UNO_QUERY
);
254 // <- SAFE ------------------------------
260 //-----------------------------------------------
261 void StorageHolder::closePath(const OUString
& rPath
)
263 OUString sNormedPath
= StorageHolder::impl_st_normPath(rPath
);
264 OUStringList lFolders
= StorageHolder::impl_st_parsePath(sNormedPath
);
266 /* convert list of paths in the following way:
267 [0] = "path_1" => "path_1
268 [1] = "path_2" => "path_1/path_2"
269 [2] = "path_3" => "path_1/path_2/path_3"
271 OUStringList::iterator pIt1
;
272 OUString sParentPath
;
273 for ( pIt1
= lFolders
.begin();
274 pIt1
!= lFolders
.end() ;
277 OUString sCurrentRelPath
= sParentPath
;
278 sCurrentRelPath
+= *pIt1
;
279 sCurrentRelPath
+= PATH_SEPARATOR
;
280 *pIt1
= sCurrentRelPath
;
281 sParentPath
= sCurrentRelPath
;
284 // SAFE -> ------------------------------
285 ReadGuard
aReadLock(m_aLock
);
287 OUStringList::reverse_iterator pIt2
;
288 for ( pIt2
= lFolders
.rbegin();
289 pIt2
!= lFolders
.rend() ;
292 OUString sPath
= *pIt2
;
293 TPath2StorageInfo::iterator pPath
= m_lStorages
.find(sPath
);
294 if (pPath
== m_lStorages
.end())
297 TStorageInfo
& rInfo
= pPath
->second
;
299 if (rInfo
.UseCount
< 1)
301 rInfo
.Storage
.clear();
302 m_lStorages
.erase(pPath
);
307 // <- SAFE ------------------------------
310 //-----------------------------------------------
311 void StorageHolder::notifyPath(const OUString
& sPath
)
313 OUString sNormedPath
= StorageHolder::impl_st_normPath(sPath
);
315 // SAFE -> ------------------------------
316 ReadGuard
aReadLock(m_aLock
);
318 TPath2StorageInfo::iterator pIt1
= m_lStorages
.find(sNormedPath
);
319 if (pIt1
== m_lStorages
.end())
322 TStorageInfo
& rInfo
= pIt1
->second
;
323 TStorageListenerList::iterator pIt2
;
324 for ( pIt2
= rInfo
.Listener
.begin();
325 pIt2
!= rInfo
.Listener
.end() ;
328 IStorageListener
* pListener
= *pIt2
;
330 pListener
->changesOccurred(sNormedPath
);
334 // <- SAFE ------------------------------
337 //-----------------------------------------------
338 void StorageHolder::addStorageListener( IStorageListener
* pListener
,
339 const OUString
& sPath
)
341 OUString sNormedPath
= StorageHolder::impl_st_normPath(sPath
);
343 // SAFE -> ------------------------------
344 ReadGuard
aReadLock(m_aLock
);
346 TPath2StorageInfo::iterator pIt1
= m_lStorages
.find(sNormedPath
);
347 if (pIt1
== m_lStorages
.end())
350 TStorageInfo
& rInfo
= pIt1
->second
;
351 TStorageListenerList::iterator pIt2
= ::std::find(rInfo
.Listener
.begin(), rInfo
.Listener
.end(), pListener
);
352 if (pIt2
== rInfo
.Listener
.end())
353 rInfo
.Listener
.push_back(pListener
);
356 // <- SAFE ------------------------------
359 //-----------------------------------------------
360 void StorageHolder::removeStorageListener( IStorageListener
* pListener
,
361 const OUString
& sPath
)
363 OUString sNormedPath
= StorageHolder::impl_st_normPath(sPath
);
365 // SAFE -> ------------------------------
366 ReadGuard
aReadLock(m_aLock
);
368 TPath2StorageInfo::iterator pIt1
= m_lStorages
.find(sNormedPath
);
369 if (pIt1
== m_lStorages
.end())
372 TStorageInfo
& rInfo
= pIt1
->second
;
373 TStorageListenerList::iterator pIt2
= ::std::find(rInfo
.Listener
.begin(), rInfo
.Listener
.end(), pListener
);
374 if (pIt2
!= rInfo
.Listener
.end())
375 rInfo
.Listener
.erase(pIt2
);
378 // <- SAFE ------------------------------
381 //-----------------------------------------------
382 OUString
StorageHolder::getPathOfStorage(const css::uno::Reference
< css::embed::XStorage
>& xStorage
)
384 // SAFE -> ------------------------------
385 ReadGuard
aReadLock(m_aLock
);
387 TPath2StorageInfo::const_iterator pIt
;
388 for ( pIt
= m_lStorages
.begin();
389 pIt
!= m_lStorages
.end() ;
392 const TStorageInfo
& rInfo
= pIt
->second
;
393 if (rInfo
.Storage
== xStorage
)
397 if (pIt
== m_lStorages
.end())
402 // <- SAFE ------------------------------
405 //-----------------------------------------------
406 css::uno::Reference
< css::embed::XStorage
> StorageHolder::getParentStorage(const css::uno::Reference
< css::embed::XStorage
>& xChild
)
408 OUString sChildPath
= getPathOfStorage(xChild
);
409 return getParentStorage(sChildPath
);
412 //-----------------------------------------------
413 css::uno::Reference
< css::embed::XStorage
> StorageHolder::getParentStorage(const OUString
& sChildPath
)
415 // normed path = "a/b/c/" ... we search for "a/b/"
416 OUString sNormedPath
= StorageHolder::impl_st_normPath(sChildPath
);
417 OUStringList lFolders
= StorageHolder::impl_st_parsePath(sNormedPath
);
418 sal_Int32 c
= lFolders
.size();
420 // a) "" => - => no parent
421 // b) "a/b/c/" => "a/b/" => return storage "a/b/"
422 // c) "a/" => "" => return root !
426 return css::uno::Reference
< css::embed::XStorage
>();
428 // SAFE -> ----------------------------------
429 ReadGuard
aReadLock(m_aLock
);
436 OUString sParentPath
;
438 for (i
=0; i
<c
-1; ++i
)
440 sParentPath
+= lFolders
[i
];
441 sParentPath
+= PATH_SEPARATOR
;
444 TPath2StorageInfo::const_iterator pParent
= m_lStorages
.find(sParentPath
);
445 if (pParent
!= m_lStorages
.end())
446 return pParent
->second
.Storage
;
449 // <- SAFE ----------------------------------
452 LOG_WARNING("StorageHolder::getParentStorage()", "Unexpected situation. Cached storage item seems to be wrong.")
453 return css::uno::Reference
< css::embed::XStorage
>();
456 //-----------------------------------------------
457 void StorageHolder::operator=(const StorageHolder
& rCopy
)
459 // SAFE -> ----------------------------------
460 WriteGuard
aWriteLock(m_aLock
);
462 m_xRoot
= rCopy
.m_xRoot
;
463 m_lStorages
= rCopy
.m_lStorages
;
466 // <- SAFE ----------------------------------
469 //-----------------------------------------------
470 css::uno::Reference
< css::embed::XStorage
> StorageHolder::openSubStorageWithFallback(const css::uno::Reference
< css::embed::XStorage
>& xBaseStorage
,
471 const OUString
& sSubStorage
,
472 sal_Int32 eOpenMode
,
473 sal_Bool bAllowFallback
)
475 // a) try it first with user specified open mode
476 // ignore errors ... but save it for later use!
477 css::uno::Exception exResult
;
480 css::uno::Reference
< css::embed::XStorage
> xSubStorage
= xBaseStorage
->openStorageElement(sSubStorage
, eOpenMode
);
481 if (xSubStorage
.is())
484 catch(const css::uno::RuntimeException
&)
486 catch(const css::uno::Exception
& ex
)
489 // b) readonly already tried? => forward last error!
491 (!bAllowFallback
) || // fallback allowed ?
492 ((eOpenMode
& css::embed::ElementModes::WRITE
) != css::embed::ElementModes::WRITE
) // fallback possible ?
496 // c) try it readonly
497 // dont catch exception here! Outside code whish to know, if operation failed or not.
498 // Otherwhise they work on NULL references ...
499 sal_Int32 eNewMode
= (eOpenMode
& ~css::embed::ElementModes::WRITE
);
500 css::uno::Reference
< css::embed::XStorage
> xSubStorage
= xBaseStorage
->openStorageElement(sSubStorage
, eNewMode
);
501 if (xSubStorage
.is())
505 LOG_WARNING("openSubStorageWithFallback()", "Unexpected situation! Got no exception for missing storage ...")
506 return css::uno::Reference
< css::embed::XStorage
>();
509 //-----------------------------------------------
510 css::uno::Reference
< css::io::XStream
> StorageHolder::openSubStreamWithFallback(const css::uno::Reference
< css::embed::XStorage
>& xBaseStorage
,
511 const OUString
& sSubStream
,
512 sal_Int32 eOpenMode
,
513 sal_Bool bAllowFallback
)
515 // a) try it first with user specified open mode
516 // ignore errors ... but save it for later use!
517 css::uno::Exception exResult
;
520 css::uno::Reference
< css::io::XStream
> xSubStream
= xBaseStorage
->openStreamElement(sSubStream
, eOpenMode
);
524 catch(const css::uno::RuntimeException
&)
526 catch(const css::uno::Exception
& ex
)
529 // b) readonly already tried? => forward last error!
531 (!bAllowFallback
) || // fallback allowed ?
532 ((eOpenMode
& css::embed::ElementModes::WRITE
) != css::embed::ElementModes::WRITE
) // fallback possible ?
536 // c) try it readonly
537 // dont catch exception here! Outside code whish to know, if operation failed or not.
538 // Otherwhise they work on NULL references ...
539 sal_Int32 eNewMode
= (eOpenMode
& ~css::embed::ElementModes::WRITE
);
540 css::uno::Reference
< css::io::XStream
> xSubStream
= xBaseStorage
->openStreamElement(sSubStream
, eNewMode
);
545 LOG_WARNING("openSubStreamWithFallbacks()", "Unexpected situation! Got no exception for missing stream ...")
546 return css::uno::Reference
< css::io::XStream
>();
549 //-----------------------------------------------
550 OUString
StorageHolder::impl_st_normPath(const OUString
& sPath
)
552 // path must start without "/" but end with "/"!
554 OUString sNormedPath
= sPath
;
556 // "/bla" => "bla" && "/" => "" (!)
557 if (sNormedPath
.indexOf(PATH_SEPARATOR
) == 0)
558 sNormedPath
+= sNormedPath
.copy(1);
560 // "/" => "" || "" => "" ?
561 if (sNormedPath
.isEmpty())
565 if (sNormedPath
.lastIndexOf(PATH_SEPARATOR
) != (sNormedPath
.getLength()-1))
566 sNormedPath
+= PATH_SEPARATOR
;
571 //-----------------------------------------------
572 OUStringList
StorageHolder::impl_st_parsePath(const OUString
& sPath
)
578 OUString sToken
= sPath
.getToken(0, PATH_SEPARATOR_UNICODE
, i
);
581 lToken
.push_back(sToken
);
586 //===============================================
587 } // namespace framework
589 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */