LibreOffice Module sc (master)  1
externalrefmgr.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <externalrefmgr.hxx>
21 #include <document.hxx>
22 #include <token.hxx>
23 #include <tokenarray.hxx>
24 #include <address.hxx>
25 #include <tablink.hxx>
26 #include <docsh.hxx>
27 #include <scextopt.hxx>
28 #include <rangenam.hxx>
29 #include <formulacell.hxx>
30 #include <viewdata.hxx>
31 #include <tabvwsh.hxx>
32 #include <sc.hrc>
33 #include <globstr.hrc>
34 #include <scresid.hxx>
35 #include <cellvalue.hxx>
36 #include <defaultsoptions.hxx>
37 #include <scmod.hxx>
38 
39 #include <o3tl/safeint.hxx>
40 #include <osl/file.hxx>
41 #include <sfx2/app.hxx>
42 #include <sfx2/docfile.hxx>
43 #include <sfx2/event.hxx>
44 #include <sfx2/fcontnr.hxx>
45 #include <sfx2/objsh.hxx>
46 #include <svl/itemset.hxx>
47 #include <svl/stritem.hxx>
48 #include <svl/urihelper.hxx>
49 #include <svl/sharedstringpool.hxx>
50 #include <sfx2/linkmgr.hxx>
51 #include <tools/urlobj.hxx>
52 #include <unotools/charclass.hxx>
53 #include <unotools/configmgr.hxx>
54 #include <unotools/ucbhelper.hxx>
55 #include <vcl/svapp.hxx>
56 #include <vcl/weld.hxx>
57 #include <stringutil.hxx>
58 #include <scmatrix.hxx>
59 #include <columnspanset.hxx>
60 #include <column.hxx>
61 #include <com/sun/star/document/MacroExecMode.hpp>
62 #include <com/sun/star/document/UpdateDocMode.hpp>
63 #include <sal/log.hxx>
64 
65 #include <memory>
66 #include <algorithm>
67 
68 using ::std::unique_ptr;
69 using ::com::sun::star::uno::Any;
70 using ::std::vector;
71 using ::std::find_if;
72 using ::std::for_each;
73 using ::std::distance;
74 using ::std::pair;
75 using namespace formula;
76 
77 #define SRCDOC_LIFE_SPAN 30000 // 5 minutes (in 100th of a sec)
78 #define SRCDOC_SCAN_INTERVAL 1000*30 // every 30 seconds (in msec)
79 
80 namespace {
81 
82 class TabNameSearchPredicate
83 {
84 public:
85  explicit TabNameSearchPredicate(const OUString& rSearchName) :
86  maSearchName(ScGlobal::getCharClassPtr()->uppercase(rSearchName))
87  {
88  }
89 
90  bool operator()(const ScExternalRefCache::TableName& rTabNameSet) const
91  {
92  // Ok, I'm doing case insensitive search here.
93  return rTabNameSet.maUpperName == maSearchName;
94  }
95 
96 private:
97  OUString maSearchName;
98 };
99 
100 class FindSrcFileByName
101 {
102 public:
103  explicit FindSrcFileByName(const OUString& rMatchName) :
104  mrMatchName(rMatchName)
105  {
106  }
107 
108  bool operator()(const ScExternalRefManager::SrcFileData& rSrcData) const
109  {
110  return rSrcData.maFileName == mrMatchName;
111  }
112 
113 private:
114  const OUString& mrMatchName;
115 };
116 
117 class NotifyLinkListener
118 {
119 public:
120  NotifyLinkListener(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType) :
121  mnFileId(nFileId), meType(eType) {}
122 
123  void operator() (ScExternalRefManager::LinkListener* p) const
124  {
125  p->notify(mnFileId, meType);
126  }
127 private:
128  sal_uInt16 mnFileId;
130 };
131 
132 struct UpdateFormulaCell
133 {
134  void operator() (ScFormulaCell* pCell) const
135  {
136  // Check to make sure the cell really contains svExternal*.
137  // External names, external cell and range references all have a
138  // token of svExternal*. Additionally check for INDIRECT() that can be
139  // called with any constructed URI string.
140  ScTokenArray* pCode = pCell->GetCode();
141  if (!pCode->HasExternalRef() && !pCode->HasOpCode(ocIndirect))
142  return;
143 
144  if (pCode->GetCodeError() != FormulaError::NONE)
145  {
146  // Clear the error code, or a cell with error won't get re-compiled.
147  pCode->SetCodeError(FormulaError::NONE);
148  pCell->SetCompile(true);
149  pCell->CompileTokenArray();
150  }
151 
152  pCell->SetDirty();
153  }
154 };
155 
156 class RemoveFormulaCell
157 {
158 public:
159  explicit RemoveFormulaCell(ScFormulaCell* p) : mpCell(p) {}
160  void operator() (pair<const sal_uInt16, ScExternalRefManager::RefCellSet>& r) const
161  {
162  r.second.erase(mpCell);
163  }
164 private:
166 };
167 
168 class ConvertFormulaToStatic
169 {
170 public:
171  explicit ConvertFormulaToStatic(ScDocument* pDoc) : mpDoc(pDoc) {}
172  void operator() (ScFormulaCell* pCell) const
173  {
174  ScAddress aPos = pCell->aPos;
175 
176  // We don't check for empty cells because empty external cells are
177  // treated as having a value of 0.
178 
179  if (pCell->IsValue())
180  {
181  // Turn this into value cell.
182  mpDoc->SetValue(aPos, pCell->GetValue());
183  }
184  else
185  {
186  // string cell otherwise.
187  ScSetStringParam aParam;
188  aParam.setTextInput();
189  mpDoc->SetString(aPos, pCell->GetString().getString(), &aParam);
190  }
191  }
192 private:
193  ScDocument* mpDoc;
194 };
195 
200 bool hasRefsToSrcDoc(ScRangeData& rData, sal_uInt16 nFileId)
201 {
202  ScTokenArray* pArray = rData.GetCode();
203  if (!pArray)
204  return false;
205 
208  for (; p; p = aIter.GetNextReference())
209  {
210  if (!p->IsExternalRef())
211  continue;
212 
213  if (p->GetIndex() == nFileId)
214  return true;
215  }
216  return false;
217 }
218 
219 class EraseRangeByIterator
220 {
221  ScRangeName& mrRanges;
222 public:
223  explicit EraseRangeByIterator(ScRangeName& rRanges) : mrRanges(rRanges) {}
224  void operator() (const ScRangeName::iterator& itr)
225  {
226  mrRanges.erase(itr);
227  }
228 };
229 
234 void removeRangeNamesBySrcDoc(ScRangeName& rRanges, sal_uInt16 nFileId)
235 {
236  ScRangeName::iterator itr = rRanges.begin(), itrEnd = rRanges.end();
237  vector<ScRangeName::iterator> v;
238  for (; itr != itrEnd; ++itr)
239  {
240  if (hasRefsToSrcDoc(*itr->second, nFileId))
241  v.push_back(itr);
242  }
243  for_each(v.begin(), v.end(), EraseRangeByIterator(rRanges));
244 }
245 
246 }
247 
249  : mbReferenced( true )
250  // Prevent accidental data loss due to lack of knowledge.
251 {
252 }
253 
255 {
256 }
257 
259 {
260  maRows.clear();
261  maCachedRanges.RemoveAll();
262  mbReferenced = true;
263 }
264 
266 {
267  mbReferenced = bReferenced;
268 }
269 
271 {
272  return mbReferenced;
273 }
274 
275 void ScExternalRefCache::Table::setCell(SCCOL nCol, SCROW nRow, TokenRef const & pToken, sal_uLong nFmtIndex, bool bSetCacheRange)
276 {
277  using ::std::pair;
278  RowsDataType::iterator itrRow = maRows.find(nRow);
279  if (itrRow == maRows.end())
280  {
281  // This row does not exist yet.
282  pair<RowsDataType::iterator, bool> res = maRows.emplace(
283  nRow, RowDataType());
284 
285  if (!res.second)
286  return;
287 
288  itrRow = res.first;
289  }
290 
291  // Insert this token into the specified column location. I don't need to
292  // check for existing data. Just overwrite it.
293  RowDataType& rRow = itrRow->second;
295  aCell.mxToken = pToken;
296  aCell.mnFmtIndex = nFmtIndex;
297  rRow.emplace(nCol, aCell);
298  if (bSetCacheRange)
299  setCachedCell(nCol, nRow);
300 }
301 
303 {
304  RowsDataType::const_iterator itrTable = maRows.find(nRow);
305  if (itrTable == maRows.end())
306  {
307  // this table doesn't have the specified row.
308  return getEmptyOrNullToken(nCol, nRow);
309  }
310 
311  const RowDataType& rRowData = itrTable->second;
312  RowDataType::const_iterator itrRow = rRowData.find(nCol);
313  if (itrRow == rRowData.end())
314  {
315  // this row doesn't have the specified column.
316  return getEmptyOrNullToken(nCol, nRow);
317  }
318 
319  const Cell& rCell = itrRow->second;
320  if (pnFmtIndex)
321  *pnFmtIndex = rCell.mnFmtIndex;
322 
323  return rCell.mxToken;
324 }
325 
327 {
328  RowsDataType::const_iterator itrRow = maRows.find(nRow);
329  return itrRow != maRows.end();
330 }
331 
332 void ScExternalRefCache::Table::getAllRows(vector<SCROW>& rRows, SCROW nLow, SCROW nHigh) const
333 {
334  vector<SCROW> aRows;
335  aRows.reserve(maRows.size());
336  for (const auto& rEntry : maRows)
337  if (nLow <= rEntry.first && rEntry.first <= nHigh)
338  aRows.push_back(rEntry.first);
339 
340  // hash map is not ordered, so we need to explicitly sort it.
341  ::std::sort(aRows.begin(), aRows.end());
342  rRows.swap(aRows);
343 }
344 
345 ::std::pair< SCROW, SCROW > ScExternalRefCache::Table::getRowRange() const
346 {
347  ::std::pair< SCROW, SCROW > aRange( 0, 0 );
348  if( !maRows.empty() )
349  {
350  // iterate over entire container (hash map is not sorted by key)
351  auto itMinMax = std::minmax_element(maRows.begin(), maRows.end(),
352  [](const RowsDataType::value_type& a, const RowsDataType::value_type& b) { return a.first < b.first; });
353  aRange.first = itMinMax.first->first;
354  aRange.second = itMinMax.second->first + 1;
355  }
356  return aRange;
357 }
358 
359 void ScExternalRefCache::Table::getAllCols(SCROW nRow, vector<SCCOL>& rCols, SCCOL nLow, SCCOL nHigh) const
360 {
361  RowsDataType::const_iterator itrRow = maRows.find(nRow);
362  if (itrRow == maRows.end())
363  // this table doesn't have the specified row.
364  return;
365 
366  const RowDataType& rRowData = itrRow->second;
367  vector<SCCOL> aCols;
368  aCols.reserve(rRowData.size());
369  for (const auto& rCol : rRowData)
370  if (nLow <= rCol.first && rCol.first <= nHigh)
371  aCols.push_back(rCol.first);
372 
373  // hash map is not ordered, so we need to explicitly sort it.
374  ::std::sort(aCols.begin(), aCols.end());
375  rCols.swap(aCols);
376 }
377 
378 ::std::pair< SCCOL, SCCOL > ScExternalRefCache::Table::getColRange( SCROW nRow ) const
379 {
380  ::std::pair< SCCOL, SCCOL > aRange( 0, 0 );
381 
382  RowsDataType::const_iterator itrRow = maRows.find( nRow );
383  if (itrRow == maRows.end())
384  // this table doesn't have the specified row.
385  return aRange;
386 
387  const RowDataType& rRowData = itrRow->second;
388  if( !rRowData.empty() )
389  {
390  // iterate over entire container (hash map is not sorted by key)
391  auto itMinMax = std::minmax_element(rRowData.begin(), rRowData.end(),
392  [](const RowDataType::value_type& a, const RowDataType::value_type& b) { return a.first < b.first; });
393  aRange.first = itMinMax.first->first;
394  aRange.second = itMinMax.second->first + 1;
395  }
396  return aRange;
397 }
398 
399 void ScExternalRefCache::Table::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const
400 {
401  for (const auto& rRow : maRows)
402  {
403  const RowDataType& rRowData = rRow.second;
404  for (const auto& rCol : rRowData)
405  {
406  const Cell& rCell = rCol.second;
407  rNumFmts.push_back(rCell.mnFmtIndex);
408  }
409  }
410 }
411 
412 bool ScExternalRefCache::Table::isRangeCached(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const
413 {
414  return maCachedRanges.In(ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0));
415 }
416 
418 {
419  setCachedCellRange(nCol, nRow, nCol, nRow);
420 }
421 
423 {
424  ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
425  maCachedRanges.Join(aRange);
426 }
427 
429 {
430  setCachedCellRange(0, 0, MAXCOL, MAXROW);
431 }
432 
434 {
435  return maCachedRanges.In(ScRange(nCol, nRow, 0, nCol, nRow, 0));
436 }
437 
439  SCCOL nCol, SCROW nRow) const
440 {
441  if (isInCachedRanges(nCol, nRow))
442  {
443  TokenRef p(new ScEmptyCellToken(false, false));
444  return p;
445  }
446  return TokenRef();
447 }
448 
449 ScExternalRefCache::TableName::TableName(const OUString& rUpper, const OUString& rReal) :
450  maUpperName(rUpper), maRealName(rReal)
451 {
452 }
453 
455  mbIsSet(false), mnType(SvNumFormatType::ALL), mnIndex(0)
456 {
457 }
458 
460  : mxFakeDoc(new ScDocument())
461 {}
462 
464 
465 const OUString* ScExternalRefCache::getRealTableName(sal_uInt16 nFileId, const OUString& rTabName) const
466 {
467  osl::MutexGuard aGuard(&maMtxDocs);
468 
469  DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
470  if (itrDoc == maDocs.end())
471  {
472  // specified document is not cached.
473  return nullptr;
474  }
475 
476  const DocItem& rDoc = itrDoc->second;
477  TableNameIndexMap::const_iterator itrTabId = rDoc.findTableNameIndex( rTabName);
478  if (itrTabId == rDoc.maTableNameIndex.end())
479  {
480  // the specified table is not in cache.
481  return nullptr;
482  }
483 
484  return &rDoc.maTableNames[itrTabId->second].maRealName;
485 }
486 
487 const OUString* ScExternalRefCache::getRealRangeName(sal_uInt16 nFileId, const OUString& rRangeName) const
488 {
489  osl::MutexGuard aGuard(&maMtxDocs);
490 
491  DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
492  if (itrDoc == maDocs.end())
493  {
494  // specified document is not cached.
495  return nullptr;
496  }
497 
498  const DocItem& rDoc = itrDoc->second;
499  NamePairMap::const_iterator itr = rDoc.maRealRangeNameMap.find(
500  ScGlobal::getCharClassPtr()->uppercase(rRangeName));
501  if (itr == rDoc.maRealRangeNameMap.end())
502  // range name not found.
503  return nullptr;
504 
505  return &itr->second;
506 }
507 
509  sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex)
510 {
511  osl::MutexGuard aGuard(&maMtxDocs);
512 
513  DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
514  if (itrDoc == maDocs.end())
515  {
516  // specified document is not cached.
517  return TokenRef();
518  }
519 
520  const DocItem& rDoc = itrDoc->second;
521  TableNameIndexMap::const_iterator itrTabId = rDoc.findTableNameIndex( rTabName);
522  if (itrTabId == rDoc.maTableNameIndex.end())
523  {
524  // the specified table is not in cache.
525  return TokenRef();
526  }
527 
528  const TableTypeRef& pTableData = rDoc.maTables[itrTabId->second];
529  if (!pTableData)
530  {
531  // the table data is not instantiated yet.
532  return TokenRef();
533  }
534 
535  return pTableData->getCell(nCol, nRow, pnFmtIndex);
536 }
537 
539  sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange)
540 {
541  osl::MutexGuard aGuard(&maMtxDocs);
542 
543  DocDataType::iterator itrDoc = maDocs.find(nFileId);
544  if (itrDoc == maDocs.end())
545  // specified document is not cached.
546  return TokenArrayRef();
547 
548  DocItem& rDoc = itrDoc->second;
549 
550  TableNameIndexMap::const_iterator itrTabId = rDoc.findTableNameIndex( rTabName);
551  if (itrTabId == rDoc.maTableNameIndex.end())
552  // the specified table is not in cache.
553  return TokenArrayRef();
554 
555  const ScAddress& s = rRange.aStart;
556  const ScAddress& e = rRange.aEnd;
557 
558  const SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
559  const SCCOL nCol1 = s.Col(), nCol2 = e.Col();
560  const SCROW nRow1 = s.Row(), nRow2 = e.Row();
561 
562  // Make sure I have all the tables cached.
563  size_t nTabFirstId = itrTabId->second;
564  size_t nTabLastId = nTabFirstId + nTab2 - nTab1;
565  if (nTabLastId >= rDoc.maTables.size())
566  // not all tables are cached.
567  return TokenArrayRef();
568 
569  ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId));
570 
571  RangeArrayMap::const_iterator itrRange = rDoc.maRangeArrays.find( aCacheRange);
572  if (itrRange != rDoc.maRangeArrays.end())
573  // Cache hit!
574  return itrRange->second;
575 
576  std::unique_ptr<ScRange> pNewRange;
577  TokenArrayRef pArray;
578  bool bFirstTab = true;
579  for (size_t nTab = nTabFirstId; nTab <= nTabLastId; ++nTab)
580  {
581  TableTypeRef pTab = rDoc.maTables[nTab];
582  if (!pTab)
583  return TokenArrayRef();
584 
585  SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2;
586  SCROW nDataRow1 = nRow1, nDataRow2 = nRow2;
587 
588  if (!pTab->isRangeCached(nDataCol1, nDataRow1, nDataCol2, nDataRow2))
589  {
590  // specified range is not entirely within cached ranges.
591  return TokenArrayRef();
592  }
593 
594  SCSIZE nMatrixColumns = static_cast<SCSIZE>(nDataCol2-nDataCol1+1);
595  SCSIZE nMatrixRows = static_cast<SCSIZE>(nDataRow2-nDataRow1+1);
596  ScMatrixRef xMat = new ScMatrix( nMatrixColumns, nMatrixRows);
597 
598  // Needed in shrink and fill.
599  vector<SCROW> aRows;
600  pTab->getAllRows(aRows, nDataRow1, nDataRow2);
601  bool bFill = true;
602 
603  // Check if size could be allocated and if not skip the fill, there's
604  // one error element instead. But retry first with the actual data area
605  // if that is smaller than the original range, which works for most
606  // functions just not some that operate/compare with the original size
607  // and expect empty values in non-data areas.
608  // Restrict this though to ranges of entire columns or rows, other
609  // ranges might be on purpose. (Other special cases to handle?)
610  /* TODO: sparse matrix could help */
611  SCSIZE nMatCols, nMatRows;
612  xMat->GetDimensions( nMatCols, nMatRows);
613  if (nMatCols != nMatrixColumns || nMatRows != nMatrixRows)
614  {
615  bFill = false;
616  if (aRows.empty())
617  {
618  // There's no data at all. Set the one matrix element to empty
619  // for column-repeated and row-repeated access.
620  xMat->PutEmpty(0,0);
621  }
622  else if ((nCol1 == 0 && nCol2 == MAXCOL) || (nRow1 == 0 && nRow2 == MAXROW))
623  {
624  nDataRow1 = aRows.front();
625  nDataRow2 = aRows.back();
626  SCCOL nMinCol = std::numeric_limits<SCCOL>::max();
627  SCCOL nMaxCol = std::numeric_limits<SCCOL>::min();
628  for (const auto& rRow : aRows)
629  {
630  vector<SCCOL> aCols;
631  pTab->getAllCols(rRow, aCols, nDataCol1, nDataCol2);
632  if (!aCols.empty())
633  {
634  nMinCol = std::min( nMinCol, aCols.front());
635  nMaxCol = std::max( nMaxCol, aCols.back());
636  }
637  }
638 
639  if (nMinCol <= nMaxCol && ((o3tl::make_unsigned(nMaxCol-nMinCol+1) < nMatrixColumns) ||
640  (o3tl::make_unsigned(nDataRow2-nDataRow1+1) < nMatrixRows)))
641  {
642  nMatrixColumns = static_cast<SCSIZE>(nMaxCol-nMinCol+1);
643  nMatrixRows = static_cast<SCSIZE>(nDataRow2-nDataRow1+1);
644  xMat = new ScMatrix( nMatrixColumns, nMatrixRows);
645  xMat->GetDimensions( nMatCols, nMatRows);
646  if (nMatCols == nMatrixColumns && nMatRows == nMatrixRows)
647  {
648  nDataCol1 = nMinCol;
649  nDataCol2 = nMaxCol;
650  bFill = true;
651  }
652  }
653  }
654  }
655 
656  if (bFill)
657  {
658  // Only fill non-empty cells, for better performance.
659  for (SCROW nRow : aRows)
660  {
661  vector<SCCOL> aCols;
662  pTab->getAllCols(nRow, aCols, nDataCol1, nDataCol2);
663  for (SCCOL nCol : aCols)
664  {
665  TokenRef pToken = pTab->getCell(nCol, nRow);
666  if (!pToken)
667  // This should never happen!
668  return TokenArrayRef();
669 
670  SCSIZE nC = nCol - nDataCol1, nR = nRow - nDataRow1;
671  switch (pToken->GetType())
672  {
673  case svDouble:
674  xMat->PutDouble(pToken->GetDouble(), nC, nR);
675  break;
676  case svString:
677  xMat->PutString(pToken->GetString(), nC, nR);
678  break;
679  default:
680  ;
681  }
682  }
683  }
684 
685  if (!bFirstTab)
686  pArray->AddOpCode(ocSep);
687 
688  ScMatrixToken aToken(xMat);
689  if (!pArray)
690  pArray = std::make_shared<ScTokenArray>(*mxFakeDoc);
691  pArray->AddToken(aToken);
692 
693  bFirstTab = false;
694 
695  if (!pNewRange)
696  pNewRange.reset(new ScRange(nDataCol1, nDataRow1, nTab, nDataCol2, nDataRow2, nTab));
697  else
698  pNewRange->ExtendTo(ScRange(nDataCol1, nDataRow1, nTab, nDataCol2, nDataRow2, nTab));
699  }
700  }
701 
702  rDoc.maRangeArrays.emplace(aCacheRange, pArray);
703  if (pNewRange && *pNewRange != aCacheRange)
704  rDoc.maRangeArrays.emplace(*pNewRange, pArray);
705 
706  return pArray;
707 }
708 
710 {
711  osl::MutexGuard aGuard(&maMtxDocs);
712 
713  DocItem* pDoc = getDocItem(nFileId);
714  if (!pDoc)
715  return TokenArrayRef();
716 
717  RangeNameMap& rMap = pDoc->maRangeNames;
718  RangeNameMap::const_iterator itr = rMap.find(
719  ScGlobal::getCharClassPtr()->uppercase(rName));
720  if (itr == rMap.end())
721  return TokenArrayRef();
722 
723  return itr->second;
724 }
725 
726 void ScExternalRefCache::setRangeNameTokens(sal_uInt16 nFileId, const OUString& rName, TokenArrayRef pArray)
727 {
728  osl::MutexGuard aGuard(&maMtxDocs);
729 
730  DocItem* pDoc = getDocItem(nFileId);
731  if (!pDoc)
732  return;
733 
734  OUString aUpperName = ScGlobal::getCharClassPtr()->uppercase(rName);
735  RangeNameMap& rMap = pDoc->maRangeNames;
736  rMap.emplace(aUpperName, pArray);
737  pDoc->maRealRangeNameMap.emplace(aUpperName, rName);
738 }
739 
740 bool ScExternalRefCache::isValidRangeName(sal_uInt16 nFileId, const OUString& rName) const
741 {
742  osl::MutexGuard aGuard(&maMtxDocs);
743 
744  DocItem* pDoc = getDocItem(nFileId);
745  if (!pDoc)
746  return false;
747 
748  const RangeNameMap& rMap = pDoc->maRangeNames;
749  return rMap.count(rName) > 0;
750 }
751 
752 void ScExternalRefCache::setRangeName(sal_uInt16 nFileId, const OUString& rName)
753 {
754  osl::MutexGuard aGuard(&maMtxDocs);
755 
756  DocItem* pDoc = getDocItem(nFileId);
757  if (!pDoc)
758  return;
759 
760  OUString aUpperName = ScGlobal::getCharClassPtr()->uppercase(rName);
761  pDoc->maRealRangeNameMap.emplace(aUpperName, rName);
762 }
763 
764 void ScExternalRefCache::setCellData(sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow,
765  TokenRef const & pToken, sal_uLong nFmtIndex)
766 {
767  if (!isDocInitialized(nFileId))
768  return;
769 
770  using ::std::pair;
771  DocItem* pDocItem = getDocItem(nFileId);
772  if (!pDocItem)
773  return;
774 
775  DocItem& rDoc = *pDocItem;
776 
777  // See if the table by this name already exists.
778  TableNameIndexMap::const_iterator itrTabName = rDoc.findTableNameIndex( rTabName);
779  if (itrTabName == rDoc.maTableNameIndex.end())
780  // Table not found. Maybe the table name or the file id is wrong ???
781  return;
782 
783  TableTypeRef& pTableData = rDoc.maTables[itrTabName->second];
784  if (!pTableData)
785  pTableData = std::make_shared<Table>();
786 
787  pTableData->setCell(nCol, nRow, pToken, nFmtIndex);
788  pTableData->setCachedCell(nCol, nRow);
789 }
790 
791 void ScExternalRefCache::setCellRangeData(sal_uInt16 nFileId, const ScRange& rRange, const vector<SingleRangeData>& rData,
792  const TokenArrayRef& pArray)
793 {
794  using ::std::pair;
795  if (rData.empty() || !isDocInitialized(nFileId))
796  // nothing to cache
797  return;
798 
799  // First, get the document item for the given file ID.
800  DocItem* pDocItem = getDocItem(nFileId);
801  if (!pDocItem)
802  return;
803 
804  DocItem& rDoc = *pDocItem;
805 
806  // Now, find the table position of the first table to cache.
807  const OUString& rFirstTabName = rData.front().maTableName;
808  TableNameIndexMap::const_iterator itrTabName = rDoc.findTableNameIndex( rFirstTabName);
809  if (itrTabName == rDoc.maTableNameIndex.end())
810  {
811  // table index not found.
812  return;
813  }
814 
815  size_t nTabFirstId = itrTabName->second;
816  SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
817  SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
818  size_t i = nTabFirstId;
819  for (const auto& rItem : rData)
820  {
821  TableTypeRef& pTabData = rDoc.maTables[i];
822  if (!pTabData)
823  pTabData = std::make_shared<Table>();
824 
825  const ScMatrixRef& pMat = rItem.mpRangeData;
826  SCSIZE nMatCols, nMatRows;
827  pMat->GetDimensions( nMatCols, nMatRows);
828  if (nMatCols > o3tl::make_unsigned(nCol2 - nCol1) && nMatRows > o3tl::make_unsigned(nRow2 - nRow1))
829  {
830  ScMatrix::DoubleOpFunction aDoubleFunc = [=](size_t row, size_t col, double val) -> void
831  {
832  pTabData->setCell(col + nCol1, row + nRow1, new formula::FormulaDoubleToken(val), 0, false);
833  };
834  ScMatrix::BoolOpFunction aBoolFunc = [=](size_t row, size_t col, bool val) -> void
835  {
836  pTabData->setCell(col + nCol1, row + nRow1, new formula::FormulaDoubleToken(val ? 1.0 : 0.0), 0, false);
837  };
838  ScMatrix::StringOpFunction aStringFunc = [=](size_t row, size_t col, svl::SharedString val) -> void
839  {
840  pTabData->setCell(col + nCol1, row + nRow1, new formula::FormulaStringToken(val), 0, false);
841  };
842  ScMatrix::EmptyOpFunction aEmptyFunc = [=](size_t /*row*/, size_t /*col*/) -> void
843  {
844  // Nothing. Empty cell.
845  };
846  pMat->ExecuteOperation(std::pair<size_t, size_t>(0, 0),
847  std::pair<size_t, size_t>(nRow2-nRow1, nCol2-nCol1),
848  aDoubleFunc, aBoolFunc, aStringFunc, aEmptyFunc);
849  // Mark the whole range 'cached'.
850  pTabData->setCachedCellRange(nCol1, nRow1, nCol2, nRow2);
851  }
852  else
853  {
854  // This may happen due to a matrix not been allocated earlier, in
855  // which case it should have exactly one error element.
856  SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - matrix size mismatch");
857  if (nMatCols != 1 || nMatRows != 1)
858  SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - not a one element matrix");
859  else
860  {
861  FormulaError nErr = GetDoubleErrorValue( pMat->GetDouble(0,0));
862  SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - matrix error value is " << static_cast<int>(nErr) <<
863  (nErr == FormulaError::MatrixSize ? ", ok" : ", not ok"));
864  }
865  }
866  ++i;
867  }
868 
869  size_t nTabLastId = nTabFirstId + rRange.aEnd.Tab() - rRange.aStart.Tab();
870  ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId));
871 
872  rDoc.maRangeArrays.emplace(aCacheRange, pArray);
873 }
874 
875 bool ScExternalRefCache::isDocInitialized(sal_uInt16 nFileId)
876 {
877  DocItem* pDoc = getDocItem(nFileId);
878  if (!pDoc)
879  return false;
880 
881  return pDoc->mbInitFromSource;
882 }
883 
884 static bool lcl_getStrictTableDataIndex(const ScExternalRefCache::TableNameIndexMap& rMap, const OUString& rName, size_t& rIndex)
885 {
886  ScExternalRefCache::TableNameIndexMap::const_iterator itr = rMap.find(rName);
887  if (itr == rMap.end())
888  return false;
889 
890  rIndex = itr->second;
891  return true;
892 }
893 
894 bool ScExternalRefCache::DocItem::getTableDataIndex( const OUString& rTabName, size_t& rIndex ) const
895 {
896  ScExternalRefCache::TableNameIndexMap::const_iterator itr = findTableNameIndex(rTabName);
897  if (itr == maTableNameIndex.end())
898  return false;
899 
900  rIndex = itr->second;
901  return true;
902 }
903 
904 namespace {
905 OUString getFirstSheetName()
906 {
907  // Get Custom prefix.
908  const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
909  // Form sheet name identical to the first generated sheet name when
910  // creating an internal document, e.g. 'Sheet1'.
911  return rOpt.GetInitTabPrefix() + "1";
912 }
913 }
914 
915 void ScExternalRefCache::initializeDoc(sal_uInt16 nFileId, const vector<OUString>& rTabNames,
916  const OUString& rBaseName)
917 {
918  DocItem* pDoc = getDocItem(nFileId);
919  if (!pDoc)
920  return;
921 
922  size_t n = rTabNames.size();
923 
924  // table name list - the list must include all table names in the source
925  // document and only to be populated when loading the source document, not
926  // when loading cached data from, say, Excel XCT/CRN records.
927  vector<TableName> aNewTabNames;
928  aNewTabNames.reserve(n);
929  for (const auto& rTabName : rTabNames)
930  {
931  TableName aNameItem(ScGlobal::getCharClassPtr()->uppercase(rTabName), rTabName);
932  aNewTabNames.push_back(aNameItem);
933  }
934  pDoc->maTableNames.swap(aNewTabNames);
935 
936  // data tables - preserve any existing data that may have been set during
937  // file import.
938  vector<TableTypeRef> aNewTables(n);
939  for (size_t i = 0; i < n; ++i)
940  {
941  size_t nIndex;
942  if (lcl_getStrictTableDataIndex(pDoc->maTableNameIndex, pDoc->maTableNames[i].maUpperName, nIndex))
943  {
944  aNewTables[i] = pDoc->maTables[nIndex];
945  }
946  }
947  pDoc->maTables.swap(aNewTables);
948 
949  // name index map
950  TableNameIndexMap aNewNameIndex;
951  for (size_t i = 0; i < n; ++i)
952  aNewNameIndex.emplace(pDoc->maTableNames[i].maUpperName, i);
953  pDoc->maTableNameIndex.swap(aNewNameIndex);
954 
955  // Setup name for Sheet1 vs base name to be able to load documents
956  // that store the base name as table name, or vice versa.
957  pDoc->maSingleTableNameAlias.clear();
958  if (!rBaseName.isEmpty() && pDoc->maTableNames.size() == 1)
959  {
960  OUString aSheetName = getFirstSheetName();
961  // If the one and only table name matches exactly, carry on the base
962  // file name for further alias use. If instead the table name matches
963  // the base name, carry on the sheet name as alias.
964  if (ScGlobal::GetpTransliteration()->isEqual( pDoc->maTableNames[0].maRealName, aSheetName))
965  pDoc->maSingleTableNameAlias = rBaseName;
966  else if (ScGlobal::GetpTransliteration()->isEqual( pDoc->maTableNames[0].maRealName, rBaseName))
967  pDoc->maSingleTableNameAlias = aSheetName;
968  }
969 
970  pDoc->mbInitFromSource = true;
971 }
972 
973 ScExternalRefCache::TableNameIndexMap::const_iterator ScExternalRefCache::DocItem::findTableNameIndex(
974  const OUString& rTabName ) const
975 {
976  const OUString aTabNameUpper = ScGlobal::getCharClassPtr()->uppercase( rTabName);
977  TableNameIndexMap::const_iterator itrTabName = maTableNameIndex.find( aTabNameUpper);
978  if (itrTabName != maTableNameIndex.end())
979  return itrTabName;
980 
981  // Since some time for external references to CSV files the base name is
982  // used as sheet name instead of Sheet1, check if we can resolve that.
983  // Also helps users that got accustomed to one or the other way.
984  if (maSingleTableNameAlias.isEmpty() || maTableNameIndex.size() != 1)
985  return itrTabName;
986 
987  // maSingleTableNameAlias has been set up only if the original file loaded
988  // had exactly one sheet and internal sheet name was Sheet1 or localized or
989  // customized equivalent, or base name.
990  if (aTabNameUpper == ScGlobal::getCharClassPtr()->uppercase( maSingleTableNameAlias))
991  return maTableNameIndex.begin();
992 
993  return itrTabName;
994 }
995 
997 {
998  if (maSingleTableNameAlias.isEmpty() || maTableNames.size() != 1)
999  return false;
1000  if (ScGlobal::GetpTransliteration()->isEqual( rTabName, maTableNames[0].maRealName))
1001  {
1002  rTabName = maSingleTableNameAlias;
1003  return true;
1004  }
1005  if (ScGlobal::GetpTransliteration()->isEqual( rTabName, maSingleTableNameAlias))
1006  {
1007  rTabName = maTableNames[0].maRealName;
1008  return true;
1009  }
1010  return false;
1011 }
1012 
1013 bool ScExternalRefCache::getSrcDocTable( const ScDocument& rSrcDoc, const OUString& rTabName, SCTAB& rTab,
1014  sal_uInt16 nFileId ) const
1015 {
1016  bool bFound = rSrcDoc.GetTable( rTabName, rTab);
1017  if (!bFound)
1018  {
1019  // Check the one table alias alternative.
1020  const DocItem* pDoc = getDocItem( nFileId );
1021  if (pDoc)
1022  {
1023  OUString aTabName( rTabName);
1024  if (pDoc->getSingleTableNameAlternative( aTabName))
1025  bFound = rSrcDoc.GetTable( aTabName, rTab);
1026  }
1027  }
1028  return bFound;
1029 }
1030 
1031 OUString ScExternalRefCache::getTableName(sal_uInt16 nFileId, size_t nCacheId) const
1032 {
1033  if( DocItem* pDoc = getDocItem( nFileId ) )
1034  if( nCacheId < pDoc->maTableNames.size() )
1035  return pDoc->maTableNames[ nCacheId ].maRealName;
1036  return EMPTY_OUSTRING;
1037 }
1038 
1039 void ScExternalRefCache::getAllTableNames(sal_uInt16 nFileId, vector<OUString>& rTabNames) const
1040 {
1041  rTabNames.clear();
1042  DocItem* pDoc = getDocItem(nFileId);
1043  if (!pDoc)
1044  return;
1045 
1046  size_t n = pDoc->maTableNames.size();
1047  rTabNames.reserve(n);
1048  for (const auto& rTableName : pDoc->maTableNames)
1049  rTabNames.push_back(rTableName.maRealName);
1050 }
1051 
1052 SCTAB ScExternalRefCache::getTabSpan( sal_uInt16 nFileId, const OUString& rStartTabName, const OUString& rEndTabName ) const
1053 {
1054  DocItem* pDoc = getDocItem(nFileId);
1055  if (!pDoc)
1056  return -1;
1057 
1058  vector<TableName>::const_iterator itrBeg = pDoc->maTableNames.begin();
1059  vector<TableName>::const_iterator itrEnd = pDoc->maTableNames.end();
1060 
1061  vector<TableName>::const_iterator itrStartTab = ::std::find_if( itrBeg, itrEnd,
1062  TabNameSearchPredicate( rStartTabName));
1063  if (itrStartTab == itrEnd)
1064  return -1;
1065 
1066  vector<TableName>::const_iterator itrEndTab = ::std::find_if( itrBeg, itrEnd,
1067  TabNameSearchPredicate( rEndTabName));
1068  if (itrEndTab == itrEnd)
1069  return 0;
1070 
1071  size_t nStartDist = ::std::distance( itrBeg, itrStartTab);
1072  size_t nEndDist = ::std::distance( itrBeg, itrEndTab);
1073  return nStartDist <= nEndDist ? static_cast<SCTAB>(nEndDist - nStartDist + 1) : -static_cast<SCTAB>(nStartDist - nEndDist + 1);
1074 }
1075 
1076 void ScExternalRefCache::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const
1077 {
1078  osl::MutexGuard aGuard(&maMtxDocs);
1079 
1080  using ::std::sort;
1081  using ::std::unique;
1082 
1083  vector<sal_uInt32> aNumFmts;
1084  for (const auto& rEntry : maDocs)
1085  {
1086  const vector<TableTypeRef>& rTables = rEntry.second.maTables;
1087  for (const TableTypeRef& pTab : rTables)
1088  {
1089  if (!pTab)
1090  continue;
1091 
1092  pTab->getAllNumberFormats(aNumFmts);
1093  }
1094  }
1095 
1096  // remove duplicates.
1097  sort(aNumFmts.begin(), aNumFmts.end());
1098  aNumFmts.erase(unique(aNumFmts.begin(), aNumFmts.end()), aNumFmts.end());
1099  rNumFmts.swap(aNumFmts);
1100 }
1101 
1103 {
1104  DocItem* pDocItem = getDocItem(nFileId);
1105  if (!pDocItem)
1106  return areAllCacheTablesReferenced();
1107 
1108  for (auto& rxTab : pDocItem->maTables)
1109  {
1110  if (rxTab)
1111  rxTab->setReferenced(true);
1112  }
1113  addCacheDocToReferenced( nFileId);
1114  return areAllCacheTablesReferenced();
1115 }
1116 
1117 bool ScExternalRefCache::setCacheTableReferenced( sal_uInt16 nFileId, const OUString& rTabName, size_t nSheets )
1118 {
1119  DocItem* pDoc = getDocItem(nFileId);
1120  if (pDoc)
1121  {
1122  size_t nIndex = 0;
1123  if (pDoc->getTableDataIndex( rTabName, nIndex))
1124  {
1125  size_t nStop = ::std::min( nIndex + nSheets, pDoc->maTables.size());
1126  for (size_t i = nIndex; i < nStop; ++i)
1127  {
1128  TableTypeRef pTab = pDoc->maTables[i];
1129  if (pTab)
1130  {
1131  if (!pTab->isReferenced())
1132  {
1133  pTab->setReferenced(true);
1134  addCacheTableToReferenced( nFileId, i);
1135  }
1136  }
1137  }
1138  }
1139  }
1140  return areAllCacheTablesReferenced();
1141 }
1142 
1144 {
1145  osl::MutexGuard aGuard(&maMtxDocs);
1146 
1147  if (bReferenced)
1148  {
1149  maReferenced.reset(0);
1150  for (auto& rEntry : maDocs)
1151  {
1152  ScExternalRefCache::DocItem& rDocItem = rEntry.second;
1153  for (auto& rxTab : rDocItem.maTables)
1154  {
1155  if (rxTab)
1156  rxTab->setReferenced(true);
1157  }
1158  }
1159  }
1160  else
1161  {
1162  size_t nDocs = 0;
1163  auto itrMax = std::max_element(maDocs.begin(), maDocs.end(),
1164  [](const DocDataType::value_type& a, const DocDataType::value_type& b) { return a.first < b.first; });
1165  if (itrMax != maDocs.end())
1166  nDocs = itrMax->first + 1;
1167  maReferenced.reset( nDocs);
1168 
1169  for (auto& [nFileId, rDocItem] : maDocs)
1170  {
1171  size_t nTables = rDocItem.maTables.size();
1172  ReferencedStatus::DocReferenced & rDocReferenced = maReferenced.maDocs[nFileId];
1173  // All referenced => non-existing tables evaluate as completed.
1174  rDocReferenced.maTables.resize( nTables, true);
1175  for (size_t i=0; i < nTables; ++i)
1176  {
1177  TableTypeRef & xTab = rDocItem.maTables[i];
1178  if (xTab)
1179  {
1180  xTab->setReferenced(false);
1181  rDocReferenced.maTables[i] = false;
1182  rDocReferenced.mbAllTablesReferenced = false;
1183  // An addCacheTableToReferenced() actually may have
1184  // resulted in mbAllReferenced been set. Clear it.
1185  maReferenced.mbAllReferenced = false;
1186  }
1187  }
1188  }
1189  }
1190 }
1191 
1192 void ScExternalRefCache::addCacheTableToReferenced( sal_uInt16 nFileId, size_t nIndex )
1193 {
1194  if (nFileId >= maReferenced.maDocs.size())
1195  return;
1196 
1197  ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables;
1198  size_t nTables = rTables.size();
1199  if (nIndex >= nTables)
1200  return;
1201 
1202  if (!rTables[nIndex])
1203  {
1204  rTables[nIndex] = true;
1205  size_t i = 0;
1206  while (i < nTables && rTables[i])
1207  ++i;
1208  if (i == nTables)
1209  {
1210  maReferenced.maDocs[nFileId].mbAllTablesReferenced = true;
1212  }
1213  }
1214 }
1215 
1217 {
1218  if (nFileId >= maReferenced.maDocs.size())
1219  return;
1220 
1221  if (!maReferenced.maDocs[nFileId].mbAllTablesReferenced)
1222  {
1223  ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables;
1224  size_t nSize = rTables.size();
1225  for (size_t i=0; i < nSize; ++i)
1226  rTables[i] = true;
1227  maReferenced.maDocs[nFileId].mbAllTablesReferenced = true;
1229  }
1230 }
1231 
1232 void ScExternalRefCache::getAllCachedDataSpans( const ScDocument& rSrcDoc, sal_uInt16 nFileId, sc::ColumnSpanSet& rSet ) const
1233 {
1234  const DocItem* pDocItem = getDocItem(nFileId);
1235  if (!pDocItem)
1236  // This document is not cached.
1237  return;
1238 
1239  const std::vector<TableTypeRef>& rTables = pDocItem->maTables;
1240  for (size_t nTab = 0, nTabCount = rTables.size(); nTab < nTabCount; ++nTab)
1241  {
1242  TableTypeRef pTab = rTables[nTab];
1243  if (!pTab)
1244  continue;
1245 
1246  std::vector<SCROW> aRows;
1247  pTab->getAllRows(aRows);
1248  for (SCROW nRow : aRows)
1249  {
1250  std::vector<SCCOL> aCols;
1251  pTab->getAllCols(nRow, aCols);
1252  for (SCCOL nCol : aCols)
1253  {
1254  rSet.set(rSrcDoc, nTab, nCol, nRow, true);
1255  }
1256  }
1257  }
1258 }
1259 
1261  mbAllReferenced(false)
1262 {
1263  reset(0);
1264 }
1265 
1267 {
1268  if (nDocs)
1269  {
1270  mbAllReferenced = false;
1271  DocReferencedVec aRefs( nDocs);
1272  maDocs.swap( aRefs);
1273  }
1274  else
1275  {
1276  mbAllReferenced = true;
1277  DocReferencedVec aRefs;
1278  maDocs.swap( aRefs);
1279  }
1280 }
1281 
1283 {
1284  if (std::all_of(maDocs.begin(), maDocs.end(), [](const DocReferenced& rDoc) { return rDoc.mbAllTablesReferenced; }))
1285  mbAllReferenced = true;
1286 }
1287 
1288 ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
1289 {
1290  DocItem* pDoc = getDocItem(nFileId);
1291  if (!pDoc || nTabIndex >= pDoc->maTables.size())
1292  return TableTypeRef();
1293 
1294  return pDoc->maTables[nTabIndex];
1295 }
1296 
1297 ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, const OUString& rTabName,
1298  bool bCreateNew, size_t* pnIndex, const OUString* pExtUrl)
1299 {
1300  // In API, the index is transported as cached sheet ID of type sal_Int32 in
1301  // sheet::SingleReference.Sheet or sheet::ComplexReference.Reference1.Sheet
1302  // in a sheet::FormulaToken, choose a sensible value for N/A. Effectively
1303  // being 0xffffffff
1304  const size_t nNotAvailable = static_cast<size_t>( static_cast<sal_Int32>( -1));
1305 
1306  DocItem* pDoc = getDocItem(nFileId);
1307  if (!pDoc)
1308  {
1309  if (pnIndex) *pnIndex = nNotAvailable;
1310  return TableTypeRef();
1311  }
1312 
1313  DocItem& rDoc = *pDoc;
1314 
1315  size_t nIndex;
1316  if (rDoc.getTableDataIndex(rTabName, nIndex))
1317  {
1318  // specified table found.
1319  if( pnIndex ) *pnIndex = nIndex;
1320  if (bCreateNew && !rDoc.maTables[nIndex])
1321  rDoc.maTables[nIndex] = std::make_shared<Table>();
1322 
1323  return rDoc.maTables[nIndex];
1324  }
1325 
1326  if (!bCreateNew)
1327  {
1328  if (pnIndex) *pnIndex = nNotAvailable;
1329  return TableTypeRef();
1330  }
1331 
1332  // If this is the first table to be created propagate the base name or
1333  // Sheet1 as an alias. For subsequent tables remove it again.
1334  if (rDoc.maTableNames.empty())
1335  {
1336  if (pExtUrl)
1337  {
1338  const OUString aBaseName( INetURLObject( *pExtUrl).GetBase());
1339  const OUString aSheetName( getFirstSheetName());
1340  if (ScGlobal::GetpTransliteration()->isEqual( rTabName, aSheetName))
1341  pDoc->maSingleTableNameAlias = aBaseName;
1342  else if (ScGlobal::GetpTransliteration()->isEqual( rTabName, aBaseName))
1343  pDoc->maSingleTableNameAlias = aSheetName;
1344  }
1345  }
1346  else
1347  {
1348  rDoc.maSingleTableNameAlias.clear();
1349  }
1350 
1351  // Specified table doesn't exist yet. Create one.
1352  OUString aTabNameUpper = ScGlobal::getCharClassPtr()->uppercase(rTabName);
1353  nIndex = rDoc.maTables.size();
1354  if( pnIndex ) *pnIndex = nIndex;
1355  TableTypeRef pTab = std::make_shared<Table>();
1356  rDoc.maTables.push_back(pTab);
1357  rDoc.maTableNames.emplace_back(aTabNameUpper, rTabName);
1358  rDoc.maTableNameIndex.emplace(aTabNameUpper, nIndex);
1359  return pTab;
1360 }
1361 
1362 void ScExternalRefCache::clearCache(sal_uInt16 nFileId)
1363 {
1364  osl::MutexGuard aGuard(&maMtxDocs);
1365  maDocs.erase(nFileId);
1366 }
1367 
1369 {
1370  osl::MutexGuard aGuard(&maMtxDocs);
1371  DocItem* pDocItem = getDocItem(nFileId);
1372  if (!pDocItem)
1373  // This document is not cached at all.
1374  return;
1375 
1376  // Clear all cache table content, but keep the tables.
1377  std::vector<TableTypeRef>& rTabs = pDocItem->maTables;
1378  for (TableTypeRef & pTab : rTabs)
1379  {
1380  if (!pTab)
1381  continue;
1382 
1383  pTab->clear();
1384  }
1385 
1386  // Clear the external range name caches.
1387  pDocItem->maRangeNames.clear();
1388  pDocItem->maRangeArrays.clear();
1389  pDocItem->maRealRangeNameMap.clear();
1390 }
1391 
1393 {
1394  osl::MutexGuard aGuard(&maMtxDocs);
1395 
1396  using ::std::pair;
1397  DocDataType::iterator itrDoc = maDocs.find(nFileId);
1398  if (itrDoc == maDocs.end())
1399  {
1400  // specified document is not cached.
1401  pair<DocDataType::iterator, bool> res = maDocs.emplace(
1402  nFileId, DocItem());
1403 
1404  if (!res.second)
1405  // insertion failed.
1406  return nullptr;
1407 
1408  itrDoc = res.first;
1409  }
1410 
1411  return &itrDoc->second;
1412 }
1413 
1416  mnFileId(nFileId),
1417  mrDoc(rDoc),
1418  mbDoRefresh(true)
1419 {
1420 }
1421 
1423 {
1424 }
1425 
1427 {
1429  pMgr->breakLink(mnFileId);
1430 }
1431 
1432 ::sfx2::SvBaseLink::UpdateResult ScExternalRefLink::DataChanged(const OUString& /*rMimeType*/, const Any& /*rValue*/)
1433 {
1434  if (!mbDoRefresh)
1435  return SUCCESS;
1436 
1437  OUString aFile, aFilter;
1438  sfx2::LinkManager::GetDisplayNames(this, nullptr, &aFile, nullptr, &aFilter);
1440 
1441  if (!pMgr->isFileLoadable(aFile))
1442  return ERROR_GENERAL;
1443 
1444  const OUString* pCurFile = pMgr->getExternalFileName(mnFileId);
1445  if (!pCurFile)
1446  return ERROR_GENERAL;
1447 
1448  if (*pCurFile == aFile)
1449  {
1450  // Refresh the current source document.
1451  if (!pMgr->refreshSrcDocument(mnFileId))
1452  return ERROR_GENERAL;
1453  }
1454  else
1455  {
1456  // The source document has changed.
1458  ScDocShellModificator aMod(*pDocShell);
1459  pMgr->switchSrcFile(mnFileId, aFile, aFilter);
1460  aMod.SetDocumentModified();
1461  }
1462 
1463  return SUCCESS;
1464 }
1465 
1466 void ScExternalRefLink::Edit(weld::Window* pParent, const Link<SvBaseLink&,void>& /*rEndEditHdl*/)
1467 {
1468  SvBaseLink::Edit(pParent, Link<SvBaseLink&,void>());
1469 }
1470 
1472 {
1473  mbDoRefresh = b;
1474 }
1475 
1476 static FormulaToken* convertToToken( ScDocument& rHostDoc, const ScDocument& rSrcDoc, ScRefCellValue& rCell )
1477 {
1478  if (rCell.hasEmptyValue())
1479  {
1480  bool bInherited = (rCell.meType == CELLTYPE_FORMULA);
1481  return new ScEmptyCellToken(bInherited, false);
1482  }
1483 
1484  switch (rCell.meType)
1485  {
1486  case CELLTYPE_EDIT:
1487  case CELLTYPE_STRING:
1488  {
1489  OUString aStr = rCell.getString(&rSrcDoc);
1490  svl::SharedString aSS = rHostDoc.GetSharedStringPool().intern(aStr);
1491  return new formula::FormulaStringToken(aSS);
1492  }
1493  case CELLTYPE_VALUE:
1494  return new formula::FormulaDoubleToken(rCell.mfValue);
1495  case CELLTYPE_FORMULA:
1496  {
1497  ScFormulaCell* pFCell = rCell.mpFormula;
1498  FormulaError nError = pFCell->GetErrCode();
1499  if (nError != FormulaError::NONE)
1500  return new FormulaErrorToken( nError);
1501  else if (pFCell->IsValue())
1502  {
1503  double fVal = pFCell->GetValue();
1504  return new formula::FormulaDoubleToken(fVal);
1505  }
1506  else
1507  {
1508  svl::SharedString aSS = rHostDoc.GetSharedStringPool().intern( pFCell->GetString().getString());
1509  return new formula::FormulaStringToken(aSS);
1510  }
1511  }
1512  default:
1513  OSL_FAIL("attempted to convert an unknown cell type.");
1514  }
1515 
1516  return nullptr;
1517 }
1518 
1519 static std::unique_ptr<ScTokenArray> convertToTokenArray(
1520  ScDocument& rHostDoc, const ScDocument& rSrcDoc, ScRange& rRange, vector<ScExternalRefCache::SingleRangeData>& rCacheData )
1521 {
1522  ScAddress& s = rRange.aStart;
1523  ScAddress& e = rRange.aEnd;
1524 
1525  const SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
1526  const SCCOL nCol1 = s.Col(), nCol2 = e.Col();
1527  const SCROW nRow1 = s.Row(), nRow2 = e.Row();
1528 
1529  if (nTab2 != nTab1)
1530  // For now, we don't support multi-sheet ranges intentionally because
1531  // we don't have a way to express them in a single token. In the
1532  // future we can introduce a new stack variable type svMatrixList with
1533  // a new token type that can store a 3D matrix value and convert a 3D
1534  // range to it.
1535  return nullptr;
1536 
1537  std::unique_ptr<ScRange> pUsedRange;
1538 
1539  unique_ptr<ScTokenArray> pArray(new ScTokenArray(rSrcDoc));
1540  bool bFirstTab = true;
1541  vector<ScExternalRefCache::SingleRangeData>::iterator
1542  itrCache = rCacheData.begin(), itrCacheEnd = rCacheData.end();
1543 
1544  for (SCTAB nTab = nTab1; nTab <= nTab2 && itrCache != itrCacheEnd; ++nTab, ++itrCache)
1545  {
1546  // Only loop within the data area.
1547  SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2;
1548  SCROW nDataRow1 = nRow1, nDataRow2 = nRow2;
1549  bool bShrunk;
1550  if (!rSrcDoc.ShrinkToUsedDataArea( bShrunk, nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2, false))
1551  // no data within specified range.
1552  continue;
1553 
1554  if (pUsedRange)
1555  // Make sure the used area only grows, not shrinks.
1556  pUsedRange->ExtendTo(ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
1557  else
1558  pUsedRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
1559 
1560  SCSIZE nMatrixColumns = static_cast<SCSIZE>(nCol2-nCol1+1);
1561  SCSIZE nMatrixRows = static_cast<SCSIZE>(nRow2-nRow1+1);
1562  ScMatrixRef xMat = new ScMatrix( nMatrixColumns, nMatrixRows);
1563 
1564  // Check if size could be allocated and if not skip the fill, there's
1565  // one error element instead. But retry first with the actual data area
1566  // if that is smaller than the original range, which works for most
1567  // functions just not some that operate/compare with the original size
1568  // and expect empty values in non-data areas.
1569  // Restrict this though to ranges of entire columns or rows, other
1570  // ranges might be on purpose. (Other special cases to handle?)
1571  /* TODO: sparse matrix could help */
1572  SCSIZE nMatCols, nMatRows;
1573  xMat->GetDimensions( nMatCols, nMatRows);
1574  if (nMatCols == nMatrixColumns && nMatRows == nMatrixRows)
1575  {
1576  rSrcDoc.FillMatrix(*xMat, nTab, nCol1, nRow1, nCol2, nRow2, &rHostDoc.GetSharedStringPool());
1577  }
1578  else if ((nCol1 == 0 && nCol2 == MAXCOL) || (nRow1 == 0 && nRow2 == MAXROW))
1579  {
1580  if ((o3tl::make_unsigned(nDataCol2-nDataCol1+1) < nMatrixColumns) ||
1581  (o3tl::make_unsigned(nDataRow2-nDataRow1+1) < nMatrixRows))
1582  {
1583  nMatrixColumns = static_cast<SCSIZE>(nDataCol2-nDataCol1+1);
1584  nMatrixRows = static_cast<SCSIZE>(nDataRow2-nDataRow1+1);
1585  xMat = new ScMatrix( nMatrixColumns, nMatrixRows);
1586  xMat->GetDimensions( nMatCols, nMatRows);
1587  if (nMatCols == nMatrixColumns && nMatRows == nMatrixRows)
1588  rSrcDoc.FillMatrix(*xMat, nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2, &rHostDoc.GetSharedStringPool());
1589  }
1590  }
1591 
1592  if (!bFirstTab)
1593  pArray->AddOpCode(ocSep);
1594 
1595  ScMatrixToken aToken(xMat);
1596  pArray->AddToken(aToken);
1597 
1598  itrCache->mpRangeData = xMat;
1599 
1600  bFirstTab = false;
1601  }
1602 
1603  if (!pUsedRange)
1604  return nullptr;
1605 
1606  s.SetCol(pUsedRange->aStart.Col());
1607  s.SetRow(pUsedRange->aStart.Row());
1608  e.SetCol(pUsedRange->aEnd.Col());
1609  e.SetRow(pUsedRange->aEnd.Row());
1610 
1611  return pArray;
1612 }
1613 
1614 static std::unique_ptr<ScTokenArray> lcl_fillEmptyMatrix(const ScDocument& rDoc, const ScRange& rRange)
1615 {
1616  SCSIZE nC = static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1);
1617  SCSIZE nR = static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1);
1618  ScMatrixRef xMat = new ScMatrix(nC, nR);
1619 
1620  ScMatrixToken aToken(xMat);
1621  unique_ptr<ScTokenArray> pArray(new ScTokenArray(rDoc));
1622  pArray->AddToken(aToken);
1623  return pArray;
1624 }
1625 
1626 namespace {
1627 bool isLinkUpdateAllowedInDoc(const ScDocument& rDoc)
1628 {
1629  SfxObjectShell* pDocShell = rDoc.GetDocumentShell();
1630  if (!pDocShell)
1631  return false;
1632 
1634 }
1635 }
1636 
1638  mrDoc(rDoc),
1639  mbInReferenceMarking(false),
1640  mbUserInteractionEnabled(true),
1641  mbDocTimerEnabled(true)
1642 {
1645  maSrcDocTimer.SetDebugName( "sc::ScExternalRefManager maSrcDocTimer" );
1646 }
1647 
1649 {
1650  clear();
1651 }
1652 
1653 OUString ScExternalRefManager::getCacheTableName(sal_uInt16 nFileId, size_t nTabIndex) const
1654 {
1655  return maRefCache.getTableName(nFileId, nTabIndex);
1656 }
1657 
1659 {
1660  return maRefCache.getCacheTable(nFileId, nTabIndex);
1661 }
1662 
1664  sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, size_t* pnIndex, const OUString* pExtUrl)
1665 {
1666  return maRefCache.getCacheTable(nFileId, rTabName, bCreateNew, pnIndex, pExtUrl);
1667 }
1668 
1670 {
1671 }
1672 
1674 {
1675 }
1676 
1678  mpMgr(rDoc.GetExternalRefManager()),
1679  mbOldInteractionEnabled(mpMgr->mbUserInteractionEnabled)
1680 {
1681  // We don't want user interaction handled in the API.
1683 }
1684 
1686 {
1687  // Restore old value.
1688  mpMgr->mbUserInteractionEnabled = mbOldInteractionEnabled;
1689 }
1690 
1691 void ScExternalRefManager::getAllCachedTableNames(sal_uInt16 nFileId, vector<OUString>& rTabNames) const
1692 {
1693  maRefCache.getAllTableNames(nFileId, rTabNames);
1694 }
1695 
1696 SCTAB ScExternalRefManager::getCachedTabSpan( sal_uInt16 nFileId, const OUString& rStartTabName, const OUString& rEndTabName ) const
1697 {
1698  return maRefCache.getTabSpan( nFileId, rStartTabName, rEndTabName);
1699 }
1700 
1701 void ScExternalRefManager::getAllCachedNumberFormats(vector<sal_uInt32>& rNumFmts) const
1702 {
1703  maRefCache.getAllNumberFormats(rNumFmts);
1704 }
1705 
1707 {
1708  return static_cast< sal_uInt16 >( maSrcFiles.size() );
1709 }
1710 
1712 {
1713  bool bAllMarked = false;
1714  for (const auto& [rFileId, rLinkListeners] : maLinkListeners)
1715  {
1716  if (!rLinkListeners.empty())
1717  bAllMarked = maRefCache.setCacheDocReferenced(rFileId);
1718 
1719  if (bAllMarked)
1720  break;
1721  /* TODO: LinkListeners should remember the table they're listening to.
1722  * As is, listening to one table will mark all tables of the document
1723  * being referenced. */
1724  }
1725 }
1726 
1728 {
1729  for (const auto& rEntry : maRefCells)
1730  {
1731  for (ScFormulaCell* pCell : rEntry.second)
1732  {
1733  bool bUsed = pCell->MarkUsedExternalReferences();
1734  if (bUsed)
1735  // Return true when at least one cell references external docs.
1736  return;
1737  }
1738  }
1739 }
1740 
1741 bool ScExternalRefManager::setCacheTableReferenced( sal_uInt16 nFileId, const OUString& rTabName, size_t nSheets )
1742 {
1743  return maRefCache.setCacheTableReferenced( nFileId, rTabName, nSheets);
1744 }
1745 
1747 {
1748  mbInReferenceMarking = !bReferenced;
1750 }
1751 
1752 void ScExternalRefManager::storeRangeNameTokens(sal_uInt16 nFileId, const OUString& rName, const ScTokenArray& rArray)
1753 {
1754  ScExternalRefCache::TokenArrayRef pArray(rArray.Clone());
1755  maRefCache.setRangeNameTokens(nFileId, rName, pArray);
1756 }
1757 
1758 namespace {
1759 
1766 void putCellDataIntoCache(
1767  ScExternalRefCache& rRefCache, const ScExternalRefCache::TokenRef& pToken,
1768  sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell,
1769  const ScExternalRefCache::CellFormat* pFmt)
1770 {
1771  // Now, insert the token into cache table but don't cache empty cells.
1772  if (pToken->GetType() != formula::svEmptyCell)
1773  {
1774  sal_uLong nFmtIndex = (pFmt && pFmt->mbIsSet) ? pFmt->mnIndex : 0;
1775  rRefCache.setCellData(nFileId, rTabName, rCell.Col(), rCell.Row(), pToken, nFmtIndex);
1776  }
1777 }
1778 
1792 void putRangeDataIntoCache(
1794  sal_uInt16 nFileId, const OUString& rTabName,
1795  const vector<ScExternalRefCache::SingleRangeData>& rCacheData,
1796  const ScRange& rCacheRange, const ScRange& rDataRange)
1797 {
1798  if (pArray)
1799  // Cache these values.
1800  rRefCache.setCellRangeData(nFileId, rDataRange, rCacheData, pArray);
1801  else
1802  {
1803  // Array is empty. Fill it with an empty matrix of the required size.
1804  pArray = lcl_fillEmptyMatrix(*rRefCache.getFakeDoc(), rCacheRange);
1805 
1806  // Make sure to set this range 'cached', to prevent unnecessarily
1807  // accessing the src document time and time again.
1809  rRefCache.getCacheTable(nFileId, rTabName, true, nullptr, nullptr);
1810  if (pCacheTab)
1811  pCacheTab->setCachedCellRange(
1812  rCacheRange.aStart.Col(), rCacheRange.aStart.Row(), rCacheRange.aEnd.Col(), rCacheRange.aEnd.Row());
1813  }
1814 }
1815 
1826 void initDocInCache(ScExternalRefCache& rRefCache, const ScDocument* pSrcDoc, sal_uInt16 nFileId)
1827 {
1828  if (!pSrcDoc)
1829  return;
1830 
1831  if (rRefCache.isDocInitialized(nFileId))
1832  // Already initialized. No need to do this twice.
1833  return;
1834 
1835  SCTAB nTabCount = pSrcDoc->GetTableCount();
1836  if (!nTabCount)
1837  return;
1838 
1839  // Populate the cache with all table names in the source document.
1840  vector<OUString> aTabNames;
1841  aTabNames.reserve(nTabCount);
1842  for (SCTAB i = 0; i < nTabCount; ++i)
1843  {
1844  OUString aName;
1845  pSrcDoc->GetName(i, aName);
1846  aTabNames.push_back(aName);
1847  }
1848 
1849  // Obtain the base name, don't bother if there are more than one sheets.
1850  OUString aBaseName;
1851  if (nTabCount == 1)
1852  {
1853  const SfxObjectShell* pShell = pSrcDoc->GetDocumentShell();
1854  if (pShell && pShell->GetMedium())
1855  {
1856  OUString aName = pShell->GetMedium()->GetName();
1857  aBaseName = INetURLObject( aName).GetBase();
1858  }
1859  }
1860 
1861  rRefCache.initializeDoc(nFileId, aTabNames, aBaseName);
1862 }
1863 
1864 }
1865 
1866 bool ScExternalRefManager::getSrcDocTable( const ScDocument& rSrcDoc, const OUString& rTabName, SCTAB& rTab,
1867  sal_uInt16 nFileId ) const
1868 {
1869  return maRefCache.getSrcDocTable( rSrcDoc, rTabName, rTab, nFileId);
1870 }
1871 
1873  sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell,
1874  const ScAddress* pCurPos, SCTAB* pTab, ScExternalRefCache::CellFormat* pFmt)
1875 {
1876  if (pCurPos)
1877  insertRefCell(nFileId, *pCurPos);
1878 
1879  maybeLinkExternalFile(nFileId);
1880 
1881  if (pTab)
1882  *pTab = -1;
1883 
1884  if (pFmt)
1885  pFmt->mbIsSet = false;
1886 
1887  ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
1888  if (pSrcDoc)
1889  {
1890  // source document already loaded in memory. Re-use this instance.
1891  SCTAB nTab;
1892  if (!getSrcDocTable( *pSrcDoc, rTabName, nTab, nFileId))
1893  {
1894  // specified table name doesn't exist in the source document.
1895  ScExternalRefCache::TokenRef pToken(new FormulaErrorToken(FormulaError::NoRef));
1896  return pToken;
1897  }
1898 
1899  if (pTab)
1900  *pTab = nTab;
1901 
1904  nFileId, *pSrcDoc, ScAddress(rCell.Col(),rCell.Row(),nTab), pFmt);
1905 
1906  putCellDataIntoCache(maRefCache, pToken, nFileId, rTabName, rCell, pFmt);
1907  return pToken;
1908  }
1909 
1910  // Check if the given table name and the cell position is cached.
1911  sal_uInt32 nFmtIndex = 0;
1913  nFileId, rTabName, rCell.Col(), rCell.Row(), &nFmtIndex);
1914  if (pToken)
1915  {
1916  // Cache hit !
1917  fillCellFormat(nFmtIndex, pFmt);
1918  return pToken;
1919  }
1920 
1921  // reference not cached. read from the source document.
1922  pSrcDoc = getSrcDocument(nFileId);
1923  if (!pSrcDoc)
1924  {
1925  // Source document not reachable.
1926  if (!isLinkUpdateAllowedInDoc(mrDoc))
1927  {
1928  // Indicate with specific error.
1929  pToken.reset(new FormulaErrorToken(FormulaError::LinkFormulaNeedingCheck));
1930  }
1931  else
1932  {
1933  // Throw a reference error.
1934  pToken.reset(new FormulaErrorToken(FormulaError::NoRef));
1935  }
1936  return pToken;
1937  }
1938 
1939  SCTAB nTab;
1940  if (!getSrcDocTable( *pSrcDoc, rTabName, nTab, nFileId))
1941  {
1942  // specified table name doesn't exist in the source document.
1943  pToken.reset(new FormulaErrorToken(FormulaError::NoRef));
1944  return pToken;
1945  }
1946 
1947  if (pTab)
1948  *pTab = nTab;
1949 
1950  SCCOL nDataCol1 = 0, nDataCol2 = MAXCOL;
1951  SCROW nDataRow1 = 0, nDataRow2 = MAXROW;
1952  bool bData = pSrcDoc->ShrinkToDataArea(nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2);
1953  if (!bData || rCell.Col() < nDataCol1 || nDataCol2 < rCell.Col() || rCell.Row() < nDataRow1 || nDataRow2 < rCell.Row())
1954  {
1955  // requested cell is outside the data area. Don't even bother caching
1956  // this data, but add it to the cached range to prevent accessing the
1957  // source document time and time again.
1959  maRefCache.getCacheTable(nFileId, rTabName, true, nullptr, nullptr);
1960  if (pCacheTab)
1961  pCacheTab->setCachedCell(rCell.Col(), rCell.Row());
1962 
1963  pToken.reset(new ScEmptyCellToken(false, false));
1964  return pToken;
1965  }
1966 
1967  pToken = getSingleRefTokenFromSrcDoc(
1968  nFileId, *pSrcDoc, ScAddress(rCell.Col(),rCell.Row(),nTab), pFmt);
1969 
1970  putCellDataIntoCache(maRefCache, pToken, nFileId, rTabName, rCell, pFmt);
1971  return pToken;
1972 }
1973 
1975  sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange, const ScAddress* pCurPos)
1976 {
1977  if (pCurPos)
1978  insertRefCell(nFileId, *pCurPos);
1979 
1980  maybeLinkExternalFile(nFileId);
1981 
1982  ScRange aDataRange(rRange);
1983  ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
1984  if (pSrcDoc)
1985  {
1986  // Document already loaded in memory.
1987  vector<ScExternalRefCache::SingleRangeData> aCacheData;
1989  getDoubleRefTokensFromSrcDoc(*pSrcDoc, rTabName, aDataRange, aCacheData);
1990 
1991  // Put the data into cache.
1992  putRangeDataIntoCache(maRefCache, pArray, nFileId, rTabName, aCacheData, rRange, aDataRange);
1993  return pArray;
1994  }
1995 
1996  // Check if the given table name and the cell position is cached.
1998  maRefCache.getCellRangeData(nFileId, rTabName, rRange);
1999  if (pArray)
2000  // Cache hit !
2001  return pArray;
2002 
2003  pSrcDoc = getSrcDocument(nFileId);
2004  if (!pSrcDoc)
2005  {
2006  // Source document is not reachable. Throw a reference error.
2007  pArray = std::make_shared<ScTokenArray>(*maRefCache.getFakeDoc());
2008  pArray->AddToken(FormulaErrorToken(FormulaError::NoRef));
2009  return pArray;
2010  }
2011 
2012  vector<ScExternalRefCache::SingleRangeData> aCacheData;
2013  pArray = getDoubleRefTokensFromSrcDoc(*pSrcDoc, rTabName, aDataRange, aCacheData);
2014 
2015  // Put the data into cache.
2016  putRangeDataIntoCache(maRefCache, pArray, nFileId, rTabName, aCacheData, rRange, aDataRange);
2017  return pArray;
2018 }
2019 
2021  sal_uInt16 nFileId, const OUString& rName, const ScAddress* pCurPos)
2022 {
2023  if (pCurPos)
2024  insertRefCell(nFileId, *pCurPos);
2025 
2026  maybeLinkExternalFile(nFileId);
2027 
2028  OUString aName = rName; // make a copy to have the casing corrected.
2029  ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
2030  if (pSrcDoc)
2031  {
2032  // Document already loaded in memory.
2034  getRangeNameTokensFromSrcDoc(nFileId, *pSrcDoc, aName);
2035 
2036  if (pArray)
2037  // Cache this range name array.
2038  maRefCache.setRangeNameTokens(nFileId, aName, pArray);
2039 
2040  return pArray;
2041  }
2042 
2044  if (pArray)
2045  // This range name is cached.
2046  return pArray;
2047 
2048  pSrcDoc = getSrcDocument(nFileId);
2049  if (!pSrcDoc)
2050  // failed to load document from disk.
2052 
2053  pArray = getRangeNameTokensFromSrcDoc(nFileId, *pSrcDoc, aName);
2054 
2055  if (pArray)
2056  // Cache this range name array.
2057  maRefCache.setRangeNameTokens(nFileId, aName, pArray);
2058 
2059  return pArray;
2060 }
2061 
2062 namespace {
2063 
2064 bool hasRangeName(const ScDocument& rDoc, const OUString& rName)
2065 {
2066  ScRangeName* pExtNames = rDoc.GetRangeName();
2067  OUString aUpperName = ScGlobal::getCharClassPtr()->uppercase(rName);
2068  const ScRangeData* pRangeData = pExtNames->findByUpperName(aUpperName);
2069  return pRangeData != nullptr;
2070 }
2071 
2072 }
2073 
2074 bool ScExternalRefManager::isValidRangeName(sal_uInt16 nFileId, const OUString& rName)
2075 {
2076  maybeLinkExternalFile(nFileId);
2077  ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
2078  if (pSrcDoc)
2079  {
2080  // Only check the presence of the name.
2081  if (hasRangeName(*pSrcDoc, rName))
2082  {
2083  maRefCache.setRangeName(nFileId, rName);
2084  return true;
2085  }
2086  return false;
2087  }
2088 
2089  if (maRefCache.isValidRangeName(nFileId, rName))
2090  // Range name is cached.
2091  return true;
2092 
2093  pSrcDoc = getSrcDocument(nFileId);
2094  if (!pSrcDoc)
2095  // failed to load document from disk.
2096  return false;
2097 
2098  if (hasRangeName(*pSrcDoc, rName))
2099  {
2100  maRefCache.setRangeName(nFileId, rName);
2101  return true;
2102  }
2103 
2104  return false;
2105 }
2106 
2108 {
2109  RefCellMap::iterator itrFile = maRefCells.find(nFileId);
2110  if (itrFile == maRefCells.end())
2111  return;
2112 
2113  RefCellSet& rRefCells = itrFile->second;
2114  for_each(rRefCells.begin(), rRefCells.end(), UpdateFormulaCell());
2115 
2116  ScViewData* pViewData = ScDocShell::GetViewData();
2117  if (!pViewData)
2118  return;
2119 
2120  ScTabViewShell* pVShell = pViewData->GetViewShell();
2121  if (!pVShell)
2122  return;
2123 
2124  // Repainting the grid also repaints the texts, but is there a better way
2125  // to refresh texts?
2126  pVShell->Invalidate(FID_REPAINT);
2127  pVShell->PaintGrid();
2128 }
2129 
2130 namespace {
2131 
2132 void insertRefCellByIterator(
2133  const ScExternalRefManager::RefCellMap::iterator& itr, ScFormulaCell* pCell)
2134 {
2135  if (pCell)
2136  {
2137  itr->second.insert(pCell);
2138  pCell->SetIsExtRef();
2139  }
2140 }
2141 
2142 }
2143 
2144 void ScExternalRefManager::insertRefCell(sal_uInt16 nFileId, const ScAddress& rCell)
2145 {
2146  RefCellMap::iterator itr = maRefCells.find(nFileId);
2147  if (itr == maRefCells.end())
2148  {
2149  RefCellSet aRefCells;
2150  pair<RefCellMap::iterator, bool> r = maRefCells.emplace(
2151  nFileId, aRefCells);
2152  if (!r.second)
2153  // insertion failed.
2154  return;
2155 
2156  itr = r.first;
2157  }
2158 
2159  insertRefCellByIterator(itr, mrDoc.GetFormulaCell(rCell));
2160 }
2161 
2163 {
2164  if (!pTemplateCell || !pCell)
2165  return;
2166 
2167  for (RefCellMap::iterator itr = maRefCells.begin(); itr != maRefCells.end(); ++itr)
2168  {
2169  if (itr->second.find(pTemplateCell) != itr->second.end())
2170  insertRefCellByIterator(itr, pCell);
2171  }
2172 }
2173 
2175 {
2176  ScFormulaCell* pCell = mrDoc.GetFormulaCell(rCell);
2177 
2178  if (pCell)
2179  return std::any_of(maRefCells.begin(), maRefCells.end(),
2180  [&pCell](const RefCellMap::value_type& rEntry) { return rEntry.second.find(pCell) != rEntry.second.end(); });
2181 
2182  return false;
2183 }
2184 
2186 {
2187  if (mbDocTimerEnabled == bEnable)
2188  return;
2189 
2190  mbDocTimerEnabled = bEnable;
2191  if (mbDocTimerEnabled)
2192  {
2193  if (!maDocShells.empty())
2194  {
2195  for (auto& rEntry : maDocShells)
2196  rEntry.second.maLastAccess = tools::Time(tools::Time::SYSTEM);
2197 
2198  maSrcDocTimer.Start();
2199  }
2200  }
2201  else
2202  maSrcDocTimer.Stop();
2203 }
2204 
2206 {
2207  if (!pFmt)
2208  return;
2209 
2210  SvNumFormatType nFmtType = mrDoc.GetFormatTable()->GetType(nFmtIndex);
2211  if (nFmtType != SvNumFormatType::UNDEFINED)
2212  {
2213  pFmt->mbIsSet = true;
2214  pFmt->mnIndex = nFmtIndex;
2215  pFmt->mnType = nFmtType;
2216  }
2217 }
2218 
2220  sal_uInt16 nFileId, ScDocument& rSrcDoc, const ScAddress& rPos,
2222 {
2223  // Get the cell from src doc, and convert it into a token.
2224  ScRefCellValue aCell(rSrcDoc, rPos);
2225  ScExternalRefCache::TokenRef pToken(convertToToken(mrDoc, rSrcDoc, aCell));
2226 
2227  if (!pToken)
2228  {
2229  // Generate an error for unresolvable cells.
2230  pToken.reset( new FormulaErrorToken( FormulaError::NoValue));
2231  }
2232 
2233  // Get number format information.
2234  sal_uInt32 nFmtIndex = 0;
2235  rSrcDoc.GetNumberFormat(rPos.Col(), rPos.Row(), rPos.Tab(), nFmtIndex);
2236  nFmtIndex = getMappedNumberFormat(nFileId, nFmtIndex, rSrcDoc);
2237  fillCellFormat(nFmtIndex, pFmt);
2238  return pToken;
2239 }
2240 
2242  const ScDocument& rSrcDoc, const OUString& rTabName, ScRange& rRange,
2243  vector<ScExternalRefCache::SingleRangeData>& rCacheData)
2244 {
2246  SCTAB nTab1;
2247 
2248  if (!rSrcDoc.GetTable(rTabName, nTab1))
2249  {
2250  // specified table name doesn't exist in the source document.
2251  pArray = std::make_shared<ScTokenArray>(rSrcDoc);
2252  pArray->AddToken(FormulaErrorToken(FormulaError::NoRef));
2253  return pArray;
2254  }
2255 
2256  ScRange aRange(rRange);
2257  aRange.PutInOrder();
2258  SCTAB nTabSpan = aRange.aEnd.Tab() - aRange.aStart.Tab();
2259 
2260  vector<ScExternalRefCache::SingleRangeData> aCacheData;
2261  aCacheData.reserve(nTabSpan+1);
2262  aCacheData.emplace_back();
2263  aCacheData.back().maTableName = ScGlobal::getCharClassPtr()->uppercase(rTabName);
2264 
2265  for (SCTAB i = 1; i < nTabSpan + 1; ++i)
2266  {
2267  OUString aTabName;
2268  if (!rSrcDoc.GetName(nTab1 + 1, aTabName))
2269  // source document doesn't have any table by the specified name.
2270  break;
2271 
2272  aCacheData.emplace_back();
2273  aCacheData.back().maTableName = ScGlobal::getCharClassPtr()->uppercase(aTabName);
2274  }
2275 
2276  aRange.aStart.SetTab(nTab1);
2277  aRange.aEnd.SetTab(nTab1 + nTabSpan);
2278 
2279  pArray = convertToTokenArray(mrDoc, rSrcDoc, aRange, aCacheData);
2280  rRange = aRange;
2281  rCacheData.swap(aCacheData);
2282  return pArray;
2283 }
2284 
2286  sal_uInt16 nFileId, const ScDocument& rSrcDoc, OUString& rName)
2287 {
2288  ScRangeName* pExtNames = rSrcDoc.GetRangeName();
2289  OUString aUpperName = ScGlobal::getCharClassPtr()->uppercase(rName);
2290  const ScRangeData* pRangeData = pExtNames->findByUpperName(aUpperName);
2291  if (!pRangeData)
2293 
2294  // Parse all tokens in this external range data, and replace each absolute
2295  // reference token with an external reference token, and cache them. Also
2296  // register the source document with the link manager if it's a new
2297  // source.
2298 
2299  ScExternalRefCache::TokenArrayRef pNew = std::make_shared<ScTokenArray>(rSrcDoc);
2300 
2301  ScTokenArray aCode(*pRangeData->GetCode());
2302  FormulaTokenArrayPlainIterator aIter(aCode);
2303  for (const FormulaToken* pToken = aIter.First(); pToken; pToken = aIter.Next())
2304  {
2305  bool bTokenAdded = false;
2306  switch (pToken->GetType())
2307  {
2308  case svSingleRef:
2309  {
2310  const ScSingleRefData& rRef = *pToken->GetSingleRef();
2311  OUString aTabName;
2312  rSrcDoc.GetName(rRef.Tab(), aTabName);
2313  ScExternalSingleRefToken aNewToken(nFileId, svl::SharedString( aTabName), // string not interned
2314  *pToken->GetSingleRef());
2315  pNew->AddToken(aNewToken);
2316  bTokenAdded = true;
2317  }
2318  break;
2319  case svDoubleRef:
2320  {
2321  const ScSingleRefData& rRef = *pToken->GetSingleRef();
2322  OUString aTabName;
2323  rSrcDoc.GetName(rRef.Tab(), aTabName);
2324  ScExternalDoubleRefToken aNewToken(nFileId, svl::SharedString( aTabName), // string not interned
2325  *pToken->GetDoubleRef());
2326  pNew->AddToken(aNewToken);
2327  bTokenAdded = true;
2328  }
2329  break;
2330  default:
2331  ; // nothing
2332  }
2333 
2334  if (!bTokenAdded)
2335  pNew->AddToken(*pToken);
2336  }
2337 
2338  rName = pRangeData->GetName(); // Get the correctly-cased name.
2339  return pNew;
2340 }
2341 
2343 {
2344  const OUString* pFileName = getExternalFileName(nFileId);
2345  if (!pFileName)
2346  return nullptr;
2347 
2348  // Do not load document until it was allowed.
2349  if (!isLinkUpdateAllowedInDoc(mrDoc))
2350  return nullptr;
2351 
2352  ScDocument* pSrcDoc = nullptr;
2353  ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<ScDocShell>, false));
2354  while (pShell)
2355  {
2356  SfxMedium* pMedium = pShell->GetMedium();
2357  if (pMedium && !pMedium->GetName().isEmpty())
2358  {
2359  // TODO: We should make the case sensitivity platform dependent.
2360  if (pFileName->equalsIgnoreAsciiCase(pMedium->GetName()))
2361  {
2362  // Found !
2363  pSrcDoc = &pShell->GetDocument();
2364  break;
2365  }
2366  }
2367  else
2368  {
2369  // handle unsaved documents here
2370  OUString aName = pShell->GetName();
2371  if (pFileName->equalsIgnoreAsciiCase(aName))
2372  {
2373  // Found !
2374  SrcShell aSrcDoc;
2375  aSrcDoc.maShell = pShell;
2376  maUnsavedDocShells.emplace(nFileId, aSrcDoc);
2377  StartListening(*pShell);
2378  pSrcDoc = &pShell->GetDocument();
2379  break;
2380  }
2381  }
2382  pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, checkSfxObjectShell<ScDocShell>, false));
2383  }
2384 
2385  initDocInCache(maRefCache, pSrcDoc, nFileId);
2386  return pSrcDoc;
2387 }
2388 
2390 {
2391  if (!mrDoc.IsExecuteLinkEnabled())
2392  return nullptr;
2393 
2394  DocShellMap::iterator itrEnd = maDocShells.end();
2395  DocShellMap::iterator itr = maDocShells.find(nFileId);
2396 
2397  if (itr != itrEnd)
2398  {
2399  // document already loaded.
2400 
2401  SfxObjectShell* p = itr->second.maShell.get();
2402  itr->second.maLastAccess = tools::Time( tools::Time::SYSTEM );
2403  return &static_cast<ScDocShell*>(p)->GetDocument();
2404  }
2405 
2406  itrEnd = maUnsavedDocShells.end();
2407  itr = maUnsavedDocShells.find(nFileId);
2408  if (itr != itrEnd)
2409  {
2410  //document is unsaved document
2411 
2412  SfxObjectShell* p = itr->second.maShell.get();
2413  itr->second.maLastAccess = tools::Time( tools::Time::SYSTEM );
2414  return &static_cast<ScDocShell*>(p)->GetDocument();
2415  }
2416 
2417  const OUString* pFile = getExternalFileName(nFileId);
2418  if (!pFile)
2419  // no file name associated with this ID.
2420  return nullptr;
2421 
2422  SrcShell aSrcDoc;
2423  try
2424  {
2425  OUString aFilter;
2426  aSrcDoc.maShell = loadSrcDocument(nFileId, aFilter);
2427  }
2428  catch (const css::uno::Exception&)
2429  {
2430  }
2431  if (!aSrcDoc.maShell.is())
2432  {
2433  // source document could not be loaded.
2434  return nullptr;
2435  }
2436 
2437  return &cacheNewDocShell(nFileId, aSrcDoc);
2438 }
2439 
2440 SfxObjectShellRef ScExternalRefManager::loadSrcDocument(sal_uInt16 nFileId, OUString& rFilter)
2441 {
2442  // Do not load document until it was allowed.
2443  if (!isLinkUpdateAllowedInDoc(mrDoc))
2444  return nullptr;
2445 
2446  const SrcFileData* pFileData = getExternalFileData(nFileId);
2447  if (!pFileData)
2448  return nullptr;
2449 
2450  // Always load the document by using the path created from the relative
2451  // path. If the referenced document is not there, simply exit. The
2452  // original file name should be used only when the relative path is not
2453  // given.
2454  OUString aFile = pFileData->maFileName;
2455  maybeCreateRealFileName(nFileId);
2456  if (!pFileData->maRealFileName.isEmpty())
2457  aFile = pFileData->maRealFileName;
2458 
2459  if (!isFileLoadable(aFile))
2460  return nullptr;
2461 
2462  OUString aOptions = pFileData->maFilterOptions;
2463  if ( !pFileData->maFilterName.isEmpty() )
2464  rFilter = pFileData->maFilterName; // don't overwrite stored filter with guessed filter
2465  else
2466  ScDocumentLoader::GetFilterName(aFile, rFilter, aOptions, true, false);
2467  std::shared_ptr<const SfxFilter> pFilter = ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName(rFilter);
2468 
2469  if (pFileData->maRelativeName.isEmpty())
2470  {
2471  // Generate a relative file path.
2472  INetURLObject aBaseURL(getOwnDocumentName());
2473  aBaseURL.insertName("content.xml");
2474 
2477 
2478  setRelativeFileName(nFileId, aStr);
2479  }
2480 
2481  std::unique_ptr<SfxItemSet> pSet(new SfxAllItemSet(SfxGetpApp()->GetPool()));
2482  if (!aOptions.isEmpty())
2483  pSet->Put(SfxStringItem(SID_FILE_FILTEROPTIONS, aOptions));
2484 
2485  // make medium hidden to prevent assertion from progress bar
2486  pSet->Put( SfxBoolItem(SID_HIDDEN, true) );
2487 
2488  // If the current document is allowed to execute macros then the referenced
2489  // document may execute macros according to the security configuration.
2490  // Similar for UpdateDocMode to update links, just that if we reach here
2491  // the user already allowed updates and intermediate documents are expected
2492  // to update as well. When loading the document ScDocShell::Load() will
2493  // check through ScDocShell::GetLinkUpdateModeState() if its location is
2494  // trusted.
2496  if (pShell)
2497  {
2498  SfxMedium* pMedium = pShell->GetMedium();
2499  if (pMedium)
2500  {
2501  const SfxPoolItem* pItem;
2502  if (pMedium->GetItemSet()->GetItemState( SID_MACROEXECMODE, false, &pItem ) == SfxItemState::SET &&
2503  static_cast<const SfxUInt16Item*>(pItem)->GetValue() != css::document::MacroExecMode::NEVER_EXECUTE)
2504  pSet->Put( SfxUInt16Item( SID_MACROEXECMODE, css::document::MacroExecMode::USE_CONFIG));
2505  }
2506 
2507  pSet->Put( SfxUInt16Item( SID_UPDATEDOCMODE, css::document::UpdateDocMode::FULL_UPDATE));
2508  }
2509 
2510  unique_ptr<SfxMedium> pMedium(new SfxMedium(aFile, StreamMode::STD_READ, pFilter, std::move(pSet)));
2511  if (pMedium->GetError() != ERRCODE_NONE)
2512  return nullptr;
2513 
2514  // To load encrypted documents with password, user interaction needs to be enabled.
2515  pMedium->UseInteractionHandler(mbUserInteractionEnabled);
2516 
2517  ScDocShell* pNewShell = new ScDocShell(SfxModelFlags::EXTERNAL_LINK);
2518  SfxObjectShellRef aRef = pNewShell;
2519 
2520  // increment the recursive link count of the source document.
2521  ScExtDocOptions* pExtOpt = mrDoc.GetExtDocOptions();
2522  sal_uInt32 nLinkCount = pExtOpt ? pExtOpt->GetDocSettings().mnLinkCnt : 0;
2523  ScDocument& rSrcDoc = pNewShell->GetDocument();
2524  rSrcDoc.EnableExecuteLink(false); // to prevent circular access of external references.
2525  rSrcDoc.EnableUndo(false);
2526  rSrcDoc.LockAdjustHeight();
2527  rSrcDoc.EnableUserInteraction(false);
2528 
2529  ScExtDocOptions* pExtOptNew = rSrcDoc.GetExtDocOptions();
2530  if (!pExtOptNew)
2531  {
2532  rSrcDoc.SetExtDocOptions(std::make_unique<ScExtDocOptions>());
2533  pExtOptNew = rSrcDoc.GetExtDocOptions();
2534  }
2535  pExtOptNew->GetDocSettings().mnLinkCnt = nLinkCount + 1;
2536 
2537  if (!pNewShell->DoLoad(pMedium.release()))
2538  {
2539  aRef->DoClose();
2540  aRef.clear();
2541  return aRef;
2542  }
2543 
2544  // with UseInteractionHandler, options may be set by dialog during DoLoad
2545  OUString aNew = ScDocumentLoader::GetOptions(*pNewShell->GetMedium());
2546  if (!aNew.isEmpty() && aNew != aOptions)
2547  aOptions = aNew;
2548  setFilterData(nFileId, rFilter, aOptions); // update the filter data, including the new options
2549 
2550  return aRef;
2551 }
2552 
2554 {
2555  if (mbDocTimerEnabled && maDocShells.empty())
2556  // If this is the first source document insertion, start up the timer.
2557  maSrcDocTimer.Start();
2558 
2559  maDocShells.emplace(nFileId, rSrcShell);
2560  SfxObjectShell& rShell = *rSrcShell.maShell;
2561  ScDocument& rSrcDoc = static_cast<ScDocShell&>(rShell).GetDocument();
2562  initDocInCache(maRefCache, &rSrcDoc, nFileId);
2563  return rSrcDoc;
2564 }
2565 
2566 bool ScExternalRefManager::isFileLoadable(const OUString& rFile) const
2567 {
2568  if (rFile.isEmpty())
2569  return false;
2570 
2571  if (isOwnDocument(rFile))
2572  return false;
2573  OUString aPhysical;
2574  if (osl::FileBase::getSystemPathFromFileURL(rFile, aPhysical)
2575  == osl::FileBase::E_None)
2576  {
2577  // #i114504# try IsFolder/Exists only for file URLs
2578 
2580  return false;
2581 
2582  return utl::UCBContentHelper::Exists(rFile);
2583  }
2584  else
2585  return true; // for http and others, Exists doesn't work, but the URL can still be opened
2586 }
2587 
2588 void ScExternalRefManager::maybeLinkExternalFile( sal_uInt16 nFileId, bool bDeferFilterDetection )
2589 {
2590  if (maLinkedDocs.count(nFileId))
2591  // file already linked, or the link has been broken.
2592  return;
2593 
2594  // Source document not linked yet. Link it now.
2595  const OUString* pFileName = getExternalFileName(nFileId);
2596  if (!pFileName)
2597  return;
2598 
2599  OUString aFilter, aOptions;
2600  const SrcFileData* pFileData = getExternalFileData(nFileId);
2601  if (pFileData)
2602  {
2603  aFilter = pFileData->maFilterName;
2604  aOptions = pFileData->maFilterOptions;
2605  }
2606 
2607  // Filter detection may access external links; defer it until we are allowed.
2608  if (!bDeferFilterDetection)
2609  bDeferFilterDetection = !isLinkUpdateAllowedInDoc(mrDoc);
2610 
2611  // If a filter was already set (for example, loading the cached table),
2612  // don't call GetFilterName which has to access the source file.
2613  // If filter detection is deferred, the next successful loadSrcDocument()
2614  // will update SrcFileData filter name.
2615  if (aFilter.isEmpty() && !bDeferFilterDetection)
2616  ScDocumentLoader::GetFilterName(*pFileName, aFilter, aOptions, true, false);
2617  sfx2::LinkManager* pLinkMgr = mrDoc.GetLinkManager();
2618  if (!pLinkMgr)
2619  {
2620  SAL_WARN( "sc.ui", "ScExternalRefManager::maybeLinkExternalFile: pLinkMgr==NULL");
2621  return;
2622  }
2624  OSL_ENSURE(pFileName, "ScExternalRefManager::maybeLinkExternalFile: file name pointer is NULL");
2625  pLinkMgr->InsertFileLink(*pLink, sfx2::SvBaseLinkObjectType::ClientFile, *pFileName,
2626  (aFilter.isEmpty() && bDeferFilterDetection ? nullptr : &aFilter));
2627 
2628  pLink->SetDoRefresh(false);
2629  pLink->Update();
2630  pLink->SetDoRefresh(true);
2631 
2632  maLinkedDocs.emplace(nFileId, true);
2633 }
2634 
2636 {
2637  if (maSrcFiles.empty())
2638  return;
2639 
2641  "sc.ui", "ScExternalRefManager::addFilesToLinkManager: files overflow");
2642  const sal_uInt16 nSize = static_cast<sal_uInt16>( std::min<size_t>( maSrcFiles.size(), SAL_MAX_UINT16));
2643  for (sal_uInt16 nFileId = 0; nFileId < nSize; ++nFileId)
2644  maybeLinkExternalFile( nFileId, true);
2645 }
2646 
2648 {
2649  if (maRelativeName.isEmpty())
2650  // No relative path given. Nothing to do.
2651  return;
2652 
2653  if (!maRealFileName.isEmpty())
2654  // Real file name already created. Nothing to do.
2655  return;
2656 
2657  // Formulate the absolute file path from the relative path.
2658  const OUString& rRelPath = maRelativeName;
2659  INetURLObject aBaseURL(rOwnDocName);
2660  aBaseURL.insertName("content.xml");
2661  bool bWasAbs = false;
2662  maRealFileName = aBaseURL.smartRel2Abs(rRelPath, bWasAbs).GetMainURL(INetURLObject::DecodeMechanism::NONE);
2663 }
2664 
2666 {
2667  if (nFileId >= maSrcFiles.size())
2668  return;
2669 
2670  maSrcFiles[nFileId].maybeCreateRealFileName(getOwnDocumentName());
2671 }
2672 
2674 {
2676  return "file:///tmp/document";
2677 
2679  if (!pShell)
2680  // This should not happen!
2681  return OUString();
2682 
2683  SfxMedium* pMed = pShell->GetMedium();
2684  if (!pMed)
2685  return OUString();
2686 
2687  return pMed->GetName();
2688 }
2689 
2690 bool ScExternalRefManager::isOwnDocument(const OUString& rFile) const
2691 {
2692  return getOwnDocumentName() == rFile;
2693 }
2694 
2695 void ScExternalRefManager::convertToAbsName(OUString& rFile) const
2696 {
2697  // unsaved documents have no AbsName
2698  ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<ScDocShell>, false));
2699  while (pShell)
2700  {
2701  if (rFile == pShell->GetName())
2702  return;
2703 
2704  pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, checkSfxObjectShell<ScDocShell>, false));
2705  }
2706 
2707  SfxObjectShell* pDocShell = mrDoc.GetDocumentShell();
2708  rFile = ScGlobal::GetAbsDocName(rFile, pDocShell);
2709 }
2710 
2711 sal_uInt16 ScExternalRefManager::getExternalFileId(const OUString& rFile)
2712 {
2713  vector<SrcFileData>::const_iterator itrBeg = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
2714  vector<SrcFileData>::const_iterator itr = find_if(itrBeg, itrEnd, FindSrcFileByName(rFile));
2715  if (itr != itrEnd)
2716  {
2717  size_t nId = distance(itrBeg, itr);
2718  return static_cast<sal_uInt16>(nId);
2719  }
2720 
2722  aData.maFileName = rFile;
2723  maSrcFiles.push_back(aData);
2724  return static_cast<sal_uInt16>(maSrcFiles.size() - 1);
2725 }
2726 
2727 const OUString* ScExternalRefManager::getExternalFileName(sal_uInt16 nFileId, bool bForceOriginal)
2728 {
2729  if (nFileId >= maSrcFiles.size())
2730  return nullptr;
2731 
2732  if (bForceOriginal)
2733  return &maSrcFiles[nFileId].maFileName;
2734 
2735  maybeCreateRealFileName(nFileId);
2736 
2737  if (!maSrcFiles[nFileId].maRealFileName.isEmpty())
2738  return &maSrcFiles[nFileId].maRealFileName;
2739 
2740  return &maSrcFiles[nFileId].maFileName;
2741 }
2742 
2744 {
2745  std::vector<OUString> aNames;
2746  aNames.reserve(maSrcFiles.size());
2747  for (const SrcFileData& rData : maSrcFiles)
2748  {
2749  aNames.push_back(rData.maFileName);
2750  }
2751 
2752  return aNames;
2753 }
2754 
2755 bool ScExternalRefManager::hasExternalFile(sal_uInt16 nFileId) const
2756 {
2757  return nFileId < maSrcFiles.size();
2758 }
2759 
2760 bool ScExternalRefManager::hasExternalFile(const OUString& rFile) const
2761 {
2762  return ::std::any_of(maSrcFiles.begin(), maSrcFiles.end(), FindSrcFileByName(rFile));
2763 }
2764 
2766 {
2767  if (nFileId >= maSrcFiles.size())
2768  return nullptr;
2769 
2770  return &maSrcFiles[nFileId];
2771 }
2772 
2773 const OUString* ScExternalRefManager::getRealTableName(sal_uInt16 nFileId, const OUString& rTabName) const
2774 {
2775  return maRefCache.getRealTableName(nFileId, rTabName);
2776 }
2777 
2778 const OUString* ScExternalRefManager::getRealRangeName(sal_uInt16 nFileId, const OUString& rRangeName) const
2779 {
2780  return maRefCache.getRealRangeName(nFileId, rRangeName);
2781 }
2782 
2783 template<typename MapContainer>
2784 static void lcl_removeByFileId(sal_uInt16 nFileId, MapContainer& rMap)
2785 {
2786  typename MapContainer::iterator itr = rMap.find(nFileId);
2787  if (itr != rMap.end())
2788  {
2789  // Close this document shell.
2790  itr->second.maShell->DoClose();
2791  rMap.erase(itr);
2792  }
2793 }
2794 
2795 void ScExternalRefManager::clearCache(sal_uInt16 nFileId)
2796 {
2797  maRefCache.clearCache(nFileId);
2798 }
2799 
2800 namespace {
2801 
2802 class RefCacheFiller : public sc::ColumnSpanSet::ColumnAction
2803 {
2804  svl::SharedStringPool& mrStrPool;
2805 
2806  ScExternalRefCache& mrRefCache;
2808  sal_uInt16 mnFileId;
2809  ScColumn* mpCurCol;
2810  sc::ColumnBlockConstPosition maBlockPos;
2811 
2812 public:
2813  RefCacheFiller( svl::SharedStringPool& rStrPool, ScExternalRefCache& rRefCache, sal_uInt16 nFileId ) :
2814  mrStrPool(rStrPool), mrRefCache(rRefCache), mnFileId(nFileId), mpCurCol(nullptr) {}
2815 
2816  virtual void startColumn( ScColumn* pCol ) override
2817  {
2818  mpCurCol = pCol;
2819  if (!mpCurCol)
2820  return;
2821 
2822  mpCurCol->InitBlockPosition(maBlockPos);
2823  mpRefTab = mrRefCache.getCacheTable(mnFileId, mpCurCol->GetTab());
2824  }
2825 
2826  virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
2827  {
2828  if (!mpCurCol || !bVal)
2829  return;
2830 
2831  if (!mpRefTab)
2832  return;
2833 
2834  for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2835  {
2837  ScRefCellValue aCell = mpCurCol->GetCellValue(maBlockPos, nRow);
2838  switch (aCell.meType)
2839  {
2840  case CELLTYPE_STRING:
2841  case CELLTYPE_EDIT:
2842  {
2843  OUString aStr = aCell.getString(&mpCurCol->GetDoc());
2844  svl::SharedString aSS = mrStrPool.intern(aStr);
2845  pTok.reset(new formula::FormulaStringToken(aSS));
2846  }
2847  break;
2848  case CELLTYPE_VALUE:
2849  pTok.reset(new formula::FormulaDoubleToken(aCell.mfValue));
2850  break;
2851  case CELLTYPE_FORMULA:
2852  {
2853  sc::FormulaResultValue aRes = aCell.mpFormula->GetResult();
2854  switch (aRes.meType)
2855  {
2857  pTok.reset(new formula::FormulaDoubleToken(aRes.mfValue));
2858  break;
2860  {
2861  // Re-intern the string to the host document pool.
2862  svl::SharedString aInterned = mrStrPool.intern(aRes.maString.getString());
2863  pTok.reset(new formula::FormulaStringToken(aInterned));
2864  }
2865  break;
2868  default:
2869  pTok.reset(new FormulaErrorToken(FormulaError::NoValue));
2870  }
2871  }
2872  break;
2873  default:
2874  pTok.reset(new FormulaErrorToken(FormulaError::NoValue));
2875  }
2876 
2877  if (pTok)
2878  {
2879  // Cache this cell.
2880  mpRefTab->setCell(mpCurCol->GetCol(), nRow, pTok, mpCurCol->GetNumberFormat(mpCurCol->GetDoc().GetNonThreadedContext(), nRow));
2881  mpRefTab->setCachedCell(mpCurCol->GetCol(), nRow);
2882  }
2883  }
2884  };
2885 };
2886 
2887 }
2888 
2890 {
2891  SfxObjectShellRef xDocShell;
2892  try
2893  {
2894  OUString aFilter;
2895  xDocShell = loadSrcDocument(nFileId, aFilter);
2896  }
2897  catch ( const css::uno::Exception& ) {}
2898 
2899  if (!xDocShell.is())
2900  // Failed to load the document. Bail out.
2901  return false;
2902 
2903  ScDocShell& rDocSh = static_cast<ScDocShell&>(*xDocShell);
2904  ScDocument& rSrcDoc = rDocSh.GetDocument();
2905 
2906  sc::ColumnSpanSet aCachedArea;
2907  maRefCache.getAllCachedDataSpans(rSrcDoc, nFileId, aCachedArea);
2908 
2909  // Clear the existing cache, and refill it. Make sure we keep the
2910  // existing cache table instances here.
2911  maRefCache.clearCacheTables(nFileId);
2912  RefCacheFiller aAction(mrDoc.GetSharedStringPool(), maRefCache, nFileId);
2913  aCachedArea.executeColumnAction(rSrcDoc, aAction);
2914 
2915  DocShellMap::iterator it = maDocShells.find(nFileId);
2916  if (it != maDocShells.end())
2917  {
2918  it->second.maShell->DoClose();
2919  it->second.maShell = xDocShell;
2920  it->second.maLastAccess = tools::Time(tools::Time::SYSTEM);
2921  }
2922  else
2923  {
2924  SrcShell aSrcDoc;
2925  aSrcDoc.maShell = xDocShell;
2927  cacheNewDocShell(nFileId, aSrcDoc);
2928  }
2929 
2930  // Update all cells containing names from this source document.
2931  refreshAllRefCells(nFileId);
2932 
2934 
2935  return true;
2936 }
2937 
2938 void ScExternalRefManager::breakLink(sal_uInt16 nFileId)
2939 {
2940  // Turn all formula cells referencing this external document into static
2941  // cells.
2942  RefCellMap::iterator itrRefs = maRefCells.find(nFileId);
2943  if (itrRefs != maRefCells.end())
2944  {
2945  // Make a copy because removing the formula cells below will modify
2946  // the original container.
2947  RefCellSet aSet = itrRefs->second;
2948  for_each(aSet.begin(), aSet.end(), ConvertFormulaToStatic(&mrDoc));
2949  maRefCells.erase(nFileId);
2950  }
2951 
2952  // Remove all named ranges that reference this document.
2953 
2954  // Global named ranges.
2955  ScRangeName* pRanges = mrDoc.GetRangeName();
2956  if (pRanges)
2957  removeRangeNamesBySrcDoc(*pRanges, nFileId);
2958 
2959  // Sheet-local named ranges.
2960  for (SCTAB i = 0, n = mrDoc.GetTableCount(); i < n; ++i)
2961  {
2962  pRanges = mrDoc.GetRangeName(i);
2963  if (pRanges)
2964  removeRangeNamesBySrcDoc(*pRanges, nFileId);
2965  }
2966 
2967  clearCache(nFileId);
2968  lcl_removeByFileId(nFileId, maDocShells);
2969 
2970  if (maDocShells.empty())
2971  maSrcDocTimer.Stop();
2972 
2973  LinkedDocMap::iterator itr = maLinkedDocs.find(nFileId);
2974  if (itr != maLinkedDocs.end())
2975  itr->second = false;
2976 
2978 }
2979 
2980 void ScExternalRefManager::switchSrcFile(sal_uInt16 nFileId, const OUString& rNewFile, const OUString& rNewFilter)
2981 {
2982  maSrcFiles[nFileId].maFileName = rNewFile;
2983  maSrcFiles[nFileId].maRelativeName.clear();
2984  maSrcFiles[nFileId].maRealFileName.clear();
2985  if (maSrcFiles[nFileId].maFilterName != rNewFilter)
2986  {
2987  // Filter type has changed.
2988  maSrcFiles[nFileId].maFilterName = rNewFilter;
2989  maSrcFiles[nFileId].maFilterOptions.clear();
2990  }
2991  refreshSrcDocument(nFileId);
2992 }
2993 
2994 void ScExternalRefManager::setRelativeFileName(sal_uInt16 nFileId, const OUString& rRelUrl)
2995 {
2996  if (nFileId >= maSrcFiles.size())
2997  return;
2998  maSrcFiles[nFileId].maRelativeName = rRelUrl;
2999 }
3000 
3001 void ScExternalRefManager::setFilterData(sal_uInt16 nFileId, const OUString& rFilterName, const OUString& rOptions)
3002 {
3003  if (nFileId >= maSrcFiles.size())
3004  return;
3005  maSrcFiles[nFileId].maFilterName = rFilterName;
3006  maSrcFiles[nFileId].maFilterOptions = rOptions;
3007 }
3008 
3010 {
3011  for (auto& rEntry : maDocShells)
3012  rEntry.second.maShell->DoClose();
3013 
3014  maDocShells.clear();
3015  maSrcDocTimer.Stop();
3016 }
3017 
3019 {
3020  return !maSrcFiles.empty();
3021 }
3022 
3023 void ScExternalRefManager::resetSrcFileData(const OUString& rBaseFileUrl)
3024 {
3025  for (auto& rSrcFile : maSrcFiles)
3026  {
3027  // Re-generate relative file name from the absolute file name.
3028  OUString aAbsName = rSrcFile.maRealFileName;
3029  if (aAbsName.isEmpty())
3030  aAbsName = rSrcFile.maFileName;
3031 
3032  rSrcFile.maRelativeName = URIHelper::simpleNormalizedMakeRelative(
3033  rBaseFileUrl, aAbsName);
3034  }
3035 }
3036 
3038 {
3039  OUString aOwn( getOwnDocumentName() );
3040  for (auto& rSrcFile : maSrcFiles)
3041  {
3042  // update maFileName to the real file name,
3043  // to be called when the original name is no longer needed (after CompileXML)
3044 
3045  rSrcFile.maybeCreateRealFileName( aOwn );
3046  OUString aReal = rSrcFile.maRealFileName;
3047  if (!aReal.isEmpty())
3048  rSrcFile.maFileName = aReal;
3049  }
3050 }
3051 
3053 {
3054  for_each(maRefCells.begin(), maRefCells.end(), RemoveFormulaCell(pCell));
3055 }
3056 
3057 void ScExternalRefManager::addLinkListener(sal_uInt16 nFileId, LinkListener* pListener)
3058 {
3059  LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
3060  if (itr == maLinkListeners.end())
3061  {
3062  pair<LinkListenerMap::iterator, bool> r = maLinkListeners.emplace(
3063  nFileId, LinkListeners());
3064  if (!r.second)
3065  {
3066  OSL_FAIL("insertion of new link listener list failed");
3067  return;
3068  }
3069 
3070  itr = r.first;
3071  }
3072 
3073  LinkListeners& rList = itr->second;
3074  rList.insert(pListener);
3075 }
3076 
3077 void ScExternalRefManager::removeLinkListener(sal_uInt16 nFileId, LinkListener* pListener)
3078 {
3079  LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
3080  if (itr == maLinkListeners.end())
3081  // no listeners for a specified file.
3082  return;
3083 
3084  LinkListeners& rList = itr->second;
3085  rList.erase(pListener);
3086 
3087  if (rList.empty())
3088  // No more listeners for this file. Remove its entry.
3089  maLinkListeners.erase(itr);
3090 }
3091 
3093 {
3094  for (auto& rEntry : maLinkListeners)
3095  rEntry.second.erase(pListener);
3096 }
3097 
3099 {
3100  LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
3101  if (itr == maLinkListeners.end())
3102  // no listeners for a specified file.
3103  return;
3104 
3105  LinkListeners& rList = itr->second;
3106  for_each(rList.begin(), rList.end(), NotifyLinkListener(nFileId, eType));
3107 }
3108 
3110 {
3111  // To avoid potentially freezing Calc, we close one stale document at a time.
3112  DocShellMap::iterator itr = std::find_if(maDocShells.begin(), maDocShells.end(),
3113  [nTimeOut](const DocShellMap::value_type& rEntry) {
3114  // in 100th of a second.
3115  sal_Int32 nSinceLastAccess = (tools::Time( tools::Time::SYSTEM ) - rEntry.second.maLastAccess).GetTime();
3116  return nSinceLastAccess >= nTimeOut;
3117  });
3118  if (itr != maDocShells.end())
3119  {
3120  // Timed out. Let's close this.
3121  itr->second.maShell->DoClose();
3122  maDocShells.erase(itr);
3123  }
3124 
3125  if (maDocShells.empty())
3126  maSrcDocTimer.Stop();
3127 }
3128 
3129 sal_uInt32 ScExternalRefManager::getMappedNumberFormat(sal_uInt16 nFileId, sal_uInt32 nNumFmt, const ScDocument& rSrcDoc)
3130 {
3131  NumFmtMap::iterator itr = maNumFormatMap.find(nFileId);
3132  if (itr == maNumFormatMap.end())
3133  {
3134  // Number formatter map is not initialized for this external document.
3135  pair<NumFmtMap::iterator, bool> r = maNumFormatMap.emplace(
3136  nFileId, SvNumberFormatterMergeMap());
3137 
3138  if (!r.second)
3139  // insertion failed.
3140  return nNumFmt;
3141 
3142  itr = r.first;
3145  itr->second.swap(aMap);
3146  }
3147  const SvNumberFormatterMergeMap& rMap = itr->second;
3148  SvNumberFormatterMergeMap::const_iterator itrNumFmt = rMap.find(nNumFmt);
3149  if (itrNumFmt != rMap.end())
3150  // mapped value found.
3151  return itrNumFmt->second;
3152 
3153  return nNumFmt;
3154 }
3155 
3157 {
3158  DocShellMap::iterator itr = maUnsavedDocShells.begin();
3159  while( itr != maUnsavedDocShells.end() )
3160  {
3161  if ( itr->second.maShell.get() == pShell )
3162  {
3163  // found that the shell is marked as unsaved
3164  OUString aFileURL = pShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
3165  switchSrcFile(itr->first, aFileURL, OUString());
3166  EndListening(*pShell);
3167  maUnsavedDocShells.erase(itr++);
3168  }
3169  }
3170 }
3171 
3173 {
3174  const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&rHint);
3175  if ( !pEventHint )
3176  return;
3177 
3178  SfxEventHintId nEventId = pEventHint->GetEventId();
3179  switch ( nEventId )
3180  {
3181  case SfxEventHintId::PrepareCloseDoc:
3182  {
3183  std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
3184  VclMessageType::Warning, VclButtonsType::Ok,
3185  ScResId(STR_CLOSE_WITH_UNSAVED_REFS)));
3186  xWarn->run();
3187  }
3188  break;
3189  case SfxEventHintId::SaveDocDone:
3190  case SfxEventHintId::SaveAsDocDone:
3191  {
3192  SfxObjectShell* pObjShell = static_cast<const SfxEventHint&>( rHint ).GetObjShell();
3193  transformUnsavedRefToSavedRef(pObjShell);
3194  }
3195  break;
3196  default:
3197  break;
3198  }
3199 }
3200 
3201 IMPL_LINK(ScExternalRefManager, TimeOutHdl, Timer*, pTimer, void)
3202 {
3203  if (pTimer == &maSrcDocTimer)
3205 }
3206 
3207 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual ~LinkListener() COVERITY_NOEXCEPT_FALSE=0
Matrix data type that can store values of mixed types.
Definition: scmatrix.hxx:113
ScDocument * getInMemorySrcDocument(sal_uInt16 nFileId)
bool is() const
bool hasCellExternalReference(const ScAddress &rCell)
void setFilterData(sal_uInt16 nFileId, const OUString &rFilterName, const OUString &rOptions)
Set the filter name and options if any for a given source document.
svString
const SrcFileData * getExternalFileData(sal_uInt16 nFileId) const
UNOTOOLS_DLLPUBLIC bool Exists(OUString const &url)
SCTAB getCachedTabSpan(sal_uInt16 nFileId, const OUString &rStartTabName, const OUString &rEndTabName) const
Get the span (distance+sign(distance)) of two sheets of a specified file.
void notifyAllLinkListeners(sal_uInt16 nFileId, LinkUpdateType eType)
Notify all listeners that are listening to a specified external document.
svl::SharedString maString
bool setCacheDocReferenced(sal_uInt16 nFileId)
Set all tables of a document as referenced, used only during store-to-file.
sal_Int32 nIndex
OUString getString() const
ScExternalRefManager(ScDocument &rDoc)
ScDocShell * GetDocShell() const
Definition: viewdata.hxx:354
ScAddress aStart
Definition: address.hxx:500
void storeRangeNameTokens(sal_uInt16 nFileId, const OUString &rName, const ScTokenArray &rArray)
SharedString intern(const OUString &rStr)
SC_DLLPUBLIC svl::SharedStringPool & GetSharedStringPool()
Definition: documen2.cxx:563
virtual void notify(sal_uInt16 nFileId, LinkUpdateType eType)=0
static std::unique_ptr< ScTokenArray > lcl_fillEmptyMatrix(const ScDocument &rDoc, const ScRange &rRange)
Store parameters used in the ScDocument::SetString() method.
Definition: stringutil.hxx:35
SC_DLLPUBLIC void setCell(SCCOL nCol, SCROW nRow, TokenRef const &pToken, sal_uLong nFmtIndex=0, bool bSetCacheRange=true)
Add cell value to the cache.
ScDocument & GetDoc() const
Definition: column.hxx:182
#define EMPTY_OUSTRING
Definition: global.hxx:214
sal_uInt16 getExternalFileCount() const
void initializeDoc(sal_uInt16 nFileId, const ::std::vector< OUString > &rTabNames, const OUString &rBaseName)
void getAllTableNames(sal_uInt16 nFileId,::std::vector< OUString > &rTabNames) const
SCROW Row() const
Definition: address.hxx:262
const char aData[]
OUString getString(const ScDocument *pDoc) const
Retrieve string value.
Definition: cellvalue.cxx:660
void setTextInput()
Call this whenever you need to unconditionally set input as text, no matter what the input is...
Definition: stringutil.cxx:38
void insertRefCellFromTemplate(ScFormulaCell *pTemplateCell, ScFormulaCell *pCell)
Add a cell to reference the same files as the template cell.
void clearCache(sal_uInt16 nFileId)
Clear all caches including the cache tables.
static SC_DLLPUBLIC::utl::TransliterationWrapper * GetpTransliteration()
Definition: global.cxx:976
Single reference (one address) into the sheet.
Definition: refdata.hxx:30
std::string GetValue
static weld::Window * GetActiveDialogParent()
Definition: docsh.cxx:2949
virtual ~ScExternalRefManager() override
const OUString * getRealRangeName(sal_uInt16 nFileId, const OUString &rRangeName) const
bool setCacheTableReferenced(sal_uInt16 nFileId, const OUString &rTabName, size_t nSheets)
Set a table as referenced, used only during store-to-file.
SC_DLLPUBLIC const ScFormulaCell * GetFormulaCell(const ScAddress &rPos) const
Definition: document.cxx:3723
std::set< ScFormulaCell * > RefCellSet
void FillMatrix(ScMatrix &rMat, SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, svl::SharedStringPool *pPool=nullptr) const
Definition: documen8.cxx:384
const OUString & GetName() const
ScTokenArray * GetCode()
sal_uIntPtr sal_uLong
void breakLink(sal_uInt16 nFileId)
SC_DLLPUBLIC ScRangeName * GetRangeName(SCTAB nTab) const
Definition: documen3.cxx:168
void removeRefCell(ScFormulaCell *pCell)
Stop tracking a specific formula cell.
void updateAbsAfterLoad()
Replace the original URL with the real URL that was generated from the relative URL.
std::vector< OUString > getAllCachedExternalFileNames() const
Get all cached external file names as an array.
void maybeCreateRealFileName(const OUString &rOwnDocName)
Source document meta-data container.
sal_Int64 n
void getAllNumberFormats(::std::vector< sal_uInt32 > &rNumFmts) const
bool HasOpCode(OpCode) const
Any GetTime(const OUString &val)
bool areAllCacheTablesReferenced() const
void setAllCacheTableReferencedStati(bool bReferenced)
static FormulaToken * convertToToken(ScDocument &rHostDoc, const ScDocument &rSrcDoc, ScRefCellValue &rCell)
SvNumFormatType GetType(sal_uInt32 nFIndex) const
ScExternalRefCache::TokenArrayRef getRangeNameTokens(sal_uInt16 nFileId, const OUString &rName, const ScAddress *pCurPos=nullptr)
Get an array of tokens corresponding with a specified name in a specified file.
sal_Int16 nId
const ContentProperties & rData
bool IsExternalRef() const
void setRangeNameTokens(sal_uInt16 nFileId, const OUString &rName, TokenArrayRef pArray)
const ScExtDocSettings & GetDocSettings() const
Definition: scextopt.cxx:170
SC_DLLPUBLIC void setWholeTableCached()
Call this to mark the entire table "cached".
SC_DLLPUBLIC void GetNumberFormat(SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt32 &rFormat) const
Definition: document.cxx:3641
void PaintGrid()
Definition: tabview3.cxx:2621
static std::unique_ptr< ScTokenArray > convertToTokenArray(ScDocument &rHostDoc, const ScDocument &rSrcDoc, ScRange &rRange, vector< ScExternalRefCache::SingleRangeData > &rCacheData)
ScAddress aEnd
Definition: address.hxx:501
Extended options held by an ScDocument containing additional settings for filters.
Definition: scextopt.hxx:77
Cache table for external reference data.
ScExternalRefManager * mpMgr
bool isValidRangeName(sal_uInt16 nFileId, const OUString &rName) const
sal_uInt16 getExternalFileId(const OUString &rFile)
void SetIsExtRef()
void resetSrcFileData(const OUString &rBaseFileUrl)
Re-generates relative names for all stored source files.
DataType::iterator iterator
Definition: rangenam.hxx:197
bool HasExternalRef() const
bool isDocInitialized(sal_uInt16 nFileId)
for(size_t a(0);a< count;a++)
This is very similar to ScCellValue, except that it references the original value instead of copying ...
Definition: cellvalue.hxx:104
SVL_DLLPUBLIC OUString simpleNormalizedMakeRelative(OUString const &baseUriReference, OUString const &uriReference)
TableNameIndexMap::const_iterator findTableNameIndex(const OUString &rTabName) const
void getAllCols(SCROW nRow,::std::vector< SCCOL > &rCols, SCCOL nLow=0, SCCOL nHigh=MAXCOL) const
Obtain a sorted vector of columns.
void insertRefCell(sal_uInt16 nFileId, const ScAddress &rCell)
SfxApplication * SfxGetpApp()
ScExternalRefCache::TokenRef getCellData(sal_uInt16 nFileId, const OUString &rTabName, SCCOL nCol, SCROW nRow, sal_uInt32 *pnFmtIndex)
Get a cached cell data at specified cell location.
bool IsExecuteLinkEnabled() const
Definition: document.hxx:1538
const OUString * getExternalFileName(sal_uInt16 nFileId, bool bForceOriginal=false)
It returns a pointer to the name of the URI associated with a given external file ID...
OUString GetBase() const
HashMap_OWString_Interface aMap
ScExternalRefCache::TokenRef getSingleRefTokenFromSrcDoc(sal_uInt16 nFileId, ScDocument &rSrcDoc, const ScAddress &rPos, ScExternalRefCache::CellFormat *pFmt)
OUString maRealFileName
original file name as loaded from the file.
TableName(const OUString &rUpper, const OUString &rReal)
ScExternalRefCache::TokenArrayRef getCellRangeData(sal_uInt16 nFileId, const OUString &rTabName, const ScRange &rRange)
Get a cached cell range data.
void getAllCachedNumberFormats(::std::vector< sal_uInt32 > &rNumFmts) const
Get all unique number format indices that are used in the cache tables.
ScRefCellValue GetCellValue(SCROW nRow) const
Definition: column.cxx:732
size_t SCSIZE
size_t typedef to be able to find places where code was changed from USHORT to size_t and is used to ...
Definition: address.hxx:45
void convertToAbsName(OUString &rFile) const
Takes a flat file name, and convert it to an absolute URL path.
#define SAL_MAX_UINT16
SC_DLLPUBLIC SCTAB GetTableCount() const
Definition: document.cxx:314
ScExternalRefCache::TokenArrayRef getRangeNameTokens(sal_uInt16 nFileId, const OUString &rName)
ScDocument * getSrcDocument(sal_uInt16 nFileId)
double GetValue()
FormulaError GetErrCode()
static bool IsFuzzing()
ScFormulaCell * mpCell
const SCROW MAXROW
Definition: address.hxx:69
SC_DLLPUBLIC bool GetTable(const OUString &rName, SCTAB &rTab) const
Definition: document.cxx:260
SC_DLLPUBLIC ScExternalRefManager * GetExternalRefManager() const
Definition: documen3.cxx:618
svDouble
IMPL_LINK(ScExternalRefManager, TimeOutHdl, Timer *, pTimer, void)
::boost::intrusive_ptr< ScMatrix > ScMatrixRef
Definition: types.hxx:26
bool getSrcDocTable(const ScDocument &rSrcDoc, const OUString &rTabName, SCTAB &rTab, sal_uInt16 nFileId) const
void set(const ScDocument &rDoc, SCTAB nTab, SCCOL nCol, SCROW nRow, bool bVal)
virtual OUString getCacheTableName(sal_uInt16 nFileId, size_t nTabIndex) const override
ScFormulaCell * mpFormula
Definition: cellvalue.hxx:111
SCTAB Tab() const
Definition: address.hxx:271
void SetRow(SCROW nRowP)
Definition: address.hxx:275
void setReferenced(bool bReferenced)
Set/clear referenced status flag only if current status is not REFERENCED_PERMANENT.
SvNumberFormatterMergeMap ConvertMergeTableToMap()
bool hasEmptyValue()
Definition: cellvalue.cxx:675
FormulaError GetDoubleErrorValue(double fVal)
void setCellData(sal_uInt16 nFileId, const OUString &rTabName, SCCOL nCol, SCROW nRow, TokenRef const &pToken, sal_uLong nFmtIndex)
sal_uInt32 GetNumberFormat(const ScInterpreterContext &rContext, SCROW nRow) const
Definition: column.cxx:420
SotClipboardFormatId
bool insertName(OUString const &rTheName, bool bAppendFinalSlash=false, sal_Int32 nIndex=LAST_SEGMENT, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
const OUString & GetName() const
SC_DLLPUBLIC const_iterator begin() const
Definition: rangenam.cxx:780
static OUString GetOptions(const SfxMedium &rMedium)
Definition: tablink.cxx:422
ScAddress aPos
void GetName(OUString &rName) const
Definition: rangenam.hxx:111
sal_uInt32 mnLinkCnt
Recursive counter for loading external documents.
Definition: scextopt.hxx:32
SC_DLLPUBLIC::std::pair< SCCOL, SCCOL > getColRange(SCROW nRow) const
Returns the half-open range of used columns in the specified row. Returns [0,0) if row is empty...
void removeLinkListener(sal_uInt16 nFileId, LinkListener *pListener)
Remove an existing link listener.
std::function< void(size_t, size_t)> EmptyOpFunction
Definition: scmatrix.hxx:133
void SetCol(SCCOL nColP)
Definition: address.hxx:279
bool MarkUsedExternalReferences()
void getAllNumberFormats(::std::vector< sal_uInt32 > &rNumFmts) const
void SetDirty(bool bDirtyFlag=true)
void InsertFileLink(sfx2::SvBaseLink &, SvBaseLinkObjectType nFileType, const OUString &rFileNm, const OUString *pFilterNm=nullptr, const OUString *pRange=nullptr)
::std::vector< DocReferenced > DocReferencedVec
ocSep
void SetDebugName(const char *pDebugName)
void clearCache(sal_uInt16 nFileId)
double distance
DocumentType eType
void setRelativeFileName(sal_uInt16 nFileId, const OUString &rRelUrl)
Set a relative file path for the specified file ID.
void executeColumnAction(ScDocument &rDoc, ColumnAction &ac) const
void getAllRows(::std::vector< SCROW > &rRows, SCROW nLow=0, SCROW nHigh=MAXROW) const
Obtain a sorted vector of rows.
static SC_DLLPUBLIC OUString GetAbsDocName(const OUString &rFileName, const SfxObjectShell *pShell)
Definition: global2.cxx:284
void SetTab(SCTAB nTabP)
Definition: address.hxx:283
struct ScExternalRefCache::ReferencedStatus maReferenced
#define SRCDOC_LIFE_SPAN
SC_DLLPUBLIC ScRangeData * findByUpperName(const OUString &rName)
Definition: rangenam.cxx:682
bool ShrinkToUsedDataArea(bool &o_bShrunk, SCTAB nTab, SCCOL &rStartCol, SCROW &rStartRow, SCCOL &rEndCol, SCROW &rEndRow, bool bColumnsOnly, bool bStickyTopRow=false, bool bStickyLeftCol=false, bool bConsiderCellNotes=false, bool bConsiderCellDrawObjects=false) const
Shrink a range to only include used data area.
Definition: document.cxx:1071
SC_DLLPUBLIC SvNumberFormatter * GetFormatTable() const
Definition: documen2.cxx:438
SC_DLLPUBLIC const_iterator end() const
Definition: rangenam.cxx:785
virtual void Notify(SfxBroadcaster &rBC, const SfxHint &rHint) override
void InitBlockPosition(sc::ColumnBlockPosition &rBlockPos)
Definition: column3.cxx:1043
void EnableExecuteLink(bool bVal)
Definition: document.hxx:1539
SfxItemState GetItemState(sal_uInt16 nWhich, bool bSrchInParent=true, const SfxPoolItem **ppItem=nullptr) const
int i
uno_Any a
SCTAB getTabSpan(sal_uInt16 nFileId, const OUString &rStartTabName, const OUString &rEndTabName) const
TokenRef getEmptyOrNullToken(SCCOL nCol, SCROW nRow) const
bool isRangeCached(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const
Base class for objects that need to listen to link updates.
SC_DLLPUBLIC TokenRef getCell(SCCOL nCol, SCROW nRow, sal_uInt32 *pnFmtIndex=nullptr) const
OUString maRelativeName
file name created from the relative name.
SC_DLLPUBLIC::std::pair< SCROW, SCROW > getRowRange() const
Returns the half-open range of used rows in this table. Returns [0,0) if table is empty...
sal_Int32 mnType
sal_Int16 SCCOL
Definition: types.hxx:22
#define SC_MOD()
Definition: scmod.hxx:253
const SCCOL MAXCOL
Definition: address.hxx:70
NamePairMap maRealRangeNameMap
Upper- to real-case mapping for range names.
::std::map< sal_uInt32, sal_uInt32 > SvNumberFormatterMergeMap
void setCellRangeData(sal_uInt16 nFileId, const ScRange &rRange, const ::std::vector< SingleRangeData > &rData, const TokenArrayRef &pArray)
::std::vector< TableName > maTableNames
Table name list in correct order, in both upper- and real-case.
sal_uInt32 getMappedNumberFormat(sal_uInt16 nFileId, sal_uInt32 nNumFmt, const ScDocument &rSrcDoc)
const OUString * getRealTableName(sal_uInt16 nFileId, const OUString &rTabName) const
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
const OUString & GetInitTabPrefix() const
bool hasExternalData() const
OUString uppercase(const OUString &rStr, sal_Int32 nPos, sal_Int32 nCount) const
void fillCellFormat(sal_uLong nFmtIndex, ScExternalRefCache::CellFormat *pFmt) const
Structure that stores segments of boolean flags per column, and perform custom action on those segmen...
OUString ScResId(const char *pId)
Definition: scdll.cxx:95
o3tl::sorted_vector< LinkListener * > LinkListeners
virtual void Start() override
OUString maSingleTableNameAlias
Either the base name that was stored as sheet name for CSV files if sheet name is Sheet1...
::std::vector< TableTypeRef > maTables
The raw cache tables.
bool ShrinkToDataArea(SCTAB nTab, SCCOL &rStartCol, SCROW &rStartRow, SCCOL &rEndCol, SCROW &rEndRow) const
Shrink a range to only include data area.
Definition: document.cxx:1040
void clearCacheTables(sal_uInt16 nFileId)
Clear all caches but keep the tables.
ScExternalRefCache::TokenArrayRef getDoubleRefTokensFromSrcDoc(const ScDocument &rSrcDoc, const OUString &rTabName, ScRange &rRange,::std::vector< ScExternalRefCache::SingleRangeData > &rCacheData)
Retrieve a range token array from a source document instance.
FormulaError GetCodeError() const
SvNumFormatType
SfxItemSet * GetItemSet() const
DocShellMap maUnsavedDocShells
DocShells to unsaved but referenced documents.
void getAllCachedTableNames(sal_uInt16 nFileId,::std::vector< OUString > &rTabNames) const
Returns a vector containing all (real) table names and cache tables of the specified file...
bool refreshSrcDocument(sal_uInt16 nFileId)
ScExternalRefCache::TableTypeRef getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
Get a cache table instance for specified table and table index.
void maybeCreateRealFileName(sal_uInt16 nFileId)
Try to create a "real" file name from the relative path.
bool mbInReferenceMarking
Status whether in reference marking state.
void StartListening(SfxBroadcaster &rBroadcaster, DuplicateHandling eDuplicateHanding=DuplicateHandling::Unexpected)
const_iterator end() const
void erase(const ScRangeData &r)
Definition: rangenam.cxx:853
bool empty() const
Shell instance for a source document.
static ScViewData * GetViewData()
Definition: docsh4.cxx:2540
void SetTimeout(sal_uInt64 nTimeoutMs)
void getAllCachedDataSpans(const ScDocument &rSrcDoc, sal_uInt16 nFileId, sc::ColumnSpanSet &rSet) const
Collect all cached non-empty cell positions, inferred directly from the cached data, not the cached range metadata stored separately in the Table.
SfxViewShell * GetViewShell() const
LinkListenerMap maLinkListeners
SCTAB Tab() const
Definition: refdata.cxx:254
void maybeLinkExternalFile(sal_uInt16 nFileId, bool bDeferFilterDetection=false)
ScExternalRefCache::TableTypeRef getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
virtual sal_uInt16 GetIndex() const
ScExternalRefCache::TokenRef getSingleRefToken(sal_uInt16 nFileId, const OUString &rTabName, const ScAddress &rCell, const ScAddress *pCurPos, SCTAB *pTab, ScExternalRefCache::CellFormat *pFmt=nullptr)
svSingleRef
bool hasExternalFile(sal_uInt16 nFileId) const
FormulaError
RefCellMap maRefCells
List of referencing cells that may contain external names.
SCCOL Col() const
Definition: address.hxx:267
std::vector< SrcFileData > maSrcFiles
List of external source document meta-data, used to keep track of external document identifiers...
static ScExternalRefCache::TokenArrayRef getRangeNameTokensFromSrcDoc(sal_uInt16 nFileId, const ScDocument &rSrcDoc, OUString &rName)
Retrieve range name token array from a source document instance.
individual cell within cached external ref table.
const_iterator begin() const
ocIndirect
OUString GetMainURL(DecodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
svDoubleRef
CellType meType
Definition: cellvalue.hxx:106
svl::SharedString GetString()
float v
static bool GetDisplayNames(const SvBaseLink *, OUString *pType, OUString *pFile=nullptr, OUString *pLink=nullptr, OUString *pFilter=nullptr)
bool getSrcDocTable(const ScDocument &rSrcDoc, const OUString &rTabName, SCTAB &rTab, sal_uInt16 nFileId) const
::formula::FormulaTokenRef TokenRef
sal_Int32 SCROW
Definition: types.hxx:18
SC_DLLPUBLIC void PutInOrder()
Definition: address.cxx:1573
comphelper::EmbeddedObjectContainer & GetEmbeddedObjectContainer() const
void enableDocTimer(bool bEnable)
void setRangeName(sal_uInt16 nFileId, const OUString &rName)
ScDocumentUniquePtr mxFakeDoc
void setAllCacheTableReferencedStati(bool bReferenced)
void Stop()
std::function< void(size_t, size_t, double)> DoubleOpFunction
Definition: scmatrix.hxx:130
void LockAdjustHeight()
Definition: document.hxx:1536
SfxEventHintId GetEventId() const
static bool lcl_getStrictTableDataIndex(const ScExternalRefCache::TableNameIndexMap &rMap, const OUString &rName, size_t &rIndex)
#define SAL_WARN_IF(condition, area, stream)
#define ERRCODE_NONE
SvBaseLink * pLink
static SfxObjectShell * GetFirst(const std::function< bool(const SfxObjectShell *)> &isObjectShell=nullptr, bool bOnlyVisible=true)
SCCOL GetCol() const
Definition: column.hxx:184
ScExternalRefCache::TokenArrayRef getDoubleRefTokens(sal_uInt16 nFileId, const OUString &rTabName, const ScRange &rRange, const ScAddress *pCurPos)
Get an array of tokens that consist of the specified external cell range.
const OUString * getRealRangeName(sal_uInt16 nFileId, const OUString &rRangeName) const
OUString getTableName(sal_uInt16 nFileId, size_t nCacheId) const
const ScDocument * getFakeDoc() const
virtual void Invalidate(sal_uInt16 nId=0)
const OUString * getRealTableName(sal_uInt16 nFileId, const OUString &rTabName) const
#define SRCDOC_SCAN_INTERVAL
ScDocument & cacheNewDocShell(sal_uInt16 nFileId, SrcShell &rSrcShell)
Caller must ensure that the passed shell is not already stored.
DocItem * getDocItem(sal_uInt16 nFileId) const
OUString aName
std::shared_ptr< ScTokenArray > TokenArrayRef
void addFilesToLinkManager()
Add all known external files to the LinkManager.
std::shared_ptr< Table > TableTypeRef
void purgeStaleSrcDocument(sal_Int32 nTimeOut)
Purge those source document instances that have not been accessed for the specified duration...
SfxEventHintId
std::function< void(size_t, size_t, svl::SharedString)> StringOpFunction
Definition: scmatrix.hxx:132
ScExtDocOptions * GetExtDocOptions()
Definition: document.hxx:639
void setCachedCellRange(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
void CompileTokenArray(bool bNoListening=false)
void EndListening(SfxBroadcaster &rBroadcaster, bool bRemoveAllDuplicates=false)
bool isValidRangeName(sal_uInt16 nFileId, const OUString &rName)
DocShellMap maDocShells
Source document cache.
const INetURLObject & GetURLObject() const
bool hasRow(SCROW nRow) const
static SC_DLLPUBLIC const CharClass * getCharClassPtr()
Definition: global.cxx:1010
void * p
void SetInvokeHandler(const Link< Timer *, void > &rLink)
void SetCompile(bool bVal)
static void lcl_removeByFileId(sal_uInt16 nFileId, MapContainer &rMap)
const ScDocument & GetDocument() const
Definition: docsh.hxx:216
sc::FormulaResultValue GetResult()
LinkedDocMap maLinkedDocs
list of source documents that are managed by the link manager.
void markUsedByLinkListeners()
Mark all tables as referenced that are used by any LinkListener, used only during store-to-file...
bool getTableDataIndex(const OUString &rTabName, size_t &rIndex) const
std::unordered_map< OUString, size_t > TableNameIndexMap
std::unordered_map< OUString, TokenArrayRef > RangeNameMap
void SetDocumentModified()
Definition: docsh.cxx:3158
static bool GetFilterName(const OUString &rFileName, OUString &rFilter, OUString &rOptions, bool bWithContent, bool bWithInteraction)
Returns the filter name and options from a file name.
Definition: tablink.cxx:432
void addCacheDocToReferenced(sal_uInt16 nFileId)
ScInterpreterContext & GetNonThreadedContext() const
Definition: document.hxx:612
ScExternalRefCache maRefCache
cache of referenced ranges and names from source documents.
bool isInCachedRanges(SCCOL nCol, SCROW nRow) const
void addCacheTableToReferenced(sal_uInt16 nFileId, size_t nIndex)
SCTAB GetTab() const
Definition: column.hxx:183
SfxObjectShell * GetDocumentShell() const
Definition: document.hxx:1058
#define SAL_WARN(area, stream)
bool mbUserInteractionEnabled
Controls whether or not to allow user interaction.
static SfxObjectShell * GetNext(const SfxObjectShell &rPrev, const std::function< bool(const SfxObjectShell *)> &isObjectShell=nullptr, bool bOnlyVisible=true)
bool isFileLoadable(const OUString &rFile) const
Check if the file specified by the path is a legitimate file that exists & can be loaded...
SfxObjectShellRef loadSrcDocument(sal_uInt16 nFileId, OUString &rFilter)
void refreshAllRefCells(sal_uInt16 nFileId)
TableNameIndexMap maTableNameIndex
Table name to index map.
ApiGuard(const ScDocument &rDoc)
if(!pCandidateA->getEnd().equal(pCandidateB->getStart()))
void SetCodeError(FormulaError n)
RangeArrayMap maRangeArrays
Token array cache for cell ranges.
bool DoLoad(SfxMedium *pMedium)
UNOTOOLS_DLLPUBLIC bool IsFolder(OUString const &url)
SC_DLLPUBLIC void SetExtDocOptions(std::unique_ptr< ScExtDocOptions > pNewOptions)
Definition: documen3.cxx:2005
void transformUnsavedRefToSavedRef(SfxObjectShell *pShell)
If in maUnsavedDocShells move it to maDocShells and create a correct external reference entry...
int mnIndex
std::pair< const_iterator, bool > insert(Value &&x)
RangeNameMap maRangeNames
Range name cache.
INetURLObject smartRel2Abs(OUString const &rTheRelURIRef, bool &rWasAbsolute, bool bIgnoreFragment=false, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8, bool bRelativeNonURIs=false, FSysStyle eStyle=FSysStyle::Detect) const
SC_DLLPUBLIC sfx2::LinkManager * GetLinkManager()
Definition: documen2.cxx:215
SC_DLLPUBLIC void EnableUndo(bool bVal)
Definition: document.cxx:6470
OUString getOwnDocumentName() const
std::unordered_map< SCCOL, Cell > RowDataType
bool isOwnDocument(const OUString &rFile) const
ScTokenArray * GetCode()
Definition: rangenam.hxx:120
RedlineType meType
void setCachedCell(SCCOL nCol, SCROW nRow)
void addLinkListener(sal_uInt16 nFileId, LinkListener *pListener)
Register a new link listener to a specified external document.
aStr
SC_DLLPUBLIC bool GetName(SCTAB nTab, OUString &rName) const
Definition: document.cxx:213
std::function< void(size_t, size_t, bool)> BoolOpFunction
Definition: scmatrix.hxx:131
bool setCacheTableReferenced(sal_uInt16 nFileId, const OUString &rTabName, size_t nSheets)
Set a table as referenced, used only during store-to-file.
std::unique_ptr< ScTokenArray > Clone() const
Definition: token.cxx:1920
static weld::MessageDialog * CreateMessageDialog(weld::Widget *pParent, VclMessageType eMessageType, VclButtonsType eButtonType, const OUString &rPrimaryMessage, bool bMobile=false)
Create before modifications of the document and destroy thereafter.
Definition: docsh.hxx:450
SfxLinkUpdateMode
sal_Int16 SCTAB
Definition: types.hxx:23
SvNumberFormatterIndexTable * MergeFormatter(SvNumberFormatter &rNewTable)
size_type erase(const Value &x)
Represents data cached for a single external document.
bool getSingleTableNameAlternative(OUString &rTabName) const
SC_DLLPUBLIC void EnableUserInteraction(bool bVal)
Definition: document.cxx:6486
SfxMedium * GetMedium() const
void switchSrcFile(sal_uInt16 nFileId, const OUString &rNewFile, const OUString &rNewFilter)