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 <threadhelp/readguard.hxx>
23 : #include <threadhelp/writeguard.hxx>
24 : #include <services.h>
25 :
26 : #include <com/sun/star/container/NoSuchElementException.hpp>
27 :
28 : #include <com/sun/star/container/XNameAccess.hpp>
29 :
30 : #include <com/sun/star/beans/XPropertySet.hpp>
31 :
32 : #include <com/sun/star/embed/ElementModes.hpp>
33 :
34 : #include <com/sun/star/embed/XTransactedObject.hpp>
35 :
36 : #include <com/sun/star/embed/XPackageStructureCreator.hpp>
37 :
38 : #include <com/sun/star/lang/XSingleServiceFactory.hpp>
39 :
40 : #include <com/sun/star/io/XSeekable.hpp>
41 :
42 :
43 : #define PATH_SEPARATOR_ASCII "/"
44 : #define PATH_SEPARATOR_UNICODE ((sal_Unicode)'/')
45 : #define PATH_SEPARATOR OUString(PATH_SEPARATOR_ASCII)
46 :
47 :
48 : namespace framework
49 : {
50 :
51 : //-----------------------------------------------
52 963 : StorageHolder::StorageHolder()
53 963 : : ThreadHelpBase( )
54 : {
55 963 : }
56 :
57 : //-----------------------------------------------
58 948 : StorageHolder::~StorageHolder()
59 : {
60 : // TODO implement me
61 : // dispose/clear etcpp.
62 948 : }
63 :
64 : //-----------------------------------------------
65 844 : void StorageHolder::forgetCachedStorages()
66 : {
67 : // SAFE -> ----------------------------------
68 844 : WriteGuard aWriteLock(m_aLock);
69 :
70 844 : TPath2StorageInfo::iterator pIt;
71 4398 : for ( pIt = m_lStorages.begin();
72 2932 : pIt != m_lStorages.end() ;
73 : ++pIt )
74 : {
75 622 : TStorageInfo& rInfo = pIt->second;
76 : // TODO think about listener !
77 622 : rInfo.Storage.clear();
78 : }
79 844 : m_lStorages.clear();
80 :
81 844 : aWriteLock.unlock();
82 : // <- SAFE ----------------------------------
83 844 : }
84 :
85 : //-----------------------------------------------
86 740 : void StorageHolder::setRootStorage(const css::uno::Reference< css::embed::XStorage >& xRoot)
87 : {
88 : // SAFE -> ----------------------------------
89 740 : WriteGuard aWriteLock(m_aLock);
90 740 : m_xRoot = xRoot;
91 740 : aWriteLock.unlock();
92 : // <- SAFE ----------------------------------
93 740 : }
94 :
95 : //-----------------------------------------------
96 539 : css::uno::Reference< css::embed::XStorage > StorageHolder::getRootStorage() const
97 : {
98 : // SAFE -> ----------------------------------
99 539 : ReadGuard aReadLock(m_aLock);
100 539 : return m_xRoot;
101 : // <- SAFE ----------------------------------
102 : }
103 :
104 : //-----------------------------------------------
105 1084 : css::uno::Reference< css::embed::XStorage > StorageHolder::openPath(const OUString& sPath ,
106 : sal_Int32 nOpenMode)
107 : {
108 1084 : OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
109 2168 : OUStringList lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
110 :
111 : // SAFE -> ----------------------------------
112 2168 : ReadGuard aReadLock(m_aLock);
113 2168 : css::uno::Reference< css::embed::XStorage > xParent = m_xRoot;
114 1084 : aReadLock.unlock();
115 : // <- SAFE ----------------------------------
116 :
117 1084 : css::uno::Reference< css::embed::XStorage > xChild ;
118 2168 : OUString sRelPath;
119 1084 : OUStringList::const_iterator pIt ;
120 :
121 9222 : for ( pIt = lFolders.begin();
122 6148 : pIt != lFolders.end() ;
123 : ++pIt )
124 : {
125 2000 : const OUString& sChild = *pIt;
126 2000 : OUString sCheckPath (sRelPath);
127 2000 : sCheckPath += sChild;
128 2000 : sCheckPath += PATH_SEPARATOR;
129 :
130 : // SAFE -> ------------------------------
131 2000 : aReadLock.lock();
132 :
133 : // If we found an already open storage ... we must increase
134 : // its use count. Otherwhise it will may be closed to early :-)
135 2000 : TPath2StorageInfo::iterator pCheck = m_lStorages.find(sCheckPath);
136 2000 : TStorageInfo* pInfo = 0;
137 2000 : if (pCheck != m_lStorages.end())
138 : {
139 646 : pInfo = &(pCheck->second);
140 646 : ++(pInfo->UseCount);
141 646 : xChild = pInfo->Storage;
142 : }
143 : else
144 : {
145 1354 : aReadLock.unlock();
146 : // <- SAFE ------------------------------
147 :
148 : try
149 : {
150 1354 : xChild = StorageHolder::openSubStorageWithFallback(xParent, sChild, nOpenMode, sal_True); // TODO think about delegating fallback decision to our own calli!
151 : }
152 0 : catch(const css::uno::RuntimeException&)
153 0 : { throw; }
154 20 : catch(const css::uno::Exception&)
155 : {
156 : /* TODO URGENT!
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!
160 :
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 ...
165 : */
166 10 : throw;
167 : }
168 :
169 : // SAFE -> ------------------------------
170 1344 : WriteGuard aWriteLock(m_aLock);
171 1344 : pInfo = &(m_lStorages[sCheckPath]);
172 1344 : pInfo->Storage = xChild;
173 1344 : pInfo->UseCount = 1;
174 1344 : aWriteLock.unlock();
175 : // <- SAFE ------------------------------
176 : }
177 :
178 1990 : xParent = xChild;
179 1990 : sRelPath += sChild;
180 1990 : sRelPath += PATH_SEPARATOR;
181 2000 : }
182 :
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.
185 :
186 2158 : return xChild;
187 : }
188 :
189 : //-----------------------------------------------
190 0 : StorageHolder::TStorageList StorageHolder::getAllPathStorages(const OUString& sPath)
191 : {
192 0 : OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
193 0 : OUStringList lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
194 :
195 0 : StorageHolder::TStorageList lStoragesOfPath;
196 0 : OUString sRelPath ;
197 0 : OUStringList::const_iterator pIt ;
198 :
199 : // SAFE -> ----------------------------------
200 0 : ReadGuard aReadLock(m_aLock);
201 :
202 0 : for ( pIt = lFolders.begin();
203 0 : pIt != lFolders.end() ;
204 : ++pIt )
205 : {
206 0 : const OUString& sChild = *pIt;
207 0 : OUString sCheckPath (sRelPath);
208 0 : sCheckPath += sChild;
209 0 : sCheckPath += PATH_SEPARATOR;
210 :
211 0 : TPath2StorageInfo::iterator pCheck = m_lStorages.find(sCheckPath);
212 0 : if (pCheck == m_lStorages.end())
213 : {
214 : // at least one path element was not found
215 : // Seems that this path isnt open ...
216 0 : lStoragesOfPath.clear();
217 0 : return lStoragesOfPath;
218 : }
219 :
220 0 : TStorageInfo& rInfo = pCheck->second;
221 0 : lStoragesOfPath.push_back(rInfo.Storage);
222 :
223 0 : sRelPath += sChild;
224 0 : sRelPath += PATH_SEPARATOR;
225 0 : }
226 :
227 0 : aReadLock.unlock();
228 : // <- SAFE ----------------------------------
229 :
230 0 : return lStoragesOfPath;
231 : }
232 :
233 : //-----------------------------------------------
234 0 : void StorageHolder::commitPath(const OUString& sPath)
235 : {
236 0 : StorageHolder::TStorageList lStorages = getAllPathStorages(sPath);
237 :
238 0 : css::uno::Reference< css::embed::XTransactedObject > xCommit;
239 0 : StorageHolder::TStorageList::reverse_iterator pIt;
240 0 : for ( pIt = lStorages.rbegin(); // order of commit is important ... otherwise changes are not recognized!
241 0 : pIt != lStorages.rend() ;
242 : ++pIt )
243 : {
244 0 : xCommit = css::uno::Reference< css::embed::XTransactedObject >(*pIt, css::uno::UNO_QUERY);
245 0 : if (!xCommit.is())
246 0 : continue;
247 0 : xCommit->commit();
248 : }
249 :
250 : // SAFE -> ------------------------------
251 0 : ReadGuard aReadLock(m_aLock);
252 0 : xCommit = css::uno::Reference< css::embed::XTransactedObject >(m_xRoot, css::uno::UNO_QUERY);
253 0 : aReadLock.unlock();
254 : // <- SAFE ------------------------------
255 :
256 0 : if (xCommit.is())
257 0 : xCommit->commit();
258 0 : }
259 :
260 : //-----------------------------------------------
261 1672 : void StorageHolder::closePath(const OUString& rPath)
262 : {
263 1672 : OUString sNormedPath = StorageHolder::impl_st_normPath(rPath);
264 3344 : OUStringList lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
265 :
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"
270 : */
271 1672 : OUStringList::iterator pIt1 ;
272 3344 : OUString sParentPath;
273 12696 : for ( pIt1 = lFolders.begin();
274 8464 : pIt1 != lFolders.end() ;
275 : ++pIt1 )
276 : {
277 2560 : OUString sCurrentRelPath = sParentPath;
278 2560 : sCurrentRelPath += *pIt1;
279 2560 : sCurrentRelPath += PATH_SEPARATOR;
280 2560 : *pIt1 = sCurrentRelPath;
281 2560 : sParentPath = sCurrentRelPath;
282 2560 : }
283 :
284 : // SAFE -> ------------------------------
285 3344 : ReadGuard aReadLock(m_aLock);
286 :
287 1672 : OUStringList::reverse_iterator pIt2;
288 12696 : for ( pIt2 = lFolders.rbegin();
289 8464 : pIt2 != lFolders.rend() ;
290 : ++pIt2 )
291 : {
292 2560 : OUString sPath = *pIt2;
293 2560 : TPath2StorageInfo::iterator pPath = m_lStorages.find(sPath);
294 2560 : if (pPath == m_lStorages.end())
295 1237 : continue; // ???
296 :
297 1323 : TStorageInfo& rInfo = pPath->second;
298 1323 : --rInfo.UseCount;
299 1323 : if (rInfo.UseCount < 1)
300 : {
301 697 : rInfo.Storage.clear();
302 697 : m_lStorages.erase(pPath);
303 : }
304 1323 : }
305 :
306 3344 : aReadLock.unlock();
307 : // <- SAFE ------------------------------
308 1672 : }
309 :
310 : //-----------------------------------------------
311 0 : void StorageHolder::notifyPath(const OUString& sPath)
312 : {
313 0 : OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
314 :
315 : // SAFE -> ------------------------------
316 0 : ReadGuard aReadLock(m_aLock);
317 :
318 0 : TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
319 0 : if (pIt1 == m_lStorages.end())
320 0 : return;
321 :
322 0 : TStorageInfo& rInfo = pIt1->second;
323 0 : TStorageListenerList::iterator pIt2;
324 0 : for ( pIt2 = rInfo.Listener.begin();
325 0 : pIt2 != rInfo.Listener.end() ;
326 : ++pIt2 )
327 : {
328 0 : IStorageListener* pListener = *pIt2;
329 0 : if (pListener)
330 0 : pListener->changesOccurred(sNormedPath);
331 : }
332 :
333 0 : aReadLock.unlock();
334 : // <- SAFE ------------------------------
335 : }
336 :
337 : //-----------------------------------------------
338 622 : void StorageHolder::addStorageListener( IStorageListener* pListener,
339 : const OUString& sPath )
340 : {
341 622 : OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
342 :
343 : // SAFE -> ------------------------------
344 1244 : ReadGuard aReadLock(m_aLock);
345 :
346 622 : TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
347 622 : if (pIt1 == m_lStorages.end())
348 622 : return;
349 :
350 622 : TStorageInfo& rInfo = pIt1->second;
351 622 : TStorageListenerList::iterator pIt2 = ::std::find(rInfo.Listener.begin(), rInfo.Listener.end(), pListener);
352 622 : if (pIt2 == rInfo.Listener.end())
353 622 : rInfo.Listener.push_back(pListener);
354 :
355 1244 : aReadLock.unlock();
356 : // <- SAFE ------------------------------
357 : }
358 :
359 : //-----------------------------------------------
360 614 : void StorageHolder::removeStorageListener( IStorageListener* pListener,
361 : const OUString& sPath )
362 : {
363 614 : OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
364 :
365 : // SAFE -> ------------------------------
366 1228 : ReadGuard aReadLock(m_aLock);
367 :
368 614 : TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
369 614 : if (pIt1 == m_lStorages.end())
370 614 : return;
371 :
372 614 : TStorageInfo& rInfo = pIt1->second;
373 614 : TStorageListenerList::iterator pIt2 = ::std::find(rInfo.Listener.begin(), rInfo.Listener.end(), pListener);
374 614 : if (pIt2 != rInfo.Listener.end())
375 614 : rInfo.Listener.erase(pIt2);
376 :
377 1228 : aReadLock.unlock();
378 : // <- SAFE ------------------------------
379 : }
380 :
381 : //-----------------------------------------------
382 154 : OUString StorageHolder::getPathOfStorage(const css::uno::Reference< css::embed::XStorage >& xStorage)
383 : {
384 : // SAFE -> ------------------------------
385 154 : ReadGuard aReadLock(m_aLock);
386 :
387 154 : TPath2StorageInfo::const_iterator pIt;
388 1584 : for ( pIt = m_lStorages.begin();
389 1056 : pIt != m_lStorages.end() ;
390 : ++pIt )
391 : {
392 525 : const TStorageInfo& rInfo = pIt->second;
393 525 : if (rInfo.Storage == xStorage)
394 151 : break;
395 : }
396 :
397 154 : if (pIt == m_lStorages.end())
398 3 : return OUString();
399 :
400 151 : return pIt->first;
401 :
402 : // <- SAFE ------------------------------
403 : }
404 :
405 : //-----------------------------------------------
406 154 : css::uno::Reference< css::embed::XStorage > StorageHolder::getParentStorage(const css::uno::Reference< css::embed::XStorage >& xChild)
407 : {
408 154 : OUString sChildPath = getPathOfStorage(xChild);
409 154 : return getParentStorage(sChildPath);
410 : }
411 :
412 : //-----------------------------------------------
413 154 : css::uno::Reference< css::embed::XStorage > StorageHolder::getParentStorage(const OUString& sChildPath)
414 : {
415 : // normed path = "a/b/c/" ... we search for "a/b/"
416 154 : OUString sNormedPath = StorageHolder::impl_st_normPath(sChildPath);
417 308 : OUStringList lFolders = StorageHolder::impl_st_parsePath(sNormedPath);
418 154 : sal_Int32 c = lFolders.size();
419 :
420 : // a) "" => - => no parent
421 : // b) "a/b/c/" => "a/b/" => return storage "a/b/"
422 : // c) "a/" => "" => return root !
423 :
424 : // a)
425 154 : if (c < 1)
426 3 : return css::uno::Reference< css::embed::XStorage >();
427 :
428 : // SAFE -> ----------------------------------
429 302 : ReadGuard aReadLock(m_aLock);
430 :
431 : // b)
432 151 : if (c < 2)
433 0 : return m_xRoot;
434 :
435 : // c)
436 302 : OUString sParentPath;
437 151 : sal_Int32 i = 0;
438 453 : for (i=0; i<c-1; ++i)
439 : {
440 302 : sParentPath += lFolders[i];
441 302 : sParentPath += PATH_SEPARATOR;
442 : }
443 :
444 151 : TPath2StorageInfo::const_iterator pParent = m_lStorages.find(sParentPath);
445 151 : if (pParent != m_lStorages.end())
446 151 : return pParent->second.Storage;
447 :
448 0 : aReadLock.unlock();
449 : // <- SAFE ----------------------------------
450 :
451 : // ?
452 : LOG_WARNING("StorageHolder::getParentStorage()", "Unexpected situation. Cached storage item seems to be wrong.")
453 154 : return css::uno::Reference< css::embed::XStorage >();
454 : }
455 :
456 : //-----------------------------------------------
457 0 : void StorageHolder::operator=(const StorageHolder& rCopy)
458 : {
459 : // SAFE -> ----------------------------------
460 0 : WriteGuard aWriteLock(m_aLock);
461 :
462 0 : m_xRoot = rCopy.m_xRoot;
463 0 : m_lStorages = rCopy.m_lStorages;
464 :
465 0 : aWriteLock.unlock();
466 : // <- SAFE ----------------------------------
467 0 : }
468 :
469 : //-----------------------------------------------
470 1354 : 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)
474 : {
475 : // a) try it first with user specified open mode
476 : // ignore errors ... but save it for later use!
477 1354 : css::uno::Exception exResult;
478 : try
479 : {
480 1354 : css::uno::Reference< css::embed::XStorage > xSubStorage = xBaseStorage->openStorageElement(sSubStorage, eOpenMode);
481 1344 : if (xSubStorage.is())
482 1344 : return xSubStorage;
483 : }
484 0 : catch(const css::uno::RuntimeException&)
485 0 : { throw; }
486 20 : catch(const css::uno::Exception& ex)
487 10 : { exResult = ex; }
488 :
489 : // b) readonly already tried? => forward last error!
490 10 : if (
491 10 : (!bAllowFallback ) || // fallback allowed ?
492 10 : ((eOpenMode & css::embed::ElementModes::WRITE) != css::embed::ElementModes::WRITE) // fallback possible ?
493 : )
494 7 : throw exResult;
495 :
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 3 : sal_Int32 eNewMode = (eOpenMode & ~css::embed::ElementModes::WRITE);
500 3 : css::uno::Reference< css::embed::XStorage > xSubStorage = xBaseStorage->openStorageElement(sSubStorage, eNewMode);
501 0 : if (xSubStorage.is())
502 0 : return xSubStorage;
503 :
504 : // d) no chance!
505 : LOG_WARNING("openSubStorageWithFallback()", "Unexpected situation! Got no exception for missing storage ...")
506 1354 : return css::uno::Reference< css::embed::XStorage >();
507 : }
508 :
509 : //-----------------------------------------------
510 0 : 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)
514 : {
515 : // a) try it first with user specified open mode
516 : // ignore errors ... but save it for later use!
517 0 : css::uno::Exception exResult;
518 : try
519 : {
520 0 : css::uno::Reference< css::io::XStream > xSubStream = xBaseStorage->openStreamElement(sSubStream, eOpenMode);
521 0 : if (xSubStream.is())
522 0 : return xSubStream;
523 : }
524 0 : catch(const css::uno::RuntimeException&)
525 0 : { throw; }
526 0 : catch(const css::uno::Exception& ex)
527 0 : { exResult = ex; }
528 :
529 : // b) readonly already tried? => forward last error!
530 0 : if (
531 0 : (!bAllowFallback ) || // fallback allowed ?
532 0 : ((eOpenMode & css::embed::ElementModes::WRITE) != css::embed::ElementModes::WRITE) // fallback possible ?
533 : )
534 0 : throw exResult;
535 :
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 0 : sal_Int32 eNewMode = (eOpenMode & ~css::embed::ElementModes::WRITE);
540 0 : css::uno::Reference< css::io::XStream > xSubStream = xBaseStorage->openStreamElement(sSubStream, eNewMode);
541 0 : if (xSubStream.is())
542 0 : return xSubStream;
543 :
544 : // d) no chance!
545 : LOG_WARNING("openSubStreamWithFallbacks()", "Unexpected situation! Got no exception for missing stream ...")
546 0 : return css::uno::Reference< css::io::XStream >();
547 : }
548 :
549 : //-----------------------------------------------
550 4146 : OUString StorageHolder::impl_st_normPath(const OUString& sPath)
551 : {
552 : // path must start without "/" but end with "/"!
553 :
554 4146 : OUString sNormedPath = sPath;
555 :
556 : // "/bla" => "bla" && "/" => "" (!)
557 4146 : if (sNormedPath.indexOf(PATH_SEPARATOR) == 0)
558 0 : sNormedPath += sNormedPath.copy(1);
559 :
560 : // "/" => "" || "" => "" ?
561 4146 : if (sNormedPath.isEmpty())
562 3 : return OUString();
563 :
564 : // "bla" => "bla/"
565 4143 : if (sNormedPath.lastIndexOf(PATH_SEPARATOR) != (sNormedPath.getLength()-1))
566 3992 : sNormedPath += PATH_SEPARATOR;
567 :
568 4143 : return sNormedPath;
569 : }
570 :
571 : //-----------------------------------------------
572 2910 : OUStringList StorageHolder::impl_st_parsePath(const OUString& sPath)
573 : {
574 2910 : OUStringList lToken;
575 2910 : sal_Int32 i = 0;
576 : while (true)
577 : {
578 7931 : OUString sToken = sPath.getToken(0, PATH_SEPARATOR_UNICODE, i);
579 7931 : if (i < 0)
580 2910 : break;
581 5021 : lToken.push_back(sToken);
582 5021 : }
583 2910 : return lToken;
584 : }
585 :
586 : //===============================================
587 : } // namespace framework
588 :
589 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|