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