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