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