1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <externalrefmgr.hxx>
21 #include <document.hxx>
23 #include <tokenarray.hxx>
24 #include <address.hxx>
25 #include <tablink.hxx>
27 #include <scextopt.hxx>
28 #include <rangenam.hxx>
29 #include <formulacell.hxx>
30 #include <viewdata.hxx>
31 #include <tabvwsh.hxx>
33 #include <globstr.hrc>
34 #include <scresid.hxx>
35 #include <cellvalue.hxx>
36 #include <defaultsoptions.hxx>
39 #include <o3tl/safeint.hxx>
40 #include <osl/file.hxx>
41 #include <sfx2/app.hxx>
42 #include <sfx2/docfile.hxx>
43 #include <sfx2/event.hxx>
44 #include <sfx2/fcontnr.hxx>
45 #include <sfx2/objsh.hxx>
46 #include <svl/itemset.hxx>
47 #include <svl/stritem.hxx>
48 #include <svl/urihelper.hxx>
49 #include <svl/sharedstringpool.hxx>
50 #include <sfx2/linkmgr.hxx>
51 #include <tools/urlobj.hxx>
52 #include <unotools/charclass.hxx>
53 #include <unotools/configmgr.hxx>
54 #include <unotools/ucbhelper.hxx>
55 #include <vcl/svapp.hxx>
56 #include <vcl/weld.hxx>
57 #include <stringutil.hxx>
58 #include <scmatrix.hxx>
59 #include <columnspanset.hxx>
61 #include <com/sun/star/document/MacroExecMode.hpp>
62 #include <com/sun/star/document/UpdateDocMode.hpp>
63 #include <sal/log.hxx>
68 using ::std::unique_ptr
;
69 using ::com::sun::star::uno::Any
;
72 using ::std::for_each
;
73 using ::std::distance
;
75 using namespace formula
;
77 #define SRCDOC_LIFE_SPAN 30000 // 5 minutes (in 100th of a sec)
78 #define SRCDOC_SCAN_INTERVAL 1000*30 // every 30 seconds (in msec)
82 class TabNameSearchPredicate
85 explicit TabNameSearchPredicate(const OUString
& rSearchName
) :
86 maSearchName(ScGlobal::getCharClassPtr()->uppercase(rSearchName
))
90 bool operator()(const ScExternalRefCache::TableName
& rTabNameSet
) const
92 // Ok, I'm doing case insensitive search here.
93 return rTabNameSet
.maUpperName
== maSearchName
;
97 OUString maSearchName
;
100 class FindSrcFileByName
103 explicit FindSrcFileByName(const OUString
& rMatchName
) :
104 mrMatchName(rMatchName
)
108 bool operator()(const ScExternalRefManager::SrcFileData
& rSrcData
) const
110 return rSrcData
.maFileName
== mrMatchName
;
114 const OUString
& mrMatchName
;
117 class NotifyLinkListener
120 NotifyLinkListener(sal_uInt16 nFileId
, ScExternalRefManager::LinkUpdateType eType
) :
121 mnFileId(nFileId
), meType(eType
) {}
123 void operator() (ScExternalRefManager::LinkListener
* p
) const
125 p
->notify(mnFileId
, meType
);
129 ScExternalRefManager::LinkUpdateType meType
;
132 struct UpdateFormulaCell
134 void operator() (ScFormulaCell
* pCell
) const
136 // Check to make sure the cell really contains svExternal*.
137 // External names, external cell and range references all have a
138 // token of svExternal*. Additionally check for INDIRECT() that can be
139 // called with any constructed URI string.
140 ScTokenArray
* pCode
= pCell
->GetCode();
141 if (!pCode
->HasExternalRef() && !pCode
->HasOpCode(ocIndirect
))
144 if (pCode
->GetCodeError() != FormulaError::NONE
)
146 // Clear the error code, or a cell with error won't get re-compiled.
147 pCode
->SetCodeError(FormulaError::NONE
);
148 pCell
->SetCompile(true);
149 pCell
->CompileTokenArray();
156 class RemoveFormulaCell
159 explicit RemoveFormulaCell(ScFormulaCell
* p
) : mpCell(p
) {}
160 void operator() (pair
<const sal_uInt16
, ScExternalRefManager::RefCellSet
>& r
) const
162 r
.second
.erase(mpCell
);
165 ScFormulaCell
* mpCell
;
168 class ConvertFormulaToStatic
171 explicit ConvertFormulaToStatic(ScDocument
* pDoc
) : mpDoc(pDoc
) {}
172 void operator() (ScFormulaCell
* pCell
) const
174 ScAddress aPos
= pCell
->aPos
;
176 // We don't check for empty cells because empty external cells are
177 // treated as having a value of 0.
179 if (pCell
->IsValue())
181 // Turn this into value cell.
182 mpDoc
->SetValue(aPos
, pCell
->GetValue());
186 // string cell otherwise.
187 ScSetStringParam aParam
;
188 aParam
.setTextInput();
189 mpDoc
->SetString(aPos
, pCell
->GetString().getString(), &aParam
);
197 * Check whether a named range contains an external reference to a
198 * particular document.
200 bool hasRefsToSrcDoc(ScRangeData
& rData
, sal_uInt16 nFileId
)
202 ScTokenArray
* pArray
= rData
.GetCode();
206 formula::FormulaTokenArrayPlainIterator
aIter(*pArray
);
207 formula::FormulaToken
* p
= aIter
.GetNextReference();
208 for (; p
; p
= aIter
.GetNextReference())
210 if (!p
->IsExternalRef())
213 if (p
->GetIndex() == nFileId
)
219 class EraseRangeByIterator
221 ScRangeName
& mrRanges
;
223 explicit EraseRangeByIterator(ScRangeName
& rRanges
) : mrRanges(rRanges
) {}
224 void operator() (const ScRangeName::iterator
& itr
)
231 * Remove all named ranges that contain references to specified source
234 void removeRangeNamesBySrcDoc(ScRangeName
& rRanges
, sal_uInt16 nFileId
)
236 ScRangeName::iterator itr
= rRanges
.begin(), itrEnd
= rRanges
.end();
237 vector
<ScRangeName::iterator
> v
;
238 for (; itr
!= itrEnd
; ++itr
)
240 if (hasRefsToSrcDoc(*itr
->second
, nFileId
))
243 for_each(v
.begin(), v
.end(), EraseRangeByIterator(rRanges
));
248 ScExternalRefCache::Table::Table()
249 : mbReferenced( true )
250 // Prevent accidental data loss due to lack of knowledge.
254 ScExternalRefCache::Table::~Table()
258 void ScExternalRefCache::Table::clear()
261 maCachedRanges
.RemoveAll();
265 void ScExternalRefCache::Table::setReferenced( bool bReferenced
)
267 mbReferenced
= bReferenced
;
270 bool ScExternalRefCache::Table::isReferenced() const
275 void ScExternalRefCache::Table::setCell(SCCOL nCol
, SCROW nRow
, TokenRef
const & pToken
, sal_uLong nFmtIndex
, bool bSetCacheRange
)
278 RowsDataType::iterator itrRow
= maRows
.find(nRow
);
279 if (itrRow
== maRows
.end())
281 // This row does not exist yet.
282 pair
<RowsDataType::iterator
, bool> res
= maRows
.emplace(
283 nRow
, RowDataType());
291 // Insert this token into the specified column location. I don't need to
292 // check for existing data. Just overwrite it.
293 RowDataType
& rRow
= itrRow
->second
;
294 ScExternalRefCache::Cell aCell
;
295 aCell
.mxToken
= pToken
;
296 aCell
.mnFmtIndex
= nFmtIndex
;
297 rRow
.emplace(nCol
, aCell
);
299 setCachedCell(nCol
, nRow
);
302 ScExternalRefCache::TokenRef
ScExternalRefCache::Table::getCell(SCCOL nCol
, SCROW nRow
, sal_uInt32
* pnFmtIndex
) const
304 RowsDataType::const_iterator itrTable
= maRows
.find(nRow
);
305 if (itrTable
== maRows
.end())
307 // this table doesn't have the specified row.
308 return getEmptyOrNullToken(nCol
, nRow
);
311 const RowDataType
& rRowData
= itrTable
->second
;
312 RowDataType::const_iterator itrRow
= rRowData
.find(nCol
);
313 if (itrRow
== rRowData
.end())
315 // this row doesn't have the specified column.
316 return getEmptyOrNullToken(nCol
, nRow
);
319 const Cell
& rCell
= itrRow
->second
;
321 *pnFmtIndex
= rCell
.mnFmtIndex
;
323 return rCell
.mxToken
;
326 bool ScExternalRefCache::Table::hasRow( SCROW nRow
) const
328 RowsDataType::const_iterator itrRow
= maRows
.find(nRow
);
329 return itrRow
!= maRows
.end();
332 void ScExternalRefCache::Table::getAllRows(vector
<SCROW
>& rRows
, SCROW nLow
, SCROW nHigh
) const
335 aRows
.reserve(maRows
.size());
336 for (const auto& rEntry
: maRows
)
337 if (nLow
<= rEntry
.first
&& rEntry
.first
<= nHigh
)
338 aRows
.push_back(rEntry
.first
);
340 // hash map is not ordered, so we need to explicitly sort it.
341 ::std::sort(aRows
.begin(), aRows
.end());
345 ::std::pair
< SCROW
, SCROW
> ScExternalRefCache::Table::getRowRange() const
347 ::std::pair
< SCROW
, SCROW
> aRange( 0, 0 );
348 if( !maRows
.empty() )
350 // iterate over entire container (hash map is not sorted by key)
351 auto itMinMax
= std::minmax_element(maRows
.begin(), maRows
.end(),
352 [](const RowsDataType::value_type
& a
, const RowsDataType::value_type
& b
) { return a
.first
< b
.first
; });
353 aRange
.first
= itMinMax
.first
->first
;
354 aRange
.second
= itMinMax
.second
->first
+ 1;
359 void ScExternalRefCache::Table::getAllCols(SCROW nRow
, vector
<SCCOL
>& rCols
, SCCOL nLow
, SCCOL nHigh
) const
361 RowsDataType::const_iterator itrRow
= maRows
.find(nRow
);
362 if (itrRow
== maRows
.end())
363 // this table doesn't have the specified row.
366 const RowDataType
& rRowData
= itrRow
->second
;
368 aCols
.reserve(rRowData
.size());
369 for (const auto& rCol
: rRowData
)
370 if (nLow
<= rCol
.first
&& rCol
.first
<= nHigh
)
371 aCols
.push_back(rCol
.first
);
373 // hash map is not ordered, so we need to explicitly sort it.
374 ::std::sort(aCols
.begin(), aCols
.end());
378 ::std::pair
< SCCOL
, SCCOL
> ScExternalRefCache::Table::getColRange( SCROW nRow
) const
380 ::std::pair
< SCCOL
, SCCOL
> aRange( 0, 0 );
382 RowsDataType::const_iterator itrRow
= maRows
.find( nRow
);
383 if (itrRow
== maRows
.end())
384 // this table doesn't have the specified row.
387 const RowDataType
& rRowData
= itrRow
->second
;
388 if( !rRowData
.empty() )
390 // iterate over entire container (hash map is not sorted by key)
391 auto itMinMax
= std::minmax_element(rRowData
.begin(), rRowData
.end(),
392 [](const RowDataType::value_type
& a
, const RowDataType::value_type
& b
) { return a
.first
< b
.first
; });
393 aRange
.first
= itMinMax
.first
->first
;
394 aRange
.second
= itMinMax
.second
->first
+ 1;
399 void ScExternalRefCache::Table::getAllNumberFormats(vector
<sal_uInt32
>& rNumFmts
) const
401 for (const auto& rRow
: maRows
)
403 const RowDataType
& rRowData
= rRow
.second
;
404 for (const auto& rCol
: rRowData
)
406 const Cell
& rCell
= rCol
.second
;
407 rNumFmts
.push_back(rCell
.mnFmtIndex
);
412 bool ScExternalRefCache::Table::isRangeCached(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
) const
414 return maCachedRanges
.In(ScRange(nCol1
, nRow1
, 0, nCol2
, nRow2
, 0));
417 void ScExternalRefCache::Table::setCachedCell(SCCOL nCol
, SCROW nRow
)
419 setCachedCellRange(nCol
, nRow
, nCol
, nRow
);
422 void ScExternalRefCache::Table::setCachedCellRange(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
424 ScRange
aRange(nCol1
, nRow1
, 0, nCol2
, nRow2
, 0);
425 maCachedRanges
.Join(aRange
);
428 void ScExternalRefCache::Table::setWholeTableCached()
430 setCachedCellRange(0, 0, MAXCOL
, MAXROW
);
433 bool ScExternalRefCache::Table::isInCachedRanges(SCCOL nCol
, SCROW nRow
) const
435 return maCachedRanges
.In(ScRange(nCol
, nRow
, 0, nCol
, nRow
, 0));
438 ScExternalRefCache::TokenRef
ScExternalRefCache::Table::getEmptyOrNullToken(
439 SCCOL nCol
, SCROW nRow
) const
441 if (isInCachedRanges(nCol
, nRow
))
443 TokenRef
p(new ScEmptyCellToken(false, false));
449 ScExternalRefCache::TableName::TableName(const OUString
& rUpper
, const OUString
& rReal
) :
450 maUpperName(rUpper
), maRealName(rReal
)
454 ScExternalRefCache::CellFormat::CellFormat() :
455 mbIsSet(false), mnType(SvNumFormatType::ALL
), mnIndex(0)
459 ScExternalRefCache::ScExternalRefCache()
460 : mxFakeDoc(new ScDocument())
463 ScExternalRefCache::~ScExternalRefCache() {}
465 const OUString
* ScExternalRefCache::getRealTableName(sal_uInt16 nFileId
, const OUString
& rTabName
) const
467 osl::MutexGuard
aGuard(&maMtxDocs
);
469 DocDataType::const_iterator itrDoc
= maDocs
.find(nFileId
);
470 if (itrDoc
== maDocs
.end())
472 // specified document is not cached.
476 const DocItem
& rDoc
= itrDoc
->second
;
477 TableNameIndexMap::const_iterator itrTabId
= rDoc
.findTableNameIndex( rTabName
);
478 if (itrTabId
== rDoc
.maTableNameIndex
.end())
480 // the specified table is not in cache.
484 return &rDoc
.maTableNames
[itrTabId
->second
].maRealName
;
487 const OUString
* ScExternalRefCache::getRealRangeName(sal_uInt16 nFileId
, const OUString
& rRangeName
) const
489 osl::MutexGuard
aGuard(&maMtxDocs
);
491 DocDataType::const_iterator itrDoc
= maDocs
.find(nFileId
);
492 if (itrDoc
== maDocs
.end())
494 // specified document is not cached.
498 const DocItem
& rDoc
= itrDoc
->second
;
499 NamePairMap::const_iterator itr
= rDoc
.maRealRangeNameMap
.find(
500 ScGlobal::getCharClassPtr()->uppercase(rRangeName
));
501 if (itr
== rDoc
.maRealRangeNameMap
.end())
502 // range name not found.
508 ScExternalRefCache::TokenRef
ScExternalRefCache::getCellData(
509 sal_uInt16 nFileId
, const OUString
& rTabName
, SCCOL nCol
, SCROW nRow
, sal_uInt32
* pnFmtIndex
)
511 osl::MutexGuard
aGuard(&maMtxDocs
);
513 DocDataType::const_iterator itrDoc
= maDocs
.find(nFileId
);
514 if (itrDoc
== maDocs
.end())
516 // specified document is not cached.
520 const DocItem
& rDoc
= itrDoc
->second
;
521 TableNameIndexMap::const_iterator itrTabId
= rDoc
.findTableNameIndex( rTabName
);
522 if (itrTabId
== rDoc
.maTableNameIndex
.end())
524 // the specified table is not in cache.
528 const TableTypeRef
& pTableData
= rDoc
.maTables
[itrTabId
->second
];
531 // the table data is not instantiated yet.
535 return pTableData
->getCell(nCol
, nRow
, pnFmtIndex
);
538 ScExternalRefCache::TokenArrayRef
ScExternalRefCache::getCellRangeData(
539 sal_uInt16 nFileId
, const OUString
& rTabName
, const ScRange
& rRange
)
541 osl::MutexGuard
aGuard(&maMtxDocs
);
543 DocDataType::iterator itrDoc
= maDocs
.find(nFileId
);
544 if (itrDoc
== maDocs
.end())
545 // specified document is not cached.
546 return TokenArrayRef();
548 DocItem
& rDoc
= itrDoc
->second
;
550 TableNameIndexMap::const_iterator itrTabId
= rDoc
.findTableNameIndex( rTabName
);
551 if (itrTabId
== rDoc
.maTableNameIndex
.end())
552 // the specified table is not in cache.
553 return TokenArrayRef();
555 const ScAddress
& s
= rRange
.aStart
;
556 const ScAddress
& e
= rRange
.aEnd
;
558 const SCTAB nTab1
= s
.Tab(), nTab2
= e
.Tab();
559 const SCCOL nCol1
= s
.Col(), nCol2
= e
.Col();
560 const SCROW nRow1
= s
.Row(), nRow2
= e
.Row();
562 // Make sure I have all the tables cached.
563 size_t nTabFirstId
= itrTabId
->second
;
564 size_t nTabLastId
= nTabFirstId
+ nTab2
- nTab1
;
565 if (nTabLastId
>= rDoc
.maTables
.size())
566 // not all tables are cached.
567 return TokenArrayRef();
569 ScRange
aCacheRange( nCol1
, nRow1
, static_cast<SCTAB
>(nTabFirstId
), nCol2
, nRow2
, static_cast<SCTAB
>(nTabLastId
));
571 RangeArrayMap::const_iterator itrRange
= rDoc
.maRangeArrays
.find( aCacheRange
);
572 if (itrRange
!= rDoc
.maRangeArrays
.end())
574 return itrRange
->second
;
576 std::unique_ptr
<ScRange
> pNewRange
;
577 TokenArrayRef pArray
;
578 bool bFirstTab
= true;
579 for (size_t nTab
= nTabFirstId
; nTab
<= nTabLastId
; ++nTab
)
581 TableTypeRef pTab
= rDoc
.maTables
[nTab
];
583 return TokenArrayRef();
585 SCCOL nDataCol1
= nCol1
, nDataCol2
= nCol2
;
586 SCROW nDataRow1
= nRow1
, nDataRow2
= nRow2
;
588 if (!pTab
->isRangeCached(nDataCol1
, nDataRow1
, nDataCol2
, nDataRow2
))
590 // specified range is not entirely within cached ranges.
591 return TokenArrayRef();
594 SCSIZE nMatrixColumns
= static_cast<SCSIZE
>(nDataCol2
-nDataCol1
+1);
595 SCSIZE nMatrixRows
= static_cast<SCSIZE
>(nDataRow2
-nDataRow1
+1);
596 ScMatrixRef xMat
= new ScMatrix( nMatrixColumns
, nMatrixRows
);
598 // Needed in shrink and fill.
600 pTab
->getAllRows(aRows
, nDataRow1
, nDataRow2
);
603 // Check if size could be allocated and if not skip the fill, there's
604 // one error element instead. But retry first with the actual data area
605 // if that is smaller than the original range, which works for most
606 // functions just not some that operate/compare with the original size
607 // and expect empty values in non-data areas.
608 // Restrict this though to ranges of entire columns or rows, other
609 // ranges might be on purpose. (Other special cases to handle?)
610 /* TODO: sparse matrix could help */
611 SCSIZE nMatCols
, nMatRows
;
612 xMat
->GetDimensions( nMatCols
, nMatRows
);
613 if (nMatCols
!= nMatrixColumns
|| nMatRows
!= nMatrixRows
)
618 // There's no data at all. Set the one matrix element to empty
619 // for column-repeated and row-repeated access.
622 else if ((nCol1
== 0 && nCol2
== MAXCOL
) || (nRow1
== 0 && nRow2
== MAXROW
))
624 nDataRow1
= aRows
.front();
625 nDataRow2
= aRows
.back();
626 SCCOL nMinCol
= std::numeric_limits
<SCCOL
>::max();
627 SCCOL nMaxCol
= std::numeric_limits
<SCCOL
>::min();
628 for (const auto& rRow
: aRows
)
631 pTab
->getAllCols(rRow
, aCols
, nDataCol1
, nDataCol2
);
634 nMinCol
= std::min( nMinCol
, aCols
.front());
635 nMaxCol
= std::max( nMaxCol
, aCols
.back());
639 if (nMinCol
<= nMaxCol
&& ((o3tl::make_unsigned(nMaxCol
-nMinCol
+1) < nMatrixColumns
) ||
640 (o3tl::make_unsigned(nDataRow2
-nDataRow1
+1) < nMatrixRows
)))
642 nMatrixColumns
= static_cast<SCSIZE
>(nMaxCol
-nMinCol
+1);
643 nMatrixRows
= static_cast<SCSIZE
>(nDataRow2
-nDataRow1
+1);
644 xMat
= new ScMatrix( nMatrixColumns
, nMatrixRows
);
645 xMat
->GetDimensions( nMatCols
, nMatRows
);
646 if (nMatCols
== nMatrixColumns
&& nMatRows
== nMatrixRows
)
658 // Only fill non-empty cells, for better performance.
659 for (SCROW nRow
: aRows
)
662 pTab
->getAllCols(nRow
, aCols
, nDataCol1
, nDataCol2
);
663 for (SCCOL nCol
: aCols
)
665 TokenRef pToken
= pTab
->getCell(nCol
, nRow
);
667 // This should never happen!
668 return TokenArrayRef();
670 SCSIZE nC
= nCol
- nDataCol1
, nR
= nRow
- nDataRow1
;
671 switch (pToken
->GetType())
674 xMat
->PutDouble(pToken
->GetDouble(), nC
, nR
);
677 xMat
->PutString(pToken
->GetString(), nC
, nR
);
686 pArray
->AddOpCode(ocSep
);
688 ScMatrixToken
aToken(xMat
);
690 pArray
= std::make_shared
<ScTokenArray
>(*mxFakeDoc
);
691 pArray
->AddToken(aToken
);
696 pNewRange
.reset(new ScRange(nDataCol1
, nDataRow1
, nTab
, nDataCol2
, nDataRow2
, nTab
));
698 pNewRange
->ExtendTo(ScRange(nDataCol1
, nDataRow1
, nTab
, nDataCol2
, nDataRow2
, nTab
));
702 rDoc
.maRangeArrays
.emplace(aCacheRange
, pArray
);
703 if (pNewRange
&& *pNewRange
!= aCacheRange
)
704 rDoc
.maRangeArrays
.emplace(*pNewRange
, pArray
);
709 ScExternalRefCache::TokenArrayRef
ScExternalRefCache::getRangeNameTokens(sal_uInt16 nFileId
, const OUString
& rName
)
711 osl::MutexGuard
aGuard(&maMtxDocs
);
713 DocItem
* pDoc
= getDocItem(nFileId
);
715 return TokenArrayRef();
717 RangeNameMap
& rMap
= pDoc
->maRangeNames
;
718 RangeNameMap::const_iterator itr
= rMap
.find(
719 ScGlobal::getCharClassPtr()->uppercase(rName
));
720 if (itr
== rMap
.end())
721 return TokenArrayRef();
726 void ScExternalRefCache::setRangeNameTokens(sal_uInt16 nFileId
, const OUString
& rName
, TokenArrayRef pArray
)
728 osl::MutexGuard
aGuard(&maMtxDocs
);
730 DocItem
* pDoc
= getDocItem(nFileId
);
734 OUString aUpperName
= ScGlobal::getCharClassPtr()->uppercase(rName
);
735 RangeNameMap
& rMap
= pDoc
->maRangeNames
;
736 rMap
.emplace(aUpperName
, pArray
);
737 pDoc
->maRealRangeNameMap
.emplace(aUpperName
, rName
);
740 bool ScExternalRefCache::isValidRangeName(sal_uInt16 nFileId
, const OUString
& rName
) const
742 osl::MutexGuard
aGuard(&maMtxDocs
);
744 DocItem
* pDoc
= getDocItem(nFileId
);
748 const RangeNameMap
& rMap
= pDoc
->maRangeNames
;
749 return rMap
.count(rName
) > 0;
752 void ScExternalRefCache::setRangeName(sal_uInt16 nFileId
, const OUString
& rName
)
754 osl::MutexGuard
aGuard(&maMtxDocs
);
756 DocItem
* pDoc
= getDocItem(nFileId
);
760 OUString aUpperName
= ScGlobal::getCharClassPtr()->uppercase(rName
);
761 pDoc
->maRealRangeNameMap
.emplace(aUpperName
, rName
);
764 void ScExternalRefCache::setCellData(sal_uInt16 nFileId
, const OUString
& rTabName
, SCCOL nCol
, SCROW nRow
,
765 TokenRef
const & pToken
, sal_uLong nFmtIndex
)
767 if (!isDocInitialized(nFileId
))
771 DocItem
* pDocItem
= getDocItem(nFileId
);
775 DocItem
& rDoc
= *pDocItem
;
777 // See if the table by this name already exists.
778 TableNameIndexMap::const_iterator itrTabName
= rDoc
.findTableNameIndex( rTabName
);
779 if (itrTabName
== rDoc
.maTableNameIndex
.end())
780 // Table not found. Maybe the table name or the file id is wrong ???
783 TableTypeRef
& pTableData
= rDoc
.maTables
[itrTabName
->second
];
785 pTableData
= std::make_shared
<Table
>();
787 pTableData
->setCell(nCol
, nRow
, pToken
, nFmtIndex
);
788 pTableData
->setCachedCell(nCol
, nRow
);
791 void ScExternalRefCache::setCellRangeData(sal_uInt16 nFileId
, const ScRange
& rRange
, const vector
<SingleRangeData
>& rData
,
792 const TokenArrayRef
& pArray
)
795 if (rData
.empty() || !isDocInitialized(nFileId
))
799 // First, get the document item for the given file ID.
800 DocItem
* pDocItem
= getDocItem(nFileId
);
804 DocItem
& rDoc
= *pDocItem
;
806 // Now, find the table position of the first table to cache.
807 const OUString
& rFirstTabName
= rData
.front().maTableName
;
808 TableNameIndexMap::const_iterator itrTabName
= rDoc
.findTableNameIndex( rFirstTabName
);
809 if (itrTabName
== rDoc
.maTableNameIndex
.end())
811 // table index not found.
815 size_t nTabFirstId
= itrTabName
->second
;
816 SCROW nRow1
= rRange
.aStart
.Row(), nRow2
= rRange
.aEnd
.Row();
817 SCCOL nCol1
= rRange
.aStart
.Col(), nCol2
= rRange
.aEnd
.Col();
818 size_t i
= nTabFirstId
;
819 for (const auto& rItem
: rData
)
821 TableTypeRef
& pTabData
= rDoc
.maTables
[i
];
823 pTabData
= std::make_shared
<Table
>();
825 const ScMatrixRef
& pMat
= rItem
.mpRangeData
;
826 SCSIZE nMatCols
, nMatRows
;
827 pMat
->GetDimensions( nMatCols
, nMatRows
);
828 if (nMatCols
> o3tl::make_unsigned(nCol2
- nCol1
) && nMatRows
> o3tl::make_unsigned(nRow2
- nRow1
))
830 ScMatrix::DoubleOpFunction aDoubleFunc
= [=](size_t row
, size_t col
, double val
) -> void
832 pTabData
->setCell(col
+ nCol1
, row
+ nRow1
, new formula::FormulaDoubleToken(val
), 0, false);
834 ScMatrix::BoolOpFunction aBoolFunc
= [=](size_t row
, size_t col
, bool val
) -> void
836 pTabData
->setCell(col
+ nCol1
, row
+ nRow1
, new formula::FormulaDoubleToken(val
? 1.0 : 0.0), 0, false);
838 ScMatrix::StringOpFunction aStringFunc
= [=](size_t row
, size_t col
, svl::SharedString val
) -> void
840 pTabData
->setCell(col
+ nCol1
, row
+ nRow1
, new formula::FormulaStringToken(val
), 0, false);
842 ScMatrix::EmptyOpFunction aEmptyFunc
= [=](size_t /*row*/, size_t /*col*/) -> void
844 // Nothing. Empty cell.
846 pMat
->ExecuteOperation(std::pair
<size_t, size_t>(0, 0),
847 std::pair
<size_t, size_t>(nRow2
-nRow1
, nCol2
-nCol1
),
848 aDoubleFunc
, aBoolFunc
, aStringFunc
, aEmptyFunc
);
849 // Mark the whole range 'cached'.
850 pTabData
->setCachedCellRange(nCol1
, nRow1
, nCol2
, nRow2
);
854 // This may happen due to a matrix not been allocated earlier, in
855 // which case it should have exactly one error element.
856 SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - matrix size mismatch");
857 if (nMatCols
!= 1 || nMatRows
!= 1)
858 SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - not a one element matrix");
861 FormulaError nErr
= GetDoubleErrorValue( pMat
->GetDouble(0,0));
862 SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - matrix error value is " << static_cast<int>(nErr
) <<
863 (nErr
== FormulaError::MatrixSize
? ", ok" : ", not ok"));
869 size_t nTabLastId
= nTabFirstId
+ rRange
.aEnd
.Tab() - rRange
.aStart
.Tab();
870 ScRange
aCacheRange( nCol1
, nRow1
, static_cast<SCTAB
>(nTabFirstId
), nCol2
, nRow2
, static_cast<SCTAB
>(nTabLastId
));
872 rDoc
.maRangeArrays
.emplace(aCacheRange
, pArray
);
875 bool ScExternalRefCache::isDocInitialized(sal_uInt16 nFileId
)
877 DocItem
* pDoc
= getDocItem(nFileId
);
881 return pDoc
->mbInitFromSource
;
884 static bool lcl_getStrictTableDataIndex(const ScExternalRefCache::TableNameIndexMap
& rMap
, const OUString
& rName
, size_t& rIndex
)
886 ScExternalRefCache::TableNameIndexMap::const_iterator itr
= rMap
.find(rName
);
887 if (itr
== rMap
.end())
890 rIndex
= itr
->second
;
894 bool ScExternalRefCache::DocItem::getTableDataIndex( const OUString
& rTabName
, size_t& rIndex
) const
896 ScExternalRefCache::TableNameIndexMap::const_iterator itr
= findTableNameIndex(rTabName
);
897 if (itr
== maTableNameIndex
.end())
900 rIndex
= itr
->second
;
905 OUString
getFirstSheetName()
907 // Get Custom prefix.
908 const ScDefaultsOptions
& rOpt
= SC_MOD()->GetDefaultsOptions();
909 // Form sheet name identical to the first generated sheet name when
910 // creating an internal document, e.g. 'Sheet1'.
911 return rOpt
.GetInitTabPrefix() + "1";
915 void ScExternalRefCache::initializeDoc(sal_uInt16 nFileId
, const vector
<OUString
>& rTabNames
,
916 const OUString
& rBaseName
)
918 DocItem
* pDoc
= getDocItem(nFileId
);
922 size_t n
= rTabNames
.size();
924 // table name list - the list must include all table names in the source
925 // document and only to be populated when loading the source document, not
926 // when loading cached data from, say, Excel XCT/CRN records.
927 vector
<TableName
> aNewTabNames
;
928 aNewTabNames
.reserve(n
);
929 for (const auto& rTabName
: rTabNames
)
931 TableName
aNameItem(ScGlobal::getCharClassPtr()->uppercase(rTabName
), rTabName
);
932 aNewTabNames
.push_back(aNameItem
);
934 pDoc
->maTableNames
.swap(aNewTabNames
);
936 // data tables - preserve any existing data that may have been set during
938 vector
<TableTypeRef
> aNewTables(n
);
939 for (size_t i
= 0; i
< n
; ++i
)
942 if (lcl_getStrictTableDataIndex(pDoc
->maTableNameIndex
, pDoc
->maTableNames
[i
].maUpperName
, nIndex
))
944 aNewTables
[i
] = pDoc
->maTables
[nIndex
];
947 pDoc
->maTables
.swap(aNewTables
);
950 TableNameIndexMap aNewNameIndex
;
951 for (size_t i
= 0; i
< n
; ++i
)
952 aNewNameIndex
.emplace(pDoc
->maTableNames
[i
].maUpperName
, i
);
953 pDoc
->maTableNameIndex
.swap(aNewNameIndex
);
955 // Setup name for Sheet1 vs base name to be able to load documents
956 // that store the base name as table name, or vice versa.
957 pDoc
->maSingleTableNameAlias
.clear();
958 if (!rBaseName
.isEmpty() && pDoc
->maTableNames
.size() == 1)
960 OUString aSheetName
= getFirstSheetName();
961 // If the one and only table name matches exactly, carry on the base
962 // file name for further alias use. If instead the table name matches
963 // the base name, carry on the sheet name as alias.
964 if (ScGlobal::GetpTransliteration()->isEqual( pDoc
->maTableNames
[0].maRealName
, aSheetName
))
965 pDoc
->maSingleTableNameAlias
= rBaseName
;
966 else if (ScGlobal::GetpTransliteration()->isEqual( pDoc
->maTableNames
[0].maRealName
, rBaseName
))
967 pDoc
->maSingleTableNameAlias
= aSheetName
;
970 pDoc
->mbInitFromSource
= true;
973 ScExternalRefCache::TableNameIndexMap::const_iterator
ScExternalRefCache::DocItem::findTableNameIndex(
974 const OUString
& rTabName
) const
976 const OUString aTabNameUpper
= ScGlobal::getCharClassPtr()->uppercase( rTabName
);
977 TableNameIndexMap::const_iterator itrTabName
= maTableNameIndex
.find( aTabNameUpper
);
978 if (itrTabName
!= maTableNameIndex
.end())
981 // Since some time for external references to CSV files the base name is
982 // used as sheet name instead of Sheet1, check if we can resolve that.
983 // Also helps users that got accustomed to one or the other way.
984 if (maSingleTableNameAlias
.isEmpty() || maTableNameIndex
.size() != 1)
987 // maSingleTableNameAlias has been set up only if the original file loaded
988 // had exactly one sheet and internal sheet name was Sheet1 or localized or
989 // customized equivalent, or base name.
990 if (aTabNameUpper
== ScGlobal::getCharClassPtr()->uppercase( maSingleTableNameAlias
))
991 return maTableNameIndex
.begin();
996 bool ScExternalRefCache::DocItem::getSingleTableNameAlternative( OUString
& rTabName
) const
998 if (maSingleTableNameAlias
.isEmpty() || maTableNames
.size() != 1)
1000 if (ScGlobal::GetpTransliteration()->isEqual( rTabName
, maTableNames
[0].maRealName
))
1002 rTabName
= maSingleTableNameAlias
;
1005 if (ScGlobal::GetpTransliteration()->isEqual( rTabName
, maSingleTableNameAlias
))
1007 rTabName
= maTableNames
[0].maRealName
;
1013 bool ScExternalRefCache::getSrcDocTable( const ScDocument
& rSrcDoc
, const OUString
& rTabName
, SCTAB
& rTab
,
1014 sal_uInt16 nFileId
) const
1016 bool bFound
= rSrcDoc
.GetTable( rTabName
, rTab
);
1019 // Check the one table alias alternative.
1020 const DocItem
* pDoc
= getDocItem( nFileId
);
1023 OUString
aTabName( rTabName
);
1024 if (pDoc
->getSingleTableNameAlternative( aTabName
))
1025 bFound
= rSrcDoc
.GetTable( aTabName
, rTab
);
1031 OUString
ScExternalRefCache::getTableName(sal_uInt16 nFileId
, size_t nCacheId
) const
1033 if( DocItem
* pDoc
= getDocItem( nFileId
) )
1034 if( nCacheId
< pDoc
->maTableNames
.size() )
1035 return pDoc
->maTableNames
[ nCacheId
].maRealName
;
1036 return EMPTY_OUSTRING
;
1039 void ScExternalRefCache::getAllTableNames(sal_uInt16 nFileId
, vector
<OUString
>& rTabNames
) const
1042 DocItem
* pDoc
= getDocItem(nFileId
);
1046 size_t n
= pDoc
->maTableNames
.size();
1047 rTabNames
.reserve(n
);
1048 for (const auto& rTableName
: pDoc
->maTableNames
)
1049 rTabNames
.push_back(rTableName
.maRealName
);
1052 SCTAB
ScExternalRefCache::getTabSpan( sal_uInt16 nFileId
, const OUString
& rStartTabName
, const OUString
& rEndTabName
) const
1054 DocItem
* pDoc
= getDocItem(nFileId
);
1058 vector
<TableName
>::const_iterator itrBeg
= pDoc
->maTableNames
.begin();
1059 vector
<TableName
>::const_iterator itrEnd
= pDoc
->maTableNames
.end();
1061 vector
<TableName
>::const_iterator itrStartTab
= ::std::find_if( itrBeg
, itrEnd
,
1062 TabNameSearchPredicate( rStartTabName
));
1063 if (itrStartTab
== itrEnd
)
1066 vector
<TableName
>::const_iterator itrEndTab
= ::std::find_if( itrBeg
, itrEnd
,
1067 TabNameSearchPredicate( rEndTabName
));
1068 if (itrEndTab
== itrEnd
)
1071 size_t nStartDist
= ::std::distance( itrBeg
, itrStartTab
);
1072 size_t nEndDist
= ::std::distance( itrBeg
, itrEndTab
);
1073 return nStartDist
<= nEndDist
? static_cast<SCTAB
>(nEndDist
- nStartDist
+ 1) : -static_cast<SCTAB
>(nStartDist
- nEndDist
+ 1);
1076 void ScExternalRefCache::getAllNumberFormats(vector
<sal_uInt32
>& rNumFmts
) const
1078 osl::MutexGuard
aGuard(&maMtxDocs
);
1081 using ::std::unique
;
1083 vector
<sal_uInt32
> aNumFmts
;
1084 for (const auto& rEntry
: maDocs
)
1086 const vector
<TableTypeRef
>& rTables
= rEntry
.second
.maTables
;
1087 for (const TableTypeRef
& pTab
: rTables
)
1092 pTab
->getAllNumberFormats(aNumFmts
);
1096 // remove duplicates.
1097 sort(aNumFmts
.begin(), aNumFmts
.end());
1098 aNumFmts
.erase(unique(aNumFmts
.begin(), aNumFmts
.end()), aNumFmts
.end());
1099 rNumFmts
.swap(aNumFmts
);
1102 bool ScExternalRefCache::setCacheDocReferenced( sal_uInt16 nFileId
)
1104 DocItem
* pDocItem
= getDocItem(nFileId
);
1106 return areAllCacheTablesReferenced();
1108 for (auto& rxTab
: pDocItem
->maTables
)
1111 rxTab
->setReferenced(true);
1113 addCacheDocToReferenced( nFileId
);
1114 return areAllCacheTablesReferenced();
1117 bool ScExternalRefCache::setCacheTableReferenced( sal_uInt16 nFileId
, const OUString
& rTabName
, size_t nSheets
)
1119 DocItem
* pDoc
= getDocItem(nFileId
);
1123 if (pDoc
->getTableDataIndex( rTabName
, nIndex
))
1125 size_t nStop
= ::std::min( nIndex
+ nSheets
, pDoc
->maTables
.size());
1126 for (size_t i
= nIndex
; i
< nStop
; ++i
)
1128 TableTypeRef pTab
= pDoc
->maTables
[i
];
1131 if (!pTab
->isReferenced())
1133 pTab
->setReferenced(true);
1134 addCacheTableToReferenced( nFileId
, i
);
1140 return areAllCacheTablesReferenced();
1143 void ScExternalRefCache::setAllCacheTableReferencedStati( bool bReferenced
)
1145 osl::MutexGuard
aGuard(&maMtxDocs
);
1149 maReferenced
.reset(0);
1150 for (auto& rEntry
: maDocs
)
1152 ScExternalRefCache::DocItem
& rDocItem
= rEntry
.second
;
1153 for (auto& rxTab
: rDocItem
.maTables
)
1156 rxTab
->setReferenced(true);
1163 auto itrMax
= std::max_element(maDocs
.begin(), maDocs
.end(),
1164 [](const DocDataType::value_type
& a
, const DocDataType::value_type
& b
) { return a
.first
< b
.first
; });
1165 if (itrMax
!= maDocs
.end())
1166 nDocs
= itrMax
->first
+ 1;
1167 maReferenced
.reset( nDocs
);
1169 for (auto& [nFileId
, rDocItem
] : maDocs
)
1171 size_t nTables
= rDocItem
.maTables
.size();
1172 ReferencedStatus::DocReferenced
& rDocReferenced
= maReferenced
.maDocs
[nFileId
];
1173 // All referenced => non-existing tables evaluate as completed.
1174 rDocReferenced
.maTables
.resize( nTables
, true);
1175 for (size_t i
=0; i
< nTables
; ++i
)
1177 TableTypeRef
& xTab
= rDocItem
.maTables
[i
];
1180 xTab
->setReferenced(false);
1181 rDocReferenced
.maTables
[i
] = false;
1182 rDocReferenced
.mbAllTablesReferenced
= false;
1183 // An addCacheTableToReferenced() actually may have
1184 // resulted in mbAllReferenced been set. Clear it.
1185 maReferenced
.mbAllReferenced
= false;
1192 void ScExternalRefCache::addCacheTableToReferenced( sal_uInt16 nFileId
, size_t nIndex
)
1194 if (nFileId
>= maReferenced
.maDocs
.size())
1197 ::std::vector
<bool> & rTables
= maReferenced
.maDocs
[nFileId
].maTables
;
1198 size_t nTables
= rTables
.size();
1199 if (nIndex
>= nTables
)
1202 if (!rTables
[nIndex
])
1204 rTables
[nIndex
] = true;
1206 while (i
< nTables
&& rTables
[i
])
1210 maReferenced
.maDocs
[nFileId
].mbAllTablesReferenced
= true;
1211 maReferenced
.checkAllDocs();
1216 void ScExternalRefCache::addCacheDocToReferenced( sal_uInt16 nFileId
)
1218 if (nFileId
>= maReferenced
.maDocs
.size())
1221 if (!maReferenced
.maDocs
[nFileId
].mbAllTablesReferenced
)
1223 ::std::vector
<bool> & rTables
= maReferenced
.maDocs
[nFileId
].maTables
;
1224 size_t nSize
= rTables
.size();
1225 for (size_t i
=0; i
< nSize
; ++i
)
1227 maReferenced
.maDocs
[nFileId
].mbAllTablesReferenced
= true;
1228 maReferenced
.checkAllDocs();
1232 void ScExternalRefCache::getAllCachedDataSpans( const ScDocument
& rSrcDoc
, sal_uInt16 nFileId
, sc::ColumnSpanSet
& rSet
) const
1234 const DocItem
* pDocItem
= getDocItem(nFileId
);
1236 // This document is not cached.
1239 const std::vector
<TableTypeRef
>& rTables
= pDocItem
->maTables
;
1240 for (size_t nTab
= 0, nTabCount
= rTables
.size(); nTab
< nTabCount
; ++nTab
)
1242 TableTypeRef pTab
= rTables
[nTab
];
1246 std::vector
<SCROW
> aRows
;
1247 pTab
->getAllRows(aRows
);
1248 for (SCROW nRow
: aRows
)
1250 std::vector
<SCCOL
> aCols
;
1251 pTab
->getAllCols(nRow
, aCols
);
1252 for (SCCOL nCol
: aCols
)
1254 rSet
.set(rSrcDoc
, nTab
, nCol
, nRow
, true);
1260 ScExternalRefCache::ReferencedStatus::ReferencedStatus() :
1261 mbAllReferenced(false)
1266 void ScExternalRefCache::ReferencedStatus::reset( size_t nDocs
)
1270 mbAllReferenced
= false;
1271 DocReferencedVec
aRefs( nDocs
);
1272 maDocs
.swap( aRefs
);
1276 mbAllReferenced
= true;
1277 DocReferencedVec aRefs
;
1278 maDocs
.swap( aRefs
);
1282 void ScExternalRefCache::ReferencedStatus::checkAllDocs()
1284 if (std::all_of(maDocs
.begin(), maDocs
.end(), [](const DocReferenced
& rDoc
) { return rDoc
.mbAllTablesReferenced
; }))
1285 mbAllReferenced
= true;
1288 ScExternalRefCache::TableTypeRef
ScExternalRefCache::getCacheTable(sal_uInt16 nFileId
, size_t nTabIndex
) const
1290 DocItem
* pDoc
= getDocItem(nFileId
);
1291 if (!pDoc
|| nTabIndex
>= pDoc
->maTables
.size())
1292 return TableTypeRef();
1294 return pDoc
->maTables
[nTabIndex
];
1297 ScExternalRefCache::TableTypeRef
ScExternalRefCache::getCacheTable(sal_uInt16 nFileId
, const OUString
& rTabName
,
1298 bool bCreateNew
, size_t* pnIndex
, const OUString
* pExtUrl
)
1300 // In API, the index is transported as cached sheet ID of type sal_Int32 in
1301 // sheet::SingleReference.Sheet or sheet::ComplexReference.Reference1.Sheet
1302 // in a sheet::FormulaToken, choose a sensible value for N/A. Effectively
1304 const size_t nNotAvailable
= static_cast<size_t>( static_cast<sal_Int32
>( -1));
1306 DocItem
* pDoc
= getDocItem(nFileId
);
1309 if (pnIndex
) *pnIndex
= nNotAvailable
;
1310 return TableTypeRef();
1313 DocItem
& rDoc
= *pDoc
;
1316 if (rDoc
.getTableDataIndex(rTabName
, nIndex
))
1318 // specified table found.
1319 if( pnIndex
) *pnIndex
= nIndex
;
1320 if (bCreateNew
&& !rDoc
.maTables
[nIndex
])
1321 rDoc
.maTables
[nIndex
] = std::make_shared
<Table
>();
1323 return rDoc
.maTables
[nIndex
];
1328 if (pnIndex
) *pnIndex
= nNotAvailable
;
1329 return TableTypeRef();
1332 // If this is the first table to be created propagate the base name or
1333 // Sheet1 as an alias. For subsequent tables remove it again.
1334 if (rDoc
.maTableNames
.empty())
1338 const OUString
aBaseName( INetURLObject( *pExtUrl
).GetBase());
1339 const OUString
aSheetName( getFirstSheetName());
1340 if (ScGlobal::GetpTransliteration()->isEqual( rTabName
, aSheetName
))
1341 pDoc
->maSingleTableNameAlias
= aBaseName
;
1342 else if (ScGlobal::GetpTransliteration()->isEqual( rTabName
, aBaseName
))
1343 pDoc
->maSingleTableNameAlias
= aSheetName
;
1348 rDoc
.maSingleTableNameAlias
.clear();
1351 // Specified table doesn't exist yet. Create one.
1352 OUString aTabNameUpper
= ScGlobal::getCharClassPtr()->uppercase(rTabName
);
1353 nIndex
= rDoc
.maTables
.size();
1354 if( pnIndex
) *pnIndex
= nIndex
;
1355 TableTypeRef pTab
= std::make_shared
<Table
>();
1356 rDoc
.maTables
.push_back(pTab
);
1357 rDoc
.maTableNames
.emplace_back(aTabNameUpper
, rTabName
);
1358 rDoc
.maTableNameIndex
.emplace(aTabNameUpper
, nIndex
);
1362 void ScExternalRefCache::clearCache(sal_uInt16 nFileId
)
1364 osl::MutexGuard
aGuard(&maMtxDocs
);
1365 maDocs
.erase(nFileId
);
1368 void ScExternalRefCache::clearCacheTables(sal_uInt16 nFileId
)
1370 osl::MutexGuard
aGuard(&maMtxDocs
);
1371 DocItem
* pDocItem
= getDocItem(nFileId
);
1373 // This document is not cached at all.
1376 // Clear all cache table content, but keep the tables.
1377 std::vector
<TableTypeRef
>& rTabs
= pDocItem
->maTables
;
1378 for (TableTypeRef
& pTab
: rTabs
)
1386 // Clear the external range name caches.
1387 pDocItem
->maRangeNames
.clear();
1388 pDocItem
->maRangeArrays
.clear();
1389 pDocItem
->maRealRangeNameMap
.clear();
1392 ScExternalRefCache::DocItem
* ScExternalRefCache::getDocItem(sal_uInt16 nFileId
) const
1394 osl::MutexGuard
aGuard(&maMtxDocs
);
1397 DocDataType::iterator itrDoc
= maDocs
.find(nFileId
);
1398 if (itrDoc
== maDocs
.end())
1400 // specified document is not cached.
1401 pair
<DocDataType::iterator
, bool> res
= maDocs
.emplace(
1402 nFileId
, DocItem());
1405 // insertion failed.
1411 return &itrDoc
->second
;
1414 ScExternalRefLink::ScExternalRefLink(ScDocument
& rDoc
, sal_uInt16 nFileId
) :
1415 ::sfx2::SvBaseLink(::SfxLinkUpdateMode::ONCALL
, SotClipboardFormatId::SIMPLE_FILE
),
1422 ScExternalRefLink::~ScExternalRefLink()
1426 void ScExternalRefLink::Closed()
1428 ScExternalRefManager
* pMgr
= mrDoc
.GetExternalRefManager();
1429 pMgr
->breakLink(mnFileId
);
1432 ::sfx2::SvBaseLink::UpdateResult
ScExternalRefLink::DataChanged(const OUString
& /*rMimeType*/, const Any
& /*rValue*/)
1437 OUString aFile
, aFilter
;
1438 sfx2::LinkManager::GetDisplayNames(this, nullptr, &aFile
, nullptr, &aFilter
);
1439 ScExternalRefManager
* pMgr
= mrDoc
.GetExternalRefManager();
1441 if (!pMgr
->isFileLoadable(aFile
))
1442 return ERROR_GENERAL
;
1444 const OUString
* pCurFile
= pMgr
->getExternalFileName(mnFileId
);
1446 return ERROR_GENERAL
;
1448 if (*pCurFile
== aFile
)
1450 // Refresh the current source document.
1451 if (!pMgr
->refreshSrcDocument(mnFileId
))
1452 return ERROR_GENERAL
;
1456 // The source document has changed.
1457 ScDocShell
* pDocShell
= ScDocShell::GetViewData()->GetDocShell();
1458 ScDocShellModificator
aMod(*pDocShell
);
1459 pMgr
->switchSrcFile(mnFileId
, aFile
, aFilter
);
1460 aMod
.SetDocumentModified();
1466 void ScExternalRefLink::Edit(weld::Window
* pParent
, const Link
<SvBaseLink
&,void>& /*rEndEditHdl*/)
1468 SvBaseLink::Edit(pParent
, Link
<SvBaseLink
&,void>());
1471 void ScExternalRefLink::SetDoRefresh(bool b
)
1476 static FormulaToken
* convertToToken( ScDocument
& rHostDoc
, const ScDocument
& rSrcDoc
, ScRefCellValue
& rCell
)
1478 if (rCell
.hasEmptyValue())
1480 bool bInherited
= (rCell
.meType
== CELLTYPE_FORMULA
);
1481 return new ScEmptyCellToken(bInherited
, false);
1484 switch (rCell
.meType
)
1487 case CELLTYPE_STRING
:
1489 OUString aStr
= rCell
.getString(&rSrcDoc
);
1490 svl::SharedString aSS
= rHostDoc
.GetSharedStringPool().intern(aStr
);
1491 return new formula::FormulaStringToken(aSS
);
1493 case CELLTYPE_VALUE
:
1494 return new formula::FormulaDoubleToken(rCell
.mfValue
);
1495 case CELLTYPE_FORMULA
:
1497 ScFormulaCell
* pFCell
= rCell
.mpFormula
;
1498 FormulaError nError
= pFCell
->GetErrCode();
1499 if (nError
!= FormulaError::NONE
)
1500 return new FormulaErrorToken( nError
);
1501 else if (pFCell
->IsValue())
1503 double fVal
= pFCell
->GetValue();
1504 return new formula::FormulaDoubleToken(fVal
);
1508 svl::SharedString aSS
= rHostDoc
.GetSharedStringPool().intern( pFCell
->GetString().getString());
1509 return new formula::FormulaStringToken(aSS
);
1513 OSL_FAIL("attempted to convert an unknown cell type.");
1519 static std::unique_ptr
<ScTokenArray
> convertToTokenArray(
1520 ScDocument
& rHostDoc
, const ScDocument
& rSrcDoc
, ScRange
& rRange
, vector
<ScExternalRefCache::SingleRangeData
>& rCacheData
)
1522 ScAddress
& s
= rRange
.aStart
;
1523 ScAddress
& e
= rRange
.aEnd
;
1525 const SCTAB nTab1
= s
.Tab(), nTab2
= e
.Tab();
1526 const SCCOL nCol1
= s
.Col(), nCol2
= e
.Col();
1527 const SCROW nRow1
= s
.Row(), nRow2
= e
.Row();
1530 // For now, we don't support multi-sheet ranges intentionally because
1531 // we don't have a way to express them in a single token. In the
1532 // future we can introduce a new stack variable type svMatrixList with
1533 // a new token type that can store a 3D matrix value and convert a 3D
1537 std::unique_ptr
<ScRange
> pUsedRange
;
1539 unique_ptr
<ScTokenArray
> pArray(new ScTokenArray(rSrcDoc
));
1540 bool bFirstTab
= true;
1541 vector
<ScExternalRefCache::SingleRangeData
>::iterator
1542 itrCache
= rCacheData
.begin(), itrCacheEnd
= rCacheData
.end();
1544 for (SCTAB nTab
= nTab1
; nTab
<= nTab2
&& itrCache
!= itrCacheEnd
; ++nTab
, ++itrCache
)
1546 // Only loop within the data area.
1547 SCCOL nDataCol1
= nCol1
, nDataCol2
= nCol2
;
1548 SCROW nDataRow1
= nRow1
, nDataRow2
= nRow2
;
1550 if (!rSrcDoc
.ShrinkToUsedDataArea( bShrunk
, nTab
, nDataCol1
, nDataRow1
, nDataCol2
, nDataRow2
, false))
1551 // no data within specified range.
1555 // Make sure the used area only grows, not shrinks.
1556 pUsedRange
->ExtendTo(ScRange(nDataCol1
, nDataRow1
, 0, nDataCol2
, nDataRow2
, 0));
1558 pUsedRange
.reset(new ScRange(nDataCol1
, nDataRow1
, 0, nDataCol2
, nDataRow2
, 0));
1560 SCSIZE nMatrixColumns
= static_cast<SCSIZE
>(nCol2
-nCol1
+1);
1561 SCSIZE nMatrixRows
= static_cast<SCSIZE
>(nRow2
-nRow1
+1);
1562 ScMatrixRef xMat
= new ScMatrix( nMatrixColumns
, nMatrixRows
);
1564 // Check if size could be allocated and if not skip the fill, there's
1565 // one error element instead. But retry first with the actual data area
1566 // if that is smaller than the original range, which works for most
1567 // functions just not some that operate/compare with the original size
1568 // and expect empty values in non-data areas.
1569 // Restrict this though to ranges of entire columns or rows, other
1570 // ranges might be on purpose. (Other special cases to handle?)
1571 /* TODO: sparse matrix could help */
1572 SCSIZE nMatCols
, nMatRows
;
1573 xMat
->GetDimensions( nMatCols
, nMatRows
);
1574 if (nMatCols
== nMatrixColumns
&& nMatRows
== nMatrixRows
)
1576 rSrcDoc
.FillMatrix(*xMat
, nTab
, nCol1
, nRow1
, nCol2
, nRow2
, &rHostDoc
.GetSharedStringPool());
1578 else if ((nCol1
== 0 && nCol2
== MAXCOL
) || (nRow1
== 0 && nRow2
== MAXROW
))
1580 if ((o3tl::make_unsigned(nDataCol2
-nDataCol1
+1) < nMatrixColumns
) ||
1581 (o3tl::make_unsigned(nDataRow2
-nDataRow1
+1) < nMatrixRows
))
1583 nMatrixColumns
= static_cast<SCSIZE
>(nDataCol2
-nDataCol1
+1);
1584 nMatrixRows
= static_cast<SCSIZE
>(nDataRow2
-nDataRow1
+1);
1585 xMat
= new ScMatrix( nMatrixColumns
, nMatrixRows
);
1586 xMat
->GetDimensions( nMatCols
, nMatRows
);
1587 if (nMatCols
== nMatrixColumns
&& nMatRows
== nMatrixRows
)
1588 rSrcDoc
.FillMatrix(*xMat
, nTab
, nDataCol1
, nDataRow1
, nDataCol2
, nDataRow2
, &rHostDoc
.GetSharedStringPool());
1593 pArray
->AddOpCode(ocSep
);
1595 ScMatrixToken
aToken(xMat
);
1596 pArray
->AddToken(aToken
);
1598 itrCache
->mpRangeData
= xMat
;
1606 s
.SetCol(pUsedRange
->aStart
.Col());
1607 s
.SetRow(pUsedRange
->aStart
.Row());
1608 e
.SetCol(pUsedRange
->aEnd
.Col());
1609 e
.SetRow(pUsedRange
->aEnd
.Row());
1614 static std::unique_ptr
<ScTokenArray
> lcl_fillEmptyMatrix(const ScDocument
& rDoc
, const ScRange
& rRange
)
1616 SCSIZE nC
= static_cast<SCSIZE
>(rRange
.aEnd
.Col()-rRange
.aStart
.Col()+1);
1617 SCSIZE nR
= static_cast<SCSIZE
>(rRange
.aEnd
.Row()-rRange
.aStart
.Row()+1);
1618 ScMatrixRef xMat
= new ScMatrix(nC
, nR
);
1620 ScMatrixToken
aToken(xMat
);
1621 unique_ptr
<ScTokenArray
> pArray(new ScTokenArray(rDoc
));
1622 pArray
->AddToken(aToken
);
1627 bool isLinkUpdateAllowedInDoc(const ScDocument
& rDoc
)
1629 SfxObjectShell
* pDocShell
= rDoc
.GetDocumentShell();
1633 return pDocShell
->GetEmbeddedObjectContainer().getUserAllowsLinkUpdate();
1637 ScExternalRefManager::ScExternalRefManager(ScDocument
& rDoc
) :
1639 mbInReferenceMarking(false),
1640 mbUserInteractionEnabled(true),
1641 mbDocTimerEnabled(true)
1643 maSrcDocTimer
.SetInvokeHandler( LINK(this, ScExternalRefManager
, TimeOutHdl
) );
1644 maSrcDocTimer
.SetTimeout(SRCDOC_SCAN_INTERVAL
);
1645 maSrcDocTimer
.SetDebugName( "sc::ScExternalRefManager maSrcDocTimer" );
1648 ScExternalRefManager::~ScExternalRefManager()
1653 OUString
ScExternalRefManager::getCacheTableName(sal_uInt16 nFileId
, size_t nTabIndex
) const
1655 return maRefCache
.getTableName(nFileId
, nTabIndex
);
1658 ScExternalRefCache::TableTypeRef
ScExternalRefManager::getCacheTable(sal_uInt16 nFileId
, size_t nTabIndex
) const
1660 return maRefCache
.getCacheTable(nFileId
, nTabIndex
);
1663 ScExternalRefCache::TableTypeRef
ScExternalRefManager::getCacheTable(
1664 sal_uInt16 nFileId
, const OUString
& rTabName
, bool bCreateNew
, size_t* pnIndex
, const OUString
* pExtUrl
)
1666 return maRefCache
.getCacheTable(nFileId
, rTabName
, bCreateNew
, pnIndex
, pExtUrl
);
1669 ScExternalRefManager::LinkListener::LinkListener()
1673 ScExternalRefManager::LinkListener::~LinkListener()
1677 ScExternalRefManager::ApiGuard::ApiGuard(const ScDocument
& rDoc
) :
1678 mpMgr(rDoc
.GetExternalRefManager()),
1679 mbOldInteractionEnabled(mpMgr
->mbUserInteractionEnabled
)
1681 // We don't want user interaction handled in the API.
1682 mpMgr
->mbUserInteractionEnabled
= false;
1685 ScExternalRefManager::ApiGuard::~ApiGuard()
1687 // Restore old value.
1688 mpMgr
->mbUserInteractionEnabled
= mbOldInteractionEnabled
;
1691 void ScExternalRefManager::getAllCachedTableNames(sal_uInt16 nFileId
, vector
<OUString
>& rTabNames
) const
1693 maRefCache
.getAllTableNames(nFileId
, rTabNames
);
1696 SCTAB
ScExternalRefManager::getCachedTabSpan( sal_uInt16 nFileId
, const OUString
& rStartTabName
, const OUString
& rEndTabName
) const
1698 return maRefCache
.getTabSpan( nFileId
, rStartTabName
, rEndTabName
);
1701 void ScExternalRefManager::getAllCachedNumberFormats(vector
<sal_uInt32
>& rNumFmts
) const
1703 maRefCache
.getAllNumberFormats(rNumFmts
);
1706 sal_uInt16
ScExternalRefManager::getExternalFileCount() const
1708 return static_cast< sal_uInt16
>( maSrcFiles
.size() );
1711 void ScExternalRefManager::markUsedByLinkListeners()
1713 bool bAllMarked
= false;
1714 for (const auto& [rFileId
, rLinkListeners
] : maLinkListeners
)
1716 if (!rLinkListeners
.empty())
1717 bAllMarked
= maRefCache
.setCacheDocReferenced(rFileId
);
1721 /* TODO: LinkListeners should remember the table they're listening to.
1722 * As is, listening to one table will mark all tables of the document
1723 * being referenced. */
1727 void ScExternalRefManager::markUsedExternalRefCells()
1729 for (const auto& rEntry
: maRefCells
)
1731 for (ScFormulaCell
* pCell
: rEntry
.second
)
1733 bool bUsed
= pCell
->MarkUsedExternalReferences();
1735 // Return true when at least one cell references external docs.
1741 bool ScExternalRefManager::setCacheTableReferenced( sal_uInt16 nFileId
, const OUString
& rTabName
, size_t nSheets
)
1743 return maRefCache
.setCacheTableReferenced( nFileId
, rTabName
, nSheets
);
1746 void ScExternalRefManager::setAllCacheTableReferencedStati( bool bReferenced
)
1748 mbInReferenceMarking
= !bReferenced
;
1749 maRefCache
.setAllCacheTableReferencedStati( bReferenced
);
1752 void ScExternalRefManager::storeRangeNameTokens(sal_uInt16 nFileId
, const OUString
& rName
, const ScTokenArray
& rArray
)
1754 ScExternalRefCache::TokenArrayRef
pArray(rArray
.Clone());
1755 maRefCache
.setRangeNameTokens(nFileId
, rName
, pArray
);
1761 * Put a single cell data into internal cache table.
1763 * @param pFmt optional cell format index that may need to be stored with
1766 void putCellDataIntoCache(
1767 ScExternalRefCache
& rRefCache
, const ScExternalRefCache::TokenRef
& pToken
,
1768 sal_uInt16 nFileId
, const OUString
& rTabName
, const ScAddress
& rCell
,
1769 const ScExternalRefCache::CellFormat
* pFmt
)
1771 // Now, insert the token into cache table but don't cache empty cells.
1772 if (pToken
->GetType() != formula::svEmptyCell
)
1774 sal_uLong nFmtIndex
= (pFmt
&& pFmt
->mbIsSet
) ? pFmt
->mnIndex
: 0;
1775 rRefCache
.setCellData(nFileId
, rTabName
, rCell
.Col(), rCell
.Row(), pToken
, nFmtIndex
);
1780 * Put the data into our internal cache table.
1782 * @param rRefCache cache table set.
1783 * @param pArray single range data to be returned.
1784 * @param nFileId external file ID
1785 * @param rTabName name of the table where the data should be cached.
1786 * @param rCacheData range data to be cached.
1787 * @param rCacheRange original cache range, including the empty region if
1789 * @param rDataRange reduced cache range that includes only the non-empty
1792 void putRangeDataIntoCache(
1793 ScExternalRefCache
& rRefCache
, ScExternalRefCache::TokenArrayRef
& pArray
,
1794 sal_uInt16 nFileId
, const OUString
& rTabName
,
1795 const vector
<ScExternalRefCache::SingleRangeData
>& rCacheData
,
1796 const ScRange
& rCacheRange
, const ScRange
& rDataRange
)
1799 // Cache these values.
1800 rRefCache
.setCellRangeData(nFileId
, rDataRange
, rCacheData
, pArray
);
1803 // Array is empty. Fill it with an empty matrix of the required size.
1804 pArray
= lcl_fillEmptyMatrix(*rRefCache
.getFakeDoc(), rCacheRange
);
1806 // Make sure to set this range 'cached', to prevent unnecessarily
1807 // accessing the src document time and time again.
1808 ScExternalRefCache::TableTypeRef pCacheTab
=
1809 rRefCache
.getCacheTable(nFileId
, rTabName
, true, nullptr, nullptr);
1811 pCacheTab
->setCachedCellRange(
1812 rCacheRange
.aStart
.Col(), rCacheRange
.aStart
.Row(), rCacheRange
.aEnd
.Col(), rCacheRange
.aEnd
.Row());
1817 * When accessing an external document for the first time, we need to
1818 * populate the cache with all its sheet names (whether they are referenced
1819 * or not) in the correct order. Many client codes that use external
1820 * references make this assumption.
1822 * @param rRefCache cache table set.
1823 * @param pSrcDoc source document instance.
1824 * @param nFileId external file ID associated with the source document.
1826 void initDocInCache(ScExternalRefCache
& rRefCache
, const ScDocument
* pSrcDoc
, sal_uInt16 nFileId
)
1831 if (rRefCache
.isDocInitialized(nFileId
))
1832 // Already initialized. No need to do this twice.
1835 SCTAB nTabCount
= pSrcDoc
->GetTableCount();
1839 // Populate the cache with all table names in the source document.
1840 vector
<OUString
> aTabNames
;
1841 aTabNames
.reserve(nTabCount
);
1842 for (SCTAB i
= 0; i
< nTabCount
; ++i
)
1845 pSrcDoc
->GetName(i
, aName
);
1846 aTabNames
.push_back(aName
);
1849 // Obtain the base name, don't bother if there are more than one sheets.
1853 const SfxObjectShell
* pShell
= pSrcDoc
->GetDocumentShell();
1854 if (pShell
&& pShell
->GetMedium())
1856 OUString aName
= pShell
->GetMedium()->GetName();
1857 aBaseName
= INetURLObject( aName
).GetBase();
1861 rRefCache
.initializeDoc(nFileId
, aTabNames
, aBaseName
);
1866 bool ScExternalRefManager::getSrcDocTable( const ScDocument
& rSrcDoc
, const OUString
& rTabName
, SCTAB
& rTab
,
1867 sal_uInt16 nFileId
) const
1869 return maRefCache
.getSrcDocTable( rSrcDoc
, rTabName
, rTab
, nFileId
);
1872 ScExternalRefCache::TokenRef
ScExternalRefManager::getSingleRefToken(
1873 sal_uInt16 nFileId
, const OUString
& rTabName
, const ScAddress
& rCell
,
1874 const ScAddress
* pCurPos
, SCTAB
* pTab
, ScExternalRefCache::CellFormat
* pFmt
)
1877 insertRefCell(nFileId
, *pCurPos
);
1879 maybeLinkExternalFile(nFileId
);
1885 pFmt
->mbIsSet
= false;
1887 ScDocument
* pSrcDoc
= getInMemorySrcDocument(nFileId
);
1890 // source document already loaded in memory. Re-use this instance.
1892 if (!getSrcDocTable( *pSrcDoc
, rTabName
, nTab
, nFileId
))
1894 // specified table name doesn't exist in the source document.
1895 ScExternalRefCache::TokenRef
pToken(new FormulaErrorToken(FormulaError::NoRef
));
1902 ScExternalRefCache::TokenRef pToken
=
1903 getSingleRefTokenFromSrcDoc(
1904 nFileId
, *pSrcDoc
, ScAddress(rCell
.Col(),rCell
.Row(),nTab
), pFmt
);
1906 putCellDataIntoCache(maRefCache
, pToken
, nFileId
, rTabName
, rCell
, pFmt
);
1910 // Check if the given table name and the cell position is cached.
1911 sal_uInt32 nFmtIndex
= 0;
1912 ScExternalRefCache::TokenRef pToken
= maRefCache
.getCellData(
1913 nFileId
, rTabName
, rCell
.Col(), rCell
.Row(), &nFmtIndex
);
1917 fillCellFormat(nFmtIndex
, pFmt
);
1921 // reference not cached. read from the source document.
1922 pSrcDoc
= getSrcDocument(nFileId
);
1925 // Source document not reachable.
1926 if (!isLinkUpdateAllowedInDoc(mrDoc
))
1928 // Indicate with specific error.
1929 pToken
.reset(new FormulaErrorToken(FormulaError::LinkFormulaNeedingCheck
));
1933 // Throw a reference error.
1934 pToken
.reset(new FormulaErrorToken(FormulaError::NoRef
));
1940 if (!getSrcDocTable( *pSrcDoc
, rTabName
, nTab
, nFileId
))
1942 // specified table name doesn't exist in the source document.
1943 pToken
.reset(new FormulaErrorToken(FormulaError::NoRef
));
1950 SCCOL nDataCol1
= 0, nDataCol2
= MAXCOL
;
1951 SCROW nDataRow1
= 0, nDataRow2
= MAXROW
;
1952 bool bData
= pSrcDoc
->ShrinkToDataArea(nTab
, nDataCol1
, nDataRow1
, nDataCol2
, nDataRow2
);
1953 if (!bData
|| rCell
.Col() < nDataCol1
|| nDataCol2
< rCell
.Col() || rCell
.Row() < nDataRow1
|| nDataRow2
< rCell
.Row())
1955 // requested cell is outside the data area. Don't even bother caching
1956 // this data, but add it to the cached range to prevent accessing the
1957 // source document time and time again.
1958 ScExternalRefCache::TableTypeRef pCacheTab
=
1959 maRefCache
.getCacheTable(nFileId
, rTabName
, true, nullptr, nullptr);
1961 pCacheTab
->setCachedCell(rCell
.Col(), rCell
.Row());
1963 pToken
.reset(new ScEmptyCellToken(false, false));
1967 pToken
= getSingleRefTokenFromSrcDoc(
1968 nFileId
, *pSrcDoc
, ScAddress(rCell
.Col(),rCell
.Row(),nTab
), pFmt
);
1970 putCellDataIntoCache(maRefCache
, pToken
, nFileId
, rTabName
, rCell
, pFmt
);
1974 ScExternalRefCache::TokenArrayRef
ScExternalRefManager::getDoubleRefTokens(
1975 sal_uInt16 nFileId
, const OUString
& rTabName
, const ScRange
& rRange
, const ScAddress
* pCurPos
)
1978 insertRefCell(nFileId
, *pCurPos
);
1980 maybeLinkExternalFile(nFileId
);
1982 ScRange
aDataRange(rRange
);
1983 ScDocument
* pSrcDoc
= getInMemorySrcDocument(nFileId
);
1986 // Document already loaded in memory.
1987 vector
<ScExternalRefCache::SingleRangeData
> aCacheData
;
1988 ScExternalRefCache::TokenArrayRef pArray
=
1989 getDoubleRefTokensFromSrcDoc(*pSrcDoc
, rTabName
, aDataRange
, aCacheData
);
1991 // Put the data into cache.
1992 putRangeDataIntoCache(maRefCache
, pArray
, nFileId
, rTabName
, aCacheData
, rRange
, aDataRange
);
1996 // Check if the given table name and the cell position is cached.
1997 ScExternalRefCache::TokenArrayRef pArray
=
1998 maRefCache
.getCellRangeData(nFileId
, rTabName
, rRange
);
2003 pSrcDoc
= getSrcDocument(nFileId
);
2006 // Source document is not reachable. Throw a reference error.
2007 pArray
= std::make_shared
<ScTokenArray
>(*maRefCache
.getFakeDoc());
2008 pArray
->AddToken(FormulaErrorToken(FormulaError::NoRef
));
2012 vector
<ScExternalRefCache::SingleRangeData
> aCacheData
;
2013 pArray
= getDoubleRefTokensFromSrcDoc(*pSrcDoc
, rTabName
, aDataRange
, aCacheData
);
2015 // Put the data into cache.
2016 putRangeDataIntoCache(maRefCache
, pArray
, nFileId
, rTabName
, aCacheData
, rRange
, aDataRange
);
2020 ScExternalRefCache::TokenArrayRef
ScExternalRefManager::getRangeNameTokens(
2021 sal_uInt16 nFileId
, const OUString
& rName
, const ScAddress
* pCurPos
)
2024 insertRefCell(nFileId
, *pCurPos
);
2026 maybeLinkExternalFile(nFileId
);
2028 OUString aName
= rName
; // make a copy to have the casing corrected.
2029 ScDocument
* pSrcDoc
= getInMemorySrcDocument(nFileId
);
2032 // Document already loaded in memory.
2033 ScExternalRefCache::TokenArrayRef pArray
=
2034 getRangeNameTokensFromSrcDoc(nFileId
, *pSrcDoc
, aName
);
2037 // Cache this range name array.
2038 maRefCache
.setRangeNameTokens(nFileId
, aName
, pArray
);
2043 ScExternalRefCache::TokenArrayRef pArray
= maRefCache
.getRangeNameTokens(nFileId
, rName
);
2045 // This range name is cached.
2048 pSrcDoc
= getSrcDocument(nFileId
);
2050 // failed to load document from disk.
2051 return ScExternalRefCache::TokenArrayRef();
2053 pArray
= getRangeNameTokensFromSrcDoc(nFileId
, *pSrcDoc
, aName
);
2056 // Cache this range name array.
2057 maRefCache
.setRangeNameTokens(nFileId
, aName
, pArray
);
2064 bool hasRangeName(const ScDocument
& rDoc
, const OUString
& rName
)
2066 ScRangeName
* pExtNames
= rDoc
.GetRangeName();
2067 OUString aUpperName
= ScGlobal::getCharClassPtr()->uppercase(rName
);
2068 const ScRangeData
* pRangeData
= pExtNames
->findByUpperName(aUpperName
);
2069 return pRangeData
!= nullptr;
2074 bool ScExternalRefManager::isValidRangeName(sal_uInt16 nFileId
, const OUString
& rName
)
2076 maybeLinkExternalFile(nFileId
);
2077 ScDocument
* pSrcDoc
= getInMemorySrcDocument(nFileId
);
2080 // Only check the presence of the name.
2081 if (hasRangeName(*pSrcDoc
, rName
))
2083 maRefCache
.setRangeName(nFileId
, rName
);
2089 if (maRefCache
.isValidRangeName(nFileId
, rName
))
2090 // Range name is cached.
2093 pSrcDoc
= getSrcDocument(nFileId
);
2095 // failed to load document from disk.
2098 if (hasRangeName(*pSrcDoc
, rName
))
2100 maRefCache
.setRangeName(nFileId
, rName
);
2107 void ScExternalRefManager::refreshAllRefCells(sal_uInt16 nFileId
)
2109 RefCellMap::iterator itrFile
= maRefCells
.find(nFileId
);
2110 if (itrFile
== maRefCells
.end())
2113 RefCellSet
& rRefCells
= itrFile
->second
;
2114 for_each(rRefCells
.begin(), rRefCells
.end(), UpdateFormulaCell());
2116 ScViewData
* pViewData
= ScDocShell::GetViewData();
2120 ScTabViewShell
* pVShell
= pViewData
->GetViewShell();
2124 // Repainting the grid also repaints the texts, but is there a better way
2125 // to refresh texts?
2126 pVShell
->Invalidate(FID_REPAINT
);
2127 pVShell
->PaintGrid();
2132 void insertRefCellByIterator(
2133 const ScExternalRefManager::RefCellMap::iterator
& itr
, ScFormulaCell
* pCell
)
2137 itr
->second
.insert(pCell
);
2138 pCell
->SetIsExtRef();
2144 void ScExternalRefManager::insertRefCell(sal_uInt16 nFileId
, const ScAddress
& rCell
)
2146 RefCellMap::iterator itr
= maRefCells
.find(nFileId
);
2147 if (itr
== maRefCells
.end())
2149 RefCellSet aRefCells
;
2150 pair
<RefCellMap::iterator
, bool> r
= maRefCells
.emplace(
2151 nFileId
, aRefCells
);
2153 // insertion failed.
2159 insertRefCellByIterator(itr
, mrDoc
.GetFormulaCell(rCell
));
2162 void ScExternalRefManager::insertRefCellFromTemplate( ScFormulaCell
* pTemplateCell
, ScFormulaCell
* pCell
)
2164 if (!pTemplateCell
|| !pCell
)
2167 for (RefCellMap::iterator itr
= maRefCells
.begin(); itr
!= maRefCells
.end(); ++itr
)
2169 if (itr
->second
.find(pTemplateCell
) != itr
->second
.end())
2170 insertRefCellByIterator(itr
, pCell
);
2174 bool ScExternalRefManager::hasCellExternalReference(const ScAddress
& rCell
)
2176 ScFormulaCell
* pCell
= mrDoc
.GetFormulaCell(rCell
);
2179 return std::any_of(maRefCells
.begin(), maRefCells
.end(),
2180 [&pCell
](const RefCellMap::value_type
& rEntry
) { return rEntry
.second
.find(pCell
) != rEntry
.second
.end(); });
2185 void ScExternalRefManager::enableDocTimer( bool bEnable
)
2187 if (mbDocTimerEnabled
== bEnable
)
2190 mbDocTimerEnabled
= bEnable
;
2191 if (mbDocTimerEnabled
)
2193 if (!maDocShells
.empty())
2195 for (auto& rEntry
: maDocShells
)
2196 rEntry
.second
.maLastAccess
= tools::Time(tools::Time::SYSTEM
);
2198 maSrcDocTimer
.Start();
2202 maSrcDocTimer
.Stop();
2205 void ScExternalRefManager::fillCellFormat(sal_uLong nFmtIndex
, ScExternalRefCache::CellFormat
* pFmt
) const
2210 SvNumFormatType nFmtType
= mrDoc
.GetFormatTable()->GetType(nFmtIndex
);
2211 if (nFmtType
!= SvNumFormatType::UNDEFINED
)
2213 pFmt
->mbIsSet
= true;
2214 pFmt
->mnIndex
= nFmtIndex
;
2215 pFmt
->mnType
= nFmtType
;
2219 ScExternalRefCache::TokenRef
ScExternalRefManager::getSingleRefTokenFromSrcDoc(
2220 sal_uInt16 nFileId
, ScDocument
& rSrcDoc
, const ScAddress
& rPos
,
2221 ScExternalRefCache::CellFormat
* pFmt
)
2223 // Get the cell from src doc, and convert it into a token.
2224 ScRefCellValue
aCell(rSrcDoc
, rPos
);
2225 ScExternalRefCache::TokenRef
pToken(convertToToken(mrDoc
, rSrcDoc
, aCell
));
2229 // Generate an error for unresolvable cells.
2230 pToken
.reset( new FormulaErrorToken( FormulaError::NoValue
));
2233 // Get number format information.
2234 sal_uInt32 nFmtIndex
= 0;
2235 rSrcDoc
.GetNumberFormat(rPos
.Col(), rPos
.Row(), rPos
.Tab(), nFmtIndex
);
2236 nFmtIndex
= getMappedNumberFormat(nFileId
, nFmtIndex
, rSrcDoc
);
2237 fillCellFormat(nFmtIndex
, pFmt
);
2241 ScExternalRefCache::TokenArrayRef
ScExternalRefManager::getDoubleRefTokensFromSrcDoc(
2242 const ScDocument
& rSrcDoc
, const OUString
& rTabName
, ScRange
& rRange
,
2243 vector
<ScExternalRefCache::SingleRangeData
>& rCacheData
)
2245 ScExternalRefCache::TokenArrayRef pArray
;
2248 if (!rSrcDoc
.GetTable(rTabName
, nTab1
))
2250 // specified table name doesn't exist in the source document.
2251 pArray
= std::make_shared
<ScTokenArray
>(rSrcDoc
);
2252 pArray
->AddToken(FormulaErrorToken(FormulaError::NoRef
));
2256 ScRange
aRange(rRange
);
2257 aRange
.PutInOrder();
2258 SCTAB nTabSpan
= aRange
.aEnd
.Tab() - aRange
.aStart
.Tab();
2260 vector
<ScExternalRefCache::SingleRangeData
> aCacheData
;
2261 aCacheData
.reserve(nTabSpan
+1);
2262 aCacheData
.emplace_back();
2263 aCacheData
.back().maTableName
= ScGlobal::getCharClassPtr()->uppercase(rTabName
);
2265 for (SCTAB i
= 1; i
< nTabSpan
+ 1; ++i
)
2268 if (!rSrcDoc
.GetName(nTab1
+ 1, aTabName
))
2269 // source document doesn't have any table by the specified name.
2272 aCacheData
.emplace_back();
2273 aCacheData
.back().maTableName
= ScGlobal::getCharClassPtr()->uppercase(aTabName
);
2276 aRange
.aStart
.SetTab(nTab1
);
2277 aRange
.aEnd
.SetTab(nTab1
+ nTabSpan
);
2279 pArray
= convertToTokenArray(mrDoc
, rSrcDoc
, aRange
, aCacheData
);
2281 rCacheData
.swap(aCacheData
);
2285 ScExternalRefCache::TokenArrayRef
ScExternalRefManager::getRangeNameTokensFromSrcDoc(
2286 sal_uInt16 nFileId
, const ScDocument
& rSrcDoc
, OUString
& rName
)
2288 ScRangeName
* pExtNames
= rSrcDoc
.GetRangeName();
2289 OUString aUpperName
= ScGlobal::getCharClassPtr()->uppercase(rName
);
2290 const ScRangeData
* pRangeData
= pExtNames
->findByUpperName(aUpperName
);
2292 return ScExternalRefCache::TokenArrayRef();
2294 // Parse all tokens in this external range data, and replace each absolute
2295 // reference token with an external reference token, and cache them. Also
2296 // register the source document with the link manager if it's a new
2299 ScExternalRefCache::TokenArrayRef pNew
= std::make_shared
<ScTokenArray
>(rSrcDoc
);
2301 ScTokenArray
aCode(*pRangeData
->GetCode());
2302 FormulaTokenArrayPlainIterator
aIter(aCode
);
2303 for (const FormulaToken
* pToken
= aIter
.First(); pToken
; pToken
= aIter
.Next())
2305 bool bTokenAdded
= false;
2306 switch (pToken
->GetType())
2310 const ScSingleRefData
& rRef
= *pToken
->GetSingleRef();
2312 rSrcDoc
.GetName(rRef
.Tab(), aTabName
);
2313 ScExternalSingleRefToken
aNewToken(nFileId
, svl::SharedString( aTabName
), // string not interned
2314 *pToken
->GetSingleRef());
2315 pNew
->AddToken(aNewToken
);
2321 const ScSingleRefData
& rRef
= *pToken
->GetSingleRef();
2323 rSrcDoc
.GetName(rRef
.Tab(), aTabName
);
2324 ScExternalDoubleRefToken
aNewToken(nFileId
, svl::SharedString( aTabName
), // string not interned
2325 *pToken
->GetDoubleRef());
2326 pNew
->AddToken(aNewToken
);
2335 pNew
->AddToken(*pToken
);
2338 rName
= pRangeData
->GetName(); // Get the correctly-cased name.
2342 ScDocument
* ScExternalRefManager::getInMemorySrcDocument(sal_uInt16 nFileId
)
2344 const OUString
* pFileName
= getExternalFileName(nFileId
);
2348 // Do not load document until it was allowed.
2349 if (!isLinkUpdateAllowedInDoc(mrDoc
))
2352 ScDocument
* pSrcDoc
= nullptr;
2353 ScDocShell
* pShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetFirst(checkSfxObjectShell
<ScDocShell
>, false));
2356 SfxMedium
* pMedium
= pShell
->GetMedium();
2357 if (pMedium
&& !pMedium
->GetName().isEmpty())
2359 // TODO: We should make the case sensitivity platform dependent.
2360 if (pFileName
->equalsIgnoreAsciiCase(pMedium
->GetName()))
2363 pSrcDoc
= &pShell
->GetDocument();
2369 // handle unsaved documents here
2370 OUString aName
= pShell
->GetName();
2371 if (pFileName
->equalsIgnoreAsciiCase(aName
))
2375 aSrcDoc
.maShell
= pShell
;
2376 maUnsavedDocShells
.emplace(nFileId
, aSrcDoc
);
2377 StartListening(*pShell
);
2378 pSrcDoc
= &pShell
->GetDocument();
2382 pShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetNext(*pShell
, checkSfxObjectShell
<ScDocShell
>, false));
2385 initDocInCache(maRefCache
, pSrcDoc
, nFileId
);
2389 ScDocument
* ScExternalRefManager::getSrcDocument(sal_uInt16 nFileId
)
2391 if (!mrDoc
.IsExecuteLinkEnabled())
2394 DocShellMap::iterator itrEnd
= maDocShells
.end();
2395 DocShellMap::iterator itr
= maDocShells
.find(nFileId
);
2399 // document already loaded.
2401 SfxObjectShell
* p
= itr
->second
.maShell
.get();
2402 itr
->second
.maLastAccess
= tools::Time( tools::Time::SYSTEM
);
2403 return &static_cast<ScDocShell
*>(p
)->GetDocument();
2406 itrEnd
= maUnsavedDocShells
.end();
2407 itr
= maUnsavedDocShells
.find(nFileId
);
2410 //document is unsaved document
2412 SfxObjectShell
* p
= itr
->second
.maShell
.get();
2413 itr
->second
.maLastAccess
= tools::Time( tools::Time::SYSTEM
);
2414 return &static_cast<ScDocShell
*>(p
)->GetDocument();
2417 const OUString
* pFile
= getExternalFileName(nFileId
);
2419 // no file name associated with this ID.
2426 aSrcDoc
.maShell
= loadSrcDocument(nFileId
, aFilter
);
2428 catch (const css::uno::Exception
&)
2431 if (!aSrcDoc
.maShell
.is())
2433 // source document could not be loaded.
2437 return &cacheNewDocShell(nFileId
, aSrcDoc
);
2440 SfxObjectShellRef
ScExternalRefManager::loadSrcDocument(sal_uInt16 nFileId
, OUString
& rFilter
)
2442 // Do not load document until it was allowed.
2443 if (!isLinkUpdateAllowedInDoc(mrDoc
))
2446 const SrcFileData
* pFileData
= getExternalFileData(nFileId
);
2450 // Always load the document by using the path created from the relative
2451 // path. If the referenced document is not there, simply exit. The
2452 // original file name should be used only when the relative path is not
2454 OUString aFile
= pFileData
->maFileName
;
2455 maybeCreateRealFileName(nFileId
);
2456 if (!pFileData
->maRealFileName
.isEmpty())
2457 aFile
= pFileData
->maRealFileName
;
2459 if (!isFileLoadable(aFile
))
2462 OUString aOptions
= pFileData
->maFilterOptions
;
2463 if ( !pFileData
->maFilterName
.isEmpty() )
2464 rFilter
= pFileData
->maFilterName
; // don't overwrite stored filter with guessed filter
2466 ScDocumentLoader::GetFilterName(aFile
, rFilter
, aOptions
, true, false);
2467 std::shared_ptr
<const SfxFilter
> pFilter
= ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName(rFilter
);
2469 if (pFileData
->maRelativeName
.isEmpty())
2471 // Generate a relative file path.
2472 INetURLObject
aBaseURL(getOwnDocumentName());
2473 aBaseURL
.insertName("content.xml");
2475 OUString aStr
= URIHelper::simpleNormalizedMakeRelative(
2476 aBaseURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
), aFile
);
2478 setRelativeFileName(nFileId
, aStr
);
2481 std::unique_ptr
<SfxItemSet
> pSet(new SfxAllItemSet(SfxGetpApp()->GetPool()));
2482 if (!aOptions
.isEmpty())
2483 pSet
->Put(SfxStringItem(SID_FILE_FILTEROPTIONS
, aOptions
));
2485 // make medium hidden to prevent assertion from progress bar
2486 pSet
->Put( SfxBoolItem(SID_HIDDEN
, true) );
2488 // If the current document is allowed to execute macros then the referenced
2489 // document may execute macros according to the security configuration.
2490 // Similar for UpdateDocMode to update links, just that if we reach here
2491 // the user already allowed updates and intermediate documents are expected
2492 // to update as well. When loading the document ScDocShell::Load() will
2493 // check through ScDocShell::GetLinkUpdateModeState() if its location is
2495 SfxObjectShell
* pShell
= mrDoc
.GetDocumentShell();
2498 SfxMedium
* pMedium
= pShell
->GetMedium();
2501 const SfxPoolItem
* pItem
;
2502 if (pMedium
->GetItemSet()->GetItemState( SID_MACROEXECMODE
, false, &pItem
) == SfxItemState::SET
&&
2503 static_cast<const SfxUInt16Item
*>(pItem
)->GetValue() != css::document::MacroExecMode::NEVER_EXECUTE
)
2504 pSet
->Put( SfxUInt16Item( SID_MACROEXECMODE
, css::document::MacroExecMode::USE_CONFIG
));
2507 pSet
->Put( SfxUInt16Item( SID_UPDATEDOCMODE
, css::document::UpdateDocMode::FULL_UPDATE
));
2510 unique_ptr
<SfxMedium
> pMedium(new SfxMedium(aFile
, StreamMode::STD_READ
, pFilter
, std::move(pSet
)));
2511 if (pMedium
->GetError() != ERRCODE_NONE
)
2514 // To load encrypted documents with password, user interaction needs to be enabled.
2515 pMedium
->UseInteractionHandler(mbUserInteractionEnabled
);
2517 ScDocShell
* pNewShell
= new ScDocShell(SfxModelFlags::EXTERNAL_LINK
);
2518 SfxObjectShellRef aRef
= pNewShell
;
2520 // increment the recursive link count of the source document.
2521 ScExtDocOptions
* pExtOpt
= mrDoc
.GetExtDocOptions();
2522 sal_uInt32 nLinkCount
= pExtOpt
? pExtOpt
->GetDocSettings().mnLinkCnt
: 0;
2523 ScDocument
& rSrcDoc
= pNewShell
->GetDocument();
2524 rSrcDoc
.EnableExecuteLink(false); // to prevent circular access of external references.
2525 rSrcDoc
.EnableUndo(false);
2526 rSrcDoc
.LockAdjustHeight();
2527 rSrcDoc
.EnableUserInteraction(false);
2529 ScExtDocOptions
* pExtOptNew
= rSrcDoc
.GetExtDocOptions();
2532 rSrcDoc
.SetExtDocOptions(std::make_unique
<ScExtDocOptions
>());
2533 pExtOptNew
= rSrcDoc
.GetExtDocOptions();
2535 pExtOptNew
->GetDocSettings().mnLinkCnt
= nLinkCount
+ 1;
2537 if (!pNewShell
->DoLoad(pMedium
.release()))
2544 // with UseInteractionHandler, options may be set by dialog during DoLoad
2545 OUString aNew
= ScDocumentLoader::GetOptions(*pNewShell
->GetMedium());
2546 if (!aNew
.isEmpty() && aNew
!= aOptions
)
2548 setFilterData(nFileId
, rFilter
, aOptions
); // update the filter data, including the new options
2553 ScDocument
& ScExternalRefManager::cacheNewDocShell( sal_uInt16 nFileId
, SrcShell
& rSrcShell
)
2555 if (mbDocTimerEnabled
&& maDocShells
.empty())
2556 // If this is the first source document insertion, start up the timer.
2557 maSrcDocTimer
.Start();
2559 maDocShells
.emplace(nFileId
, rSrcShell
);
2560 SfxObjectShell
& rShell
= *rSrcShell
.maShell
;
2561 ScDocument
& rSrcDoc
= static_cast<ScDocShell
&>(rShell
).GetDocument();
2562 initDocInCache(maRefCache
, &rSrcDoc
, nFileId
);
2566 bool ScExternalRefManager::isFileLoadable(const OUString
& rFile
) const
2568 if (rFile
.isEmpty())
2571 if (isOwnDocument(rFile
))
2574 if (osl::FileBase::getSystemPathFromFileURL(rFile
, aPhysical
)
2575 == osl::FileBase::E_None
)
2577 // #i114504# try IsFolder/Exists only for file URLs
2579 if (utl::UCBContentHelper::IsFolder(rFile
))
2582 return utl::UCBContentHelper::Exists(rFile
);
2585 return true; // for http and others, Exists doesn't work, but the URL can still be opened
2588 void ScExternalRefManager::maybeLinkExternalFile( sal_uInt16 nFileId
, bool bDeferFilterDetection
)
2590 if (maLinkedDocs
.count(nFileId
))
2591 // file already linked, or the link has been broken.
2594 // Source document not linked yet. Link it now.
2595 const OUString
* pFileName
= getExternalFileName(nFileId
);
2599 OUString aFilter
, aOptions
;
2600 const SrcFileData
* pFileData
= getExternalFileData(nFileId
);
2603 aFilter
= pFileData
->maFilterName
;
2604 aOptions
= pFileData
->maFilterOptions
;
2607 // Filter detection may access external links; defer it until we are allowed.
2608 if (!bDeferFilterDetection
)
2609 bDeferFilterDetection
= !isLinkUpdateAllowedInDoc(mrDoc
);
2611 // If a filter was already set (for example, loading the cached table),
2612 // don't call GetFilterName which has to access the source file.
2613 // If filter detection is deferred, the next successful loadSrcDocument()
2614 // will update SrcFileData filter name.
2615 if (aFilter
.isEmpty() && !bDeferFilterDetection
)
2616 ScDocumentLoader::GetFilterName(*pFileName
, aFilter
, aOptions
, true, false);
2617 sfx2::LinkManager
* pLinkMgr
= mrDoc
.GetLinkManager();
2620 SAL_WARN( "sc.ui", "ScExternalRefManager::maybeLinkExternalFile: pLinkMgr==NULL");
2623 ScExternalRefLink
* pLink
= new ScExternalRefLink(mrDoc
, nFileId
);
2624 OSL_ENSURE(pFileName
, "ScExternalRefManager::maybeLinkExternalFile: file name pointer is NULL");
2625 pLinkMgr
->InsertFileLink(*pLink
, sfx2::SvBaseLinkObjectType::ClientFile
, *pFileName
,
2626 (aFilter
.isEmpty() && bDeferFilterDetection
? nullptr : &aFilter
));
2628 pLink
->SetDoRefresh(false);
2630 pLink
->SetDoRefresh(true);
2632 maLinkedDocs
.emplace(nFileId
, true);
2635 void ScExternalRefManager::addFilesToLinkManager()
2637 if (maSrcFiles
.empty())
2640 SAL_WARN_IF( maSrcFiles
.size() >= SAL_MAX_UINT16
,
2641 "sc.ui", "ScExternalRefManager::addFilesToLinkManager: files overflow");
2642 const sal_uInt16 nSize
= static_cast<sal_uInt16
>( std::min
<size_t>( maSrcFiles
.size(), SAL_MAX_UINT16
));
2643 for (sal_uInt16 nFileId
= 0; nFileId
< nSize
; ++nFileId
)
2644 maybeLinkExternalFile( nFileId
, true);
2647 void ScExternalRefManager::SrcFileData::maybeCreateRealFileName(const OUString
& rOwnDocName
)
2649 if (maRelativeName
.isEmpty())
2650 // No relative path given. Nothing to do.
2653 if (!maRealFileName
.isEmpty())
2654 // Real file name already created. Nothing to do.
2657 // Formulate the absolute file path from the relative path.
2658 const OUString
& rRelPath
= maRelativeName
;
2659 INetURLObject
aBaseURL(rOwnDocName
);
2660 aBaseURL
.insertName("content.xml");
2661 bool bWasAbs
= false;
2662 maRealFileName
= aBaseURL
.smartRel2Abs(rRelPath
, bWasAbs
).GetMainURL(INetURLObject::DecodeMechanism::NONE
);
2665 void ScExternalRefManager::maybeCreateRealFileName(sal_uInt16 nFileId
)
2667 if (nFileId
>= maSrcFiles
.size())
2670 maSrcFiles
[nFileId
].maybeCreateRealFileName(getOwnDocumentName());
2673 OUString
ScExternalRefManager::getOwnDocumentName() const
2675 if (utl::ConfigManager::IsFuzzing())
2676 return "file:///tmp/document";
2678 SfxObjectShell
* pShell
= mrDoc
.GetDocumentShell();
2680 // This should not happen!
2683 SfxMedium
* pMed
= pShell
->GetMedium();
2687 return pMed
->GetName();
2690 bool ScExternalRefManager::isOwnDocument(const OUString
& rFile
) const
2692 return getOwnDocumentName() == rFile
;
2695 void ScExternalRefManager::convertToAbsName(OUString
& rFile
) const
2697 // unsaved documents have no AbsName
2698 ScDocShell
* pShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetFirst(checkSfxObjectShell
<ScDocShell
>, false));
2701 if (rFile
== pShell
->GetName())
2704 pShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetNext(*pShell
, checkSfxObjectShell
<ScDocShell
>, false));
2707 SfxObjectShell
* pDocShell
= mrDoc
.GetDocumentShell();
2708 rFile
= ScGlobal::GetAbsDocName(rFile
, pDocShell
);
2711 sal_uInt16
ScExternalRefManager::getExternalFileId(const OUString
& rFile
)
2713 vector
<SrcFileData
>::const_iterator itrBeg
= maSrcFiles
.begin(), itrEnd
= maSrcFiles
.end();
2714 vector
<SrcFileData
>::const_iterator itr
= find_if(itrBeg
, itrEnd
, FindSrcFileByName(rFile
));
2717 size_t nId
= distance(itrBeg
, itr
);
2718 return static_cast<sal_uInt16
>(nId
);
2722 aData
.maFileName
= rFile
;
2723 maSrcFiles
.push_back(aData
);
2724 return static_cast<sal_uInt16
>(maSrcFiles
.size() - 1);
2727 const OUString
* ScExternalRefManager::getExternalFileName(sal_uInt16 nFileId
, bool bForceOriginal
)
2729 if (nFileId
>= maSrcFiles
.size())
2733 return &maSrcFiles
[nFileId
].maFileName
;
2735 maybeCreateRealFileName(nFileId
);
2737 if (!maSrcFiles
[nFileId
].maRealFileName
.isEmpty())
2738 return &maSrcFiles
[nFileId
].maRealFileName
;
2740 return &maSrcFiles
[nFileId
].maFileName
;
2743 sal_uInt16
ScExternalRefManager::convertFileIdToUsedFileId(sal_uInt16 nFileId
)
2745 if (!mbSkipUnusedFileIds
)
2748 return maConvertFileIdToUsedFileId
[nFileId
];
2751 void ScExternalRefManager::setSkipUnusedFileIds(std::vector
<sal_uInt16
>& rExternFileIds
)
2753 mbSkipUnusedFileIds
= true;
2754 maConvertFileIdToUsedFileId
.resize(maSrcFiles
.size());
2755 std::fill(maConvertFileIdToUsedFileId
.begin(), maConvertFileIdToUsedFileId
.end(), 0);
2757 for (auto nEntry
: rExternFileIds
)
2759 maConvertFileIdToUsedFileId
[nEntry
] = nUsedCount
++;
2763 void ScExternalRefManager::disableSkipUnusedFileIds()
2765 mbSkipUnusedFileIds
= false;
2768 std::vector
<OUString
> ScExternalRefManager::getAllCachedExternalFileNames() const
2770 std::vector
<OUString
> aNames
;
2771 aNames
.reserve(maSrcFiles
.size());
2772 for (const SrcFileData
& rData
: maSrcFiles
)
2774 aNames
.push_back(rData
.maFileName
);
2780 bool ScExternalRefManager::hasExternalFile(sal_uInt16 nFileId
) const
2782 return nFileId
< maSrcFiles
.size();
2785 bool ScExternalRefManager::hasExternalFile(const OUString
& rFile
) const
2787 return ::std::any_of(maSrcFiles
.begin(), maSrcFiles
.end(), FindSrcFileByName(rFile
));
2790 const ScExternalRefManager::SrcFileData
* ScExternalRefManager::getExternalFileData(sal_uInt16 nFileId
) const
2792 if (nFileId
>= maSrcFiles
.size())
2795 return &maSrcFiles
[nFileId
];
2798 const OUString
* ScExternalRefManager::getRealTableName(sal_uInt16 nFileId
, const OUString
& rTabName
) const
2800 return maRefCache
.getRealTableName(nFileId
, rTabName
);
2803 const OUString
* ScExternalRefManager::getRealRangeName(sal_uInt16 nFileId
, const OUString
& rRangeName
) const
2805 return maRefCache
.getRealRangeName(nFileId
, rRangeName
);
2808 template<typename MapContainer
>
2809 static void lcl_removeByFileId(sal_uInt16 nFileId
, MapContainer
& rMap
)
2811 typename
MapContainer::iterator itr
= rMap
.find(nFileId
);
2812 if (itr
!= rMap
.end())
2814 // Close this document shell.
2815 itr
->second
.maShell
->DoClose();
2820 void ScExternalRefManager::clearCache(sal_uInt16 nFileId
)
2822 maRefCache
.clearCache(nFileId
);
2827 class RefCacheFiller
: public sc::ColumnSpanSet::ColumnAction
2829 svl::SharedStringPool
& mrStrPool
;
2831 ScExternalRefCache
& mrRefCache
;
2832 ScExternalRefCache::TableTypeRef mpRefTab
;
2833 sal_uInt16 mnFileId
;
2835 sc::ColumnBlockConstPosition maBlockPos
;
2838 RefCacheFiller( svl::SharedStringPool
& rStrPool
, ScExternalRefCache
& rRefCache
, sal_uInt16 nFileId
) :
2839 mrStrPool(rStrPool
), mrRefCache(rRefCache
), mnFileId(nFileId
), mpCurCol(nullptr) {}
2841 virtual void startColumn( ScColumn
* pCol
) override
2847 mpCurCol
->InitBlockPosition(maBlockPos
);
2848 mpRefTab
= mrRefCache
.getCacheTable(mnFileId
, mpCurCol
->GetTab());
2851 virtual void execute( SCROW nRow1
, SCROW nRow2
, bool bVal
) override
2853 if (!mpCurCol
|| !bVal
)
2859 for (SCROW nRow
= nRow1
; nRow
<= nRow2
; ++nRow
)
2861 ScExternalRefCache::TokenRef pTok
;
2862 ScRefCellValue aCell
= mpCurCol
->GetCellValue(maBlockPos
, nRow
);
2863 switch (aCell
.meType
)
2865 case CELLTYPE_STRING
:
2868 OUString aStr
= aCell
.getString(&mpCurCol
->GetDoc());
2869 svl::SharedString aSS
= mrStrPool
.intern(aStr
);
2870 pTok
.reset(new formula::FormulaStringToken(aSS
));
2873 case CELLTYPE_VALUE
:
2874 pTok
.reset(new formula::FormulaDoubleToken(aCell
.mfValue
));
2876 case CELLTYPE_FORMULA
:
2878 sc::FormulaResultValue aRes
= aCell
.mpFormula
->GetResult();
2879 switch (aRes
.meType
)
2881 case sc::FormulaResultValue::Value
:
2882 pTok
.reset(new formula::FormulaDoubleToken(aRes
.mfValue
));
2884 case sc::FormulaResultValue::String
:
2886 // Re-intern the string to the host document pool.
2887 svl::SharedString aInterned
= mrStrPool
.intern(aRes
.maString
.getString());
2888 pTok
.reset(new formula::FormulaStringToken(aInterned
));
2891 case sc::FormulaResultValue::Error
:
2892 case sc::FormulaResultValue::Invalid
:
2894 pTok
.reset(new FormulaErrorToken(FormulaError::NoValue
));
2899 pTok
.reset(new FormulaErrorToken(FormulaError::NoValue
));
2905 mpRefTab
->setCell(mpCurCol
->GetCol(), nRow
, pTok
, mpCurCol
->GetNumberFormat(mpCurCol
->GetDoc().GetNonThreadedContext(), nRow
));
2906 mpRefTab
->setCachedCell(mpCurCol
->GetCol(), nRow
);
2914 bool ScExternalRefManager::refreshSrcDocument(sal_uInt16 nFileId
)
2916 SfxObjectShellRef xDocShell
;
2920 xDocShell
= loadSrcDocument(nFileId
, aFilter
);
2922 catch ( const css::uno::Exception
& ) {}
2924 if (!xDocShell
.is())
2925 // Failed to load the document. Bail out.
2928 ScDocShell
& rDocSh
= static_cast<ScDocShell
&>(*xDocShell
);
2929 ScDocument
& rSrcDoc
= rDocSh
.GetDocument();
2931 sc::ColumnSpanSet aCachedArea
;
2932 maRefCache
.getAllCachedDataSpans(rSrcDoc
, nFileId
, aCachedArea
);
2934 // Clear the existing cache, and refill it. Make sure we keep the
2935 // existing cache table instances here.
2936 maRefCache
.clearCacheTables(nFileId
);
2937 RefCacheFiller
aAction(mrDoc
.GetSharedStringPool(), maRefCache
, nFileId
);
2938 aCachedArea
.executeColumnAction(rSrcDoc
, aAction
);
2940 DocShellMap::iterator it
= maDocShells
.find(nFileId
);
2941 if (it
!= maDocShells
.end())
2943 it
->second
.maShell
->DoClose();
2944 it
->second
.maShell
= xDocShell
;
2945 it
->second
.maLastAccess
= tools::Time(tools::Time::SYSTEM
);
2950 aSrcDoc
.maShell
= xDocShell
;
2951 aSrcDoc
.maLastAccess
= tools::Time(tools::Time::SYSTEM
);
2952 cacheNewDocShell(nFileId
, aSrcDoc
);
2955 // Update all cells containing names from this source document.
2956 refreshAllRefCells(nFileId
);
2958 notifyAllLinkListeners(nFileId
, LINK_MODIFIED
);
2963 void ScExternalRefManager::breakLink(sal_uInt16 nFileId
)
2965 // Turn all formula cells referencing this external document into static
2967 RefCellMap::iterator itrRefs
= maRefCells
.find(nFileId
);
2968 if (itrRefs
!= maRefCells
.end())
2970 // Make a copy because removing the formula cells below will modify
2971 // the original container.
2972 RefCellSet aSet
= itrRefs
->second
;
2973 for_each(aSet
.begin(), aSet
.end(), ConvertFormulaToStatic(&mrDoc
));
2974 maRefCells
.erase(nFileId
);
2977 // Remove all named ranges that reference this document.
2979 // Global named ranges.
2980 ScRangeName
* pRanges
= mrDoc
.GetRangeName();
2982 removeRangeNamesBySrcDoc(*pRanges
, nFileId
);
2984 // Sheet-local named ranges.
2985 for (SCTAB i
= 0, n
= mrDoc
.GetTableCount(); i
< n
; ++i
)
2987 pRanges
= mrDoc
.GetRangeName(i
);
2989 removeRangeNamesBySrcDoc(*pRanges
, nFileId
);
2992 clearCache(nFileId
);
2993 lcl_removeByFileId(nFileId
, maDocShells
);
2995 if (maDocShells
.empty())
2996 maSrcDocTimer
.Stop();
2998 LinkedDocMap::iterator itr
= maLinkedDocs
.find(nFileId
);
2999 if (itr
!= maLinkedDocs
.end())
3000 itr
->second
= false;
3002 notifyAllLinkListeners(nFileId
, LINK_BROKEN
);
3005 void ScExternalRefManager::switchSrcFile(sal_uInt16 nFileId
, const OUString
& rNewFile
, const OUString
& rNewFilter
)
3007 maSrcFiles
[nFileId
].maFileName
= rNewFile
;
3008 maSrcFiles
[nFileId
].maRelativeName
.clear();
3009 maSrcFiles
[nFileId
].maRealFileName
.clear();
3010 if (maSrcFiles
[nFileId
].maFilterName
!= rNewFilter
)
3012 // Filter type has changed.
3013 maSrcFiles
[nFileId
].maFilterName
= rNewFilter
;
3014 maSrcFiles
[nFileId
].maFilterOptions
.clear();
3016 refreshSrcDocument(nFileId
);
3019 void ScExternalRefManager::setRelativeFileName(sal_uInt16 nFileId
, const OUString
& rRelUrl
)
3021 if (nFileId
>= maSrcFiles
.size())
3023 maSrcFiles
[nFileId
].maRelativeName
= rRelUrl
;
3026 void ScExternalRefManager::setFilterData(sal_uInt16 nFileId
, const OUString
& rFilterName
, const OUString
& rOptions
)
3028 if (nFileId
>= maSrcFiles
.size())
3030 maSrcFiles
[nFileId
].maFilterName
= rFilterName
;
3031 maSrcFiles
[nFileId
].maFilterOptions
= rOptions
;
3034 void ScExternalRefManager::clear()
3036 for (auto& rEntry
: maDocShells
)
3037 rEntry
.second
.maShell
->DoClose();
3039 maDocShells
.clear();
3040 maSrcDocTimer
.Stop();
3043 bool ScExternalRefManager::hasExternalData() const
3045 return !maSrcFiles
.empty();
3048 void ScExternalRefManager::resetSrcFileData(const OUString
& rBaseFileUrl
)
3050 for (auto& rSrcFile
: maSrcFiles
)
3052 // Re-generate relative file name from the absolute file name.
3053 OUString aAbsName
= rSrcFile
.maRealFileName
;
3054 if (aAbsName
.isEmpty())
3055 aAbsName
= rSrcFile
.maFileName
;
3057 rSrcFile
.maRelativeName
= URIHelper::simpleNormalizedMakeRelative(
3058 rBaseFileUrl
, aAbsName
);
3062 void ScExternalRefManager::updateAbsAfterLoad()
3064 OUString
aOwn( getOwnDocumentName() );
3065 for (auto& rSrcFile
: maSrcFiles
)
3067 // update maFileName to the real file name,
3068 // to be called when the original name is no longer needed (after CompileXML)
3070 rSrcFile
.maybeCreateRealFileName( aOwn
);
3071 OUString aReal
= rSrcFile
.maRealFileName
;
3072 if (!aReal
.isEmpty())
3073 rSrcFile
.maFileName
= aReal
;
3077 void ScExternalRefManager::removeRefCell(ScFormulaCell
* pCell
)
3079 for_each(maRefCells
.begin(), maRefCells
.end(), RemoveFormulaCell(pCell
));
3082 void ScExternalRefManager::addLinkListener(sal_uInt16 nFileId
, LinkListener
* pListener
)
3084 LinkListenerMap::iterator itr
= maLinkListeners
.find(nFileId
);
3085 if (itr
== maLinkListeners
.end())
3087 pair
<LinkListenerMap::iterator
, bool> r
= maLinkListeners
.emplace(
3088 nFileId
, LinkListeners());
3091 OSL_FAIL("insertion of new link listener list failed");
3098 LinkListeners
& rList
= itr
->second
;
3099 rList
.insert(pListener
);
3102 void ScExternalRefManager::removeLinkListener(sal_uInt16 nFileId
, LinkListener
* pListener
)
3104 LinkListenerMap::iterator itr
= maLinkListeners
.find(nFileId
);
3105 if (itr
== maLinkListeners
.end())
3106 // no listeners for a specified file.
3109 LinkListeners
& rList
= itr
->second
;
3110 rList
.erase(pListener
);
3113 // No more listeners for this file. Remove its entry.
3114 maLinkListeners
.erase(itr
);
3117 void ScExternalRefManager::removeLinkListener(LinkListener
* pListener
)
3119 for (auto& rEntry
: maLinkListeners
)
3120 rEntry
.second
.erase(pListener
);
3123 void ScExternalRefManager::notifyAllLinkListeners(sal_uInt16 nFileId
, LinkUpdateType eType
)
3125 LinkListenerMap::iterator itr
= maLinkListeners
.find(nFileId
);
3126 if (itr
== maLinkListeners
.end())
3127 // no listeners for a specified file.
3130 LinkListeners
& rList
= itr
->second
;
3131 for_each(rList
.begin(), rList
.end(), NotifyLinkListener(nFileId
, eType
));
3134 void ScExternalRefManager::purgeStaleSrcDocument(sal_Int32 nTimeOut
)
3136 // To avoid potentially freezing Calc, we close one stale document at a time.
3137 DocShellMap::iterator itr
= std::find_if(maDocShells
.begin(), maDocShells
.end(),
3138 [nTimeOut
](const DocShellMap::value_type
& rEntry
) {
3139 // in 100th of a second.
3140 sal_Int32 nSinceLastAccess
= (tools::Time( tools::Time::SYSTEM
) - rEntry
.second
.maLastAccess
).GetTime();
3141 return nSinceLastAccess
>= nTimeOut
;
3143 if (itr
!= maDocShells
.end())
3145 // Timed out. Let's close this.
3146 itr
->second
.maShell
->DoClose();
3147 maDocShells
.erase(itr
);
3150 if (maDocShells
.empty())
3151 maSrcDocTimer
.Stop();
3154 sal_uInt32
ScExternalRefManager::getMappedNumberFormat(sal_uInt16 nFileId
, sal_uInt32 nNumFmt
, const ScDocument
& rSrcDoc
)
3156 NumFmtMap::iterator itr
= maNumFormatMap
.find(nFileId
);
3157 if (itr
== maNumFormatMap
.end())
3159 // Number formatter map is not initialized for this external document.
3160 pair
<NumFmtMap::iterator
, bool> r
= maNumFormatMap
.emplace(
3161 nFileId
, SvNumberFormatterMergeMap());
3164 // insertion failed.
3168 mrDoc
.GetFormatTable()->MergeFormatter(*rSrcDoc
.GetFormatTable());
3169 SvNumberFormatterMergeMap aMap
= mrDoc
.GetFormatTable()->ConvertMergeTableToMap();
3170 itr
->second
.swap(aMap
);
3172 const SvNumberFormatterMergeMap
& rMap
= itr
->second
;
3173 SvNumberFormatterMergeMap::const_iterator itrNumFmt
= rMap
.find(nNumFmt
);
3174 if (itrNumFmt
!= rMap
.end())
3175 // mapped value found.
3176 return itrNumFmt
->second
;
3181 void ScExternalRefManager::transformUnsavedRefToSavedRef( SfxObjectShell
* pShell
)
3183 DocShellMap::iterator itr
= maUnsavedDocShells
.begin();
3184 while( itr
!= maUnsavedDocShells
.end() )
3186 if ( itr
->second
.maShell
.get() == pShell
)
3188 // found that the shell is marked as unsaved
3189 OUString aFileURL
= pShell
->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri
);
3190 switchSrcFile(itr
->first
, aFileURL
, OUString());
3191 EndListening(*pShell
);
3192 maUnsavedDocShells
.erase(itr
++);
3197 void ScExternalRefManager::Notify( SfxBroadcaster
&, const SfxHint
& rHint
)
3199 const SfxEventHint
* pEventHint
= dynamic_cast<const SfxEventHint
*>(&rHint
);
3203 SfxEventHintId nEventId
= pEventHint
->GetEventId();
3206 case SfxEventHintId::PrepareCloseDoc
:
3208 std::unique_ptr
<weld::MessageDialog
> xWarn(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
3209 VclMessageType::Warning
, VclButtonsType::Ok
,
3210 ScResId(STR_CLOSE_WITH_UNSAVED_REFS
)));
3214 case SfxEventHintId::SaveDocDone
:
3215 case SfxEventHintId::SaveAsDocDone
:
3217 SfxObjectShell
* pObjShell
= static_cast<const SfxEventHint
&>( rHint
).GetObjShell();
3218 transformUnsavedRefToSavedRef(pObjShell
);
3226 IMPL_LINK(ScExternalRefManager
, TimeOutHdl
, Timer
*, pTimer
, void)
3228 if (pTimer
== &maSrcDocTimer
)
3229 purgeStaleSrcDocument(SRCDOC_LIFE_SPAN
);
3232 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */