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_sc.hxx" 26 27 28 29 // INCLUDE --------------------------------------------------------------- 30 31 #include "externalrefmgr.hxx" 32 #include "document.hxx" 33 #include "token.hxx" 34 #include "tokenarray.hxx" 35 #include "address.hxx" 36 #include "tablink.hxx" 37 #include "docsh.hxx" 38 #include "scextopt.hxx" 39 #include "rangenam.hxx" 40 #include "cell.hxx" 41 #include "viewdata.hxx" 42 #include "tabvwsh.hxx" 43 #include "sc.hrc" 44 45 #include "sfx2/app.hxx" 46 #include "sfx2/docfilt.hxx" 47 #include "sfx2/docfile.hxx" 48 #include "sfx2/fcontnr.hxx" 49 #include "sfx2/sfxsids.hrc" 50 #include "sfx2/objsh.hxx" 51 #include "svl/broadcast.hxx" 52 #include "svl/smplhint.hxx" 53 #include "svl/itemset.hxx" 54 #include "svl/stritem.hxx" 55 #include "svl/urihelper.hxx" 56 #include "svl/zformat.hxx" 57 #include "sfx2/linkmgr.hxx" 58 #include "tools/urlobj.hxx" 59 #include "unotools/ucbhelper.hxx" 60 #include "unotools/localfilehelper.hxx" 61 62 #include <memory> 63 #include <algorithm> 64 65 #include <boost/scoped_ptr.hpp> 66 67 using ::std::auto_ptr; 68 using ::com::sun::star::uno::Any; 69 using ::rtl::OUString; 70 using ::std::vector; 71 using ::std::find; 72 using ::std::find_if; 73 using ::std::distance; 74 using ::std::pair; 75 using ::std::list; 76 using ::std::unary_function; 77 using namespace formula; 78 79 #define SRCDOC_LIFE_SPAN 6000 // 1 minute (in 100th of a sec) 80 #define SRCDOC_SCAN_INTERVAL 1000*5 // every 5 seconds (in msec) 81 82 namespace { 83 84 class TabNameSearchPredicate : public unary_function<bool, ScExternalRefCache::TableName> 85 { 86 public: 87 explicit TabNameSearchPredicate(const String& rSearchName) : 88 maSearchName(ScGlobal::pCharClass->upper(rSearchName)) 89 { 90 } 91 92 bool operator()(const ScExternalRefCache::TableName& rTabNameSet) const 93 { 94 // Ok, I'm doing case insensitive search here. 95 return rTabNameSet.maUpperName.Equals(maSearchName); 96 } 97 98 private: 99 String maSearchName; 100 }; 101 102 class FindSrcFileByName : public unary_function<ScExternalRefManager::SrcFileData, bool> 103 { 104 public: 105 FindSrcFileByName(const String& rMatchName) : 106 mrMatchName(rMatchName) 107 { 108 } 109 110 bool operator()(const ScExternalRefManager::SrcFileData& rSrcData) const 111 { 112 return rSrcData.maFileName.Equals(mrMatchName); 113 } 114 115 private: 116 const String& mrMatchName; 117 }; 118 119 class NotifyLinkListener : public unary_function<ScExternalRefManager::LinkListener*, void> 120 { 121 public: 122 NotifyLinkListener(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType) : 123 mnFileId(nFileId), meType(eType) {} 124 125 NotifyLinkListener(const NotifyLinkListener& r) : 126 mnFileId(r.mnFileId), meType(r.meType) {} 127 128 void operator() (ScExternalRefManager::LinkListener* p) const 129 { 130 p->notify(mnFileId, meType); 131 } 132 private: 133 sal_uInt16 mnFileId; 134 ScExternalRefManager::LinkUpdateType meType; 135 }; 136 137 struct UpdateFormulaCell : public unary_function<ScFormulaCell*, void> 138 { 139 void operator() (ScFormulaCell* pCell) const 140 { 141 // Check to make sure the cell really contains ocExternalRef. 142 // External names, external cell and range references all have a 143 // ocExternalRef token. 144 const ScTokenArray* pCode = pCell->GetCode(); 145 if (!pCode->HasOpCode( ocExternalRef)) 146 return; 147 148 ScTokenArray* pArray = pCell->GetCode(); 149 if (pArray) 150 // Clear the error code, or a cell with error won't get re-compiled. 151 pArray->SetCodeError(0); 152 153 pCell->SetCompile(true); 154 pCell->CompileTokenArray(); 155 pCell->SetDirty(); 156 } 157 }; 158 159 class RemoveFormulaCell : public unary_function<pair<const sal_uInt16, ScExternalRefManager::RefCellSet>, void> 160 { 161 public: 162 explicit RemoveFormulaCell(ScFormulaCell* p) : mpCell(p) {} 163 void operator() (pair<const sal_uInt16, ScExternalRefManager::RefCellSet>& r) const 164 { 165 r.second.erase(mpCell); 166 } 167 private: 168 ScFormulaCell* mpCell; 169 }; 170 171 class ConvertFormulaToStatic : public unary_function<ScFormulaCell*, void> 172 { 173 public: 174 explicit ConvertFormulaToStatic(ScDocument* pDoc) : mpDoc(pDoc) {} 175 void operator() (ScFormulaCell* pCell) const 176 { 177 ScAddress aPos = pCell->aPos; 178 179 // We don't check for empty cells because empty external cells are 180 // treated as having a value of 0. 181 182 if (pCell->IsValue()) 183 { 184 // Turn this into value cell. 185 double fVal = pCell->GetValue(); 186 mpDoc->PutCell(aPos, new ScValueCell(fVal)); 187 } 188 else 189 { 190 // string cell otherwise. 191 String aVal; 192 pCell->GetString(aVal); 193 mpDoc->PutCell(aPos, new ScStringCell(aVal)); 194 } 195 } 196 private: 197 ScDocument* mpDoc; 198 }; 199 200 } 201 202 // ============================================================================ 203 204 ScExternalRefCache::Table::Table() 205 : meReferenced( REFERENCED_MARKED ) 206 // Prevent accidental data loss due to lack of knowledge. 207 { 208 } 209 210 ScExternalRefCache::Table::~Table() 211 { 212 } 213 214 void ScExternalRefCache::Table::setReferencedFlag( ScExternalRefCache::Table::ReferencedFlag eFlag ) 215 { 216 meReferenced = eFlag; 217 } 218 219 void ScExternalRefCache::Table::setReferenced( bool bReferenced ) 220 { 221 if (meReferenced != REFERENCED_PERMANENT) 222 meReferenced = (bReferenced ? REFERENCED_MARKED : UNREFERENCED); 223 } 224 225 ScExternalRefCache::Table::ReferencedFlag ScExternalRefCache::Table::getReferencedFlag() const 226 { 227 return meReferenced; 228 } 229 230 bool ScExternalRefCache::Table::isReferenced() const 231 { 232 return meReferenced != UNREFERENCED; 233 } 234 235 void ScExternalRefCache::Table::setCell(SCCOL nCol, SCROW nRow, TokenRef pToken, sal_uInt32 nFmtIndex, bool bSetCacheRange) 236 { 237 using ::std::pair; 238 RowsDataType::iterator itrRow = maRows.find(nRow); 239 if (itrRow == maRows.end()) 240 { 241 // This row does not exist yet. 242 pair<RowsDataType::iterator, bool> res = maRows.insert( 243 RowsDataType::value_type(nRow, RowDataType())); 244 245 if (!res.second) 246 return; 247 248 itrRow = res.first; 249 } 250 251 // Insert this token into the specified column location. I don't need to 252 // check for existing data. Just overwrite it. 253 RowDataType& rRow = itrRow->second; 254 ScExternalRefCache::Cell aCell; 255 aCell.mxToken = pToken; 256 aCell.mnFmtIndex = nFmtIndex; 257 rRow.insert(RowDataType::value_type(nCol, aCell)); 258 if (bSetCacheRange) 259 setCachedCell(nCol, nRow); 260 } 261 262 ScExternalRefCache::TokenRef ScExternalRefCache::Table::getCell(SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex) const 263 { 264 RowsDataType::const_iterator itrTable = maRows.find(nRow); 265 if (itrTable == maRows.end()) 266 { 267 // this table doesn't have the specified row. 268 return getEmptyOrNullToken(nCol, nRow); 269 } 270 271 const RowDataType& rRowData = itrTable->second; 272 RowDataType::const_iterator itrRow = rRowData.find(nCol); 273 if (itrRow == rRowData.end()) 274 { 275 // this row doesn't have the specified column. 276 return getEmptyOrNullToken(nCol, nRow); 277 } 278 279 const Cell& rCell = itrRow->second; 280 if (pnFmtIndex) 281 *pnFmtIndex = rCell.mnFmtIndex; 282 283 return rCell.mxToken; 284 } 285 286 bool ScExternalRefCache::Table::hasRow( SCROW nRow ) const 287 { 288 RowsDataType::const_iterator itrRow = maRows.find(nRow); 289 return itrRow != maRows.end(); 290 } 291 292 void ScExternalRefCache::Table::getAllRows(vector<SCROW>& rRows, SCROW nLow, SCROW nHigh) const 293 { 294 vector<SCROW> aRows; 295 aRows.reserve(maRows.size()); 296 RowsDataType::const_iterator itr = maRows.begin(), itrEnd = maRows.end(); 297 for (; itr != itrEnd; ++itr) 298 if (nLow <= itr->first && itr->first <= nHigh) 299 aRows.push_back(itr->first); 300 301 // hash map is not ordered, so we need to explicitly sort it. 302 ::std::sort(aRows.begin(), aRows.end()); 303 rRows.swap(aRows); 304 } 305 306 ::std::pair< SCROW, SCROW > ScExternalRefCache::Table::getRowRange() const 307 { 308 ::std::pair< SCROW, SCROW > aRange( 0, 0 ); 309 if( !maRows.empty() ) 310 { 311 // iterate over entire container (hash map is not sorted by key) 312 RowsDataType::const_iterator itr = maRows.begin(), itrEnd = maRows.end(); 313 aRange.first = itr->first; 314 aRange.second = itr->first + 1; 315 while( ++itr != itrEnd ) 316 { 317 if( itr->first < aRange.first ) 318 aRange.first = itr->first; 319 else if( itr->first >= aRange.second ) 320 aRange.second = itr->first + 1; 321 } 322 } 323 return aRange; 324 } 325 326 void ScExternalRefCache::Table::getAllCols(SCROW nRow, vector<SCCOL>& rCols, SCCOL nLow, SCCOL nHigh) const 327 { 328 RowsDataType::const_iterator itrRow = maRows.find(nRow); 329 if (itrRow == maRows.end()) 330 // this table doesn't have the specified row. 331 return; 332 333 const RowDataType& rRowData = itrRow->second; 334 vector<SCCOL> aCols; 335 aCols.reserve(rRowData.size()); 336 RowDataType::const_iterator itrCol = rRowData.begin(), itrColEnd = rRowData.end(); 337 for (; itrCol != itrColEnd; ++itrCol) 338 if (nLow <= itrCol->first && itrCol->first <= nHigh) 339 aCols.push_back(itrCol->first); 340 341 // hash map is not ordered, so we need to explicitly sort it. 342 ::std::sort(aCols.begin(), aCols.end()); 343 rCols.swap(aCols); 344 } 345 346 ::std::pair< SCCOL, SCCOL > ScExternalRefCache::Table::getColRange( SCROW nRow ) const 347 { 348 ::std::pair< SCCOL, SCCOL > aRange( 0, 0 ); 349 350 RowsDataType::const_iterator itrRow = maRows.find( nRow ); 351 if (itrRow == maRows.end()) 352 // this table doesn't have the specified row. 353 return aRange; 354 355 const RowDataType& rRowData = itrRow->second; 356 if( !rRowData.empty() ) 357 { 358 // iterate over entire container (hash map is not sorted by key) 359 RowDataType::const_iterator itr = rRowData.begin(), itrEnd = rRowData.end(); 360 aRange.first = itr->first; 361 aRange.second = itr->first + 1; 362 while( ++itr != itrEnd ) 363 { 364 if( itr->first < aRange.first ) 365 aRange.first = itr->first; 366 else if( itr->first >= aRange.second ) 367 aRange.second = itr->first + 1; 368 } 369 } 370 return aRange; 371 } 372 373 void ScExternalRefCache::Table::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const 374 { 375 RowsDataType::const_iterator itrRow = maRows.begin(), itrRowEnd = maRows.end(); 376 for (; itrRow != itrRowEnd; ++itrRow) 377 { 378 const RowDataType& rRowData = itrRow->second; 379 RowDataType::const_iterator itrCol = rRowData.begin(), itrColEnd = rRowData.end(); 380 for (; itrCol != itrColEnd; ++itrCol) 381 { 382 const Cell& rCell = itrCol->second; 383 rNumFmts.push_back(rCell.mnFmtIndex); 384 } 385 } 386 } 387 388 const ScRangeList& ScExternalRefCache::Table::getCachedRanges() const 389 { 390 return maCachedRanges; 391 } 392 393 bool ScExternalRefCache::Table::isRangeCached(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const 394 { 395 return maCachedRanges.In(ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0)); 396 } 397 398 void ScExternalRefCache::Table::setCachedCell(SCCOL nCol, SCROW nRow) 399 { 400 setCachedCellRange(nCol, nRow, nCol, nRow); 401 } 402 403 void ScExternalRefCache::Table::setCachedCellRange(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) 404 { 405 ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0); 406 if (!maCachedRanges.Count()) 407 maCachedRanges.Append(aRange); 408 else 409 maCachedRanges.Join(aRange); 410 411 String aStr; 412 maCachedRanges.Format(aStr, SCA_VALID); 413 } 414 415 void ScExternalRefCache::Table::setWholeTableCached() 416 { 417 setCachedCellRange(0, 0, MAXCOL, MAXROW); 418 } 419 420 bool ScExternalRefCache::Table::isInCachedRanges(SCCOL nCol, SCROW nRow) const 421 { 422 return maCachedRanges.In(ScRange(nCol, nRow, 0, nCol, nRow, 0)); 423 } 424 425 ScExternalRefCache::TokenRef ScExternalRefCache::Table::getEmptyOrNullToken( 426 SCCOL nCol, SCROW nRow) const 427 { 428 if (isInCachedRanges(nCol, nRow)) 429 { 430 TokenRef p(new ScEmptyCellToken(false, false)); 431 return p; 432 } 433 return TokenRef(); 434 } 435 436 // ---------------------------------------------------------------------------- 437 438 ScExternalRefCache::TableName::TableName(const String& rUpper, const String& rReal) : 439 maUpperName(rUpper), maRealName(rReal) 440 { 441 } 442 443 // ---------------------------------------------------------------------------- 444 445 ScExternalRefCache::CellFormat::CellFormat() : 446 mbIsSet(false), mnType(NUMBERFORMAT_ALL), mnIndex(0) 447 { 448 } 449 450 // ---------------------------------------------------------------------------- 451 452 ScExternalRefCache::ScExternalRefCache() 453 { 454 } 455 ScExternalRefCache::~ScExternalRefCache() 456 { 457 } 458 459 const String* ScExternalRefCache::getRealTableName(sal_uInt16 nFileId, const String& rTabName) const 460 { 461 DocDataType::const_iterator itrDoc = maDocs.find(nFileId); 462 if (itrDoc == maDocs.end()) 463 { 464 // specified document is not cached. 465 return NULL; 466 } 467 468 const DocItem& rDoc = itrDoc->second; 469 TableNameIndexMap::const_iterator itrTabId = rDoc.maTableNameIndex.find( 470 ScGlobal::pCharClass->upper(rTabName)); 471 if (itrTabId == rDoc.maTableNameIndex.end()) 472 { 473 // the specified table is not in cache. 474 return NULL; 475 } 476 477 return &rDoc.maTableNames[itrTabId->second].maRealName; 478 } 479 480 const String* ScExternalRefCache::getRealRangeName(sal_uInt16 nFileId, const String& rRangeName) const 481 { 482 DocDataType::const_iterator itrDoc = maDocs.find(nFileId); 483 if (itrDoc == maDocs.end()) 484 { 485 // specified document is not cached. 486 return NULL; 487 } 488 489 const DocItem& rDoc = itrDoc->second; 490 NamePairMap::const_iterator itr = rDoc.maRealRangeNameMap.find( 491 ScGlobal::pCharClass->upper(rRangeName)); 492 if (itr == rDoc.maRealRangeNameMap.end()) 493 // range name not found. 494 return NULL; 495 496 return &itr->second; 497 } 498 499 ScExternalRefCache::TokenRef ScExternalRefCache::getCellData( 500 sal_uInt16 nFileId, const String& rTabName, SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex) 501 { 502 DocDataType::const_iterator itrDoc = maDocs.find(nFileId); 503 if (itrDoc == maDocs.end()) 504 { 505 // specified document is not cached. 506 return TokenRef(); 507 } 508 509 const DocItem& rDoc = itrDoc->second; 510 TableNameIndexMap::const_iterator itrTabId = rDoc.maTableNameIndex.find( 511 ScGlobal::pCharClass->upper(rTabName)); 512 if (itrTabId == rDoc.maTableNameIndex.end()) 513 { 514 // the specified table is not in cache. 515 return TokenRef(); 516 } 517 518 const TableTypeRef& pTableData = rDoc.maTables[itrTabId->second]; 519 if (!pTableData.get()) 520 { 521 // the table data is not instantiated yet. 522 return TokenRef(); 523 } 524 525 return pTableData->getCell(nCol, nRow, pnFmtIndex); 526 } 527 528 ScExternalRefCache::TokenArrayRef ScExternalRefCache::getCellRangeData( 529 sal_uInt16 nFileId, const String& rTabName, const ScRange& rRange) 530 { 531 DocDataType::iterator itrDoc = maDocs.find(nFileId); 532 if (itrDoc == maDocs.end()) 533 // specified document is not cached. 534 return TokenArrayRef(); 535 536 DocItem& rDoc = itrDoc->second; 537 538 TableNameIndexMap::iterator itrTabId = rDoc.maTableNameIndex.find( 539 ScGlobal::pCharClass->upper(rTabName)); 540 if (itrTabId == rDoc.maTableNameIndex.end()) 541 // the specified table is not in cache. 542 return TokenArrayRef(); 543 544 const ScAddress& s = rRange.aStart; 545 const ScAddress& e = rRange.aEnd; 546 547 SCTAB nTab1 = s.Tab(), nTab2 = e.Tab(); 548 SCCOL nCol1 = s.Col(), nCol2 = e.Col(); 549 SCROW nRow1 = s.Row(), nRow2 = e.Row(); 550 551 // Make sure I have all the tables cached. 552 size_t nTabFirstId = itrTabId->second; 553 size_t nTabLastId = nTabFirstId + nTab2 - nTab1; 554 if (nTabLastId >= rDoc.maTables.size()) 555 // not all tables are cached. 556 return TokenArrayRef(); 557 558 ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId)); 559 560 RangeArrayMap::const_iterator itrRange = rDoc.maRangeArrays.find( aCacheRange); 561 if (itrRange != rDoc.maRangeArrays.end()) 562 // Cache hit! 563 return itrRange->second; 564 565 ::boost::scoped_ptr<ScRange> pNewRange; 566 TokenArrayRef pArray; 567 bool bFirstTab = true; 568 for (size_t nTab = nTabFirstId; nTab <= nTabLastId; ++nTab) 569 { 570 TableTypeRef pTab = rDoc.maTables[nTab]; 571 if (!pTab.get()) 572 return TokenArrayRef(); 573 574 SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2; 575 SCROW nDataRow1 = nRow1, nDataRow2 = nRow2; 576 577 if (!pTab->isRangeCached(nDataCol1, nDataRow1, nDataCol2, nDataRow2)) 578 { 579 // specified range is not entirely within cached ranges. 580 return TokenArrayRef(); 581 } 582 583 ScMatrixRef xMat = new ScMatrix( 584 static_cast<SCSIZE>(nDataCol2-nDataCol1+1), static_cast<SCSIZE>(nDataRow2-nDataRow1+1)); 585 586 #if 0 587 // TODO: Switch to this code block once we have support for sparsely-filled 588 // matrices in ScMatrix. 589 590 // Only fill non-empty cells, for better performance. 591 vector<SCROW> aRows; 592 pTab->getAllRows(aRows, nDataRow1, nDataRow2); 593 for (vector<SCROW>::const_iterator itr = aRows.begin(), itrEnd = aRows.end(); itr != itrEnd; ++itr) 594 { 595 SCROW nRow = *itr; 596 vector<SCCOL> aCols; 597 pTab->getAllCols(nRow, aCols, nDataCol1, nDataCol2); 598 for (vector<SCCOL>::const_iterator itrCol = aCols.begin(), itrColEnd = aCols.end(); itrCol != itrColEnd; ++itrCol) 599 { 600 SCCOL nCol = *itrCol; 601 TokenRef pToken = pTab->getCell(nCol, nRow); 602 if (!pToken) 603 // This should never happen! 604 return TokenArrayRef(); 605 606 SCSIZE nC = nCol - nDataCol1, nR = nRow - nDataRow1; 607 switch (pToken->GetType()) 608 { 609 case svDouble: 610 xMat->PutDouble(pToken->GetDouble(), nC, nR); 611 break; 612 case svString: 613 xMat->PutString(pToken->GetString(), nC, nR); 614 break; 615 default: 616 ; 617 } 618 } 619 } 620 #else 621 vector<SCROW> aRows; 622 pTab->getAllRows(aRows, nDataRow1, nDataRow2); 623 if (aRows.empty()) 624 // Cache is empty. 625 return TokenArrayRef(); 626 else 627 // Trim the column below the last non-empty row. 628 nDataRow2 = aRows.back(); 629 630 // Empty all matrix elements first, and fill only non-empty elements. 631 for (SCROW nRow = nDataRow1; nRow <= nDataRow2; ++nRow) 632 { 633 for (SCCOL nCol = nDataCol1; nCol <= nDataCol2; ++nCol) 634 { 635 TokenRef pToken = pTab->getCell(nCol, nRow); 636 SCSIZE nC = nCol - nCol1, nR = nRow - nRow1; 637 if (!pToken) 638 return TokenArrayRef(); 639 640 switch (pToken->GetType()) 641 { 642 case svDouble: 643 xMat->PutDouble(pToken->GetDouble(), nC, nR); 644 break; 645 case svString: 646 xMat->PutString(pToken->GetString(), nC, nR); 647 break; 648 default: 649 xMat->PutEmpty(nC, nR); 650 } 651 } 652 } 653 #endif 654 655 if (!bFirstTab) 656 pArray->AddOpCode(ocSep); 657 658 ScMatrix* pMat2 = xMat; 659 ScMatrixToken aToken(pMat2); 660 if (!pArray) 661 pArray.reset(new ScTokenArray); 662 pArray->AddToken(aToken); 663 664 bFirstTab = false; 665 666 if (!pNewRange) 667 pNewRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0)); 668 else 669 pNewRange->ExtendTo(ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0)); 670 } 671 672 if (pNewRange) 673 rDoc.maRangeArrays.insert( RangeArrayMap::value_type(*pNewRange, pArray)); 674 return pArray; 675 } 676 677 ScExternalRefCache::TokenArrayRef ScExternalRefCache::getRangeNameTokens(sal_uInt16 nFileId, const String& rName) 678 { 679 DocItem* pDoc = getDocItem(nFileId); 680 if (!pDoc) 681 return TokenArrayRef(); 682 683 RangeNameMap& rMap = pDoc->maRangeNames; 684 RangeNameMap::const_iterator itr = rMap.find( 685 ScGlobal::pCharClass->upper(rName)); 686 if (itr == rMap.end()) 687 return TokenArrayRef(); 688 689 return itr->second; 690 } 691 692 void ScExternalRefCache::setRangeNameTokens(sal_uInt16 nFileId, const String& rName, TokenArrayRef pArray) 693 { 694 DocItem* pDoc = getDocItem(nFileId); 695 if (!pDoc) 696 return; 697 698 String aUpperName = ScGlobal::pCharClass->upper(rName); 699 RangeNameMap& rMap = pDoc->maRangeNames; 700 rMap.insert(RangeNameMap::value_type(aUpperName, pArray)); 701 pDoc->maRealRangeNameMap.insert(NamePairMap::value_type(aUpperName, rName)); 702 } 703 704 void ScExternalRefCache::setCellData(sal_uInt16 nFileId, const String& rTabName, SCCOL nCol, SCROW nRow, 705 TokenRef pToken, sal_uInt32 nFmtIndex) 706 { 707 if (!isDocInitialized(nFileId)) 708 return; 709 710 using ::std::pair; 711 DocItem* pDocItem = getDocItem(nFileId); 712 if (!pDocItem) 713 return; 714 715 DocItem& rDoc = *pDocItem; 716 717 // See if the table by this name already exists. 718 TableNameIndexMap::iterator itrTabName = rDoc.maTableNameIndex.find( 719 ScGlobal::pCharClass->upper(rTabName)); 720 if (itrTabName == rDoc.maTableNameIndex.end()) 721 // Table not found. Maybe the table name or the file id is wrong ??? 722 return; 723 724 TableTypeRef& pTableData = rDoc.maTables[itrTabName->second]; 725 if (!pTableData.get()) 726 pTableData.reset(new Table); 727 728 pTableData->setCell(nCol, nRow, pToken, nFmtIndex); 729 pTableData->setCachedCell(nCol, nRow); 730 } 731 732 void ScExternalRefCache::setCellRangeData(sal_uInt16 nFileId, const ScRange& rRange, const vector<SingleRangeData>& rData, 733 TokenArrayRef pArray) 734 { 735 using ::std::pair; 736 if (rData.empty() || !isDocInitialized(nFileId)) 737 // nothing to cache 738 return; 739 740 // First, get the document item for the given file ID. 741 DocItem* pDocItem = getDocItem(nFileId); 742 if (!pDocItem) 743 return; 744 745 DocItem& rDoc = *pDocItem; 746 747 // Now, find the table position of the first table to cache. 748 const String& rFirstTabName = rData.front().maTableName; 749 TableNameIndexMap::iterator itrTabName = rDoc.maTableNameIndex.find( 750 ScGlobal::pCharClass->upper(rFirstTabName)); 751 if (itrTabName == rDoc.maTableNameIndex.end()) 752 { 753 // table index not found. 754 return; 755 } 756 757 size_t nTabFirstId = itrTabName->second; 758 SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row(); 759 SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col(); 760 vector<SingleRangeData>::const_iterator itrDataBeg = rData.begin(), itrDataEnd = rData.end(); 761 for (vector<SingleRangeData>::const_iterator itrData = itrDataBeg; itrData != itrDataEnd; ++itrData) 762 { 763 size_t i = nTabFirstId + ::std::distance(itrDataBeg, itrData); 764 TableTypeRef& pTabData = rDoc.maTables[i]; 765 if (!pTabData.get()) 766 pTabData.reset(new Table); 767 768 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) 769 { 770 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) 771 { 772 SCSIZE nC = nCol - nCol1, nR = nRow - nRow1; 773 TokenRef pToken; 774 const ScMatrixRef& pMat = itrData->mpRangeData; 775 if (pMat->IsEmpty(nC, nR)) 776 // Don't cache empty cells. 777 continue; 778 779 if (pMat->IsValue(nC, nR)) 780 pToken.reset(new formula::FormulaDoubleToken(pMat->GetDouble(nC, nR))); 781 else if (pMat->IsString(nC, nR)) 782 pToken.reset(new formula::FormulaStringToken(pMat->GetString(nC, nR))); 783 784 if (pToken) 785 // Don't mark this cell 'cached' here, for better performance. 786 pTabData->setCell(nCol, nRow, pToken, 0, false); 787 } 788 } 789 // Mark the whole range 'cached'. 790 pTabData->setCachedCellRange(nCol1, nRow1, nCol2, nRow2); 791 } 792 793 size_t nTabLastId = nTabFirstId + rRange.aEnd.Tab() - rRange.aStart.Tab(); 794 ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId)); 795 796 rDoc.maRangeArrays.insert( RangeArrayMap::value_type( aCacheRange, pArray)); 797 } 798 799 bool ScExternalRefCache::isDocInitialized(sal_uInt16 nFileId) 800 { 801 DocItem* pDoc = getDocItem(nFileId); 802 if (!pDoc) 803 return false; 804 805 return pDoc->mbInitFromSource; 806 } 807 808 static bool lcl_getTableDataIndex(const ScExternalRefCache::TableNameIndexMap& rMap, const String& rName, size_t& rIndex) 809 { 810 ScExternalRefCache::TableNameIndexMap::const_iterator itr = rMap.find(rName); 811 if (itr == rMap.end()) 812 return false; 813 814 rIndex = itr->second; 815 return true; 816 } 817 818 void ScExternalRefCache::initializeDoc(sal_uInt16 nFileId, const vector<String>& rTabNames) 819 { 820 DocItem* pDoc = getDocItem(nFileId); 821 if (!pDoc) 822 return; 823 824 size_t n = rTabNames.size(); 825 826 // table name list - the list must include all table names in the source 827 // document and only to be populated when loading the source document, not 828 // when loading cached data from, say, Excel XCT/CRN records. 829 vector<TableName> aNewTabNames; 830 aNewTabNames.reserve(n); 831 for (vector<String>::const_iterator itr = rTabNames.begin(), itrEnd = rTabNames.end(); 832 itr != itrEnd; ++itr) 833 { 834 TableName aNameItem(ScGlobal::pCharClass->upper(*itr), *itr); 835 aNewTabNames.push_back(aNameItem); 836 } 837 pDoc->maTableNames.swap(aNewTabNames); 838 839 // data tables - preserve any existing data that may have been set during 840 // file import. 841 vector<TableTypeRef> aNewTables(n); 842 for (size_t i = 0; i < n; ++i) 843 { 844 size_t nIndex; 845 if (lcl_getTableDataIndex(pDoc->maTableNameIndex, pDoc->maTableNames[i].maUpperName, nIndex)) 846 { 847 aNewTables[i] = pDoc->maTables[nIndex]; 848 } 849 } 850 pDoc->maTables.swap(aNewTables); 851 852 // name index map 853 TableNameIndexMap aNewNameIndex; 854 for (size_t i = 0; i < n; ++i) 855 aNewNameIndex.insert(TableNameIndexMap::value_type(pDoc->maTableNames[i].maUpperName, i)); 856 pDoc->maTableNameIndex.swap(aNewNameIndex); 857 858 pDoc->mbInitFromSource = true; 859 } 860 861 String ScExternalRefCache::getTableName(sal_uInt16 nFileId, size_t nCacheId) const 862 { 863 if( DocItem* pDoc = getDocItem( nFileId ) ) 864 if( nCacheId < pDoc->maTableNames.size() ) 865 return pDoc->maTableNames[ nCacheId ].maRealName; 866 return EMPTY_STRING; 867 } 868 869 void ScExternalRefCache::getAllTableNames(sal_uInt16 nFileId, vector<String>& rTabNames) const 870 { 871 rTabNames.clear(); 872 DocItem* pDoc = getDocItem(nFileId); 873 if (!pDoc) 874 return; 875 876 size_t n = pDoc->maTableNames.size(); 877 rTabNames.reserve(n); 878 for (vector<TableName>::const_iterator itr = pDoc->maTableNames.begin(), itrEnd = pDoc->maTableNames.end(); 879 itr != itrEnd; ++itr) 880 rTabNames.push_back(itr->maRealName); 881 } 882 883 SCsTAB ScExternalRefCache::getTabSpan( sal_uInt16 nFileId, const String& rStartTabName, const String& rEndTabName ) const 884 { 885 DocItem* pDoc = getDocItem(nFileId); 886 if (!pDoc) 887 return -1; 888 889 vector<TableName>::const_iterator itrBeg = pDoc->maTableNames.begin(); 890 vector<TableName>::const_iterator itrEnd = pDoc->maTableNames.end(); 891 892 vector<TableName>::const_iterator itrStartTab = ::std::find_if( itrBeg, itrEnd, 893 TabNameSearchPredicate( rStartTabName)); 894 if (itrStartTab == itrEnd) 895 return -1; 896 897 vector<TableName>::const_iterator itrEndTab = ::std::find_if( itrBeg, itrEnd, 898 TabNameSearchPredicate( rEndTabName)); 899 if (itrEndTab == itrEnd) 900 return 0; 901 902 size_t nStartDist = ::std::distance( itrBeg, itrStartTab); 903 size_t nEndDist = ::std::distance( itrBeg, itrEndTab); 904 return nStartDist <= nEndDist ? static_cast<SCsTAB>(nEndDist - nStartDist + 1) : -static_cast<SCsTAB>(nStartDist - nEndDist + 1); 905 } 906 907 void ScExternalRefCache::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const 908 { 909 using ::std::sort; 910 using ::std::unique; 911 912 vector<sal_uInt32> aNumFmts; 913 for (DocDataType::const_iterator itrDoc = maDocs.begin(), itrDocEnd = maDocs.end(); 914 itrDoc != itrDocEnd; ++itrDoc) 915 { 916 const vector<TableTypeRef>& rTables = itrDoc->second.maTables; 917 for (vector<TableTypeRef>::const_iterator itrTab = rTables.begin(), itrTabEnd = rTables.end(); 918 itrTab != itrTabEnd; ++itrTab) 919 { 920 TableTypeRef pTab = *itrTab; 921 if (!pTab) 922 continue; 923 924 pTab->getAllNumberFormats(aNumFmts); 925 } 926 } 927 928 // remove duplicates. 929 sort(aNumFmts.begin(), aNumFmts.end()); 930 aNumFmts.erase(unique(aNumFmts.begin(), aNumFmts.end()), aNumFmts.end()); 931 rNumFmts.swap(aNumFmts); 932 } 933 934 bool ScExternalRefCache::setCacheDocReferenced( sal_uInt16 nFileId ) 935 { 936 DocItem* pDocItem = getDocItem(nFileId); 937 if (!pDocItem) 938 return areAllCacheTablesReferenced(); 939 940 for (::std::vector<TableTypeRef>::iterator itrTab = pDocItem->maTables.begin(); 941 itrTab != pDocItem->maTables.end(); ++itrTab) 942 { 943 if ((*itrTab).get()) 944 (*itrTab)->setReferenced( true); 945 } 946 addCacheDocToReferenced( nFileId); 947 return areAllCacheTablesReferenced(); 948 } 949 950 bool ScExternalRefCache::setCacheTableReferenced( sal_uInt16 nFileId, const String& rTabName, size_t nSheets, bool bPermanent ) 951 { 952 DocItem* pDoc = getDocItem(nFileId); 953 if (pDoc) 954 { 955 size_t nIndex = 0; 956 String aTabNameUpper = ScGlobal::pCharClass->upper( rTabName); 957 if (lcl_getTableDataIndex( pDoc->maTableNameIndex, aTabNameUpper, nIndex)) 958 { 959 size_t nStop = ::std::min( nIndex + nSheets, pDoc->maTables.size()); 960 for (size_t i = nIndex; i < nStop; ++i) 961 { 962 TableTypeRef pTab = pDoc->maTables[i]; 963 if (pTab.get()) 964 { 965 Table::ReferencedFlag eNewFlag = (bPermanent ? 966 Table::REFERENCED_PERMANENT : 967 Table::REFERENCED_MARKED); 968 Table::ReferencedFlag eOldFlag = pTab->getReferencedFlag(); 969 if (eOldFlag != Table::REFERENCED_PERMANENT && eNewFlag != eOldFlag) 970 { 971 pTab->setReferencedFlag( eNewFlag); 972 addCacheTableToReferenced( nFileId, i); 973 } 974 } 975 } 976 } 977 } 978 return areAllCacheTablesReferenced(); 979 } 980 981 void ScExternalRefCache::setCacheTableReferencedPermanently( sal_uInt16 nFileId, const String& rTabName, size_t nSheets ) 982 { 983 DocItem* pDoc = getDocItem(nFileId); 984 if (pDoc) 985 { 986 size_t nIndex = 0; 987 String aTabNameUpper = ScGlobal::pCharClass->upper( rTabName); 988 if (lcl_getTableDataIndex( pDoc->maTableNameIndex, aTabNameUpper, nIndex)) 989 { 990 size_t nStop = ::std::min( nIndex + nSheets, pDoc->maTables.size()); 991 for (size_t i = nIndex; i < nStop; ++i) 992 { 993 TableTypeRef pTab = pDoc->maTables[i]; 994 if (pTab.get()) 995 pTab->setReferencedFlag( Table::REFERENCED_PERMANENT); 996 } 997 } 998 } 999 } 1000 1001 void ScExternalRefCache::setAllCacheTableReferencedStati( bool bReferenced ) 1002 { 1003 if (bReferenced) 1004 { 1005 maReferenced.reset(0); 1006 for (DocDataType::iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc) 1007 { 1008 ScExternalRefCache::DocItem& rDocItem = (*itrDoc).second; 1009 for (::std::vector<TableTypeRef>::iterator itrTab = rDocItem.maTables.begin(); 1010 itrTab != rDocItem.maTables.end(); ++itrTab) 1011 { 1012 if ((*itrTab).get()) 1013 (*itrTab)->setReferenced( true); 1014 } 1015 } 1016 } 1017 else 1018 { 1019 size_t nDocs = 0; 1020 for (DocDataType::const_iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc) 1021 { 1022 if (nDocs <= (*itrDoc).first) 1023 nDocs = (*itrDoc).first + 1; 1024 } 1025 maReferenced.reset( nDocs); 1026 1027 for (DocDataType::iterator itrDoc = maDocs.begin(); itrDoc != maDocs.end(); ++itrDoc) 1028 { 1029 ScExternalRefCache::DocItem& rDocItem = (*itrDoc).second; 1030 sal_uInt16 nFileId = (*itrDoc).first; 1031 size_t nTables = rDocItem.maTables.size(); 1032 ReferencedStatus::DocReferenced & rDocReferenced = maReferenced.maDocs[nFileId]; 1033 // All referenced => non-existing tables evaluate as completed. 1034 rDocReferenced.maTables.resize( nTables, true); 1035 for (size_t i=0; i < nTables; ++i) 1036 { 1037 TableTypeRef & xTab = rDocItem.maTables[i]; 1038 if (xTab.get()) 1039 { 1040 if (xTab->getReferencedFlag() == Table::REFERENCED_PERMANENT) 1041 addCacheTableToReferenced( nFileId, i); 1042 else 1043 { 1044 xTab->setReferencedFlag( Table::UNREFERENCED); 1045 rDocReferenced.maTables[i] = false; 1046 rDocReferenced.mbAllTablesReferenced = false; 1047 // An addCacheTableToReferenced() actually may have 1048 // resulted in mbAllReferenced been set. Clear it. 1049 maReferenced.mbAllReferenced = false; 1050 } 1051 } 1052 } 1053 } 1054 } 1055 } 1056 1057 void ScExternalRefCache::addCacheTableToReferenced( sal_uInt16 nFileId, size_t nIndex ) 1058 { 1059 if (nFileId >= maReferenced.maDocs.size()) 1060 return; 1061 1062 ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables; 1063 size_t nTables = rTables.size(); 1064 if (nIndex >= nTables) 1065 return; 1066 1067 if (!rTables[nIndex]) 1068 { 1069 rTables[nIndex] = true; 1070 size_t i = 0; 1071 while (i < nTables && rTables[i]) 1072 ++i; 1073 if (i == nTables) 1074 { 1075 maReferenced.maDocs[nFileId].mbAllTablesReferenced = true; 1076 maReferenced.checkAllDocs(); 1077 } 1078 } 1079 } 1080 1081 void ScExternalRefCache::addCacheDocToReferenced( sal_uInt16 nFileId ) 1082 { 1083 if (nFileId >= maReferenced.maDocs.size()) 1084 return; 1085 1086 if (!maReferenced.maDocs[nFileId].mbAllTablesReferenced) 1087 { 1088 ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables; 1089 size_t nSize = rTables.size(); 1090 for (size_t i=0; i < nSize; ++i) 1091 rTables[i] = true; 1092 maReferenced.maDocs[nFileId].mbAllTablesReferenced = true; 1093 maReferenced.checkAllDocs(); 1094 } 1095 } 1096 1097 bool ScExternalRefCache::areAllCacheTablesReferenced() const 1098 { 1099 return maReferenced.mbAllReferenced; 1100 } 1101 1102 ScExternalRefCache::ReferencedStatus::ReferencedStatus() : 1103 mbAllReferenced(false) 1104 { 1105 reset(0); 1106 } 1107 1108 ScExternalRefCache::ReferencedStatus::ReferencedStatus( size_t nDocs ) : 1109 mbAllReferenced(false) 1110 { 1111 reset( nDocs); 1112 } 1113 1114 void ScExternalRefCache::ReferencedStatus::reset( size_t nDocs ) 1115 { 1116 if (nDocs) 1117 { 1118 mbAllReferenced = false; 1119 DocReferencedVec aRefs( nDocs); 1120 maDocs.swap( aRefs); 1121 } 1122 else 1123 { 1124 mbAllReferenced = true; 1125 DocReferencedVec aRefs; 1126 maDocs.swap( aRefs); 1127 } 1128 } 1129 1130 void ScExternalRefCache::ReferencedStatus::checkAllDocs() 1131 { 1132 for (DocReferencedVec::const_iterator itr = maDocs.begin(); itr != maDocs.end(); ++itr) 1133 { 1134 if (!(*itr).mbAllTablesReferenced) 1135 return; 1136 } 1137 mbAllReferenced = true; 1138 } 1139 1140 ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const 1141 { 1142 DocItem* pDoc = getDocItem(nFileId); 1143 if (!pDoc || nTabIndex >= pDoc->maTables.size()) 1144 return TableTypeRef(); 1145 1146 return pDoc->maTables[nTabIndex]; 1147 } 1148 1149 ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, const String& rTabName, bool bCreateNew, size_t* pnIndex) 1150 { 1151 // In API, the index is transported as cached sheet ID of type sal_Int32 in 1152 // sheet::SingleReference.Sheet or sheet::ComplexReference.Reference1.Sheet 1153 // in a sheet::FormulaToken, choose a sensible value for N/A. Effectively 1154 // being 0xffffffff 1155 const size_t nNotAvailable = static_cast<size_t>( static_cast<sal_Int32>( -1)); 1156 1157 DocItem* pDoc = getDocItem(nFileId); 1158 if (!pDoc) 1159 { 1160 if (pnIndex) *pnIndex = nNotAvailable; 1161 return TableTypeRef(); 1162 } 1163 1164 DocItem& rDoc = *pDoc; 1165 1166 size_t nIndex; 1167 String aTabNameUpper = ScGlobal::pCharClass->upper(rTabName); 1168 if (lcl_getTableDataIndex(rDoc.maTableNameIndex, aTabNameUpper, nIndex)) 1169 { 1170 // specified table found. 1171 if( pnIndex ) *pnIndex = nIndex; 1172 if (bCreateNew && !rDoc.maTables[nIndex]) 1173 rDoc.maTables[nIndex].reset(new Table); 1174 1175 return rDoc.maTables[nIndex]; 1176 } 1177 1178 if (!bCreateNew) 1179 { 1180 if (pnIndex) *pnIndex = nNotAvailable; 1181 return TableTypeRef(); 1182 } 1183 1184 // Specified table doesn't exist yet. Create one. 1185 nIndex = rDoc.maTables.size(); 1186 if( pnIndex ) *pnIndex = nIndex; 1187 TableTypeRef pTab(new Table); 1188 rDoc.maTables.push_back(pTab); 1189 rDoc.maTableNames.push_back(TableName(aTabNameUpper, rTabName)); 1190 rDoc.maTableNameIndex.insert( 1191 TableNameIndexMap::value_type(aTabNameUpper, nIndex)); 1192 return pTab; 1193 } 1194 1195 void ScExternalRefCache::clearCache(sal_uInt16 nFileId) 1196 { 1197 maDocs.erase(nFileId); 1198 } 1199 1200 ScExternalRefCache::DocItem* ScExternalRefCache::getDocItem(sal_uInt16 nFileId) const 1201 { 1202 using ::std::pair; 1203 DocDataType::iterator itrDoc = maDocs.find(nFileId); 1204 if (itrDoc == maDocs.end()) 1205 { 1206 // specified document is not cached. 1207 pair<DocDataType::iterator, bool> res = maDocs.insert( 1208 DocDataType::value_type(nFileId, DocItem())); 1209 1210 if (!res.second) 1211 // insertion failed. 1212 return NULL; 1213 1214 itrDoc = res.first; 1215 } 1216 1217 return &itrDoc->second; 1218 } 1219 1220 // ============================================================================ 1221 1222 ScExternalRefLink::ScExternalRefLink(ScDocument* pDoc, sal_uInt16 nFileId, const String& rFilter) : 1223 ::sfx2::SvBaseLink(::sfx2::LINKUPDATE_ONCALL, FORMAT_FILE), 1224 mnFileId(nFileId), 1225 maFilterName(rFilter), 1226 mpDoc(pDoc), 1227 mbDoRefresh(true) 1228 { 1229 } 1230 1231 ScExternalRefLink::~ScExternalRefLink() 1232 { 1233 } 1234 1235 void ScExternalRefLink::Closed() 1236 { 1237 ScExternalRefManager* pMgr = mpDoc->GetExternalRefManager(); 1238 pMgr->breakLink(mnFileId); 1239 } 1240 1241 void ScExternalRefLink::DataChanged(const String& /*rMimeType*/, const Any& /*rValue*/) 1242 { 1243 if (!mbDoRefresh) 1244 return; 1245 1246 String aFile, aFilter; 1247 mpDoc->GetLinkManager()->GetDisplayNames(this, NULL, &aFile, NULL, &aFilter); 1248 ScExternalRefManager* pMgr = mpDoc->GetExternalRefManager(); 1249 const String* pCurFile = pMgr->getExternalFileName(mnFileId); 1250 if (!pCurFile) 1251 return; 1252 1253 if (pCurFile->Equals(aFile)) 1254 { 1255 // Refresh the current source document. 1256 pMgr->refreshNames(mnFileId); 1257 } 1258 else 1259 { 1260 // The source document has changed. 1261 ScDocShell* pDocShell = ScDocShell::GetViewData()->GetDocShell(); 1262 ScDocShellModificator aMod(*pDocShell); 1263 pMgr->switchSrcFile(mnFileId, aFile, aFilter); 1264 maFilterName = aFilter; 1265 aMod.SetDocumentModified(); 1266 } 1267 } 1268 1269 void ScExternalRefLink::Edit(Window* pParent, const Link& /*rEndEditHdl*/) 1270 { 1271 SvBaseLink::Edit(pParent, LINK(this, ScExternalRefLink, ExternalRefEndEditHdl)); 1272 } 1273 1274 void ScExternalRefLink::SetDoReferesh(bool b) 1275 { 1276 mbDoRefresh = b; 1277 } 1278 1279 IMPL_LINK( ScExternalRefLink, ExternalRefEndEditHdl, ::sfx2::SvBaseLink*, EMPTYARG ) 1280 { 1281 return 0; 1282 } 1283 1284 // ============================================================================ 1285 1286 static FormulaToken* lcl_convertToToken(ScBaseCell* pCell) 1287 { 1288 if (!pCell || pCell->HasEmptyData()) 1289 { 1290 bool bInherited = (pCell && pCell->GetCellType() == CELLTYPE_FORMULA); 1291 return new ScEmptyCellToken( bInherited, false); 1292 } 1293 1294 switch (pCell->GetCellType()) 1295 { 1296 case CELLTYPE_EDIT: 1297 { 1298 String aStr; 1299 static_cast<ScEditCell*>(pCell)->GetString(aStr); 1300 return new formula::FormulaStringToken(aStr); 1301 } 1302 //break; 1303 case CELLTYPE_STRING: 1304 { 1305 String aStr; 1306 static_cast<ScStringCell*>(pCell)->GetString(aStr); 1307 return new formula::FormulaStringToken(aStr); 1308 } 1309 //break; 1310 case CELLTYPE_VALUE: 1311 { 1312 double fVal = static_cast<ScValueCell*>(pCell)->GetValue(); 1313 return new formula::FormulaDoubleToken(fVal); 1314 } 1315 //break; 1316 case CELLTYPE_FORMULA: 1317 { 1318 ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell); 1319 sal_uInt16 nError = pFCell->GetErrCode(); 1320 if (nError) 1321 return new FormulaErrorToken( nError); 1322 else if (pFCell->IsValue()) 1323 { 1324 double fVal = pFCell->GetValue(); 1325 return new formula::FormulaDoubleToken(fVal); 1326 } 1327 else 1328 { 1329 String aStr; 1330 pFCell->GetString(aStr); 1331 return new formula::FormulaStringToken(aStr); 1332 } 1333 } 1334 //break; 1335 default: 1336 DBG_ERROR("attempted to convert an unknown cell type."); 1337 } 1338 1339 return NULL; 1340 } 1341 1342 static ScTokenArray* lcl_convertToTokenArray(ScDocument* pSrcDoc, ScRange& rRange, 1343 vector<ScExternalRefCache::SingleRangeData>& rCacheData) 1344 { 1345 ScAddress& s = rRange.aStart; 1346 ScAddress& e = rRange.aEnd; 1347 1348 SCTAB nTab1 = s.Tab(), nTab2 = e.Tab(); 1349 SCCOL nCol1 = s.Col(), nCol2 = e.Col(); 1350 SCROW nRow1 = s.Row(), nRow2 = e.Row(); 1351 1352 if (nTab2 != nTab1) 1353 // For now, we don't support multi-sheet ranges intentionally because 1354 // we don't have a way to express them in a single token. In the 1355 // future we can introduce a new stack variable type svMatrixList with 1356 // a new token type that can store a 3D matrix value and convert a 3D 1357 // range to it. 1358 return NULL; 1359 1360 ::boost::scoped_ptr<ScRange> pUsedRange; 1361 1362 auto_ptr<ScTokenArray> pArray(new ScTokenArray); 1363 bool bFirstTab = true; 1364 vector<ScExternalRefCache::SingleRangeData>::iterator 1365 itrCache = rCacheData.begin(), itrCacheEnd = rCacheData.end(); 1366 1367 for (SCTAB nTab = nTab1; nTab <= nTab2 && itrCache != itrCacheEnd; ++nTab, ++itrCache) 1368 { 1369 // Only loop within the data area. 1370 SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2; 1371 SCROW nDataRow1 = nRow1, nDataRow2 = nRow2; 1372 bool bShrunk; 1373 if (!pSrcDoc->ShrinkToUsedDataArea( bShrunk, nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2, false)) 1374 // no data within specified range. 1375 continue; 1376 1377 if (pUsedRange.get()) 1378 // Make sure the used area only grows, not shrinks. 1379 pUsedRange->ExtendTo(ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0)); 1380 else 1381 pUsedRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0)); 1382 1383 ScMatrixRef xMat = new ScMatrix( 1384 static_cast<SCSIZE>(nDataCol2-nDataCol1+1), 1385 static_cast<SCSIZE>(nDataRow2-nDataRow1+1)); 1386 1387 for (SCCOL nCol = nDataCol1; nCol <= nDataCol2; ++nCol) 1388 { 1389 for (SCROW nRow = nDataRow1; nRow <= nDataRow2; ++nRow) 1390 { 1391 SCSIZE nC = nCol - nCol1, nR = nRow - nRow1; 1392 ScBaseCell* pCell; 1393 pSrcDoc->GetCell(nCol, nRow, nTab, pCell); 1394 if (!pCell || pCell->HasEmptyData()) 1395 xMat->PutEmpty(nC, nR); 1396 else 1397 { 1398 switch (pCell->GetCellType()) 1399 { 1400 case CELLTYPE_EDIT: 1401 { 1402 String aStr; 1403 static_cast<ScEditCell*>(pCell)->GetString(aStr); 1404 xMat->PutString(aStr, nC, nR); 1405 } 1406 break; 1407 case CELLTYPE_STRING: 1408 { 1409 String aStr; 1410 static_cast<ScStringCell*>(pCell)->GetString(aStr); 1411 xMat->PutString(aStr, nC, nR); 1412 } 1413 break; 1414 case CELLTYPE_VALUE: 1415 { 1416 double fVal = static_cast<ScValueCell*>(pCell)->GetValue(); 1417 xMat->PutDouble(fVal, nC, nR); 1418 } 1419 break; 1420 case CELLTYPE_FORMULA: 1421 { 1422 ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell); 1423 sal_uInt16 nError = pFCell->GetErrCode(); 1424 if (nError) 1425 xMat->PutDouble( CreateDoubleError( nError), nC, nR); 1426 else if (pFCell->IsValue()) 1427 { 1428 double fVal = pFCell->GetValue(); 1429 xMat->PutDouble(fVal, nC, nR); 1430 } 1431 else 1432 { 1433 String aStr; 1434 pFCell->GetString(aStr); 1435 xMat->PutString(aStr, nC, nR); 1436 } 1437 } 1438 break; 1439 default: 1440 DBG_ERROR("attempted to convert an unknown cell type."); 1441 } 1442 } 1443 } 1444 } 1445 if (!bFirstTab) 1446 pArray->AddOpCode(ocSep); 1447 1448 ScMatrix* pMat2 = xMat; 1449 ScMatrixToken aToken(pMat2); 1450 pArray->AddToken(aToken); 1451 1452 itrCache->mpRangeData = xMat; 1453 1454 bFirstTab = false; 1455 } 1456 1457 if (!pUsedRange.get()) 1458 return NULL; 1459 1460 s.SetCol(pUsedRange->aStart.Col()); 1461 s.SetRow(pUsedRange->aStart.Row()); 1462 e.SetCol(pUsedRange->aEnd.Col()); 1463 e.SetRow(pUsedRange->aEnd.Row()); 1464 1465 return pArray.release(); 1466 } 1467 1468 static ScTokenArray* lcl_fillEmptyMatrix(const ScRange& rRange) 1469 { 1470 SCSIZE nC = static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1); 1471 SCSIZE nR = static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1); 1472 ScMatrixRef xMat = new ScMatrix(nC, nR); 1473 for (SCSIZE i = 0; i < nC; ++i) 1474 for (SCSIZE j = 0; j < nR; ++j) 1475 xMat->PutEmpty(i, j); 1476 1477 ScMatrix* pMat2 = xMat; 1478 ScMatrixToken aToken(pMat2); 1479 auto_ptr<ScTokenArray> pArray(new ScTokenArray); 1480 pArray->AddToken(aToken); 1481 return pArray.release(); 1482 } 1483 1484 ScExternalRefManager::ScExternalRefManager(ScDocument* pDoc) : 1485 mpDoc(pDoc), 1486 mbInReferenceMarking(false), 1487 mbUserInteractionEnabled(true) 1488 { 1489 maSrcDocTimer.SetTimeoutHdl( LINK(this, ScExternalRefManager, TimeOutHdl) ); 1490 maSrcDocTimer.SetTimeout(SRCDOC_SCAN_INTERVAL); 1491 } 1492 1493 ScExternalRefManager::~ScExternalRefManager() 1494 { 1495 clear(); 1496 } 1497 1498 String ScExternalRefManager::getCacheTableName(sal_uInt16 nFileId, size_t nTabIndex) const 1499 { 1500 return maRefCache.getTableName(nFileId, nTabIndex); 1501 } 1502 1503 ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const 1504 { 1505 return maRefCache.getCacheTable(nFileId, nTabIndex); 1506 } 1507 1508 ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(sal_uInt16 nFileId, const String& rTabName, bool bCreateNew, size_t* pnIndex) 1509 { 1510 return maRefCache.getCacheTable(nFileId, rTabName, bCreateNew, pnIndex); 1511 } 1512 1513 // ============================================================================ 1514 1515 ScExternalRefManager::LinkListener::LinkListener() 1516 { 1517 } 1518 1519 ScExternalRefManager::LinkListener::~LinkListener() 1520 { 1521 } 1522 1523 // ---------------------------------------------------------------------------- 1524 1525 ScExternalRefManager::ApiGuard::ApiGuard(ScDocument* pDoc) : 1526 mpMgr(pDoc->GetExternalRefManager()), 1527 mbOldInteractionEnabled(mpMgr->mbUserInteractionEnabled) 1528 { 1529 // We don't want user interaction handled in the API. 1530 mpMgr->mbUserInteractionEnabled = false; 1531 } 1532 1533 ScExternalRefManager::ApiGuard::~ApiGuard() 1534 { 1535 // Restore old value. 1536 mpMgr->mbUserInteractionEnabled = mbOldInteractionEnabled; 1537 } 1538 1539 // ---------------------------------------------------------------------------- 1540 1541 void ScExternalRefManager::getAllCachedTableNames(sal_uInt16 nFileId, vector<String>& rTabNames) const 1542 { 1543 maRefCache.getAllTableNames(nFileId, rTabNames); 1544 } 1545 1546 SCsTAB ScExternalRefManager::getCachedTabSpan( sal_uInt16 nFileId, const String& rStartTabName, const String& rEndTabName ) const 1547 { 1548 return maRefCache.getTabSpan( nFileId, rStartTabName, rEndTabName); 1549 } 1550 1551 void ScExternalRefManager::getAllCachedNumberFormats(vector<sal_uInt32>& rNumFmts) const 1552 { 1553 maRefCache.getAllNumberFormats(rNumFmts); 1554 } 1555 1556 sal_uInt16 ScExternalRefManager::getExternalFileCount() const 1557 { 1558 return static_cast< sal_uInt16 >( maSrcFiles.size() ); 1559 } 1560 1561 bool ScExternalRefManager::markUsedByLinkListeners() 1562 { 1563 bool bAllMarked = false; 1564 for (LinkListenerMap::const_iterator itr = maLinkListeners.begin(); 1565 itr != maLinkListeners.end() && !bAllMarked; ++itr) 1566 { 1567 if (!(*itr).second.empty()) 1568 bAllMarked = maRefCache.setCacheDocReferenced( (*itr).first); 1569 /* TODO: LinkListeners should remember the table they're listening to. 1570 * As is, listening to one table will mark all tables of the document 1571 * being referenced. */ 1572 } 1573 return bAllMarked; 1574 } 1575 1576 bool ScExternalRefManager::markUsedExternalRefCells() 1577 { 1578 RefCellMap::iterator itr = maRefCells.begin(), itrEnd = maRefCells.end(); 1579 for (; itr != itrEnd; ++itr) 1580 { 1581 RefCellSet::iterator itrCell = itr->second.begin(), itrCellEnd = itr->second.end(); 1582 for (; itrCell != itrCellEnd; ++itrCell) 1583 { 1584 ScFormulaCell* pCell = *itrCell; 1585 bool bUsed = pCell->MarkUsedExternalReferences(); 1586 if (bUsed) 1587 // Return true when at least one cell references external docs. 1588 return true; 1589 } 1590 } 1591 return false; 1592 } 1593 1594 bool ScExternalRefManager::setCacheTableReferenced( sal_uInt16 nFileId, const String& rTabName, size_t nSheets ) 1595 { 1596 return maRefCache.setCacheTableReferenced( nFileId, rTabName, nSheets, false); 1597 } 1598 1599 void ScExternalRefManager::setCacheTableReferencedPermanently( sal_uInt16 nFileId, const String& rTabName, size_t nSheets ) 1600 { 1601 if (isInReferenceMarking()) 1602 // Do all maintenance work. 1603 maRefCache.setCacheTableReferenced( nFileId, rTabName, nSheets, true); 1604 else 1605 // Set only the permanent flag. 1606 maRefCache.setCacheTableReferencedPermanently( nFileId, rTabName, nSheets); 1607 } 1608 1609 void ScExternalRefManager::setAllCacheTableReferencedStati( bool bReferenced ) 1610 { 1611 mbInReferenceMarking = !bReferenced; 1612 maRefCache.setAllCacheTableReferencedStati( bReferenced ); 1613 } 1614 1615 void ScExternalRefManager::storeRangeNameTokens(sal_uInt16 nFileId, const String& rName, const ScTokenArray& rArray) 1616 { 1617 ScExternalRefCache::TokenArrayRef pArray(rArray.Clone()); 1618 maRefCache.setRangeNameTokens(nFileId, rName, pArray); 1619 } 1620 1621 ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefToken( 1622 sal_uInt16 nFileId, const String& rTabName, const ScAddress& rCell, 1623 const ScAddress* pCurPos, SCTAB* pTab, ScExternalRefCache::CellFormat* pFmt) 1624 { 1625 if (pCurPos) 1626 insertRefCell(nFileId, *pCurPos); 1627 1628 maybeLinkExternalFile(nFileId); 1629 1630 if (pTab) 1631 *pTab = -1; 1632 1633 if (pFmt) 1634 pFmt->mbIsSet = false; 1635 1636 // Check if the given table name and the cell position is cached. 1637 sal_uInt32 nFmtIndex = 0; 1638 ScExternalRefCache::TokenRef pToken = maRefCache.getCellData( 1639 nFileId, rTabName, rCell.Col(), rCell.Row(), &nFmtIndex); 1640 if (pToken) 1641 { 1642 // Cache hit ! 1643 if (pFmt) 1644 { 1645 short nFmtType = mpDoc->GetFormatTable()->GetType(nFmtIndex); 1646 if (nFmtType != NUMBERFORMAT_UNDEFINED) 1647 { 1648 pFmt->mbIsSet = true; 1649 pFmt->mnIndex = nFmtIndex; 1650 pFmt->mnType = nFmtType; 1651 } 1652 } 1653 return pToken; 1654 } 1655 1656 // reference not cached. read from the source document. 1657 ScDocument* pSrcDoc = getSrcDocument(nFileId); 1658 if (!pSrcDoc) 1659 { 1660 // Source document not reachable. Throw a reference error. 1661 pToken.reset(new FormulaErrorToken(errNoRef)); 1662 return pToken; 1663 } 1664 1665 ScBaseCell* pCell = NULL; 1666 SCTAB nTab; 1667 if (!pSrcDoc->GetTable(rTabName, nTab)) 1668 { 1669 // specified table name doesn't exist in the source document. 1670 pToken.reset(new FormulaErrorToken(errNoRef)); 1671 return pToken; 1672 } 1673 1674 if (pTab) 1675 *pTab = nTab; 1676 1677 SCCOL nDataCol1 = 0, nDataCol2 = MAXCOL; 1678 SCROW nDataRow1 = 0, nDataRow2 = MAXROW; 1679 bool bData = pSrcDoc->ShrinkToDataArea(nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2); 1680 if (!bData || rCell.Col() < nDataCol1 || nDataCol2 < rCell.Col() || rCell.Row() < nDataRow1 || nDataRow2 < rCell.Row()) 1681 { 1682 // requested cell is outside the data area. Don't even bother caching 1683 // this data, but add it to the cached range to prevent accessing the 1684 // source document time and time again. 1685 ScExternalRefCache::TableTypeRef pCacheTab = 1686 maRefCache.getCacheTable(nFileId, rTabName, true, NULL); 1687 if (pCacheTab) 1688 pCacheTab->setCachedCell(rCell.Col(), rCell.Row()); 1689 1690 pToken.reset(new ScEmptyCellToken(false, false)); 1691 return pToken; 1692 } 1693 1694 pSrcDoc->GetCell(rCell.Col(), rCell.Row(), nTab, pCell); 1695 ScExternalRefCache::TokenRef pTok(lcl_convertToToken(pCell)); 1696 1697 pSrcDoc->GetNumberFormat(rCell.Col(), rCell.Row(), nTab, nFmtIndex); 1698 nFmtIndex = getMappedNumberFormat(nFileId, nFmtIndex, pSrcDoc); 1699 if (pFmt) 1700 { 1701 short nFmtType = mpDoc->GetFormatTable()->GetType(nFmtIndex); 1702 if (nFmtType != NUMBERFORMAT_UNDEFINED) 1703 { 1704 pFmt->mbIsSet = true; 1705 pFmt->mnIndex = nFmtIndex; 1706 pFmt->mnType = nFmtType; 1707 } 1708 } 1709 1710 if (!pTok.get()) 1711 { 1712 // Generate an error for unresolvable cells. 1713 pTok.reset( new FormulaErrorToken( errNoValue)); 1714 } 1715 1716 // Now, insert the token into cache table but don't cache empty cells. 1717 if (pTok->GetType() != formula::svEmptyCell) 1718 maRefCache.setCellData(nFileId, rTabName, rCell.Col(), rCell.Row(), pTok, nFmtIndex); 1719 1720 return pTok; 1721 } 1722 1723 ScExternalRefCache::TokenArrayRef ScExternalRefManager::getDoubleRefTokens( 1724 sal_uInt16 nFileId, const String& rTabName, const ScRange& rRange, const ScAddress* pCurPos) 1725 { 1726 if (pCurPos) 1727 insertRefCell(nFileId, *pCurPos); 1728 1729 maybeLinkExternalFile(nFileId); 1730 1731 // Check if the given table name and the cell position is cached. 1732 ScExternalRefCache::TokenArrayRef pArray = 1733 maRefCache.getCellRangeData(nFileId, rTabName, rRange); 1734 if (pArray) 1735 // Cache hit ! 1736 return pArray; 1737 1738 ScDocument* pSrcDoc = getSrcDocument(nFileId); 1739 if (!pSrcDoc) 1740 { 1741 // Source document is not reachable. Throw a reference error. 1742 pArray.reset(new ScTokenArray); 1743 pArray->AddToken(FormulaErrorToken(errNoRef)); 1744 return pArray; 1745 } 1746 1747 SCTAB nTab1; 1748 if (!pSrcDoc->GetTable(rTabName, nTab1)) 1749 { 1750 // specified table name doesn't exist in the source document. 1751 pArray.reset(new ScTokenArray); 1752 pArray->AddToken(FormulaErrorToken(errNoRef)); 1753 return pArray; 1754 } 1755 1756 ScRange aRange(rRange); 1757 SCTAB nTabSpan = aRange.aEnd.Tab() - aRange.aStart.Tab(); 1758 1759 vector<ScExternalRefCache::SingleRangeData> aCacheData; 1760 aCacheData.reserve(nTabSpan+1); 1761 aCacheData.push_back(ScExternalRefCache::SingleRangeData()); 1762 aCacheData.back().maTableName = ScGlobal::pCharClass->upper(rTabName); 1763 1764 for (SCTAB i = 1; i < nTabSpan + 1; ++i) 1765 { 1766 String aTabName; 1767 if (!pSrcDoc->GetName(nTab1 + 1, aTabName)) 1768 // source document doesn't have any table by the specified name. 1769 break; 1770 1771 aCacheData.push_back(ScExternalRefCache::SingleRangeData()); 1772 aCacheData.back().maTableName = ScGlobal::pCharClass->upper(aTabName); 1773 } 1774 1775 aRange.aStart.SetTab(nTab1); 1776 aRange.aEnd.SetTab(nTab1 + nTabSpan); 1777 1778 pArray.reset(lcl_convertToTokenArray(pSrcDoc, aRange, aCacheData)); 1779 1780 if (pArray) 1781 // Cache these values. 1782 maRefCache.setCellRangeData(nFileId, aRange, aCacheData, pArray); 1783 else 1784 { 1785 // Array is empty. Fill it with an empty matrix of the required size. 1786 pArray.reset(lcl_fillEmptyMatrix(rRange)); 1787 1788 // Make sure to set this range 'cached', to prevent unnecessarily 1789 // accessing the src document time and time again. 1790 ScExternalRefCache::TableTypeRef pCacheTab = 1791 maRefCache.getCacheTable(nFileId, rTabName, true, NULL); 1792 if (pCacheTab) 1793 pCacheTab->setCachedCellRange( 1794 rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row()); 1795 } 1796 1797 return pArray; 1798 } 1799 1800 ScExternalRefCache::TokenArrayRef ScExternalRefManager::getRangeNameTokens(sal_uInt16 nFileId, const String& rName, const ScAddress* pCurPos) 1801 { 1802 if (pCurPos) 1803 insertRefCell(nFileId, *pCurPos); 1804 1805 maybeLinkExternalFile(nFileId); 1806 1807 ScExternalRefCache::TokenArrayRef pArray = maRefCache.getRangeNameTokens(nFileId, rName); 1808 if (pArray.get()) 1809 return pArray; 1810 1811 ScDocument* pSrcDoc = getSrcDocument(nFileId); 1812 if (!pSrcDoc) 1813 return ScExternalRefCache::TokenArrayRef(); 1814 1815 ScRangeName* pExtNames = pSrcDoc->GetRangeName(); 1816 String aUpperName = ScGlobal::pCharClass->upper(rName); 1817 sal_uInt16 n; 1818 bool bRes = pExtNames->SearchNameUpper(aUpperName, n); 1819 if (!bRes) 1820 return ScExternalRefCache::TokenArrayRef(); 1821 1822 ScRangeData* pRangeData = (*pExtNames)[n]; 1823 if (!pRangeData) 1824 return ScExternalRefCache::TokenArrayRef(); 1825 1826 // Parse all tokens in this external range data, and replace each absolute 1827 // reference token with an external reference token, and cache them. Also 1828 // register the source document with the link manager if it's a new 1829 // source. 1830 1831 ScExternalRefCache::TokenArrayRef pNew(new ScTokenArray); 1832 1833 ScTokenArray* pCode = pRangeData->GetCode(); 1834 for (FormulaToken* pToken = pCode->First(); pToken; pToken = pCode->Next()) 1835 { 1836 bool bTokenAdded = false; 1837 switch (pToken->GetType()) 1838 { 1839 case svSingleRef: 1840 { 1841 const ScSingleRefData& rRef = static_cast<ScToken*>(pToken)->GetSingleRef(); 1842 String aTabName; 1843 pSrcDoc->GetName(rRef.nTab, aTabName); 1844 ScExternalSingleRefToken aNewToken(nFileId, aTabName, static_cast<ScToken*>(pToken)->GetSingleRef()); 1845 pNew->AddToken(aNewToken); 1846 bTokenAdded = true; 1847 } 1848 break; 1849 case svDoubleRef: 1850 { 1851 const ScSingleRefData& rRef = static_cast<ScToken*>(pToken)->GetSingleRef(); 1852 String aTabName; 1853 pSrcDoc->GetName(rRef.nTab, aTabName); 1854 ScExternalDoubleRefToken aNewToken(nFileId, aTabName, static_cast<ScToken*>(pToken)->GetDoubleRef()); 1855 pNew->AddToken(aNewToken); 1856 bTokenAdded = true; 1857 } 1858 break; 1859 default: 1860 ; // nothing 1861 } 1862 1863 if (!bTokenAdded) 1864 pNew->AddToken(*pToken); 1865 } 1866 1867 // Make sure to pass the correctly-cased range name here. 1868 maRefCache.setRangeNameTokens(nFileId, pRangeData->GetName(), pNew); 1869 return pNew; 1870 } 1871 1872 void ScExternalRefManager::refreshAllRefCells(sal_uInt16 nFileId) 1873 { 1874 RefCellMap::iterator itrFile = maRefCells.find(nFileId); 1875 if (itrFile == maRefCells.end()) 1876 return; 1877 1878 RefCellSet& rRefCells = itrFile->second; 1879 for_each(rRefCells.begin(), rRefCells.end(), UpdateFormulaCell()); 1880 1881 ScViewData* pViewData = ScDocShell::GetViewData(); 1882 if (!pViewData) 1883 return; 1884 1885 ScTabViewShell* pVShell = pViewData->GetViewShell(); 1886 if (!pVShell) 1887 return; 1888 1889 // Repainting the grid also repaints the texts, but is there a better way 1890 // to refresh texts? 1891 pVShell->Invalidate(FID_REPAINT); 1892 pVShell->PaintGrid(); 1893 } 1894 1895 void ScExternalRefManager::insertRefCell(sal_uInt16 nFileId, const ScAddress& rCell) 1896 { 1897 RefCellMap::iterator itr = maRefCells.find(nFileId); 1898 if (itr == maRefCells.end()) 1899 { 1900 RefCellSet aRefCells; 1901 pair<RefCellMap::iterator, bool> r = maRefCells.insert( 1902 RefCellMap::value_type(nFileId, aRefCells)); 1903 if (!r.second) 1904 // insertion failed. 1905 return; 1906 1907 itr = r.first; 1908 } 1909 1910 ScBaseCell* pCell = mpDoc->GetCell(rCell); 1911 if (pCell && pCell->GetCellType() == CELLTYPE_FORMULA) 1912 itr->second.insert(static_cast<ScFormulaCell*>(pCell)); 1913 } 1914 1915 ScDocument* ScExternalRefManager::getSrcDocument(sal_uInt16 nFileId) 1916 { 1917 if (!mpDoc->IsExecuteLinkEnabled()) 1918 return NULL; 1919 1920 DocShellMap::iterator itrEnd = maDocShells.end(); 1921 DocShellMap::iterator itr = maDocShells.find(nFileId); 1922 1923 if (itr != itrEnd) 1924 { 1925 // document already loaded. 1926 1927 // TODO: Find out a way to access a document that's already open in 1928 // memory and re-use that instance, instead of loading it from the 1929 // disk again. 1930 1931 SfxObjectShell* p = itr->second.maShell; 1932 itr->second.maLastAccess = Time(); 1933 return static_cast<ScDocShell*>(p)->GetDocument(); 1934 } 1935 1936 const String* pFile = getExternalFileName(nFileId); 1937 if (!pFile) 1938 // no file name associated with this ID. 1939 return NULL; 1940 1941 String aFilter; 1942 SrcShell aSrcDoc; 1943 aSrcDoc.maShell = loadSrcDocument(nFileId, aFilter); 1944 if (!aSrcDoc.maShell.Is()) 1945 { 1946 // source document could not be loaded. 1947 return NULL; 1948 } 1949 1950 if (maDocShells.empty()) 1951 { 1952 // If this is the first source document insertion, start up the timer. 1953 maSrcDocTimer.Start(); 1954 } 1955 1956 maDocShells.insert(DocShellMap::value_type(nFileId, aSrcDoc)); 1957 SfxObjectShell* p = aSrcDoc.maShell; 1958 ScDocument* pSrcDoc = static_cast<ScDocShell*>(p)->GetDocument(); 1959 1960 SCTAB nTabCount = pSrcDoc->GetTableCount(); 1961 if (!maRefCache.isDocInitialized(nFileId) && nTabCount) 1962 { 1963 // Populate the cache with all table names in the source document. 1964 vector<String> aTabNames; 1965 aTabNames.reserve(nTabCount); 1966 for (SCTAB i = 0; i < nTabCount; ++i) 1967 { 1968 String aName; 1969 pSrcDoc->GetName(i, aName); 1970 aTabNames.push_back(aName); 1971 } 1972 maRefCache.initializeDoc(nFileId, aTabNames); 1973 } 1974 return pSrcDoc; 1975 } 1976 1977 SfxObjectShellRef ScExternalRefManager::loadSrcDocument(sal_uInt16 nFileId, String& rFilter) 1978 { 1979 const SrcFileData* pFileData = getExternalFileData(nFileId); 1980 if (!pFileData) 1981 return NULL; 1982 1983 // Always load the document by using the path created from the relative 1984 // path. If the referenced document is not there, simply exit. The 1985 // original file name should be used only when the relative path is not 1986 // given. 1987 String aFile = pFileData->maFileName; 1988 maybeCreateRealFileName(nFileId); 1989 if (pFileData->maRealFileName.Len()) 1990 aFile = pFileData->maRealFileName; 1991 1992 if (!isFileLoadable(aFile)) 1993 return NULL; 1994 1995 String aOptions( pFileData->maFilterOptions ); 1996 if ( pFileData->maFilterName.Len() ) 1997 rFilter = pFileData->maFilterName; // don't overwrite stored filter with guessed filter 1998 else 1999 ScDocumentLoader::GetFilterName(aFile, rFilter, aOptions, true, false); 2000 const SfxFilter* pFilter = ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName(rFilter); 2001 2002 if (!pFileData->maRelativeName.Len()) 2003 { 2004 // Generate a relative file path. 2005 INetURLObject aBaseURL(getOwnDocumentName()); 2006 aBaseURL.insertName(OUString::createFromAscii("content.xml")); 2007 2008 String aStr = URIHelper::simpleNormalizedMakeRelative( 2009 aBaseURL.GetMainURL(INetURLObject::NO_DECODE), aFile); 2010 2011 setRelativeFileName(nFileId, aStr); 2012 } 2013 2014 SfxItemSet* pSet = new SfxAllItemSet(SFX_APP()->GetPool()); 2015 if (aOptions.Len()) 2016 pSet->Put(SfxStringItem(SID_FILE_FILTEROPTIONS, aOptions)); 2017 2018 // make medium hidden to prevent assertion from progress bar 2019 pSet->Put( SfxBoolItem( SID_HIDDEN, sal_True ) ); 2020 2021 auto_ptr<SfxMedium> pMedium(new SfxMedium(aFile, STREAM_STD_READ, false, pFilter, pSet)); 2022 if (pMedium->GetError() != ERRCODE_NONE) 2023 return NULL; 2024 2025 // To load encrypted documents with password, user interaction needs to be enabled. 2026 pMedium->UseInteractionHandler(mbUserInteractionEnabled); 2027 2028 ScDocShell* pNewShell = new ScDocShell(SFX_CREATE_MODE_INTERNAL); 2029 SfxObjectShellRef aRef = pNewShell; 2030 2031 // increment the recursive link count of the source document. 2032 ScExtDocOptions* pExtOpt = mpDoc->GetExtDocOptions(); 2033 sal_uInt32 nLinkCount = pExtOpt ? pExtOpt->GetDocSettings().mnLinkCnt : 0; 2034 ScDocument* pSrcDoc = pNewShell->GetDocument(); 2035 pSrcDoc->EnableExecuteLink(false); // to prevent circular access of external references. 2036 pSrcDoc->EnableUndo(false); 2037 pSrcDoc->EnableAdjustHeight(false); 2038 2039 ScExtDocOptions* pExtOptNew = pSrcDoc->GetExtDocOptions(); 2040 if (!pExtOptNew) 2041 { 2042 pExtOptNew = new ScExtDocOptions; 2043 pSrcDoc->SetExtDocOptions(pExtOptNew); 2044 } 2045 pExtOptNew->GetDocSettings().mnLinkCnt = nLinkCount + 1; 2046 2047 pNewShell->DoLoad(pMedium.release()); 2048 2049 // with UseInteractionHandler, options may be set by dialog during DoLoad 2050 String aNew = ScDocumentLoader::GetOptions(*pNewShell->GetMedium()); 2051 if (aNew.Len() && aNew != aOptions) 2052 aOptions = aNew; 2053 setFilterData(nFileId, rFilter, aOptions); // update the filter data, including the new options 2054 2055 return aRef; 2056 } 2057 2058 bool ScExternalRefManager::isFileLoadable(const String& rFile) const 2059 { 2060 if (!rFile.Len()) 2061 return false; 2062 2063 if (isOwnDocument(rFile)) 2064 return false; 2065 2066 String aPhysical; 2067 if (utl::LocalFileHelper::ConvertURLToPhysicalName(rFile, aPhysical) && aPhysical.Len()) 2068 { 2069 // #i114504# try IsFolder/Exists only for file URLs 2070 2071 if (utl::UCBContentHelper::IsFolder(rFile)) 2072 return false; 2073 2074 return utl::UCBContentHelper::Exists(rFile); 2075 } 2076 else 2077 return true; // for http and others, Exists doesn't work, but the URL can still be opened 2078 } 2079 2080 void ScExternalRefManager::maybeLinkExternalFile(sal_uInt16 nFileId) 2081 { 2082 if (maLinkedDocs.count(nFileId)) 2083 // file alerady linked, or the link has been broken. 2084 return; 2085 2086 // Source document not linked yet. Link it now. 2087 const String* pFileName = getExternalFileName(nFileId); 2088 if (!pFileName) 2089 return; 2090 2091 String aFilter, aOptions; 2092 const SrcFileData* pFileData = getExternalFileData(nFileId); 2093 if (pFileData) 2094 { 2095 aFilter = pFileData->maFilterName; 2096 aOptions = pFileData->maFilterOptions; 2097 } 2098 // If a filter was already set (for example, loading the cached table), 2099 // don't call GetFilterName which has to access the source file. 2100 if (!aFilter.Len()) 2101 ScDocumentLoader::GetFilterName(*pFileName, aFilter, aOptions, true, false); 2102 sfx2::LinkManager* pLinkMgr = mpDoc->GetLinkManager(); 2103 ScExternalRefLink* pLink = new ScExternalRefLink(mpDoc, nFileId, aFilter); 2104 DBG_ASSERT(pFileName, "ScExternalRefManager::insertExternalFileLink: file name pointer is NULL"); 2105 pLinkMgr->InsertFileLink(*pLink, OBJECT_CLIENT_FILE, *pFileName, &aFilter); 2106 2107 pLink->SetDoReferesh(false); 2108 pLink->Update(); 2109 pLink->SetDoReferesh(true); 2110 2111 maLinkedDocs.insert(LinkedDocMap::value_type(nFileId, true)); 2112 } 2113 2114 void ScExternalRefManager::SrcFileData::maybeCreateRealFileName(const String& rOwnDocName) 2115 { 2116 if (!maRelativeName.Len()) 2117 // No relative path given. Nothing to do. 2118 return; 2119 2120 if (maRealFileName.Len()) 2121 // Real file name already created. Nothing to do. 2122 return; 2123 2124 // Formulate the absolute file path from the relative path. 2125 const String& rRelPath = maRelativeName; 2126 INetURLObject aBaseURL(rOwnDocName); 2127 aBaseURL.insertName(OUString::createFromAscii("content.xml")); 2128 bool bWasAbs = false; 2129 maRealFileName = aBaseURL.smartRel2Abs(rRelPath, bWasAbs).GetMainURL(INetURLObject::NO_DECODE); 2130 } 2131 2132 void ScExternalRefManager::maybeCreateRealFileName(sal_uInt16 nFileId) 2133 { 2134 if (nFileId >= maSrcFiles.size()) 2135 return; 2136 2137 maSrcFiles[nFileId].maybeCreateRealFileName(getOwnDocumentName()); 2138 } 2139 2140 const String& ScExternalRefManager::getOwnDocumentName() const 2141 { 2142 SfxObjectShell* pShell = mpDoc->GetDocumentShell(); 2143 if (!pShell) 2144 // This should not happen! 2145 return EMPTY_STRING; 2146 2147 SfxMedium* pMed = pShell->GetMedium(); 2148 if (!pMed) 2149 return EMPTY_STRING; 2150 2151 return pMed->GetName(); 2152 } 2153 2154 bool ScExternalRefManager::isOwnDocument(const String& rFile) const 2155 { 2156 return getOwnDocumentName().Equals(rFile); 2157 } 2158 2159 void ScExternalRefManager::convertToAbsName(String& rFile) const 2160 { 2161 SfxObjectShell* pDocShell = mpDoc->GetDocumentShell(); 2162 rFile = ScGlobal::GetAbsDocName(rFile, pDocShell); 2163 } 2164 2165 sal_uInt16 ScExternalRefManager::getExternalFileId(const String& rFile) 2166 { 2167 vector<SrcFileData>::const_iterator itrBeg = maSrcFiles.begin(), itrEnd = maSrcFiles.end(); 2168 vector<SrcFileData>::const_iterator itr = find_if(itrBeg, itrEnd, FindSrcFileByName(rFile)); 2169 if (itr != itrEnd) 2170 { 2171 size_t nId = distance(itrBeg, itr); 2172 return static_cast<sal_uInt16>(nId); 2173 } 2174 2175 SrcFileData aData; 2176 aData.maFileName = rFile; 2177 maSrcFiles.push_back(aData); 2178 return static_cast<sal_uInt16>(maSrcFiles.size() - 1); 2179 } 2180 2181 const String* ScExternalRefManager::getExternalFileName(sal_uInt16 nFileId, bool bForceOriginal) 2182 { 2183 if (nFileId >= maSrcFiles.size()) 2184 return NULL; 2185 2186 if (bForceOriginal) 2187 return &maSrcFiles[nFileId].maFileName; 2188 2189 maybeCreateRealFileName(nFileId); 2190 2191 if (maSrcFiles[nFileId].maRealFileName.Len()) 2192 return &maSrcFiles[nFileId].maRealFileName; 2193 else 2194 return &maSrcFiles[nFileId].maFileName; 2195 } 2196 2197 bool ScExternalRefManager::hasExternalFile(sal_uInt16 nFileId) const 2198 { 2199 return nFileId < maSrcFiles.size(); 2200 } 2201 2202 bool ScExternalRefManager::hasExternalFile(const String& rFile) const 2203 { 2204 vector<SrcFileData>::const_iterator itrBeg = maSrcFiles.begin(), itrEnd = maSrcFiles.end(); 2205 vector<SrcFileData>::const_iterator itr = find_if(itrBeg, itrEnd, FindSrcFileByName(rFile)); 2206 return itr != itrEnd; 2207 } 2208 2209 const ScExternalRefManager::SrcFileData* ScExternalRefManager::getExternalFileData(sal_uInt16 nFileId) const 2210 { 2211 if (nFileId >= maSrcFiles.size()) 2212 return NULL; 2213 2214 return &maSrcFiles[nFileId]; 2215 } 2216 2217 const String* ScExternalRefManager::getRealTableName(sal_uInt16 nFileId, const String& rTabName) const 2218 { 2219 return maRefCache.getRealTableName(nFileId, rTabName); 2220 } 2221 2222 const String* ScExternalRefManager::getRealRangeName(sal_uInt16 nFileId, const String& rRangeName) const 2223 { 2224 return maRefCache.getRealRangeName(nFileId, rRangeName); 2225 } 2226 2227 template<typename MapContainer> 2228 void lcl_removeByFileId(sal_uInt16 nFileId, MapContainer& rMap) 2229 { 2230 typename MapContainer::iterator itr = rMap.find(nFileId); 2231 if (itr != rMap.end()) 2232 rMap.erase(itr); 2233 } 2234 2235 void ScExternalRefManager::refreshNames(sal_uInt16 nFileId) 2236 { 2237 maRefCache.clearCache(nFileId); 2238 lcl_removeByFileId(nFileId, maDocShells); 2239 2240 if (maDocShells.empty()) 2241 maSrcDocTimer.Stop(); 2242 2243 // Update all cells containing names from this source document. 2244 refreshAllRefCells(nFileId); 2245 2246 notifyAllLinkListeners(nFileId, LINK_MODIFIED); 2247 } 2248 2249 void ScExternalRefManager::breakLink(sal_uInt16 nFileId) 2250 { 2251 // Turn all formula cells referencing this external document into static 2252 // cells. 2253 RefCellMap::iterator itrRefs = maRefCells.find(nFileId); 2254 if (itrRefs != maRefCells.end()) 2255 { 2256 // Make a copy because removing the formula cells below will modify 2257 // the original container. 2258 RefCellSet aSet = itrRefs->second; 2259 for_each(aSet.begin(), aSet.end(), ConvertFormulaToStatic(mpDoc)); 2260 maRefCells.erase(nFileId); 2261 } 2262 2263 lcl_removeByFileId(nFileId, maDocShells); 2264 2265 if (maDocShells.empty()) 2266 maSrcDocTimer.Stop(); 2267 2268 LinkedDocMap::iterator itr = maLinkedDocs.find(nFileId); 2269 if (itr != maLinkedDocs.end()) 2270 itr->second = false; 2271 2272 notifyAllLinkListeners(nFileId, LINK_BROKEN); 2273 } 2274 2275 void ScExternalRefManager::switchSrcFile(sal_uInt16 nFileId, const String& rNewFile, const String& rNewFilter) 2276 { 2277 maSrcFiles[nFileId].maFileName = rNewFile; 2278 maSrcFiles[nFileId].maRelativeName.Erase(); 2279 maSrcFiles[nFileId].maRealFileName.Erase(); 2280 if (!maSrcFiles[nFileId].maFilterName.Equals(rNewFilter)) 2281 { 2282 // Filter type has changed. 2283 maSrcFiles[nFileId].maFilterName = rNewFilter; 2284 maSrcFiles[nFileId].maFilterOptions.Erase(); 2285 } 2286 refreshNames(nFileId); 2287 } 2288 2289 void ScExternalRefManager::setRelativeFileName(sal_uInt16 nFileId, const String& rRelUrl) 2290 { 2291 if (nFileId >= maSrcFiles.size()) 2292 return; 2293 maSrcFiles[nFileId].maRelativeName = rRelUrl; 2294 } 2295 2296 void ScExternalRefManager::setFilterData(sal_uInt16 nFileId, const String& rFilterName, const String& rOptions) 2297 { 2298 if (nFileId >= maSrcFiles.size()) 2299 return; 2300 maSrcFiles[nFileId].maFilterName = rFilterName; 2301 maSrcFiles[nFileId].maFilterOptions = rOptions; 2302 } 2303 2304 void ScExternalRefManager::clear() 2305 { 2306 DocShellMap::iterator itrEnd = maDocShells.end(); 2307 for (DocShellMap::iterator itr = maDocShells.begin(); itr != itrEnd; ++itr) 2308 itr->second.maShell->DoClose(); 2309 2310 maDocShells.clear(); 2311 maSrcDocTimer.Stop(); 2312 } 2313 2314 bool ScExternalRefManager::hasExternalData() const 2315 { 2316 return !maSrcFiles.empty(); 2317 } 2318 2319 void ScExternalRefManager::resetSrcFileData(const String& rBaseFileUrl) 2320 { 2321 for (vector<SrcFileData>::iterator itr = maSrcFiles.begin(), itrEnd = maSrcFiles.end(); 2322 itr != itrEnd; ++itr) 2323 { 2324 // Re-generate relative file name from the absolute file name. 2325 String aAbsName = itr->maRealFileName; 2326 if (!aAbsName.Len()) 2327 aAbsName = itr->maFileName; 2328 2329 itr->maRelativeName = URIHelper::simpleNormalizedMakeRelative( 2330 rBaseFileUrl, aAbsName); 2331 } 2332 } 2333 2334 void ScExternalRefManager::updateAbsAfterLoad() 2335 { 2336 String aOwn( getOwnDocumentName() ); 2337 for (vector<SrcFileData>::iterator itr = maSrcFiles.begin(), itrEnd = maSrcFiles.end(); 2338 itr != itrEnd; ++itr) 2339 { 2340 // update maFileName to the real file name, 2341 // to be called when the original name is no longer needed (after CompileXML) 2342 2343 itr->maybeCreateRealFileName( aOwn ); 2344 String aReal = itr->maRealFileName; 2345 if (aReal.Len()) 2346 itr->maFileName = aReal; 2347 } 2348 } 2349 2350 void ScExternalRefManager::removeRefCell(ScFormulaCell* pCell) 2351 { 2352 for_each(maRefCells.begin(), maRefCells.end(), RemoveFormulaCell(pCell)); 2353 } 2354 2355 void ScExternalRefManager::addLinkListener(sal_uInt16 nFileId, LinkListener* pListener) 2356 { 2357 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId); 2358 if (itr == maLinkListeners.end()) 2359 { 2360 pair<LinkListenerMap::iterator, bool> r = maLinkListeners.insert( 2361 LinkListenerMap::value_type(nFileId, LinkListeners())); 2362 if (!r.second) 2363 { 2364 DBG_ERROR("insertion of new link listener list failed"); 2365 return; 2366 } 2367 2368 itr = r.first; 2369 } 2370 2371 LinkListeners& rList = itr->second; 2372 rList.insert(pListener); 2373 } 2374 2375 void ScExternalRefManager::removeLinkListener(sal_uInt16 nFileId, LinkListener* pListener) 2376 { 2377 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId); 2378 if (itr == maLinkListeners.end()) 2379 // no listeners for a specified file. 2380 return; 2381 2382 LinkListeners& rList = itr->second; 2383 rList.erase(pListener); 2384 2385 if (rList.empty()) 2386 // No more listeners for this file. Remove its entry. 2387 maLinkListeners.erase(itr); 2388 } 2389 2390 void ScExternalRefManager::removeLinkListener(LinkListener* pListener) 2391 { 2392 LinkListenerMap::iterator itr = maLinkListeners.begin(), itrEnd = maLinkListeners.end(); 2393 for (; itr != itrEnd; ++itr) 2394 itr->second.erase(pListener); 2395 } 2396 2397 void ScExternalRefManager::notifyAllLinkListeners(sal_uInt16 nFileId, LinkUpdateType eType) 2398 { 2399 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId); 2400 if (itr == maLinkListeners.end()) 2401 // no listeners for a specified file. 2402 return; 2403 2404 LinkListeners& rList = itr->second; 2405 for_each(rList.begin(), rList.end(), NotifyLinkListener(nFileId, eType)); 2406 } 2407 2408 void ScExternalRefManager::purgeStaleSrcDocument(sal_Int32 nTimeOut) 2409 { 2410 DocShellMap aNewDocShells; 2411 DocShellMap::iterator itr = maDocShells.begin(), itrEnd = maDocShells.end(); 2412 for (; itr != itrEnd; ++itr) 2413 { 2414 // in 100th of a second. 2415 sal_Int32 nSinceLastAccess = (Time() - itr->second.maLastAccess).GetTime(); 2416 if (nSinceLastAccess < nTimeOut) 2417 aNewDocShells.insert(*itr); 2418 } 2419 maDocShells.swap(aNewDocShells); 2420 2421 if (maDocShells.empty()) 2422 maSrcDocTimer.Stop(); 2423 } 2424 2425 sal_uInt32 ScExternalRefManager::getMappedNumberFormat(sal_uInt16 nFileId, sal_uInt32 nNumFmt, ScDocument* pSrcDoc) 2426 { 2427 NumFmtMap::iterator itr = maNumFormatMap.find(nFileId); 2428 if (itr == maNumFormatMap.end()) 2429 { 2430 // Number formatter map is not initialized for this external document. 2431 pair<NumFmtMap::iterator, bool> r = maNumFormatMap.insert( 2432 NumFmtMap::value_type(nFileId, SvNumberFormatterMergeMap())); 2433 2434 if (!r.second) 2435 // insertion failed. 2436 return nNumFmt; 2437 2438 itr = r.first; 2439 mpDoc->GetFormatTable()->MergeFormatter( *pSrcDoc->GetFormatTable()); 2440 SvNumberFormatterMergeMap aMap = mpDoc->GetFormatTable()->ConvertMergeTableToMap(); 2441 itr->second.swap(aMap); 2442 } 2443 const SvNumberFormatterMergeMap& rMap = itr->second; 2444 SvNumberFormatterMergeMap::const_iterator itrNumFmt = rMap.find(nNumFmt); 2445 if (itrNumFmt != rMap.end()) 2446 // mapped value found. 2447 return itrNumFmt->second; 2448 2449 return nNumFmt; 2450 } 2451 2452 IMPL_LINK(ScExternalRefManager, TimeOutHdl, AutoTimer*, pTimer) 2453 { 2454 if (pTimer == &maSrcDocTimer) 2455 purgeStaleSrcDocument(SRCDOC_LIFE_SPAN); 2456 2457 return 0; 2458 } 2459 2460