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