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