Line data Source code
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 <accelerators/storageholder.hxx>
21 :
22 : #include <services.h>
23 :
24 : #include <com/sun/star/container/NoSuchElementException.hpp>
25 :
26 : #include <com/sun/star/container/XNameAccess.hpp>
27 :
28 : #include <com/sun/star/beans/XPropertySet.hpp>
29 :
30 : #include <com/sun/star/embed/ElementModes.hpp>
31 :
32 : #include <com/sun/star/embed/XTransactedObject.hpp>
33 :
34 : #include <com/sun/star/embed/XPackageStructureCreator.hpp>
35 :
36 : #include <com/sun/star/lang/XSingleServiceFactory.hpp>
37 :
38 : #include <com/sun/star/io/XSeekable.hpp>
39 :
40 : #define PATH_SEPARATOR_ASCII "/"
41 : #define PATH_SEPARATOR_UNICODE ((sal_Unicode)'/')
42 : #define PATH_SEPARATOR OUString(PATH_SEPARATOR_ASCII)
43 :
44 : namespace framework
45 : {
46 :
47 2410 : StorageHolder::StorageHolder()
48 : {
49 2410 : }
50 :
51 2340 : StorageHolder::~StorageHolder()
52 : {
53 : // TODO implement me
54 : // dispose/clear etcpp.
55 2340 : }
56 :
57 2048 : void StorageHolder::forgetCachedStorages()
58 : {
59 2048 : osl::MutexGuard g(m_mutex);
60 2048 : TPath2StorageInfo::iterator pIt;
61 10542 : for ( pIt = m_lStorages.begin();
62 7028 : pIt != m_lStorages.end();
63 : ++pIt )
64 : {
65 1466 : TStorageInfo& rInfo = pIt->second;
66 : // TODO think about listener !
67 1466 : rInfo.Storage.clear();
68 : }
69 2048 : m_lStorages.clear();
70 2048 : }
71 :
72 1786 : void StorageHolder::setRootStorage(const css::uno::Reference< css::embed::XStorage >& xRoot)
73 : {
74 1786 : osl::MutexGuard g(m_mutex);
75 1786 : m_xRoot = xRoot;
76 1786 : }
77 :
78 1432 : css::uno::Reference< css::embed::XStorage > StorageHolder::getRootStorage() const
79 : {
80 1432 : osl::MutexGuard g(m_mutex);
81 1432 : return m_xRoot;
82 : }
83 :
84 2682 : css::uno::Reference< css::embed::XStorage > StorageHolder::openPath(const OUString& sPath ,
85 : sal_Int32 nOpenMode)
86 : {
87 2682 : OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
88 5364 : OUStringList lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
89 :
90 : // SAFE -> ----------------------------------
91 5364 : osl::ResettableMutexGuard aReadLock(m_mutex);
92 5364 : css::uno::Reference< css::embed::XStorage > xParent = m_xRoot;
93 2682 : aReadLock.clear();
94 : // <- SAFE ----------------------------------
95 :
96 2682 : css::uno::Reference< css::embed::XStorage > xChild;
97 5364 : OUString sRelPath;
98 2682 : OUStringList::const_iterator pIt;
99 :
100 23250 : for ( pIt = lFolders.begin();
101 15500 : pIt != lFolders.end();
102 : ++pIt )
103 : {
104 5088 : const OUString& sChild = *pIt;
105 5088 : OUString sCheckPath (sRelPath);
106 5088 : sCheckPath += sChild;
107 5088 : sCheckPath += PATH_SEPARATOR;
108 :
109 : // SAFE -> ------------------------------
110 5088 : aReadLock.reset();
111 :
112 : // If we found an already open storage ... we must increase
113 : // its use count. Otherwhise it will may be closed to early :-)
114 5088 : TPath2StorageInfo::iterator pCheck = m_lStorages.find(sCheckPath);
115 5088 : TStorageInfo* pInfo = 0;
116 5088 : if (pCheck != m_lStorages.end())
117 : {
118 1690 : pInfo = &(pCheck->second);
119 1690 : ++(pInfo->UseCount);
120 1690 : xChild = pInfo->Storage;
121 :
122 1690 : aReadLock.clear();
123 : // <- SAFE ------------------------------
124 : }
125 : else
126 : {
127 3398 : aReadLock.clear();
128 : // <- SAFE ------------------------------
129 :
130 : try
131 : {
132 3398 : xChild = StorageHolder::openSubStorageWithFallback(xParent, sChild, nOpenMode, true); // TODO think about delegating fallback decision to our own calli!
133 : }
134 0 : catch(const css::uno::RuntimeException&)
135 0 : { throw; }
136 40 : catch(const css::uno::Exception&)
137 : {
138 : /* TODO URGENT!
139 : in case we found some "already existing storages" on the path before and increased its UseCount ...
140 : and now we will get an exception on creating a new sub storage ...
141 : we must decrease all UseCounts, which was touched before. Otherwise these storages can't be closed!
142 :
143 : Idea: Using of another structure member "PossibleUseCount" as vector of unique numbers.
144 : Every thread use another unique number to identify all "owned candidates".
145 : A flush method with the same unique number force increasing of the "UseCount" variable then
146 : inside a synchronized block ...
147 : */
148 20 : throw;
149 : }
150 :
151 3378 : osl::MutexGuard g(m_mutex);
152 3378 : pInfo = &(m_lStorages[sCheckPath]);
153 3378 : pInfo->Storage = xChild;
154 3378 : pInfo->UseCount = 1;
155 : }
156 :
157 5068 : xParent = xChild;
158 5068 : sRelPath += sChild;
159 5068 : sRelPath += PATH_SEPARATOR;
160 5088 : }
161 :
162 : // TODO think about return last storage as working storage ... but dont caching it inside this holder!
163 : // => otherwise the same storage is may be commit more than once.
164 :
165 5344 : return xChild;
166 : }
167 :
168 0 : StorageHolder::TStorageList StorageHolder::getAllPathStorages(const OUString& sPath)
169 : {
170 0 : OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
171 0 : OUStringList lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
172 :
173 0 : StorageHolder::TStorageList lStoragesOfPath;
174 0 : OUString sRelPath;
175 0 : OUStringList::const_iterator pIt;
176 :
177 0 : osl::MutexGuard g(m_mutex);
178 :
179 0 : for ( pIt = lFolders.begin();
180 0 : pIt != lFolders.end();
181 : ++pIt )
182 : {
183 0 : const OUString& sChild = *pIt;
184 0 : OUString sCheckPath (sRelPath);
185 0 : sCheckPath += sChild;
186 0 : sCheckPath += PATH_SEPARATOR;
187 :
188 0 : TPath2StorageInfo::iterator pCheck = m_lStorages.find(sCheckPath);
189 0 : if (pCheck == m_lStorages.end())
190 : {
191 : // at least one path element was not found
192 : // Seems that this path isn't open ...
193 0 : lStoragesOfPath.clear();
194 0 : return lStoragesOfPath;
195 : }
196 :
197 0 : TStorageInfo& rInfo = pCheck->second;
198 0 : lStoragesOfPath.push_back(rInfo.Storage);
199 :
200 0 : sRelPath += sChild;
201 0 : sRelPath += PATH_SEPARATOR;
202 0 : }
203 :
204 0 : return lStoragesOfPath;
205 : }
206 :
207 0 : void StorageHolder::commitPath(const OUString& sPath)
208 : {
209 0 : StorageHolder::TStorageList lStorages = getAllPathStorages(sPath);
210 :
211 0 : css::uno::Reference< css::embed::XTransactedObject > xCommit;
212 0 : StorageHolder::TStorageList::reverse_iterator pIt;
213 0 : for ( pIt = lStorages.rbegin(); // order of commit is important ... otherwise changes are not recognized!
214 0 : pIt != lStorages.rend();
215 : ++pIt )
216 : {
217 0 : xCommit = css::uno::Reference< css::embed::XTransactedObject >(*pIt, css::uno::UNO_QUERY);
218 0 : if (!xCommit.is())
219 0 : continue;
220 0 : xCommit->commit();
221 : }
222 :
223 : // SAFE -> ------------------------------
224 0 : osl::ClearableMutexGuard aReadLock(m_mutex);
225 0 : xCommit = css::uno::Reference< css::embed::XTransactedObject >(m_xRoot, css::uno::UNO_QUERY);
226 0 : aReadLock.clear();
227 : // <- SAFE ------------------------------
228 :
229 0 : if (xCommit.is())
230 0 : xCommit->commit();
231 0 : }
232 :
233 4080 : void StorageHolder::closePath(const OUString& rPath)
234 : {
235 4080 : OUString sNormedPath = StorageHolder::impl_st_normPath(rPath);
236 8160 : OUStringList lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
237 :
238 : /* convert list of paths in the following way:
239 : [0] = "path_1" => "path_1
240 : [1] = "path_2" => "path_1/path_2"
241 : [2] = "path_3" => "path_1/path_2/path_3"
242 : */
243 4080 : OUStringList::iterator pIt1;
244 8160 : OUString sParentPath;
245 31464 : for ( pIt1 = lFolders.begin();
246 20976 : pIt1 != lFolders.end();
247 : ++pIt1 )
248 : {
249 6408 : OUString sCurrentRelPath = sParentPath;
250 6408 : sCurrentRelPath += *pIt1;
251 6408 : sCurrentRelPath += PATH_SEPARATOR;
252 6408 : *pIt1 = sCurrentRelPath;
253 6408 : sParentPath = sCurrentRelPath;
254 6408 : }
255 :
256 8160 : osl::MutexGuard g(m_mutex);
257 :
258 4080 : OUStringList::reverse_iterator pIt2;
259 31464 : for ( pIt2 = lFolders.rbegin();
260 20976 : pIt2 != lFolders.rend();
261 : ++pIt2 )
262 : {
263 6408 : OUString sPath = *pIt2;
264 6408 : TPath2StorageInfo::iterator pPath = m_lStorages.find(sPath);
265 6408 : if (pPath == m_lStorages.end())
266 2936 : continue; // ???
267 :
268 3472 : TStorageInfo& rInfo = pPath->second;
269 3472 : --rInfo.UseCount;
270 3472 : if (rInfo.UseCount < 1)
271 : {
272 1838 : rInfo.Storage.clear();
273 1838 : m_lStorages.erase(pPath);
274 : }
275 7552 : }
276 4080 : }
277 :
278 0 : void StorageHolder::notifyPath(const OUString& sPath)
279 : {
280 0 : OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
281 :
282 0 : osl::MutexGuard g(m_mutex);
283 :
284 0 : TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
285 0 : if (pIt1 == m_lStorages.end())
286 0 : return;
287 :
288 0 : TStorageInfo& rInfo = pIt1->second;
289 0 : TStorageListenerList::iterator pIt2;
290 0 : for ( pIt2 = rInfo.Listener.begin();
291 0 : pIt2 != rInfo.Listener.end();
292 : ++pIt2 )
293 : {
294 0 : IStorageListener* pListener = *pIt2;
295 0 : if (pListener)
296 0 : pListener->changesOccurred(sNormedPath);
297 0 : }
298 : }
299 :
300 1470 : void StorageHolder::addStorageListener( IStorageListener* pListener,
301 : const OUString& sPath )
302 : {
303 1470 : OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
304 :
305 2940 : osl::MutexGuard g(m_mutex);
306 :
307 1470 : TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
308 1470 : if (pIt1 == m_lStorages.end())
309 1470 : return;
310 :
311 1470 : TStorageInfo& rInfo = pIt1->second;
312 1470 : TStorageListenerList::iterator pIt2 = ::std::find(rInfo.Listener.begin(), rInfo.Listener.end(), pListener);
313 1470 : if (pIt2 == rInfo.Listener.end())
314 2940 : rInfo.Listener.push_back(pListener);
315 : }
316 :
317 1458 : void StorageHolder::removeStorageListener( IStorageListener* pListener,
318 : const OUString& sPath )
319 : {
320 1458 : OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
321 :
322 2916 : osl::MutexGuard g(m_mutex);
323 :
324 1458 : TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
325 1458 : if (pIt1 == m_lStorages.end())
326 1458 : return;
327 :
328 1458 : TStorageInfo& rInfo = pIt1->second;
329 1458 : TStorageListenerList::iterator pIt2 = ::std::find(rInfo.Listener.begin(), rInfo.Listener.end(), pListener);
330 1458 : if (pIt2 != rInfo.Listener.end())
331 2916 : rInfo.Listener.erase(pIt2);
332 : }
333 :
334 404 : OUString StorageHolder::getPathOfStorage(const css::uno::Reference< css::embed::XStorage >& xStorage)
335 : {
336 404 : osl::MutexGuard g(m_mutex);
337 :
338 404 : TPath2StorageInfo::const_iterator pIt;
339 3483 : for ( pIt = m_lStorages.begin();
340 2322 : pIt != m_lStorages.end();
341 : ++pIt )
342 : {
343 1155 : const TStorageInfo& rInfo = pIt->second;
344 1155 : if (rInfo.Storage == xStorage)
345 398 : break;
346 : }
347 :
348 404 : if (pIt == m_lStorages.end())
349 6 : return OUString();
350 :
351 398 : return pIt->first;
352 : }
353 :
354 404 : css::uno::Reference< css::embed::XStorage > StorageHolder::getParentStorage(const css::uno::Reference< css::embed::XStorage >& xChild)
355 : {
356 404 : OUString sChildPath = getPathOfStorage(xChild);
357 404 : return getParentStorage(sChildPath);
358 : }
359 :
360 404 : css::uno::Reference< css::embed::XStorage > StorageHolder::getParentStorage(const OUString& sChildPath)
361 : {
362 : // normed path = "a/b/c/" ... we search for "a/b/"
363 404 : OUString sNormedPath = StorageHolder::impl_st_normPath(sChildPath);
364 808 : OUStringList lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
365 404 : sal_Int32 c = lFolders.size();
366 :
367 : // a) "" => - => no parent
368 : // b) "a/b/c/" => "a/b/" => return storage "a/b/"
369 : // c) "a/" => "" => return root !
370 :
371 : // a)
372 404 : if (c < 1)
373 6 : return css::uno::Reference< css::embed::XStorage >();
374 :
375 : // SAFE -> ----------------------------------
376 796 : osl::ClearableMutexGuard aReadLock(m_mutex);
377 :
378 : // b)
379 398 : if (c < 2)
380 0 : return m_xRoot;
381 :
382 : // c)
383 796 : OUString sParentPath;
384 398 : sal_Int32 i = 0;
385 1194 : for (i=0; i<c-1; ++i)
386 : {
387 796 : sParentPath += lFolders[i];
388 796 : sParentPath += PATH_SEPARATOR;
389 : }
390 :
391 398 : TPath2StorageInfo::const_iterator pParent = m_lStorages.find(sParentPath);
392 398 : if (pParent != m_lStorages.end())
393 398 : return pParent->second.Storage;
394 :
395 0 : aReadLock.clear();
396 : // <- SAFE ----------------------------------
397 :
398 : // ?
399 : SAL_INFO("fwk", "StorageHolder::getParentStorage(): Unexpected situation. Cached storage item seems to be wrong.");
400 404 : return css::uno::Reference< css::embed::XStorage >();
401 : }
402 :
403 0 : void StorageHolder::operator=(const StorageHolder& rCopy)
404 : {
405 0 : osl::MutexGuard g(m_mutex);
406 0 : m_xRoot = rCopy.m_xRoot;
407 0 : m_lStorages = rCopy.m_lStorages;
408 0 : }
409 :
410 3398 : css::uno::Reference< css::embed::XStorage > StorageHolder::openSubStorageWithFallback(const css::uno::Reference< css::embed::XStorage >& xBaseStorage ,
411 : const OUString& sSubStorage ,
412 : sal_Int32 eOpenMode ,
413 : bool bAllowFallback)
414 : {
415 : // a) try it first with user specified open mode
416 : // ignore errors ... but save it for later use!
417 3398 : css::uno::Exception exResult;
418 : try
419 : {
420 3398 : css::uno::Reference< css::embed::XStorage > xSubStorage = xBaseStorage->openStorageElement(sSubStorage, eOpenMode);
421 3378 : if (xSubStorage.is())
422 3378 : return xSubStorage;
423 : }
424 0 : catch(const css::uno::RuntimeException&)
425 0 : { throw; }
426 40 : catch(const css::uno::Exception& ex)
427 20 : { exResult = ex; }
428 :
429 : // b) readonly already tried? => forward last error!
430 20 : if (
431 40 : (!bAllowFallback ) || // fallback allowed ?
432 20 : ((eOpenMode & css::embed::ElementModes::WRITE) != css::embed::ElementModes::WRITE) // fallback possible ?
433 : )
434 14 : throw exResult;
435 :
436 : // c) try it readonly
437 : // dont catch exception here! Outside code wish to know, if operation failed or not.
438 : // Otherwhise they work on NULL references ...
439 6 : sal_Int32 eNewMode = (eOpenMode & ~css::embed::ElementModes::WRITE);
440 6 : css::uno::Reference< css::embed::XStorage > xSubStorage = xBaseStorage->openStorageElement(sSubStorage, eNewMode);
441 0 : if (xSubStorage.is())
442 0 : return xSubStorage;
443 :
444 : // d) no chance!
445 : SAL_INFO("fwk", "openSubStorageWithFallback(): Unexpected situation! Got no exception for missing storage ...");
446 3398 : return css::uno::Reference< css::embed::XStorage >();
447 : }
448 :
449 0 : css::uno::Reference< css::io::XStream > StorageHolder::openSubStreamWithFallback(const css::uno::Reference< css::embed::XStorage >& xBaseStorage ,
450 : const OUString& sSubStream ,
451 : sal_Int32 eOpenMode ,
452 : bool bAllowFallback)
453 : {
454 : // a) try it first with user specified open mode
455 : // ignore errors ... but save it for later use!
456 0 : css::uno::Exception exResult;
457 : try
458 : {
459 0 : css::uno::Reference< css::io::XStream > xSubStream = xBaseStorage->openStreamElement(sSubStream, eOpenMode);
460 0 : if (xSubStream.is())
461 0 : return xSubStream;
462 : }
463 0 : catch(const css::uno::RuntimeException&)
464 0 : { throw; }
465 0 : catch(const css::uno::Exception& ex)
466 0 : { exResult = ex; }
467 :
468 : // b) readonly already tried? => forward last error!
469 0 : if (
470 0 : (!bAllowFallback ) || // fallback allowed ?
471 0 : ((eOpenMode & css::embed::ElementModes::WRITE) != css::embed::ElementModes::WRITE) // fallback possible ?
472 : )
473 0 : throw exResult;
474 :
475 : // c) try it readonly
476 : // dont catch exception here! Outside code wish to know, if operation failed or not.
477 : // Otherwhise they work on NULL references ...
478 0 : sal_Int32 eNewMode = (eOpenMode & ~css::embed::ElementModes::WRITE);
479 0 : css::uno::Reference< css::io::XStream > xSubStream = xBaseStorage->openStreamElement(sSubStream, eNewMode);
480 0 : if (xSubStream.is())
481 0 : return xSubStream;
482 :
483 : // d) no chance!
484 : SAL_INFO("fwk", "openSubStreamWithFallbacks(): Unexpected situation! Got no exception for missing stream ...");
485 0 : return css::uno::Reference< css::io::XStream >();
486 : }
487 :
488 10094 : OUString StorageHolder::impl_st_normPath(const OUString& sPath)
489 : {
490 : // path must start without "/" but end with "/"!
491 :
492 10094 : OUString sNormedPath = sPath;
493 :
494 : // "/bla" => "bla" && "/" => "" (!)
495 10094 : if (sNormedPath.startsWith(PATH_SEPARATOR))
496 0 : sNormedPath += sNormedPath.copy(1);
497 :
498 : // "/" => "" || "" => "" ?
499 10094 : if (sNormedPath.isEmpty())
500 6 : return OUString();
501 :
502 : // "bla" => "bla/"
503 10088 : if (sNormedPath.lastIndexOf(PATH_SEPARATOR) != (sNormedPath.getLength()-1))
504 9690 : sNormedPath += PATH_SEPARATOR;
505 :
506 10088 : return sNormedPath;
507 : }
508 :
509 7166 : OUStringList StorageHolder::impl_st_parsePath(const OUString& sPath)
510 : {
511 7166 : OUStringList lToken;
512 7166 : sal_Int32 i = 0;
513 : while (true)
514 : {
515 19874 : OUString sToken = sPath.getToken(0, PATH_SEPARATOR_UNICODE, i);
516 19874 : if (i < 0)
517 7166 : break;
518 12708 : lToken.push_back(sToken);
519 12708 : }
520 7166 : return lToken;
521 : }
522 :
523 : } // namespace framework
524 :
525 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|