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