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 : #include <algorithm>
41 :
42 : #define PATH_SEPARATOR_ASCII "/"
43 : #define PATH_SEPARATOR_UNICODE ((sal_Unicode)'/')
44 : #define PATH_SEPARATOR OUString(PATH_SEPARATOR_ASCII)
45 :
46 : namespace framework
47 : {
48 :
49 1208 : StorageHolder::StorageHolder()
50 : {
51 1208 : }
52 :
53 1147 : StorageHolder::~StorageHolder()
54 : {
55 : // TODO implement me
56 : // dispose/clear etcpp.
57 1147 : }
58 :
59 955 : void StorageHolder::forgetCachedStorages()
60 : {
61 955 : osl::MutexGuard g(m_mutex);
62 955 : TPath2StorageInfo::iterator pIt;
63 4632 : for ( pIt = m_lStorages.begin();
64 3088 : pIt != m_lStorages.end();
65 : ++pIt )
66 : {
67 589 : TStorageInfo& rInfo = pIt->second;
68 : // TODO think about listener !
69 589 : rInfo.Storage.clear();
70 : }
71 955 : m_lStorages.clear();
72 955 : }
73 :
74 806 : void StorageHolder::setRootStorage(const css::uno::Reference< css::embed::XStorage >& xRoot)
75 : {
76 806 : osl::MutexGuard g(m_mutex);
77 806 : m_xRoot = xRoot;
78 806 : }
79 :
80 926 : css::uno::Reference< css::embed::XStorage > StorageHolder::getRootStorage() const
81 : {
82 926 : osl::MutexGuard g(m_mutex);
83 926 : return m_xRoot;
84 : }
85 :
86 1378 : css::uno::Reference< css::embed::XStorage > StorageHolder::openPath(const OUString& sPath ,
87 : sal_Int32 nOpenMode)
88 : {
89 1378 : OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
90 2756 : OUStringList lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
91 :
92 : // SAFE -> ----------------------------------
93 2756 : osl::ResettableMutexGuard aReadLock(m_mutex);
94 2756 : css::uno::Reference< css::embed::XStorage > xParent = m_xRoot;
95 1378 : aReadLock.clear();
96 : // <- SAFE ----------------------------------
97 :
98 1378 : css::uno::Reference< css::embed::XStorage > xChild;
99 2756 : OUString sRelPath;
100 1378 : OUStringList::const_iterator pIt;
101 :
102 12864 : for ( pIt = lFolders.begin();
103 8576 : pIt != lFolders.end();
104 : ++pIt )
105 : {
106 2932 : const OUString& sChild = *pIt;
107 2932 : OUString sCheckPath (sRelPath);
108 2932 : sCheckPath += sChild;
109 2932 : sCheckPath += PATH_SEPARATOR;
110 :
111 : // SAFE -> ------------------------------
112 2932 : aReadLock.reset();
113 :
114 : // If we found an already open storage ... we must increase
115 : // its use count. Otherwhise it will may be closed to early :-)
116 2932 : TPath2StorageInfo::iterator pCheck = m_lStorages.find(sCheckPath);
117 2932 : TStorageInfo* pInfo = 0;
118 2932 : if (pCheck != m_lStorages.end())
119 : {
120 1082 : pInfo = &(pCheck->second);
121 1082 : ++(pInfo->UseCount);
122 1082 : xChild = pInfo->Storage;
123 :
124 1082 : aReadLock.clear();
125 : // <- SAFE ------------------------------
126 : }
127 : else
128 : {
129 1850 : aReadLock.clear();
130 : // <- SAFE ------------------------------
131 :
132 : try
133 : {
134 1850 : xChild = StorageHolder::openSubStorageWithFallback(xParent, sChild, nOpenMode, true); // TODO think about delegating fallback decision to our own calli!
135 : }
136 0 : catch(const css::uno::RuntimeException&)
137 0 : { throw; }
138 44 : catch(const css::uno::Exception&)
139 : {
140 : /* TODO URGENT!
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!
144 :
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 ...
149 : */
150 22 : throw;
151 : }
152 :
153 1828 : osl::MutexGuard g(m_mutex);
154 1828 : pInfo = &(m_lStorages[sCheckPath]);
155 1828 : pInfo->Storage = xChild;
156 1828 : pInfo->UseCount = 1;
157 : }
158 :
159 2910 : xParent = xChild;
160 2910 : sRelPath += sChild;
161 2910 : sRelPath += PATH_SEPARATOR;
162 2932 : }
163 :
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.
166 :
167 2734 : return xChild;
168 : }
169 :
170 0 : StorageHolder::TStorageList StorageHolder::getAllPathStorages(const OUString& sPath)
171 : {
172 0 : OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
173 0 : OUStringList lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
174 :
175 0 : StorageHolder::TStorageList lStoragesOfPath;
176 0 : OUString sRelPath;
177 0 : OUStringList::const_iterator pIt;
178 :
179 0 : osl::MutexGuard g(m_mutex);
180 :
181 0 : for ( pIt = lFolders.begin();
182 0 : pIt != lFolders.end();
183 : ++pIt )
184 : {
185 0 : const OUString& sChild = *pIt;
186 0 : OUString sCheckPath (sRelPath);
187 0 : sCheckPath += sChild;
188 0 : sCheckPath += PATH_SEPARATOR;
189 :
190 0 : TPath2StorageInfo::iterator pCheck = m_lStorages.find(sCheckPath);
191 0 : if (pCheck == m_lStorages.end())
192 : {
193 : // at least one path element was not found
194 : // Seems that this path isn't open ...
195 0 : lStoragesOfPath.clear();
196 0 : return lStoragesOfPath;
197 : }
198 :
199 0 : TStorageInfo& rInfo = pCheck->second;
200 0 : lStoragesOfPath.push_back(rInfo.Storage);
201 :
202 0 : sRelPath += sChild;
203 0 : sRelPath += PATH_SEPARATOR;
204 0 : }
205 :
206 0 : return lStoragesOfPath;
207 : }
208 :
209 0 : void StorageHolder::commitPath(const OUString& sPath)
210 : {
211 0 : StorageHolder::TStorageList lStorages = getAllPathStorages(sPath);
212 :
213 0 : css::uno::Reference< css::embed::XTransactedObject > xCommit;
214 0 : StorageHolder::TStorageList::reverse_iterator pIt;
215 0 : for ( pIt = lStorages.rbegin(); // order of commit is important ... otherwise changes are not recognized!
216 0 : pIt != lStorages.rend();
217 : ++pIt )
218 : {
219 0 : xCommit = css::uno::Reference< css::embed::XTransactedObject >(*pIt, css::uno::UNO_QUERY);
220 0 : if (!xCommit.is())
221 0 : continue;
222 0 : xCommit->commit();
223 : }
224 :
225 : // SAFE -> ------------------------------
226 0 : osl::ClearableMutexGuard aReadLock(m_mutex);
227 0 : xCommit = css::uno::Reference< css::embed::XTransactedObject >(m_xRoot, css::uno::UNO_QUERY);
228 0 : aReadLock.clear();
229 : // <- SAFE ------------------------------
230 :
231 0 : if (xCommit.is())
232 0 : xCommit->commit();
233 0 : }
234 :
235 1902 : void StorageHolder::closePath(const OUString& rPath)
236 : {
237 1902 : OUString sNormedPath = StorageHolder::impl_st_normPath(rPath);
238 3804 : OUStringList lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
239 :
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"
244 : */
245 1902 : OUStringList::iterator pIt1;
246 3804 : OUString sParentPath;
247 15804 : for ( pIt1 = lFolders.begin();
248 10536 : pIt1 != lFolders.end();
249 : ++pIt1 )
250 : {
251 3366 : OUString sCurrentRelPath = sParentPath;
252 3366 : sCurrentRelPath += *pIt1;
253 3366 : sCurrentRelPath += PATH_SEPARATOR;
254 3366 : *pIt1 = sCurrentRelPath;
255 3366 : sParentPath = sCurrentRelPath;
256 3366 : }
257 :
258 3804 : osl::MutexGuard g(m_mutex);
259 :
260 1902 : OUStringList::reverse_iterator pIt2;
261 15804 : for ( pIt2 = lFolders.rbegin();
262 10536 : pIt2 != lFolders.rend();
263 : ++pIt2 )
264 : {
265 3366 : OUString sPath = *pIt2;
266 3366 : TPath2StorageInfo::iterator pPath = m_lStorages.find(sPath);
267 3366 : if (pPath == m_lStorages.end())
268 1192 : continue; // ???
269 :
270 2174 : TStorageInfo& rInfo = pPath->second;
271 2174 : --rInfo.UseCount;
272 2174 : if (rInfo.UseCount < 1)
273 : {
274 1158 : rInfo.Storage.clear();
275 1158 : m_lStorages.erase(pPath);
276 : }
277 4076 : }
278 1902 : }
279 :
280 0 : void StorageHolder::notifyPath(const OUString& sPath)
281 : {
282 0 : OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
283 :
284 0 : osl::MutexGuard g(m_mutex);
285 :
286 0 : TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
287 0 : if (pIt1 == m_lStorages.end())
288 0 : return;
289 :
290 0 : TStorageInfo& rInfo = pIt1->second;
291 0 : TStorageListenerList::iterator pIt2;
292 0 : for ( pIt2 = rInfo.Listener.begin();
293 0 : pIt2 != rInfo.Listener.end();
294 : ++pIt2 )
295 : {
296 0 : IStorageListener* pListener = *pIt2;
297 0 : if (pListener)
298 0 : pListener->changesOccurred(sNormedPath);
299 0 : }
300 : }
301 :
302 592 : void StorageHolder::addStorageListener( IStorageListener* pListener,
303 : const OUString& sPath )
304 : {
305 592 : OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
306 :
307 1184 : osl::MutexGuard g(m_mutex);
308 :
309 592 : TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
310 592 : if (pIt1 == m_lStorages.end())
311 592 : return;
312 :
313 592 : TStorageInfo& rInfo = pIt1->second;
314 592 : TStorageListenerList::iterator pIt2 = ::std::find(rInfo.Listener.begin(), rInfo.Listener.end(), pListener);
315 592 : if (pIt2 == rInfo.Listener.end())
316 1184 : rInfo.Listener.push_back(pListener);
317 : }
318 :
319 585 : void StorageHolder::removeStorageListener( IStorageListener* pListener,
320 : const OUString& sPath )
321 : {
322 585 : OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
323 :
324 1170 : osl::MutexGuard g(m_mutex);
325 :
326 585 : TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
327 585 : if (pIt1 == m_lStorages.end())
328 585 : return;
329 :
330 585 : TStorageInfo& rInfo = pIt1->second;
331 585 : TStorageListenerList::iterator pIt2 = ::std::find(rInfo.Listener.begin(), rInfo.Listener.end(), pListener);
332 585 : if (pIt2 != rInfo.Listener.end())
333 1170 : rInfo.Listener.erase(pIt2);
334 : }
335 :
336 262 : OUString StorageHolder::getPathOfStorage(const css::uno::Reference< css::embed::XStorage >& xStorage)
337 : {
338 262 : osl::MutexGuard g(m_mutex);
339 :
340 262 : TPath2StorageInfo::const_iterator pIt;
341 2421 : for ( pIt = m_lStorages.begin();
342 1614 : pIt != m_lStorages.end();
343 : ++pIt )
344 : {
345 800 : const TStorageInfo& rInfo = pIt->second;
346 800 : if (rInfo.Storage == xStorage)
347 255 : break;
348 : }
349 :
350 262 : if (pIt == m_lStorages.end())
351 7 : return OUString();
352 :
353 255 : return pIt->first;
354 : }
355 :
356 262 : css::uno::Reference< css::embed::XStorage > StorageHolder::getParentStorage(const css::uno::Reference< css::embed::XStorage >& xChild)
357 : {
358 262 : OUString sChildPath = getPathOfStorage(xChild);
359 262 : return getParentStorage(sChildPath);
360 : }
361 :
362 262 : css::uno::Reference< css::embed::XStorage > StorageHolder::getParentStorage(const OUString& sChildPath)
363 : {
364 : // normed path = "a/b/c/" ... we search for "a/b/"
365 262 : OUString sNormedPath = StorageHolder::impl_st_normPath(sChildPath);
366 524 : OUStringList lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
367 262 : sal_Int32 c = lFolders.size();
368 :
369 : // a) "" => - => no parent
370 : // b) "a/b/c/" => "a/b/" => return storage "a/b/"
371 : // c) "a/" => "" => return root !
372 :
373 : // a)
374 262 : if (c < 1)
375 7 : return css::uno::Reference< css::embed::XStorage >();
376 :
377 : // SAFE -> ----------------------------------
378 510 : osl::ClearableMutexGuard aReadLock(m_mutex);
379 :
380 : // b)
381 255 : if (c < 2)
382 0 : return m_xRoot;
383 :
384 : // c)
385 510 : OUString sParentPath;
386 255 : sal_Int32 i = 0;
387 765 : for (i=0; i<c-1; ++i)
388 : {
389 510 : sParentPath += lFolders[i];
390 510 : sParentPath += PATH_SEPARATOR;
391 : }
392 :
393 255 : TPath2StorageInfo::const_iterator pParent = m_lStorages.find(sParentPath);
394 255 : if (pParent != m_lStorages.end())
395 255 : return pParent->second.Storage;
396 :
397 0 : aReadLock.clear();
398 : // <- SAFE ----------------------------------
399 :
400 : // ?
401 : SAL_INFO("fwk", "StorageHolder::getParentStorage(): Unexpected situation. Cached storage item seems to be wrong.");
402 262 : return css::uno::Reference< css::embed::XStorage >();
403 : }
404 :
405 0 : void StorageHolder::operator=(const StorageHolder& rCopy)
406 : {
407 0 : osl::MutexGuard g(m_mutex);
408 0 : m_xRoot = rCopy.m_xRoot;
409 0 : m_lStorages = rCopy.m_lStorages;
410 0 : }
411 :
412 1850 : css::uno::Reference< css::embed::XStorage > StorageHolder::openSubStorageWithFallback(const css::uno::Reference< css::embed::XStorage >& xBaseStorage ,
413 : const OUString& sSubStorage ,
414 : sal_Int32 eOpenMode ,
415 : bool bAllowFallback)
416 : {
417 : // a) try it first with user specified open mode
418 : // ignore errors ... but save it for later use!
419 1850 : css::uno::Exception exResult;
420 : try
421 : {
422 1850 : css::uno::Reference< css::embed::XStorage > xSubStorage = xBaseStorage->openStorageElement(sSubStorage, eOpenMode);
423 1828 : if (xSubStorage.is())
424 1828 : return xSubStorage;
425 : }
426 0 : catch(const css::uno::RuntimeException&)
427 0 : { throw; }
428 44 : catch(const css::uno::Exception& ex)
429 22 : { exResult = ex; }
430 :
431 : // b) readonly already tried? => forward last error!
432 22 : if (
433 44 : (!bAllowFallback ) || // fallback allowed ?
434 22 : ((eOpenMode & css::embed::ElementModes::WRITE) != css::embed::ElementModes::WRITE) // fallback possible ?
435 : )
436 16 : throw exResult;
437 :
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 6 : sal_Int32 eNewMode = (eOpenMode & ~css::embed::ElementModes::WRITE);
442 6 : css::uno::Reference< css::embed::XStorage > xSubStorage = xBaseStorage->openStorageElement(sSubStorage, eNewMode);
443 0 : if (xSubStorage.is())
444 0 : return xSubStorage;
445 :
446 : // d) no chance!
447 : SAL_INFO("fwk", "openSubStorageWithFallback(): Unexpected situation! Got no exception for missing storage ...");
448 1850 : return css::uno::Reference< css::embed::XStorage >();
449 : }
450 :
451 0 : css::uno::Reference< css::io::XStream > StorageHolder::openSubStreamWithFallback(const css::uno::Reference< css::embed::XStorage >& xBaseStorage ,
452 : const OUString& sSubStream ,
453 : sal_Int32 eOpenMode ,
454 : bool bAllowFallback)
455 : {
456 : // a) try it first with user specified open mode
457 : // ignore errors ... but save it for later use!
458 0 : css::uno::Exception exResult;
459 : try
460 : {
461 0 : css::uno::Reference< css::io::XStream > xSubStream = xBaseStorage->openStreamElement(sSubStream, eOpenMode);
462 0 : if (xSubStream.is())
463 0 : return xSubStream;
464 : }
465 0 : catch(const css::uno::RuntimeException&)
466 0 : { throw; }
467 0 : catch(const css::uno::Exception& ex)
468 0 : { exResult = ex; }
469 :
470 : // b) readonly already tried? => forward last error!
471 0 : if (
472 0 : (!bAllowFallback ) || // fallback allowed ?
473 0 : ((eOpenMode & css::embed::ElementModes::WRITE) != css::embed::ElementModes::WRITE) // fallback possible ?
474 : )
475 0 : throw exResult;
476 :
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 0 : sal_Int32 eNewMode = (eOpenMode & ~css::embed::ElementModes::WRITE);
481 0 : css::uno::Reference< css::io::XStream > xSubStream = xBaseStorage->openStreamElement(sSubStream, eNewMode);
482 0 : if (xSubStream.is())
483 0 : return xSubStream;
484 :
485 : // d) no chance!
486 : SAL_INFO("fwk", "openSubStreamWithFallbacks(): Unexpected situation! Got no exception for missing stream ...");
487 0 : return css::uno::Reference< css::io::XStream >();
488 : }
489 :
490 4719 : OUString StorageHolder::impl_st_normPath(const OUString& sPath)
491 : {
492 : // path must start without "/" but end with "/"!
493 :
494 4719 : OUString sNormedPath = sPath;
495 :
496 : // "/bla" => "bla" && "/" => "" (!)
497 4719 : if (sNormedPath.startsWith(PATH_SEPARATOR))
498 0 : sNormedPath += sNormedPath.copy(1);
499 :
500 : // "/" => "" || "" => "" ?
501 4719 : if (sNormedPath.isEmpty())
502 7 : return OUString();
503 :
504 : // "bla" => "bla/"
505 4712 : if (sNormedPath.lastIndexOf(PATH_SEPARATOR) != (sNormedPath.getLength()-1))
506 4457 : sNormedPath += PATH_SEPARATOR;
507 :
508 4712 : return sNormedPath;
509 : }
510 :
511 3542 : OUStringList StorageHolder::impl_st_parsePath(const OUString& sPath)
512 : {
513 3542 : OUStringList lToken;
514 3542 : sal_Int32 i = 0;
515 : while (true)
516 : {
517 10623 : OUString sToken = sPath.getToken(0, PATH_SEPARATOR_UNICODE, i);
518 10623 : if (i < 0)
519 3542 : break;
520 7081 : lToken.push_back(sToken);
521 7081 : }
522 3542 : return lToken;
523 : }
524 :
525 : } // namespace framework
526 :
527 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|