1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_framework.hxx"
30 #include <accelerators/storageholder.hxx>
31 
32 //===============================================
33 // own includes
34 #include <threadhelp/readguard.hxx>
35 #include <threadhelp/writeguard.hxx>
36 #include <services.h>
37 
38 //===============================================
39 // interface includes
40 
41 #ifndef __COM_SUN_STAR_CONTAINER_NOSUCHELEMENTEXCEPTION_HPP_
42 #include <com/sun/star/container/NoSuchElementException.hpp>
43 #endif
44 
45 #ifndef __COM_SUN_STAR_CONTAINER_XNAMEACCESS_HPP_
46 #include <com/sun/star/container/XNameAccess.hpp>
47 #endif
48 
49 #ifndef __COM_SUN_STAR_BEANS_XPROPERTYSET_HPP_
50 #include <com/sun/star/beans/XPropertySet.hpp>
51 #endif
52 
53 #ifndef __COM_SUN_STAR_EMBED_ELEMENTMODES_HPP_
54 #include <com/sun/star/embed/ElementModes.hpp>
55 #endif
56 
57 #ifndef __COM_SUN_STAR_EMBED_XTRANSACTEDOBJECT_HPP_
58 #include <com/sun/star/embed/XTransactedObject.hpp>
59 #endif
60 
61 #ifndef __COM_SUN_STAR_EMBED_XPACKAGESTRUCTURECREATOR_HPP_
62 #include <com/sun/star/embed/XPackageStructureCreator.hpp>
63 #endif
64 
65 #ifndef __COM_SUN_STAR_LANG_XSINGLESERVICEFACTORY_HPP_
66 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
67 #endif
68 
69 #ifndef __COM_SUN_STAR_IO_XSEEKABLE_HPP_
70 #include <com/sun/star/io/XSeekable.hpp>
71 #endif
72 
73 //===============================================
74 // other includes
75 #include <comphelper/processfactory.hxx>
76 
77 //===============================================
78 // const
79 
80 #define PATH_SEPERATOR_ASCII        "/"
81 #define PATH_SEPERATOR_UNICODE      ((sal_Unicode)'/')
82 #define PATH_SEPERATOR              ::rtl::OUString::createFromAscii(PATH_SEPERATOR_ASCII)
83 
84 //===============================================
85 // namespace
86 
87 namespace framework
88 {
89 
90 namespace css = ::com::sun::star;
91 
92 //-----------------------------------------------
93 StorageHolder::StorageHolder()
94     : ThreadHelpBase(                                        )
95     , m_xSMGR       (::comphelper::getProcessServiceFactory())
96 {
97 }
98 
99 //-----------------------------------------------
100 StorageHolder::StorageHolder(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR)
101     : ThreadHelpBase(     )
102     , m_xSMGR       (xSMGR)
103 {
104 }
105 
106 //-----------------------------------------------
107 StorageHolder::~StorageHolder()
108 {
109     // TODO implement me
110     // dispose/clear etcpp.
111 }
112 
113 //-----------------------------------------------
114 void StorageHolder::forgetCachedStorages()
115 {
116     // SAFE -> ----------------------------------
117     WriteGuard aWriteLock(m_aLock);
118 
119     TPath2StorageInfo::iterator pIt;
120     for (  pIt  = m_lStorages.begin();
121            pIt != m_lStorages.end()  ;
122          ++pIt                       )
123     {
124         TStorageInfo& rInfo = pIt->second;
125         // TODO think about listener !
126         rInfo.Storage.clear();
127     }
128     m_lStorages.clear();
129 
130     aWriteLock.unlock();
131     // <- SAFE ----------------------------------
132 }
133 
134 //-----------------------------------------------
135 void StorageHolder::setRootStorage(const css::uno::Reference< css::embed::XStorage >& xRoot)
136 {
137     // SAFE -> ----------------------------------
138     WriteGuard aWriteLock(m_aLock);
139     m_xRoot = xRoot;
140     aWriteLock.unlock();
141     // <- SAFE ----------------------------------
142 }
143 
144 //-----------------------------------------------
145 css::uno::Reference< css::embed::XStorage > StorageHolder::getRootStorage() const
146 {
147     // SAFE -> ----------------------------------
148     ReadGuard aReadLock(m_aLock);
149     return m_xRoot;
150     // <- SAFE ----------------------------------
151 }
152 
153 //-----------------------------------------------
154 css::uno::Reference< css::embed::XStorage > StorageHolder::openPath(const ::rtl::OUString& sPath    ,
155                                                                           sal_Int32        nOpenMode)
156 {
157     ::rtl::OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
158     OUStringList    lFolders    = StorageHolder::impl_st_parsePath(sNormedPath);
159 
160     // SAFE -> ----------------------------------
161     ReadGuard aReadLock(m_aLock);
162     css::uno::Reference< css::embed::XStorage > xParent = m_xRoot;
163     aReadLock.unlock();
164     // <- SAFE ----------------------------------
165 
166     css::uno::Reference< css::embed::XStorage > xChild  ;
167     ::rtl::OUString                             sRelPath;
168     OUStringList::const_iterator                pIt     ;
169 
170     for (  pIt  = lFolders.begin();
171            pIt != lFolders.end()  ;
172          ++pIt                    )
173     {
174         const ::rtl::OUString& sChild     = *pIt;
175               ::rtl::OUString  sCheckPath (sRelPath);
176                                sCheckPath += sChild;
177                                sCheckPath += PATH_SEPERATOR;
178 
179         // SAFE -> ------------------------------
180         aReadLock.lock();
181 
182         // If we found an already open storage ... we must increase
183         // its use count. Otherwhise it will may be closed to early :-)
184         TPath2StorageInfo::iterator pCheck = m_lStorages.find(sCheckPath);
185         TStorageInfo*               pInfo  = 0;
186         if (pCheck != m_lStorages.end())
187         {
188             pInfo = &(pCheck->second);
189             ++(pInfo->UseCount);
190             xChild = pInfo->Storage;
191         }
192         else
193         {
194             aReadLock.unlock();
195             // <- SAFE ------------------------------
196 
197             try
198             {
199                 xChild = StorageHolder::openSubStorageWithFallback(xParent, sChild, nOpenMode, sal_True); // TODO think about delegating fallback decision to our own calli!
200             }
201             catch(const css::uno::RuntimeException& exRun)
202                 { throw exRun; }
203             catch(const css::uno::Exception& exAny)
204                 {
205                     /* TODO URGENT!
206                         in case we found some "already existing storages" on the path before and increased its UseCount ...
207                         and now we will get an exception on creating a new sub storage ...
208                         we must decrease all UseCounts, which was touched before. Otherwise these storages cant be closed!
209 
210                         Idea: Using of another structure member "PossibleUseCount" as vector of unique numbers.
211                         Every thread use another unique number to identify all "owned candidates".
212                         A flush method with the same unique number force increasing of the "UseCount" variable then
213                         inside a synchronized block ...
214                     */
215                     throw exAny;
216                 }
217 
218             // SAFE -> ------------------------------
219             WriteGuard aWriteLock(m_aLock);
220             pInfo = &(m_lStorages[sCheckPath]);
221             pInfo->Storage  = xChild;
222             pInfo->UseCount = 1;
223             aWriteLock.unlock();
224             // <- SAFE ------------------------------
225         }
226 
227         xParent   = xChild;
228         sRelPath += sChild;
229         sRelPath += PATH_SEPERATOR;
230     }
231 
232     // TODO think about return last storage as working storage ... but dont caching it inside this holder!
233     // => otherwhise the same storage is may be commit more then once.
234 
235     return xChild;
236 }
237 
238 //-----------------------------------------------
239 StorageHolder::TStorageList StorageHolder::getAllPathStorages(const ::rtl::OUString& sPath)
240 {
241     ::rtl::OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
242     OUStringList    lFolders    = StorageHolder::impl_st_parsePath(sNormedPath);
243 
244     StorageHolder::TStorageList  lStoragesOfPath;
245     ::rtl::OUString              sRelPath       ;
246     OUStringList::const_iterator pIt            ;
247 
248     // SAFE -> ----------------------------------
249     ReadGuard aReadLock(m_aLock);
250 
251     for (  pIt  = lFolders.begin();
252            pIt != lFolders.end()  ;
253          ++pIt                    )
254     {
255         const ::rtl::OUString& sChild     = *pIt;
256               ::rtl::OUString  sCheckPath (sRelPath);
257                                sCheckPath += sChild;
258                                sCheckPath += PATH_SEPERATOR;
259 
260         TPath2StorageInfo::iterator pCheck = m_lStorages.find(sCheckPath);
261         if (pCheck == m_lStorages.end())
262         {
263             // at least one path element was not found
264             // Seems that this path isnt open ...
265             lStoragesOfPath.clear();
266             return lStoragesOfPath;
267         }
268 
269         TStorageInfo& rInfo = pCheck->second;
270         lStoragesOfPath.push_back(rInfo.Storage);
271 
272         sRelPath += sChild;
273         sRelPath += PATH_SEPERATOR;
274     }
275 
276     aReadLock.unlock();
277     // <- SAFE ----------------------------------
278 
279     return lStoragesOfPath;
280 }
281 
282 //-----------------------------------------------
283 void StorageHolder::commitPath(const ::rtl::OUString& sPath)
284 {
285     StorageHolder::TStorageList lStorages = getAllPathStorages(sPath);
286 
287     css::uno::Reference< css::embed::XTransactedObject > xCommit;
288     StorageHolder::TStorageList::reverse_iterator pIt;
289     for (  pIt  = lStorages.rbegin(); // order of commit is important ... otherwhise changes are not recognized!
290            pIt != lStorages.rend()  ;
291          ++pIt                      )
292     {
293         xCommit = css::uno::Reference< css::embed::XTransactedObject >(*pIt, css::uno::UNO_QUERY);
294         if (!xCommit.is())
295             continue;
296         xCommit->commit();
297     }
298 
299     // SAFE -> ------------------------------
300     ReadGuard aReadLock(m_aLock);
301     xCommit = css::uno::Reference< css::embed::XTransactedObject >(m_xRoot, css::uno::UNO_QUERY);
302     aReadLock.unlock();
303     // <- SAFE ------------------------------
304 
305     if (xCommit.is())
306         xCommit->commit();
307 }
308 
309 //-----------------------------------------------
310 void StorageHolder::closePath(const ::rtl::OUString& rPath)
311 {
312     ::rtl::OUString sNormedPath = StorageHolder::impl_st_normPath(rPath);
313     OUStringList    lFolders    = StorageHolder::impl_st_parsePath(sNormedPath);
314 
315     /* convert list of pathes in the following way:
316         [0] = "path_1" => "path_1
317         [1] = "path_2" => "path_1/path_2"
318         [2] = "path_3" => "path_1/path_2/path_3"
319     */
320     OUStringList::iterator pIt1       ;
321     ::rtl::OUString        sParentPath;
322     for (  pIt1  = lFolders.begin();
323            pIt1 != lFolders.end()  ;
324          ++pIt1                    )
325     {
326         ::rtl::OUString sCurrentRelPath  = sParentPath;
327                         sCurrentRelPath += *pIt1;
328                         sCurrentRelPath += PATH_SEPERATOR;
329         *pIt1       = sCurrentRelPath;
330         sParentPath = sCurrentRelPath;
331     }
332 
333     // SAFE -> ------------------------------
334     ReadGuard aReadLock(m_aLock);
335 
336     OUStringList::reverse_iterator pIt2;
337     for (  pIt2  = lFolders.rbegin();
338            pIt2 != lFolders.rend()  ;
339          ++pIt2                     )
340     {
341         ::rtl::OUString             sPath = *pIt2;
342         TPath2StorageInfo::iterator pPath = m_lStorages.find(sPath);
343         if (pPath == m_lStorages.end())
344             continue; // ???
345 
346         TStorageInfo& rInfo = pPath->second;
347         --rInfo.UseCount;
348         if (rInfo.UseCount < 1)
349         {
350             rInfo.Storage.clear();
351             m_lStorages.erase(pPath);
352         }
353     }
354 
355     aReadLock.unlock();
356     // <- SAFE ------------------------------
357 }
358 
359 //-----------------------------------------------
360 void StorageHolder::notifyPath(const ::rtl::OUString& sPath)
361 {
362     ::rtl::OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
363 
364     // SAFE -> ------------------------------
365     ReadGuard aReadLock(m_aLock);
366 
367     TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
368     if (pIt1 == m_lStorages.end())
369         return;
370 
371     TStorageInfo& rInfo = pIt1->second;
372     TStorageListenerList::iterator pIt2;
373     for (  pIt2  = rInfo.Listener.begin();
374            pIt2 != rInfo.Listener.end()  ;
375          ++pIt2                          )
376     {
377         IStorageListener* pListener = *pIt2;
378         if (pListener)
379             pListener->changesOccured(sNormedPath);
380     }
381 
382     aReadLock.unlock();
383     // <- SAFE ------------------------------
384 }
385 
386 //-----------------------------------------------
387 void StorageHolder::addStorageListener(      IStorageListener* pListener,
388                                        const ::rtl::OUString&  sPath    )
389 {
390     ::rtl::OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
391 
392     // SAFE -> ------------------------------
393     ReadGuard aReadLock(m_aLock);
394 
395     TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
396     if (pIt1 == m_lStorages.end())
397         return;
398 
399     TStorageInfo& rInfo = pIt1->second;
400     TStorageListenerList::iterator pIt2 = ::std::find(rInfo.Listener.begin(), rInfo.Listener.end(), pListener);
401     if (pIt2 == rInfo.Listener.end())
402         rInfo.Listener.push_back(pListener);
403 
404     aReadLock.unlock();
405     // <- SAFE ------------------------------
406 }
407 
408 //-----------------------------------------------
409 void StorageHolder::removeStorageListener(      IStorageListener* pListener,
410                                           const ::rtl::OUString&  sPath    )
411 {
412     ::rtl::OUString sNormedPath = StorageHolder::impl_st_normPath(sPath);
413 
414     // SAFE -> ------------------------------
415     ReadGuard aReadLock(m_aLock);
416 
417     TPath2StorageInfo::iterator pIt1 = m_lStorages.find(sNormedPath);
418     if (pIt1 == m_lStorages.end())
419         return;
420 
421     TStorageInfo& rInfo = pIt1->second;
422     TStorageListenerList::iterator pIt2 = ::std::find(rInfo.Listener.begin(), rInfo.Listener.end(), pListener);
423     if (pIt2 != rInfo.Listener.end())
424         rInfo.Listener.erase(pIt2);
425 
426     aReadLock.unlock();
427     // <- SAFE ------------------------------
428 }
429 
430 //-----------------------------------------------
431 ::rtl::OUString StorageHolder::getPathOfStorage(const css::uno::Reference< css::embed::XStorage >& xStorage)
432 {
433     // SAFE -> ------------------------------
434     ReadGuard aReadLock(m_aLock);
435 
436     TPath2StorageInfo::const_iterator pIt;
437     for (  pIt  = m_lStorages.begin();
438            pIt != m_lStorages.end()  ;
439          ++pIt                       )
440     {
441         const TStorageInfo& rInfo = pIt->second;
442         if (rInfo.Storage == xStorage)
443             break;
444     }
445 
446     if (pIt == m_lStorages.end())
447         return ::rtl::OUString();
448 
449     return pIt->first;
450 
451     // <- SAFE ------------------------------
452 }
453 
454 //-----------------------------------------------
455 css::uno::Reference< css::embed::XStorage > StorageHolder::getParentStorage(const css::uno::Reference< css::embed::XStorage >& xChild)
456 {
457     ::rtl::OUString sChildPath = getPathOfStorage(xChild);
458     return getParentStorage(sChildPath);
459 }
460 
461 //-----------------------------------------------
462 css::uno::Reference< css::embed::XStorage > StorageHolder::getParentStorage(const ::rtl::OUString& sChildPath)
463 {
464     // normed path = "a/b/c/" ... we search for "a/b/"
465     ::rtl::OUString sNormedPath = StorageHolder::impl_st_normPath(sChildPath);
466     OUStringList    lFolders    = StorageHolder::impl_st_parsePath(sNormedPath);
467     sal_Int32       c           = lFolders.size();
468 
469     // a) ""       => -       => no parent
470     // b) "a/b/c/" => "a/b/"  => return storage "a/b/"
471     // c) "a/"     => ""      => return root !
472 
473     // a)
474     if (c < 1)
475         return css::uno::Reference< css::embed::XStorage >();
476 
477     // SAFE -> ----------------------------------
478     ReadGuard aReadLock(m_aLock);
479 
480     // b)
481     if (c < 2)
482         return m_xRoot;
483 
484     // c)
485     ::rtl::OUString sParentPath;
486     sal_Int32       i = 0;
487     for (i=0; i<c-1; ++i)
488     {
489         sParentPath += lFolders[i];
490         sParentPath += PATH_SEPERATOR;
491     }
492 
493     TPath2StorageInfo::const_iterator pParent = m_lStorages.find(sParentPath);
494     if (pParent != m_lStorages.end())
495         return pParent->second.Storage;
496 
497     aReadLock.unlock();
498     // <- SAFE ----------------------------------
499 
500     // ?
501     LOG_WARNING("StorageHolder::getParentStorage()", "Unexpected situation. Cached storage item seems to be wrong.")
502     return css::uno::Reference< css::embed::XStorage >();
503 }
504 
505 //-----------------------------------------------
506 void StorageHolder::operator=(const StorageHolder& rCopy)
507 {
508     // SAFE -> ----------------------------------
509     WriteGuard aWriteLock(m_aLock);
510 
511     m_xSMGR     = rCopy.m_xSMGR; // ???
512     m_xRoot     = rCopy.m_xRoot;
513     m_lStorages = rCopy.m_lStorages;
514 
515     aWriteLock.unlock();
516     // <- SAFE ----------------------------------
517 }
518 
519 //-----------------------------------------------
520 css::uno::Reference< css::embed::XStorage > StorageHolder::openSubStorageWithFallback(const css::uno::Reference< css::embed::XStorage >& xBaseStorage  ,
521                                                                                       const ::rtl::OUString&                             sSubStorage   ,
522                                                                                             sal_Int32                                    eOpenMode     ,
523                                                                                             sal_Bool                                     bAllowFallback)
524 {
525     // a) try it first with user specified open mode
526     //    ignore errors ... but save it for later use!
527     css::uno::Exception exResult;
528     try
529     {
530         css::uno::Reference< css::embed::XStorage > xSubStorage = xBaseStorage->openStorageElement(sSubStorage, eOpenMode);
531         if (xSubStorage.is())
532             return xSubStorage;
533     }
534     catch(const css::uno::RuntimeException&)
535         { throw; }
536     catch(const css::uno::Exception& ex)
537         { exResult = ex; }
538 
539     // b) readonly already tried? => forward last error!
540     if (
541         (!bAllowFallback                                                                 ) ||   // fallback allowed  ?
542         ((eOpenMode & css::embed::ElementModes::WRITE) != css::embed::ElementModes::WRITE)      // fallback possible ?
543        )
544         throw exResult;
545 
546     // c) try it readonly
547     //    dont catch exception here! Outside code whish to know, if operation failed or not.
548     //    Otherwhise they work on NULL references ...
549     sal_Int32 eNewMode = (eOpenMode & ~css::embed::ElementModes::WRITE);
550     css::uno::Reference< css::embed::XStorage > xSubStorage = xBaseStorage->openStorageElement(sSubStorage, eNewMode);
551     if (xSubStorage.is())
552         return xSubStorage;
553 
554     // d) no chance!
555     LOG_WARNING("openSubStorageWithFallback()", "Unexpected situation! Got no exception for missing storage ...")
556     return css::uno::Reference< css::embed::XStorage >();
557 }
558 
559 //-----------------------------------------------
560 css::uno::Reference< css::io::XStream > StorageHolder::openSubStreamWithFallback(const css::uno::Reference< css::embed::XStorage >& xBaseStorage  ,
561                                                                                  const ::rtl::OUString&                             sSubStream    ,
562                                                                                        sal_Int32                                    eOpenMode     ,
563                                                                                        sal_Bool                                     bAllowFallback)
564 {
565     // a) try it first with user specified open mode
566     //    ignore errors ... but save it for later use!
567     css::uno::Exception exResult;
568     try
569     {
570         css::uno::Reference< css::io::XStream > xSubStream = xBaseStorage->openStreamElement(sSubStream, eOpenMode);
571         if (xSubStream.is())
572             return xSubStream;
573     }
574     catch(const css::uno::RuntimeException&)
575         { throw; }
576     catch(const css::uno::Exception& ex)
577         { exResult = ex; }
578 
579     // b) readonly already tried? => forward last error!
580     if (
581         (!bAllowFallback                                                                 ) ||   // fallback allowed  ?
582         ((eOpenMode & css::embed::ElementModes::WRITE) != css::embed::ElementModes::WRITE)      // fallback possible ?
583        )
584         throw exResult;
585 
586     // c) try it readonly
587     //    dont catch exception here! Outside code whish to know, if operation failed or not.
588     //    Otherwhise they work on NULL references ...
589     sal_Int32 eNewMode = (eOpenMode & ~css::embed::ElementModes::WRITE);
590     css::uno::Reference< css::io::XStream > xSubStream = xBaseStorage->openStreamElement(sSubStream, eNewMode);
591     if (xSubStream.is())
592         return xSubStream;
593 
594     // d) no chance!
595     LOG_WARNING("openSubStreamWithFallbacks()", "Unexpected situation! Got no exception for missing stream ...")
596     return css::uno::Reference< css::io::XStream >();
597 }
598 
599 //-----------------------------------------------
600 ::rtl::OUString StorageHolder::impl_st_normPath(const ::rtl::OUString& sPath)
601 {
602     // path must start without "/" but end with "/"!
603 
604     ::rtl::OUString sNormedPath = sPath;
605 
606     // "/bla" => "bla" && "/" => "" (!)
607     if (sNormedPath.indexOf(PATH_SEPERATOR) == 0)
608         sNormedPath += sNormedPath.copy(1);
609 
610     // "/" => "" || "" => "" ?
611     if (sNormedPath.getLength() < 1)
612         return ::rtl::OUString();
613 
614     // "bla" => "bla/"
615     if (sNormedPath.lastIndexOf(PATH_SEPERATOR) != (sNormedPath.getLength()-1))
616         sNormedPath += PATH_SEPERATOR;
617 
618     return sNormedPath;
619 }
620 
621 //-----------------------------------------------
622 OUStringList StorageHolder::impl_st_parsePath(const ::rtl::OUString& sPath)
623 {
624     OUStringList lToken;
625     sal_Int32    i  = 0;
626     while (sal_True)
627     {
628         ::rtl::OUString sToken = sPath.getToken(0, PATH_SEPERATOR_UNICODE, i);
629         if (i < 0)
630             break;
631         lToken.push_back(sToken);
632     }
633     return lToken;
634 }
635 
636 //===============================================
637 } // namespace framework
638