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