Bug Summary

File:home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx
Warning:line 2437, column 5
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name externalrefmgr.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -isystem /usr/include/libxml2 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D SC_DLLIMPLEMENTATION -D SC_INFO_OSVERSION="LINUX" -D SYSTEM_LIBXML -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/liborcus/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/mdds/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/i18n -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/common -I /home/maarten/src/libreoffice/core/external/clew/source/include -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/sc/source/core/inc -I /home/maarten/src/libreoffice/core/sc/source/filter/inc -I /home/maarten/src/libreoffice/core/sc/source/ui/inc -I /home/maarten/src/libreoffice/core/sc/inc -I /home/maarten/src/libreoffice/core/workdir/SdiTarget/sc/sdi -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/officecfg/registry -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/oovbaapi/normal -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx

/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx

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/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>
60#include <column.hxx>
61#include <com/sun/star/document/MacroExecMode.hpp>
62#include <com/sun/star/document/UpdateDocMode.hpp>
63#include <sal/log.hxx>
64
65#include <memory>
66#include <algorithm>
67
68using ::std::unique_ptr;
69using ::com::sun::star::uno::Any;
70using ::std::vector;
71using ::std::find_if;
72using ::std::for_each;
73using ::std::distance;
74using ::std::pair;
75using namespace formula;
76
77#define SRCDOC_LIFE_SPAN30000 30000 // 5 minutes (in 100th of a sec)
78#define SRCDOC_SCAN_INTERVAL1000*30 1000*30 // every 30 seconds (in msec)
79
80namespace {
81
82class TabNameSearchPredicate
83{
84public:
85 explicit TabNameSearchPredicate(const OUString& rSearchName) :
86 maSearchName(ScGlobal::getCharClassPtr()->uppercase(rSearchName))
87 {
88 }
89
90 bool operator()(const ScExternalRefCache::TableName& rTabNameSet) const
91 {
92 // Ok, I'm doing case insensitive search here.
93 return rTabNameSet.maUpperName == maSearchName;
94 }
95
96private:
97 OUString maSearchName;
98};
99
100class FindSrcFileByName
101{
102public:
103 explicit FindSrcFileByName(const OUString& rMatchName) :
104 mrMatchName(rMatchName)
105 {
106 }
107
108 bool operator()(const ScExternalRefManager::SrcFileData& rSrcData) const
109 {
110 return rSrcData.maFileName == mrMatchName;
111 }
112
113private:
114 const OUString& mrMatchName;
115};
116
117class NotifyLinkListener
118{
119public:
120 NotifyLinkListener(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType) :
121 mnFileId(nFileId), meType(eType) {}
122
123 void operator() (ScExternalRefManager::LinkListener* p) const
124 {
125 p->notify(mnFileId, meType);
126 }
127private:
128 sal_uInt16 mnFileId;
129 ScExternalRefManager::LinkUpdateType meType;
130};
131
132struct UpdateFormulaCell
133{
134 void operator() (ScFormulaCell* pCell) const
135 {
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))
142 return;
143
144 if (pCode->GetCodeError() != FormulaError::NONE)
145 {
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();
150 }
151
152 pCell->SetDirty();
153 }
154};
155
156class RemoveFormulaCell
157{
158public:
159 explicit RemoveFormulaCell(ScFormulaCell* p) : mpCell(p) {}
160 void operator() (pair<const sal_uInt16, ScExternalRefManager::RefCellSet>& r) const
161 {
162 r.second.erase(mpCell);
163 }
164private:
165 ScFormulaCell* mpCell;
166};
167
168class ConvertFormulaToStatic
169{
170public:
171 explicit ConvertFormulaToStatic(ScDocument* pDoc) : mpDoc(pDoc) {}
172 void operator() (ScFormulaCell* pCell) const
173 {
174 ScAddress aPos = pCell->aPos;
175
176 // We don't check for empty cells because empty external cells are
177 // treated as having a value of 0.
178
179 if (pCell->IsValue())
180 {
181 // Turn this into value cell.
182 mpDoc->SetValue(aPos, pCell->GetValue());
183 }
184 else
185 {
186 // string cell otherwise.
187 ScSetStringParam aParam;
188 aParam.setTextInput();
189 mpDoc->SetString(aPos, pCell->GetString().getString(), &aParam);
190 }
191 }
192private:
193 ScDocument* mpDoc;
194};
195
196/**
197 * Check whether a named range contains an external reference to a
198 * particular document.
199 */
200bool hasRefsToSrcDoc(ScRangeData& rData, sal_uInt16 nFileId)
201{
202 ScTokenArray* pArray = rData.GetCode();
203 if (!pArray)
204 return false;
205
206 formula::FormulaTokenArrayPlainIterator aIter(*pArray);
207 formula::FormulaToken* p = aIter.GetNextReference();
208 for (; p; p = aIter.GetNextReference())
209 {
210 if (!p->IsExternalRef())
211 continue;
212
213 if (p->GetIndex() == nFileId)
214 return true;
215 }
216 return false;
217}
218
219class EraseRangeByIterator
220{
221 ScRangeName& mrRanges;
222public:
223 explicit EraseRangeByIterator(ScRangeName& rRanges) : mrRanges(rRanges) {}
224 void operator() (const ScRangeName::iterator& itr)
225 {
226 mrRanges.erase(itr);
227 }
228};
229
230/**
231 * Remove all named ranges that contain references to specified source
232 * document.
233 */
234void removeRangeNamesBySrcDoc(ScRangeName& rRanges, sal_uInt16 nFileId)
235{
236 ScRangeName::iterator itr = rRanges.begin(), itrEnd = rRanges.end();
237 vector<ScRangeName::iterator> v;
238 for (; itr != itrEnd; ++itr)
239 {
240 if (hasRefsToSrcDoc(*itr->second, nFileId))
241 v.push_back(itr);
242 }
243 for_each(v.begin(), v.end(), EraseRangeByIterator(rRanges));
244}
245
246}
247
248ScExternalRefCache::Table::Table()
249 : mbReferenced( true )
250 // Prevent accidental data loss due to lack of knowledge.
251{
252}
253
254ScExternalRefCache::Table::~Table()
255{
256}
257
258void ScExternalRefCache::Table::clear()
259{
260 maRows.clear();
261 maCachedRanges.RemoveAll();
262 mbReferenced = true;
263}
264
265void ScExternalRefCache::Table::setReferenced( bool bReferenced )
266{
267 mbReferenced = bReferenced;
268}
269
270bool ScExternalRefCache::Table::isReferenced() const
271{
272 return mbReferenced;
273}
274
275void ScExternalRefCache::Table::setCell(SCCOL nCol, SCROW nRow, TokenRef const & pToken, sal_uLong nFmtIndex, bool bSetCacheRange)
276{
277 using ::std::pair;
278 RowsDataType::iterator itrRow = maRows.find(nRow);
279 if (itrRow == maRows.end())
280 {
281 // This row does not exist yet.
282 pair<RowsDataType::iterator, bool> res = maRows.emplace(
283 nRow, RowDataType());
284
285 if (!res.second)
286 return;
287
288 itrRow = res.first;
289 }
290
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);
298 if (bSetCacheRange)
299 setCachedCell(nCol, nRow);
300}
301
302ScExternalRefCache::TokenRef ScExternalRefCache::Table::getCell(SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex) const
303{
304 RowsDataType::const_iterator itrTable = maRows.find(nRow);
305 if (itrTable == maRows.end())
306 {
307 // this table doesn't have the specified row.
308 return getEmptyOrNullToken(nCol, nRow);
309 }
310
311 const RowDataType& rRowData = itrTable->second;
312 RowDataType::const_iterator itrRow = rRowData.find(nCol);
313 if (itrRow == rRowData.end())
314 {
315 // this row doesn't have the specified column.
316 return getEmptyOrNullToken(nCol, nRow);
317 }
318
319 const Cell& rCell = itrRow->second;
320 if (pnFmtIndex)
321 *pnFmtIndex = rCell.mnFmtIndex;
322
323 return rCell.mxToken;
324}
325
326bool ScExternalRefCache::Table::hasRow( SCROW nRow ) const
327{
328 RowsDataType::const_iterator itrRow = maRows.find(nRow);
329 return itrRow != maRows.end();
330}
331
332void ScExternalRefCache::Table::getAllRows(vector<SCROW>& rRows, SCROW nLow, SCROW nHigh) const
333{
334 vector<SCROW> aRows;
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);
339
340 // hash map is not ordered, so we need to explicitly sort it.
341 ::std::sort(aRows.begin(), aRows.end());
342 rRows.swap(aRows);
343}
344
345::std::pair< SCROW, SCROW > ScExternalRefCache::Table::getRowRange() const
346{
347 ::std::pair< SCROW, SCROW > aRange( 0, 0 );
348 if( !maRows.empty() )
349 {
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;
355 }
356 return aRange;
357}
358
359void ScExternalRefCache::Table::getAllCols(SCROW nRow, vector<SCCOL>& rCols, SCCOL nLow, SCCOL nHigh) const
360{
361 RowsDataType::const_iterator itrRow = maRows.find(nRow);
362 if (itrRow == maRows.end())
363 // this table doesn't have the specified row.
364 return;
365
366 const RowDataType& rRowData = itrRow->second;
367 vector<SCCOL> aCols;
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);
372
373 // hash map is not ordered, so we need to explicitly sort it.
374 ::std::sort(aCols.begin(), aCols.end());
375 rCols.swap(aCols);
376}
377
378::std::pair< SCCOL, SCCOL > ScExternalRefCache::Table::getColRange( SCROW nRow ) const
379{
380 ::std::pair< SCCOL, SCCOL > aRange( 0, 0 );
381
382 RowsDataType::const_iterator itrRow = maRows.find( nRow );
383 if (itrRow == maRows.end())
384 // this table doesn't have the specified row.
385 return aRange;
386
387 const RowDataType& rRowData = itrRow->second;
388 if( !rRowData.empty() )
389 {
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;
395 }
396 return aRange;
397}
398
399void ScExternalRefCache::Table::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const
400{
401 for (const auto& rRow : maRows)
402 {
403 const RowDataType& rRowData = rRow.second;
404 for (const auto& rCol : rRowData)
405 {
406 const Cell& rCell = rCol.second;
407 rNumFmts.push_back(rCell.mnFmtIndex);
408 }
409 }
410}
411
412bool ScExternalRefCache::Table::isRangeCached(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const
413{
414 return maCachedRanges.In(ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0));
415}
416
417void ScExternalRefCache::Table::setCachedCell(SCCOL nCol, SCROW nRow)
418{
419 setCachedCellRange(nCol, nRow, nCol, nRow);
420}
421
422void ScExternalRefCache::Table::setCachedCellRange(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
423{
424 ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
425 maCachedRanges.Join(aRange);
426}
427
428void ScExternalRefCache::Table::setWholeTableCached()
429{
430 setCachedCellRange(0, 0, MAXCOL, MAXROW);
431}
432
433bool ScExternalRefCache::Table::isInCachedRanges(SCCOL nCol, SCROW nRow) const
434{
435 return maCachedRanges.In(ScRange(nCol, nRow, 0, nCol, nRow, 0));
436}
437
438ScExternalRefCache::TokenRef ScExternalRefCache::Table::getEmptyOrNullToken(
439 SCCOL nCol, SCROW nRow) const
440{
441 if (isInCachedRanges(nCol, nRow))
442 {
443 TokenRef p(new ScEmptyCellToken(false, false));
444 return p;
445 }
446 return TokenRef();
447}
448
449ScExternalRefCache::TableName::TableName(const OUString& rUpper, const OUString& rReal) :
450 maUpperName(rUpper), maRealName(rReal)
451{
452}
453
454ScExternalRefCache::CellFormat::CellFormat() :
455 mbIsSet(false), mnType(SvNumFormatType::ALL), mnIndex(0)
456{
457}
458
459ScExternalRefCache::ScExternalRefCache()
460 : mxFakeDoc(new ScDocument())
461{}
462
463ScExternalRefCache::~ScExternalRefCache() {}
464
465const OUString* ScExternalRefCache::getRealTableName(sal_uInt16 nFileId, const OUString& rTabName) const
466{
467 osl::MutexGuard aGuard(&maMtxDocs);
468
469 DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
470 if (itrDoc == maDocs.end())
471 {
472 // specified document is not cached.
473 return nullptr;
474 }
475
476 const DocItem& rDoc = itrDoc->second;
477 TableNameIndexMap::const_iterator itrTabId = rDoc.findTableNameIndex( rTabName);
478 if (itrTabId == rDoc.maTableNameIndex.end())
479 {
480 // the specified table is not in cache.
481 return nullptr;
482 }
483
484 return &rDoc.maTableNames[itrTabId->second].maRealName;
485}
486
487const OUString* ScExternalRefCache::getRealRangeName(sal_uInt16 nFileId, const OUString& rRangeName) const
488{
489 osl::MutexGuard aGuard(&maMtxDocs);
490
491 DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
492 if (itrDoc == maDocs.end())
493 {
494 // specified document is not cached.
495 return nullptr;
496 }
497
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.
503 return nullptr;
504
505 return &itr->second;
506}
507
508ScExternalRefCache::TokenRef ScExternalRefCache::getCellData(
509 sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow, sal_uInt32* pnFmtIndex)
510{
511 osl::MutexGuard aGuard(&maMtxDocs);
512
513 DocDataType::const_iterator itrDoc = maDocs.find(nFileId);
514 if (itrDoc == maDocs.end())
515 {
516 // specified document is not cached.
517 return TokenRef();
518 }
519
520 const DocItem& rDoc = itrDoc->second;
521 TableNameIndexMap::const_iterator itrTabId = rDoc.findTableNameIndex( rTabName);
522 if (itrTabId == rDoc.maTableNameIndex.end())
523 {
524 // the specified table is not in cache.
525 return TokenRef();
526 }
527
528 const TableTypeRef& pTableData = rDoc.maTables[itrTabId->second];
529 if (!pTableData)
530 {
531 // the table data is not instantiated yet.
532 return TokenRef();
533 }
534
535 return pTableData->getCell(nCol, nRow, pnFmtIndex);
536}
537
538ScExternalRefCache::TokenArrayRef ScExternalRefCache::getCellRangeData(
539 sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange)
540{
541 osl::MutexGuard aGuard(&maMtxDocs);
542
543 DocDataType::iterator itrDoc = maDocs.find(nFileId);
544 if (itrDoc == maDocs.end())
545 // specified document is not cached.
546 return TokenArrayRef();
547
548 DocItem& rDoc = itrDoc->second;
549
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();
554
555 const ScAddress& s = rRange.aStart;
556 const ScAddress& e = rRange.aEnd;
557
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();
561
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();
568
569 ScRange aCacheRange( nCol1, nRow1, static_cast<SCTAB>(nTabFirstId), nCol2, nRow2, static_cast<SCTAB>(nTabLastId));
570
571 RangeArrayMap::const_iterator itrRange = rDoc.maRangeArrays.find( aCacheRange);
572 if (itrRange != rDoc.maRangeArrays.end())
573 // Cache hit!
574 return itrRange->second;
575
576 std::unique_ptr<ScRange> pNewRange;
577 TokenArrayRef pArray;
578 bool bFirstTab = true;
579 for (size_t nTab = nTabFirstId; nTab <= nTabLastId; ++nTab)
580 {
581 TableTypeRef pTab = rDoc.maTables[nTab];
582 if (!pTab)
583 return TokenArrayRef();
584
585 SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2;
586 SCROW nDataRow1 = nRow1, nDataRow2 = nRow2;
587
588 if (!pTab->isRangeCached(nDataCol1, nDataRow1, nDataCol2, nDataRow2))
589 {
590 // specified range is not entirely within cached ranges.
591 return TokenArrayRef();
592 }
593
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);
597
598 // Needed in shrink and fill.
599 vector<SCROW> aRows;
600 pTab->getAllRows(aRows, nDataRow1, nDataRow2);
601 bool bFill = true;
602
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)
614 {
615 bFill = false;
616 if (aRows.empty())
617 {
618 // There's no data at all. Set the one matrix element to empty
619 // for column-repeated and row-repeated access.
620 xMat->PutEmpty(0,0);
621 }
622 else if ((nCol1 == 0 && nCol2 == MAXCOL) || (nRow1 == 0 && nRow2 == MAXROW))
623 {
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)
629 {
630 vector<SCCOL> aCols;
631 pTab->getAllCols(rRow, aCols, nDataCol1, nDataCol2);
632 if (!aCols.empty())
633 {
634 nMinCol = std::min( nMinCol, aCols.front());
635 nMaxCol = std::max( nMaxCol, aCols.back());
636 }
637 }
638
639 if (nMinCol <= nMaxCol && ((o3tl::make_unsigned(nMaxCol-nMinCol+1) < nMatrixColumns) ||
640 (o3tl::make_unsigned(nDataRow2-nDataRow1+1) < nMatrixRows)))
641 {
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)
647 {
648 nDataCol1 = nMinCol;
649 nDataCol2 = nMaxCol;
650 bFill = true;
651 }
652 }
653 }
654 }
655
656 if (bFill)
657 {
658 // Only fill non-empty cells, for better performance.
659 for (SCROW nRow : aRows)
660 {
661 vector<SCCOL> aCols;
662 pTab->getAllCols(nRow, aCols, nDataCol1, nDataCol2);
663 for (SCCOL nCol : aCols)
664 {
665 TokenRef pToken = pTab->getCell(nCol, nRow);
666 if (!pToken)
667 // This should never happen!
668 return TokenArrayRef();
669
670 SCSIZE nC = nCol - nDataCol1, nR = nRow - nDataRow1;
671 switch (pToken->GetType())
672 {
673 case svDouble:
674 xMat->PutDouble(pToken->GetDouble(), nC, nR);
675 break;
676 case svString:
677 xMat->PutString(pToken->GetString(), nC, nR);
678 break;
679 default:
680 ;
681 }
682 }
683 }
684
685 if (!bFirstTab)
686 pArray->AddOpCode(ocSep);
687
688 ScMatrixToken aToken(xMat);
689 if (!pArray)
690 pArray = std::make_shared<ScTokenArray>(*mxFakeDoc);
691 pArray->AddToken(aToken);
692
693 bFirstTab = false;
694
695 if (!pNewRange)
696 pNewRange.reset(new ScRange(nDataCol1, nDataRow1, nTab, nDataCol2, nDataRow2, nTab));
697 else
698 pNewRange->ExtendTo(ScRange(nDataCol1, nDataRow1, nTab, nDataCol2, nDataRow2, nTab));
699 }
700 }
701
702 rDoc.maRangeArrays.emplace(aCacheRange, pArray);
703 if (pNewRange && *pNewRange != aCacheRange)
704 rDoc.maRangeArrays.emplace(*pNewRange, pArray);
705
706 return pArray;
707}
708
709ScExternalRefCache::TokenArrayRef ScExternalRefCache::getRangeNameTokens(sal_uInt16 nFileId, const OUString& rName)
710{
711 osl::MutexGuard aGuard(&maMtxDocs);
712
713 DocItem* pDoc = getDocItem(nFileId);
714 if (!pDoc)
715 return TokenArrayRef();
716
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();
722
723 return itr->second;
724}
725
726void ScExternalRefCache::setRangeNameTokens(sal_uInt16 nFileId, const OUString& rName, TokenArrayRef pArray)
727{
728 osl::MutexGuard aGuard(&maMtxDocs);
729
730 DocItem* pDoc = getDocItem(nFileId);
731 if (!pDoc)
732 return;
733
734 OUString aUpperName = ScGlobal::getCharClassPtr()->uppercase(rName);
735 RangeNameMap& rMap = pDoc->maRangeNames;
736 rMap.emplace(aUpperName, pArray);
737 pDoc->maRealRangeNameMap.emplace(aUpperName, rName);
738}
739
740bool ScExternalRefCache::isValidRangeName(sal_uInt16 nFileId, const OUString& rName) const
741{
742 osl::MutexGuard aGuard(&maMtxDocs);
743
744 DocItem* pDoc = getDocItem(nFileId);
745 if (!pDoc)
746 return false;
747
748 const RangeNameMap& rMap = pDoc->maRangeNames;
749 return rMap.count(rName) > 0;
750}
751
752void ScExternalRefCache::setRangeName(sal_uInt16 nFileId, const OUString& rName)
753{
754 osl::MutexGuard aGuard(&maMtxDocs);
755
756 DocItem* pDoc = getDocItem(nFileId);
757 if (!pDoc)
758 return;
759
760 OUString aUpperName = ScGlobal::getCharClassPtr()->uppercase(rName);
761 pDoc->maRealRangeNameMap.emplace(aUpperName, rName);
762}
763
764void ScExternalRefCache::setCellData(sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow,
765 TokenRef const & pToken, sal_uLong nFmtIndex)
766{
767 if (!isDocInitialized(nFileId))
768 return;
769
770 using ::std::pair;
771 DocItem* pDocItem = getDocItem(nFileId);
772 if (!pDocItem)
773 return;
774
775 DocItem& rDoc = *pDocItem;
776
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 ???
781 return;
782
783 TableTypeRef& pTableData = rDoc.maTables[itrTabName->second];
784 if (!pTableData)
785 pTableData = std::make_shared<Table>();
786
787 pTableData->setCell(nCol, nRow, pToken, nFmtIndex);
788 pTableData->setCachedCell(nCol, nRow);
789}
790
791void ScExternalRefCache::setCellRangeData(sal_uInt16 nFileId, const ScRange& rRange, const vector<SingleRangeData>& rData,
792 const TokenArrayRef& pArray)
793{
794 using ::std::pair;
795 if (rData.empty() || !isDocInitialized(nFileId))
796 // nothing to cache
797 return;
798
799 // First, get the document item for the given file ID.
800 DocItem* pDocItem = getDocItem(nFileId);
801 if (!pDocItem)
802 return;
803
804 DocItem& rDoc = *pDocItem;
805
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())
810 {
811 // table index not found.
812 return;
813 }
814
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)
820 {
821 TableTypeRef& pTabData = rDoc.maTables[i];
822 if (!pTabData)
823 pTabData = std::make_shared<Table>();
824
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))
829 {
830 ScMatrix::DoubleOpFunction aDoubleFunc = [=](size_t row, size_t col, double val) -> void
831 {
832 pTabData->setCell(col + nCol1, row + nRow1, new formula::FormulaDoubleToken(val), 0, false);
833 };
834 ScMatrix::BoolOpFunction aBoolFunc = [=](size_t row, size_t col, bool val) -> void
835 {
836 pTabData->setCell(col + nCol1, row + nRow1, new formula::FormulaDoubleToken(val ? 1.0 : 0.0), 0, false);
837 };
838 ScMatrix::StringOpFunction aStringFunc = [=](size_t row, size_t col, svl::SharedString val) -> void
839 {
840 pTabData->setCell(col + nCol1, row + nRow1, new formula::FormulaStringToken(val), 0, false);
841 };
842 ScMatrix::EmptyOpFunction aEmptyFunc = [=](size_t /*row*/, size_t /*col*/) -> void
843 {
844 // Nothing. Empty cell.
845 };
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);
851 }
852 else
853 {
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")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "sc.ui")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "ScExternalRefCache::setCellRangeData - matrix size mismatch"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "856" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "ScExternalRefCache::setCellRangeData - matrix size mismatch"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "ScExternalRefCache::setCellRangeData - matrix size mismatch"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "856" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "ScExternalRefCache::setCellRangeData - matrix size mismatch"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "856" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "ScExternalRefCache::setCellRangeData - matrix size mismatch"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "ScExternalRefCache::setCellRangeData - matrix size mismatch"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "856" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
857 if (nMatCols != 1 || nMatRows != 1)
858 SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - not a one element matrix")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "sc.ui")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "ScExternalRefCache::setCellRangeData - not a one element matrix"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "858" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "ScExternalRefCache::setCellRangeData - not a one element matrix"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "ScExternalRefCache::setCellRangeData - not a one element matrix"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "858" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "ScExternalRefCache::setCellRangeData - not a one element matrix"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "858" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "ScExternalRefCache::setCellRangeData - not a one element matrix"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "ScExternalRefCache::setCellRangeData - not a one element matrix"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "858" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
859 else
860 {
861 FormulaError nErr = GetDoubleErrorValue( pMat->GetDouble(0,0));
862 SAL_WARN("sc.ui","ScExternalRefCache::setCellRangeData - matrix error value is " << static_cast<int>(nErr) <<do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "sc.ui")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "ScExternalRefCache::setCellRangeData - matrix error value is "
<< static_cast<int>(nErr) << (nErr == FormulaError
::MatrixSize ? ", ok" : ", not ok")) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "863" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "ScExternalRefCache::setCellRangeData - matrix error value is "
<< static_cast<int>(nErr) << (nErr == FormulaError
::MatrixSize ? ", ok" : ", not ok")), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "ScExternalRefCache::setCellRangeData - matrix error value is "
<< static_cast<int>(nErr) << (nErr == FormulaError
::MatrixSize ? ", ok" : ", not ok"); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("sc.ui"), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "863" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "ScExternalRefCache::setCellRangeData - matrix error value is "
<< static_cast<int>(nErr) << (nErr == FormulaError
::MatrixSize ? ", ok" : ", not ok")) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "863" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "ScExternalRefCache::setCellRangeData - matrix error value is "
<< static_cast<int>(nErr) << (nErr == FormulaError
::MatrixSize ? ", ok" : ", not ok")), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "ScExternalRefCache::setCellRangeData - matrix error value is "
<< static_cast<int>(nErr) << (nErr == FormulaError
::MatrixSize ? ", ok" : ", not ok"); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("sc.ui"), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "863" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
863 (nErr == FormulaError::MatrixSize ? ", ok" : ", not ok"))do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "sc.ui")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "ScExternalRefCache::setCellRangeData - matrix error value is "
<< static_cast<int>(nErr) << (nErr == FormulaError
::MatrixSize ? ", ok" : ", not ok")) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "863" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "ScExternalRefCache::setCellRangeData - matrix error value is "
<< static_cast<int>(nErr) << (nErr == FormulaError
::MatrixSize ? ", ok" : ", not ok")), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "ScExternalRefCache::setCellRangeData - matrix error value is "
<< static_cast<int>(nErr) << (nErr == FormulaError
::MatrixSize ? ", ok" : ", not ok"); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("sc.ui"), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "863" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "ScExternalRefCache::setCellRangeData - matrix error value is "
<< static_cast<int>(nErr) << (nErr == FormulaError
::MatrixSize ? ", ok" : ", not ok")) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "863" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "ScExternalRefCache::setCellRangeData - matrix error value is "
<< static_cast<int>(nErr) << (nErr == FormulaError
::MatrixSize ? ", ok" : ", not ok")), 0); } else { ::std::ostringstream
sal_detail_stream; sal_detail_stream << "ScExternalRefCache::setCellRangeData - matrix error value is "
<< static_cast<int>(nErr) << (nErr == FormulaError
::MatrixSize ? ", ok" : ", not ok"); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN
), ("sc.ui"), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "863" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
864 }
865 }
866 ++i;
867 }
868
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));
871
872 rDoc.maRangeArrays.emplace(aCacheRange, pArray);
873}
874
875bool ScExternalRefCache::isDocInitialized(sal_uInt16 nFileId)
876{
877 DocItem* pDoc = getDocItem(nFileId);
878 if (!pDoc)
879 return false;
880
881 return pDoc->mbInitFromSource;
882}
883
884static bool lcl_getStrictTableDataIndex(const ScExternalRefCache::TableNameIndexMap& rMap, const OUString& rName, size_t& rIndex)
885{
886 ScExternalRefCache::TableNameIndexMap::const_iterator itr = rMap.find(rName);
887 if (itr == rMap.end())
888 return false;
889
890 rIndex = itr->second;
891 return true;
892}
893
894bool ScExternalRefCache::DocItem::getTableDataIndex( const OUString& rTabName, size_t& rIndex ) const
895{
896 ScExternalRefCache::TableNameIndexMap::const_iterator itr = findTableNameIndex(rTabName);
897 if (itr == maTableNameIndex.end())
898 return false;
899
900 rIndex = itr->second;
901 return true;
902}
903
904namespace {
905OUString getFirstSheetName()
906{
907 // Get Custom prefix.
908 const ScDefaultsOptions& rOpt = SC_MOD()( static_cast<ScModule*>(SfxApplication::GetModule(SfxToolsModule
::Calc)) )
->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";
912}
913}
914
915void ScExternalRefCache::initializeDoc(sal_uInt16 nFileId, const vector<OUString>& rTabNames,
916 const OUString& rBaseName)
917{
918 DocItem* pDoc = getDocItem(nFileId);
919 if (!pDoc)
920 return;
921
922 size_t n = rTabNames.size();
923
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)
930 {
931 TableName aNameItem(ScGlobal::getCharClassPtr()->uppercase(rTabName), rTabName);
932 aNewTabNames.push_back(aNameItem);
933 }
934 pDoc->maTableNames.swap(aNewTabNames);
935
936 // data tables - preserve any existing data that may have been set during
937 // file import.
938 vector<TableTypeRef> aNewTables(n);
939 for (size_t i = 0; i < n; ++i)
940 {
941 size_t nIndex;
942 if (lcl_getStrictTableDataIndex(pDoc->maTableNameIndex, pDoc->maTableNames[i].maUpperName, nIndex))
943 {
944 aNewTables[i] = pDoc->maTables[nIndex];
945 }
946 }
947 pDoc->maTables.swap(aNewTables);
948
949 // name index map
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);
954
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)
959 {
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;
968 }
969
970 pDoc->mbInitFromSource = true;
971}
972
973ScExternalRefCache::TableNameIndexMap::const_iterator ScExternalRefCache::DocItem::findTableNameIndex(
974 const OUString& rTabName ) const
975{
976 const OUString aTabNameUpper = ScGlobal::getCharClassPtr()->uppercase( rTabName);
977 TableNameIndexMap::const_iterator itrTabName = maTableNameIndex.find( aTabNameUpper);
978 if (itrTabName != maTableNameIndex.end())
979 return itrTabName;
980
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)
985 return itrTabName;
986
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();
992
993 return itrTabName;
994}
995
996bool ScExternalRefCache::DocItem::getSingleTableNameAlternative( OUString& rTabName ) const
997{
998 if (maSingleTableNameAlias.isEmpty() || maTableNames.size() != 1)
999 return false;
1000 if (ScGlobal::GetpTransliteration()->isEqual( rTabName, maTableNames[0].maRealName))
1001 {
1002 rTabName = maSingleTableNameAlias;
1003 return true;
1004 }
1005 if (ScGlobal::GetpTransliteration()->isEqual( rTabName, maSingleTableNameAlias))
1006 {
1007 rTabName = maTableNames[0].maRealName;
1008 return true;
1009 }
1010 return false;
1011}
1012
1013bool ScExternalRefCache::getSrcDocTable( const ScDocument& rSrcDoc, const OUString& rTabName, SCTAB& rTab,
1014 sal_uInt16 nFileId ) const
1015{
1016 bool bFound = rSrcDoc.GetTable( rTabName, rTab);
1017 if (!bFound)
1018 {
1019 // Check the one table alias alternative.
1020 const DocItem* pDoc = getDocItem( nFileId );
1021 if (pDoc)
1022 {
1023 OUString aTabName( rTabName);
1024 if (pDoc->getSingleTableNameAlternative( aTabName))
1025 bFound = rSrcDoc.GetTable( aTabName, rTab);
1026 }
1027 }
1028 return bFound;
1029}
1030
1031OUString ScExternalRefCache::getTableName(sal_uInt16 nFileId, size_t nCacheId) const
1032{
1033 if( DocItem* pDoc = getDocItem( nFileId ) )
1034 if( nCacheId < pDoc->maTableNames.size() )
1035 return pDoc->maTableNames[ nCacheId ].maRealName;
1036 return EMPTY_OUSTRINGScGlobal::GetEmptyOUString();
1037}
1038
1039void ScExternalRefCache::getAllTableNames(sal_uInt16 nFileId, vector<OUString>& rTabNames) const
1040{
1041 rTabNames.clear();
1042 DocItem* pDoc = getDocItem(nFileId);
1043 if (!pDoc)
1044 return;
1045
1046 size_t n = pDoc->maTableNames.size();
1047 rTabNames.reserve(n);
1048 for (const auto& rTableName : pDoc->maTableNames)
1049 rTabNames.push_back(rTableName.maRealName);
1050}
1051
1052SCTAB ScExternalRefCache::getTabSpan( sal_uInt16 nFileId, const OUString& rStartTabName, const OUString& rEndTabName ) const
1053{
1054 DocItem* pDoc = getDocItem(nFileId);
1055 if (!pDoc)
1056 return -1;
1057
1058 vector<TableName>::const_iterator itrBeg = pDoc->maTableNames.begin();
1059 vector<TableName>::const_iterator itrEnd = pDoc->maTableNames.end();
1060
1061 vector<TableName>::const_iterator itrStartTab = ::std::find_if( itrBeg, itrEnd,
1062 TabNameSearchPredicate( rStartTabName));
1063 if (itrStartTab == itrEnd)
1064 return -1;
1065
1066 vector<TableName>::const_iterator itrEndTab = ::std::find_if( itrBeg, itrEnd,
1067 TabNameSearchPredicate( rEndTabName));
1068 if (itrEndTab == itrEnd)
1069 return 0;
1070
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);
1074}
1075
1076void ScExternalRefCache::getAllNumberFormats(vector<sal_uInt32>& rNumFmts) const
1077{
1078 osl::MutexGuard aGuard(&maMtxDocs);
1079
1080 using ::std::sort;
1081 using ::std::unique;
1082
1083 vector<sal_uInt32> aNumFmts;
1084 for (const auto& rEntry : maDocs)
1085 {
1086 const vector<TableTypeRef>& rTables = rEntry.second.maTables;
1087 for (const TableTypeRef& pTab : rTables)
1088 {
1089 if (!pTab)
1090 continue;
1091
1092 pTab->getAllNumberFormats(aNumFmts);
1093 }
1094 }
1095
1096 // remove duplicates.
1097 sort(aNumFmts.begin(), aNumFmts.end());
1098 aNumFmts.erase(unique(aNumFmts.begin(), aNumFmts.end()), aNumFmts.end());
1099 rNumFmts.swap(aNumFmts);
1100}
1101
1102bool ScExternalRefCache::setCacheDocReferenced( sal_uInt16 nFileId )
1103{
1104 DocItem* pDocItem = getDocItem(nFileId);
1105 if (!pDocItem)
1106 return areAllCacheTablesReferenced();
1107
1108 for (auto& rxTab : pDocItem->maTables)
1109 {
1110 if (rxTab)
1111 rxTab->setReferenced(true);
1112 }
1113 addCacheDocToReferenced( nFileId);
1114 return areAllCacheTablesReferenced();
1115}
1116
1117bool ScExternalRefCache::setCacheTableReferenced( sal_uInt16 nFileId, const OUString& rTabName, size_t nSheets )
1118{
1119 DocItem* pDoc = getDocItem(nFileId);
1120 if (pDoc)
1121 {
1122 size_t nIndex = 0;
1123 if (pDoc->getTableDataIndex( rTabName, nIndex))
1124 {
1125 size_t nStop = ::std::min( nIndex + nSheets, pDoc->maTables.size());
1126 for (size_t i = nIndex; i < nStop; ++i)
1127 {
1128 TableTypeRef pTab = pDoc->maTables[i];
1129 if (pTab)
1130 {
1131 if (!pTab->isReferenced())
1132 {
1133 pTab->setReferenced(true);
1134 addCacheTableToReferenced( nFileId, i);
1135 }
1136 }
1137 }
1138 }
1139 }
1140 return areAllCacheTablesReferenced();
1141}
1142
1143void ScExternalRefCache::setAllCacheTableReferencedStati( bool bReferenced )
1144{
1145 osl::MutexGuard aGuard(&maMtxDocs);
1146
1147 if (bReferenced)
1148 {
1149 maReferenced.reset(0);
1150 for (auto& rEntry : maDocs)
1151 {
1152 ScExternalRefCache::DocItem& rDocItem = rEntry.second;
1153 for (auto& rxTab : rDocItem.maTables)
1154 {
1155 if (rxTab)
1156 rxTab->setReferenced(true);
1157 }
1158 }
1159 }
1160 else
1161 {
1162 size_t nDocs = 0;
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);
1168
1169 for (auto& [nFileId, rDocItem] : maDocs)
1170 {
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)
1176 {
1177 TableTypeRef & xTab = rDocItem.maTables[i];
1178 if (xTab)
1179 {
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;
1186 }
1187 }
1188 }
1189 }
1190}
1191
1192void ScExternalRefCache::addCacheTableToReferenced( sal_uInt16 nFileId, size_t nIndex )
1193{
1194 if (nFileId >= maReferenced.maDocs.size())
1195 return;
1196
1197 ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables;
1198 size_t nTables = rTables.size();
1199 if (nIndex >= nTables)
1200 return;
1201
1202 if (!rTables[nIndex])
1203 {
1204 rTables[nIndex] = true;
1205 size_t i = 0;
1206 while (i < nTables && rTables[i])
1207 ++i;
1208 if (i == nTables)
1209 {
1210 maReferenced.maDocs[nFileId].mbAllTablesReferenced = true;
1211 maReferenced.checkAllDocs();
1212 }
1213 }
1214}
1215
1216void ScExternalRefCache::addCacheDocToReferenced( sal_uInt16 nFileId )
1217{
1218 if (nFileId >= maReferenced.maDocs.size())
1219 return;
1220
1221 if (!maReferenced.maDocs[nFileId].mbAllTablesReferenced)
1222 {
1223 ::std::vector<bool> & rTables = maReferenced.maDocs[nFileId].maTables;
1224 size_t nSize = rTables.size();
1225 for (size_t i=0; i < nSize; ++i)
1226 rTables[i] = true;
1227 maReferenced.maDocs[nFileId].mbAllTablesReferenced = true;
1228 maReferenced.checkAllDocs();
1229 }
1230}
1231
1232void ScExternalRefCache::getAllCachedDataSpans( const ScDocument& rSrcDoc, sal_uInt16 nFileId, sc::ColumnSpanSet& rSet ) const
1233{
1234 const DocItem* pDocItem = getDocItem(nFileId);
1235 if (!pDocItem)
1236 // This document is not cached.
1237 return;
1238
1239 const std::vector<TableTypeRef>& rTables = pDocItem->maTables;
1240 for (size_t nTab = 0, nTabCount = rTables.size(); nTab < nTabCount; ++nTab)
1241 {
1242 TableTypeRef pTab = rTables[nTab];
1243 if (!pTab)
1244 continue;
1245
1246 std::vector<SCROW> aRows;
1247 pTab->getAllRows(aRows);
1248 for (SCROW nRow : aRows)
1249 {
1250 std::vector<SCCOL> aCols;
1251 pTab->getAllCols(nRow, aCols);
1252 for (SCCOL nCol : aCols)
1253 {
1254 rSet.set(rSrcDoc, nTab, nCol, nRow, true);
1255 }
1256 }
1257 }
1258}
1259
1260ScExternalRefCache::ReferencedStatus::ReferencedStatus() :
1261 mbAllReferenced(false)
1262{
1263 reset(0);
1264}
1265
1266void ScExternalRefCache::ReferencedStatus::reset( size_t nDocs )
1267{
1268 if (nDocs)
1269 {
1270 mbAllReferenced = false;
1271 DocReferencedVec aRefs( nDocs);
1272 maDocs.swap( aRefs);
1273 }
1274 else
1275 {
1276 mbAllReferenced = true;
1277 DocReferencedVec aRefs;
1278 maDocs.swap( aRefs);
1279 }
1280}
1281
1282void ScExternalRefCache::ReferencedStatus::checkAllDocs()
1283{
1284 if (std::all_of(maDocs.begin(), maDocs.end(), [](const DocReferenced& rDoc) { return rDoc.mbAllTablesReferenced; }))
1285 mbAllReferenced = true;
1286}
1287
1288ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
1289{
1290 DocItem* pDoc = getDocItem(nFileId);
1291 if (!pDoc || nTabIndex >= pDoc->maTables.size())
1292 return TableTypeRef();
1293
1294 return pDoc->maTables[nTabIndex];
1295}
1296
1297ScExternalRefCache::TableTypeRef ScExternalRefCache::getCacheTable(sal_uInt16 nFileId, const OUString& rTabName,
1298 bool bCreateNew, size_t* pnIndex, const OUString* pExtUrl)
1299{
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
1303 // being 0xffffffff
1304 const size_t nNotAvailable = static_cast<size_t>( static_cast<sal_Int32>( -1));
1305
1306 DocItem* pDoc = getDocItem(nFileId);
1307 if (!pDoc)
1308 {
1309 if (pnIndex) *pnIndex = nNotAvailable;
1310 return TableTypeRef();
1311 }
1312
1313 DocItem& rDoc = *pDoc;
1314
1315 size_t nIndex;
1316 if (rDoc.getTableDataIndex(rTabName, nIndex))
1317 {
1318 // specified table found.
1319 if( pnIndex ) *pnIndex = nIndex;
1320 if (bCreateNew && !rDoc.maTables[nIndex])
1321 rDoc.maTables[nIndex] = std::make_shared<Table>();
1322
1323 return rDoc.maTables[nIndex];
1324 }
1325
1326 if (!bCreateNew)
1327 {
1328 if (pnIndex) *pnIndex = nNotAvailable;
1329 return TableTypeRef();
1330 }
1331
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())
1335 {
1336 if (pExtUrl)
1337 {
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;
1344 }
1345 }
1346 else
1347 {
1348 rDoc.maSingleTableNameAlias.clear();
1349 }
1350
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);
1359 return pTab;
1360}
1361
1362void ScExternalRefCache::clearCache(sal_uInt16 nFileId)
1363{
1364 osl::MutexGuard aGuard(&maMtxDocs);
1365 maDocs.erase(nFileId);
1366}
1367
1368void ScExternalRefCache::clearCacheTables(sal_uInt16 nFileId)
1369{
1370 osl::MutexGuard aGuard(&maMtxDocs);
1371 DocItem* pDocItem = getDocItem(nFileId);
1372 if (!pDocItem)
1373 // This document is not cached at all.
1374 return;
1375
1376 // Clear all cache table content, but keep the tables.
1377 std::vector<TableTypeRef>& rTabs = pDocItem->maTables;
1378 for (TableTypeRef & pTab : rTabs)
1379 {
1380 if (!pTab)
1381 continue;
1382
1383 pTab->clear();
1384 }
1385
1386 // Clear the external range name caches.
1387 pDocItem->maRangeNames.clear();
1388 pDocItem->maRangeArrays.clear();
1389 pDocItem->maRealRangeNameMap.clear();
1390}
1391
1392ScExternalRefCache::DocItem* ScExternalRefCache::getDocItem(sal_uInt16 nFileId) const
1393{
1394 osl::MutexGuard aGuard(&maMtxDocs);
1395
1396 using ::std::pair;
1397 DocDataType::iterator itrDoc = maDocs.find(nFileId);
1398 if (itrDoc == maDocs.end())
1399 {
1400 // specified document is not cached.
1401 pair<DocDataType::iterator, bool> res = maDocs.emplace(
1402 nFileId, DocItem());
1403
1404 if (!res.second)
1405 // insertion failed.
1406 return nullptr;
1407
1408 itrDoc = res.first;
1409 }
1410
1411 return &itrDoc->second;
1412}
1413
1414ScExternalRefLink::ScExternalRefLink(ScDocument& rDoc, sal_uInt16 nFileId) :
1415 ::sfx2::SvBaseLink(::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SIMPLE_FILE),
1416 mnFileId(nFileId),
1417 mrDoc(rDoc),
1418 mbDoRefresh(true)
1419{
1420}
1421
1422ScExternalRefLink::~ScExternalRefLink()
1423{
1424}
1425
1426void ScExternalRefLink::Closed()
1427{
1428 ScExternalRefManager* pMgr = mrDoc.GetExternalRefManager();
1429 pMgr->breakLink(mnFileId);
1430}
1431
1432::sfx2::SvBaseLink::UpdateResult ScExternalRefLink::DataChanged(const OUString& /*rMimeType*/, const Any& /*rValue*/)
1433{
1434 if (!mbDoRefresh)
1435 return SUCCESS;
1436
1437 OUString aFile, aFilter;
1438 sfx2::LinkManager::GetDisplayNames(this, nullptr, &aFile, nullptr, &aFilter);
1439 ScExternalRefManager* pMgr = mrDoc.GetExternalRefManager();
1440
1441 if (!pMgr->isFileLoadable(aFile))
1442 return ERROR_GENERAL;
1443
1444 const OUString* pCurFile = pMgr->getExternalFileName(mnFileId);
1445 if (!pCurFile)
1446 return ERROR_GENERAL;
1447
1448 if (*pCurFile == aFile)
1449 {
1450 // Refresh the current source document.
1451 if (!pMgr->refreshSrcDocument(mnFileId))
1452 return ERROR_GENERAL;
1453 }
1454 else
1455 {
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();
1461 }
1462
1463 return SUCCESS;
1464}
1465
1466void ScExternalRefLink::Edit(weld::Window* pParent, const Link<SvBaseLink&,void>& /*rEndEditHdl*/)
1467{
1468 SvBaseLink::Edit(pParent, Link<SvBaseLink&,void>());
1469}
1470
1471void ScExternalRefLink::SetDoRefresh(bool b)
1472{
1473 mbDoRefresh = b;
1474}
1475
1476static FormulaToken* convertToToken( ScDocument& rHostDoc, const ScDocument& rSrcDoc, ScRefCellValue& rCell )
1477{
1478 if (rCell.hasEmptyValue())
1479 {
1480 bool bInherited = (rCell.meType == CELLTYPE_FORMULA);
1481 return new ScEmptyCellToken(bInherited, false);
1482 }
1483
1484 switch (rCell.meType)
1485 {
1486 case CELLTYPE_EDIT:
1487 case CELLTYPE_STRING:
1488 {
1489 OUString aStr = rCell.getString(&rSrcDoc);
1490 svl::SharedString aSS = rHostDoc.GetSharedStringPool().intern(aStr);
1491 return new formula::FormulaStringToken(aSS);
1492 }
1493 case CELLTYPE_VALUE:
1494 return new formula::FormulaDoubleToken(rCell.mfValue);
1495 case CELLTYPE_FORMULA:
1496 {
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())
1502 {
1503 double fVal = pFCell->GetValue();
1504 return new formula::FormulaDoubleToken(fVal);
1505 }
1506 else
1507 {
1508 svl::SharedString aSS = rHostDoc.GetSharedStringPool().intern( pFCell->GetString().getString());
1509 return new formula::FormulaStringToken(aSS);
1510 }
1511 }
1512 default:
1513 OSL_FAIL("attempted to convert an unknown cell type.")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "1513" ": "), "%s", "attempted to convert an unknown cell type."
); } } while (false)
;
1514 }
1515
1516 return nullptr;
1517}
1518
1519static std::unique_ptr<ScTokenArray> convertToTokenArray(
1520 ScDocument& rHostDoc, const ScDocument& rSrcDoc, ScRange& rRange, vector<ScExternalRefCache::SingleRangeData>& rCacheData )
1521{
1522 ScAddress& s = rRange.aStart;
1523 ScAddress& e = rRange.aEnd;
1524
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();
1528
1529 if (nTab2 != nTab1)
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
1534 // range to it.
1535 return nullptr;
1536
1537 std::unique_ptr<ScRange> pUsedRange;
1538
1539 unique_ptr<ScTokenArray> pArray(new ScTokenArray(rSrcDoc));
1540 bool bFirstTab = true;
1541 vector<ScExternalRefCache::SingleRangeData>::iterator
1542 itrCache = rCacheData.begin(), itrCacheEnd = rCacheData.end();
1543
1544 for (SCTAB nTab = nTab1; nTab <= nTab2 && itrCache != itrCacheEnd; ++nTab, ++itrCache)
1545 {
1546 // Only loop within the data area.
1547 SCCOL nDataCol1 = nCol1, nDataCol2 = nCol2;
1548 SCROW nDataRow1 = nRow1, nDataRow2 = nRow2;
1549 bool bShrunk;
1550 if (!rSrcDoc.ShrinkToUsedDataArea( bShrunk, nTab, nDataCol1, nDataRow1, nDataCol2, nDataRow2, false))
1551 // no data within specified range.
1552 continue;
1553
1554 if (pUsedRange)
1555 // Make sure the used area only grows, not shrinks.
1556 pUsedRange->ExtendTo(ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
1557 else
1558 pUsedRange.reset(new ScRange(nDataCol1, nDataRow1, 0, nDataCol2, nDataRow2, 0));
1559
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);
1563
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)
1575 {
1576 rSrcDoc.FillMatrix(*xMat, nTab, nCol1, nRow1, nCol2, nRow2, &rHostDoc.GetSharedStringPool());
1577 }
1578 else if ((nCol1 == 0 && nCol2 == MAXCOL) || (nRow1 == 0 && nRow2 == MAXROW))
1579 {
1580 if ((o3tl::make_unsigned(nDataCol2-nDataCol1+1) < nMatrixColumns) ||
1581 (o3tl::make_unsigned(nDataRow2-nDataRow1+1) < nMatrixRows))
1582 {
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());
1589 }
1590 }
1591
1592 if (!bFirstTab)
1593 pArray->AddOpCode(ocSep);
1594
1595 ScMatrixToken aToken(xMat);
1596 pArray->AddToken(aToken);
1597
1598 itrCache->mpRangeData = xMat;
1599
1600 bFirstTab = false;
1601 }
1602
1603 if (!pUsedRange)
1604 return nullptr;
1605
1606 s.SetCol(pUsedRange->aStart.Col());
1607 s.SetRow(pUsedRange->aStart.Row());
1608 e.SetCol(pUsedRange->aEnd.Col());
1609 e.SetRow(pUsedRange->aEnd.Row());
1610
1611 return pArray;
1612}
1613
1614static std::unique_ptr<ScTokenArray> lcl_fillEmptyMatrix(const ScDocument& rDoc, const ScRange& rRange)
1615{
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);
1619
1620 ScMatrixToken aToken(xMat);
1621 unique_ptr<ScTokenArray> pArray(new ScTokenArray(rDoc));
1622 pArray->AddToken(aToken);
1623 return pArray;
1624}
1625
1626namespace {
1627bool isLinkUpdateAllowedInDoc(const ScDocument& rDoc)
1628{
1629 SfxObjectShell* pDocShell = rDoc.GetDocumentShell();
1630 if (!pDocShell)
1631 return false;
1632
1633 return pDocShell->GetEmbeddedObjectContainer().getUserAllowsLinkUpdate();
1634}
1635}
1636
1637ScExternalRefManager::ScExternalRefManager(ScDocument& rDoc) :
1638 mrDoc(rDoc),
1639 mbInReferenceMarking(false),
1640 mbUserInteractionEnabled(true),
1641 mbDocTimerEnabled(true)
1642{
1643 maSrcDocTimer.SetInvokeHandler( LINK(this, ScExternalRefManager, TimeOutHdl)::tools::detail::makeLink( ::tools::detail::castTo<ScExternalRefManager
*>(this), &ScExternalRefManager::LinkStubTimeOutHdl)
);
1644 maSrcDocTimer.SetTimeout(SRCDOC_SCAN_INTERVAL1000*30);
1645 maSrcDocTimer.SetDebugName( "sc::ScExternalRefManager maSrcDocTimer" );
1646}
1647
1648ScExternalRefManager::~ScExternalRefManager()
1649{
1650 clear();
1651}
1652
1653OUString ScExternalRefManager::getCacheTableName(sal_uInt16 nFileId, size_t nTabIndex) const
1654{
1655 return maRefCache.getTableName(nFileId, nTabIndex);
1656}
1657
1658ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(sal_uInt16 nFileId, size_t nTabIndex) const
1659{
1660 return maRefCache.getCacheTable(nFileId, nTabIndex);
1661}
1662
1663ScExternalRefCache::TableTypeRef ScExternalRefManager::getCacheTable(
1664 sal_uInt16 nFileId, const OUString& rTabName, bool bCreateNew, size_t* pnIndex, const OUString* pExtUrl)
1665{
1666 return maRefCache.getCacheTable(nFileId, rTabName, bCreateNew, pnIndex, pExtUrl);
1667}
1668
1669ScExternalRefManager::LinkListener::LinkListener()
1670{
1671}
1672
1673ScExternalRefManager::LinkListener::~LinkListener()
1674{
1675}
1676
1677ScExternalRefManager::ApiGuard::ApiGuard(const ScDocument& rDoc) :
1678 mpMgr(rDoc.GetExternalRefManager()),
1679 mbOldInteractionEnabled(mpMgr->mbUserInteractionEnabled)
1680{
1681 // We don't want user interaction handled in the API.
1682 mpMgr->mbUserInteractionEnabled = false;
1683}
1684
1685ScExternalRefManager::ApiGuard::~ApiGuard()
1686{
1687 // Restore old value.
1688 mpMgr->mbUserInteractionEnabled = mbOldInteractionEnabled;
1689}
1690
1691void ScExternalRefManager::getAllCachedTableNames(sal_uInt16 nFileId, vector<OUString>& rTabNames) const
1692{
1693 maRefCache.getAllTableNames(nFileId, rTabNames);
1694}
1695
1696SCTAB ScExternalRefManager::getCachedTabSpan( sal_uInt16 nFileId, const OUString& rStartTabName, const OUString& rEndTabName ) const
1697{
1698 return maRefCache.getTabSpan( nFileId, rStartTabName, rEndTabName);
1699}
1700
1701void ScExternalRefManager::getAllCachedNumberFormats(vector<sal_uInt32>& rNumFmts) const
1702{
1703 maRefCache.getAllNumberFormats(rNumFmts);
1704}
1705
1706sal_uInt16 ScExternalRefManager::getExternalFileCount() const
1707{
1708 return static_cast< sal_uInt16 >( maSrcFiles.size() );
1709}
1710
1711void ScExternalRefManager::markUsedByLinkListeners()
1712{
1713 bool bAllMarked = false;
1714 for (const auto& [rFileId, rLinkListeners] : maLinkListeners)
1715 {
1716 if (!rLinkListeners.empty())
1717 bAllMarked = maRefCache.setCacheDocReferenced(rFileId);
1718
1719 if (bAllMarked)
1720 break;
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. */
1724 }
1725}
1726
1727void ScExternalRefManager::markUsedExternalRefCells()
1728{
1729 for (const auto& rEntry : maRefCells)
1730 {
1731 for (ScFormulaCell* pCell : rEntry.second)
1732 {
1733 bool bUsed = pCell->MarkUsedExternalReferences();
1734 if (bUsed)
1735 // Return true when at least one cell references external docs.
1736 return;
1737 }
1738 }
1739}
1740
1741bool ScExternalRefManager::setCacheTableReferenced( sal_uInt16 nFileId, const OUString& rTabName, size_t nSheets )
1742{
1743 return maRefCache.setCacheTableReferenced( nFileId, rTabName, nSheets);
1744}
1745
1746void ScExternalRefManager::setAllCacheTableReferencedStati( bool bReferenced )
1747{
1748 mbInReferenceMarking = !bReferenced;
1749 maRefCache.setAllCacheTableReferencedStati( bReferenced );
1750}
1751
1752void ScExternalRefManager::storeRangeNameTokens(sal_uInt16 nFileId, const OUString& rName, const ScTokenArray& rArray)
1753{
1754 ScExternalRefCache::TokenArrayRef pArray(rArray.Clone());
1755 maRefCache.setRangeNameTokens(nFileId, rName, pArray);
1756}
1757
1758namespace {
1759
1760/**
1761 * Put a single cell data into internal cache table.
1762 *
1763 * @param pFmt optional cell format index that may need to be stored with
1764 * the cell value.
1765 */
1766void putCellDataIntoCache(
1767 ScExternalRefCache& rRefCache, const ScExternalRefCache::TokenRef& pToken,
1768 sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell,
1769 const ScExternalRefCache::CellFormat* pFmt)
1770{
1771 // Now, insert the token into cache table but don't cache empty cells.
1772 if (pToken->GetType() != formula::svEmptyCell)
1773 {
1774 sal_uLong nFmtIndex = (pFmt && pFmt->mbIsSet) ? pFmt->mnIndex : 0;
1775 rRefCache.setCellData(nFileId, rTabName, rCell.Col(), rCell.Row(), pToken, nFmtIndex);
1776 }
1777}
1778
1779/**
1780 * Put the data into our internal cache table.
1781 *
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
1788 * any.
1789 * @param rDataRange reduced cache range that includes only the non-empty
1790 * data area.
1791 */
1792void 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)
1797{
1798 if (pArray)
1799 // Cache these values.
1800 rRefCache.setCellRangeData(nFileId, rDataRange, rCacheData, pArray);
1801 else
1802 {
1803 // Array is empty. Fill it with an empty matrix of the required size.
1804 pArray = lcl_fillEmptyMatrix(*rRefCache.getFakeDoc(), rCacheRange);
1805
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);
1810 if (pCacheTab)
1811 pCacheTab->setCachedCellRange(
1812 rCacheRange.aStart.Col(), rCacheRange.aStart.Row(), rCacheRange.aEnd.Col(), rCacheRange.aEnd.Row());
1813 }
1814}
1815
1816/**
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.
1821 *
1822 * @param rRefCache cache table set.
1823 * @param pSrcDoc source document instance.
1824 * @param nFileId external file ID associated with the source document.
1825 */
1826void initDocInCache(ScExternalRefCache& rRefCache, const ScDocument* pSrcDoc, sal_uInt16 nFileId)
1827{
1828 if (!pSrcDoc)
1829 return;
1830
1831 if (rRefCache.isDocInitialized(nFileId))
1832 // Already initialized. No need to do this twice.
1833 return;
1834
1835 SCTAB nTabCount = pSrcDoc->GetTableCount();
1836 if (!nTabCount)
1837 return;
1838
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)
1843 {
1844 OUString aName;
1845 pSrcDoc->GetName(i, aName);
1846 aTabNames.push_back(aName);
1847 }
1848
1849 // Obtain the base name, don't bother if there are more than one sheets.
1850 OUString aBaseName;
1851 if (nTabCount == 1)
1852 {
1853 const SfxObjectShell* pShell = pSrcDoc->GetDocumentShell();
1854 if (pShell && pShell->GetMedium())
1855 {
1856 OUString aName = pShell->GetMedium()->GetName();
1857 aBaseName = INetURLObject( aName).GetBase();
1858 }
1859 }
1860
1861 rRefCache.initializeDoc(nFileId, aTabNames, aBaseName);
1862}
1863
1864}
1865
1866bool ScExternalRefManager::getSrcDocTable( const ScDocument& rSrcDoc, const OUString& rTabName, SCTAB& rTab,
1867 sal_uInt16 nFileId ) const
1868{
1869 return maRefCache.getSrcDocTable( rSrcDoc, rTabName, rTab, nFileId);
1870}
1871
1872ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefToken(
1873 sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell,
1874 const ScAddress* pCurPos, SCTAB* pTab, ScExternalRefCache::CellFormat* pFmt)
1875{
1876 if (pCurPos)
1877 insertRefCell(nFileId, *pCurPos);
1878
1879 maybeLinkExternalFile(nFileId);
1880
1881 if (pTab)
1882 *pTab = -1;
1883
1884 if (pFmt)
1885 pFmt->mbIsSet = false;
1886
1887 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
1888 if (pSrcDoc)
1889 {
1890 // source document already loaded in memory. Re-use this instance.
1891 SCTAB nTab;
1892 if (!getSrcDocTable( *pSrcDoc, rTabName, nTab, nFileId))
1893 {
1894 // specified table name doesn't exist in the source document.
1895 ScExternalRefCache::TokenRef pToken(new FormulaErrorToken(FormulaError::NoRef));
1896 return pToken;
1897 }
1898
1899 if (pTab)
1900 *pTab = nTab;
1901
1902 ScExternalRefCache::TokenRef pToken =
1903 getSingleRefTokenFromSrcDoc(
1904 nFileId, *pSrcDoc, ScAddress(rCell.Col(),rCell.Row(),nTab), pFmt);
1905
1906 putCellDataIntoCache(maRefCache, pToken, nFileId, rTabName, rCell, pFmt);
1907 return pToken;
1908 }
1909
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);
1914 if (pToken)
1915 {
1916 // Cache hit !
1917 fillCellFormat(nFmtIndex, pFmt);
1918 return pToken;
1919 }
1920
1921 // reference not cached. read from the source document.
1922 pSrcDoc = getSrcDocument(nFileId);
1923 if (!pSrcDoc)
1924 {
1925 // Source document not reachable.
1926 if (!isLinkUpdateAllowedInDoc(mrDoc))
1927 {
1928 // Indicate with specific error.
1929 pToken.reset(new FormulaErrorToken(FormulaError::LinkFormulaNeedingCheck));
1930 }
1931 else
1932 {
1933 // Throw a reference error.
1934 pToken.reset(new FormulaErrorToken(FormulaError::NoRef));
1935 }
1936 return pToken;
1937 }
1938
1939 SCTAB nTab;
1940 if (!getSrcDocTable( *pSrcDoc, rTabName, nTab, nFileId))
1941 {
1942 // specified table name doesn't exist in the source document.
1943 pToken.reset(new FormulaErrorToken(FormulaError::NoRef));
1944 return pToken;
1945 }
1946
1947 if (pTab)
1948 *pTab = nTab;
1949
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())
1954 {
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);
1960 if (pCacheTab)
1961 pCacheTab->setCachedCell(rCell.Col(), rCell.Row());
1962
1963 pToken.reset(new ScEmptyCellToken(false, false));
1964 return pToken;
1965 }
1966
1967 pToken = getSingleRefTokenFromSrcDoc(
1968 nFileId, *pSrcDoc, ScAddress(rCell.Col(),rCell.Row(),nTab), pFmt);
1969
1970 putCellDataIntoCache(maRefCache, pToken, nFileId, rTabName, rCell, pFmt);
1971 return pToken;
1972}
1973
1974ScExternalRefCache::TokenArrayRef ScExternalRefManager::getDoubleRefTokens(
1975 sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange, const ScAddress* pCurPos)
1976{
1977 if (pCurPos)
1978 insertRefCell(nFileId, *pCurPos);
1979
1980 maybeLinkExternalFile(nFileId);
1981
1982 ScRange aDataRange(rRange);
1983 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
1984 if (pSrcDoc)
1985 {
1986 // Document already loaded in memory.
1987 vector<ScExternalRefCache::SingleRangeData> aCacheData;
1988 ScExternalRefCache::TokenArrayRef pArray =
1989 getDoubleRefTokensFromSrcDoc(*pSrcDoc, rTabName, aDataRange, aCacheData);
1990
1991 // Put the data into cache.
1992 putRangeDataIntoCache(maRefCache, pArray, nFileId, rTabName, aCacheData, rRange, aDataRange);
1993 return pArray;
1994 }
1995
1996 // Check if the given table name and the cell position is cached.
1997 ScExternalRefCache::TokenArrayRef pArray =
1998 maRefCache.getCellRangeData(nFileId, rTabName, rRange);
1999 if (pArray)
2000 // Cache hit !
2001 return pArray;
2002
2003 pSrcDoc = getSrcDocument(nFileId);
2004 if (!pSrcDoc)
2005 {
2006 // Source document is not reachable. Throw a reference error.
2007 pArray = std::make_shared<ScTokenArray>(*maRefCache.getFakeDoc());
2008 pArray->AddToken(FormulaErrorToken(FormulaError::NoRef));
2009 return pArray;
2010 }
2011
2012 vector<ScExternalRefCache::SingleRangeData> aCacheData;
2013 pArray = getDoubleRefTokensFromSrcDoc(*pSrcDoc, rTabName, aDataRange, aCacheData);
2014
2015 // Put the data into cache.
2016 putRangeDataIntoCache(maRefCache, pArray, nFileId, rTabName, aCacheData, rRange, aDataRange);
2017 return pArray;
2018}
2019
2020ScExternalRefCache::TokenArrayRef ScExternalRefManager::getRangeNameTokens(
2021 sal_uInt16 nFileId, const OUString& rName, const ScAddress* pCurPos)
2022{
2023 if (pCurPos)
2024 insertRefCell(nFileId, *pCurPos);
2025
2026 maybeLinkExternalFile(nFileId);
2027
2028 OUString aName = rName; // make a copy to have the casing corrected.
2029 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
2030 if (pSrcDoc)
2031 {
2032 // Document already loaded in memory.
2033 ScExternalRefCache::TokenArrayRef pArray =
2034 getRangeNameTokensFromSrcDoc(nFileId, *pSrcDoc, aName);
2035
2036 if (pArray)
2037 // Cache this range name array.
2038 maRefCache.setRangeNameTokens(nFileId, aName, pArray);
2039
2040 return pArray;
2041 }
2042
2043 ScExternalRefCache::TokenArrayRef pArray = maRefCache.getRangeNameTokens(nFileId, rName);
2044 if (pArray)
2045 // This range name is cached.
2046 return pArray;
2047
2048 pSrcDoc = getSrcDocument(nFileId);
2049 if (!pSrcDoc)
2050 // failed to load document from disk.
2051 return ScExternalRefCache::TokenArrayRef();
2052
2053 pArray = getRangeNameTokensFromSrcDoc(nFileId, *pSrcDoc, aName);
2054
2055 if (pArray)
2056 // Cache this range name array.
2057 maRefCache.setRangeNameTokens(nFileId, aName, pArray);
2058
2059 return pArray;
2060}
2061
2062namespace {
2063
2064bool hasRangeName(const ScDocument& rDoc, const OUString& rName)
2065{
2066 ScRangeName* pExtNames = rDoc.GetRangeName();
2067 OUString aUpperName = ScGlobal::getCharClassPtr()->uppercase(rName);
2068 const ScRangeData* pRangeData = pExtNames->findByUpperName(aUpperName);
2069 return pRangeData != nullptr;
2070}
2071
2072}
2073
2074bool ScExternalRefManager::isValidRangeName(sal_uInt16 nFileId, const OUString& rName)
2075{
2076 maybeLinkExternalFile(nFileId);
2077 ScDocument* pSrcDoc = getInMemorySrcDocument(nFileId);
2078 if (pSrcDoc
0.1
'pSrcDoc' is null
0.1
'pSrcDoc' is null
)
1
Taking false branch
2079 {
2080 // Only check the presence of the name.
2081 if (hasRangeName(*pSrcDoc, rName))
2082 {
2083 maRefCache.setRangeName(nFileId, rName);
2084 return true;
2085 }
2086 return false;
2087 }
2088
2089 if (maRefCache.isValidRangeName(nFileId, rName))
2
Taking false branch
2090 // Range name is cached.
2091 return true;
2092
2093 pSrcDoc = getSrcDocument(nFileId);
3
Calling 'ScExternalRefManager::getSrcDocument'
2094 if (!pSrcDoc)
2095 // failed to load document from disk.
2096 return false;
2097
2098 if (hasRangeName(*pSrcDoc, rName))
2099 {
2100 maRefCache.setRangeName(nFileId, rName);
2101 return true;
2102 }
2103
2104 return false;
2105}
2106
2107void ScExternalRefManager::refreshAllRefCells(sal_uInt16 nFileId)
2108{
2109 RefCellMap::iterator itrFile = maRefCells.find(nFileId);
2110 if (itrFile == maRefCells.end())
2111 return;
2112
2113 RefCellSet& rRefCells = itrFile->second;
2114 for_each(rRefCells.begin(), rRefCells.end(), UpdateFormulaCell());
2115
2116 ScViewData* pViewData = ScDocShell::GetViewData();
2117 if (!pViewData)
2118 return;
2119
2120 ScTabViewShell* pVShell = pViewData->GetViewShell();
2121 if (!pVShell)
2122 return;
2123
2124 // Repainting the grid also repaints the texts, but is there a better way
2125 // to refresh texts?
2126 pVShell->Invalidate(FID_REPAINT((26000 + 100) + 2));
2127 pVShell->PaintGrid();
2128}
2129
2130namespace {
2131
2132void insertRefCellByIterator(
2133 const ScExternalRefManager::RefCellMap::iterator& itr, ScFormulaCell* pCell)
2134{
2135 if (pCell)
2136 {
2137 itr->second.insert(pCell);
2138 pCell->SetIsExtRef();
2139 }
2140}
2141
2142}
2143
2144void ScExternalRefManager::insertRefCell(sal_uInt16 nFileId, const ScAddress& rCell)
2145{
2146 RefCellMap::iterator itr = maRefCells.find(nFileId);
2147 if (itr == maRefCells.end())
2148 {
2149 RefCellSet aRefCells;
2150 pair<RefCellMap::iterator, bool> r = maRefCells.emplace(
2151 nFileId, aRefCells);
2152 if (!r.second)
2153 // insertion failed.
2154 return;
2155
2156 itr = r.first;
2157 }
2158
2159 insertRefCellByIterator(itr, mrDoc.GetFormulaCell(rCell));
2160}
2161
2162void ScExternalRefManager::insertRefCellFromTemplate( ScFormulaCell* pTemplateCell, ScFormulaCell* pCell )
2163{
2164 if (!pTemplateCell || !pCell)
2165 return;
2166
2167 for (RefCellMap::iterator itr = maRefCells.begin(); itr != maRefCells.end(); ++itr)
2168 {
2169 if (itr->second.find(pTemplateCell) != itr->second.end())
2170 insertRefCellByIterator(itr, pCell);
2171 }
2172}
2173
2174bool ScExternalRefManager::hasCellExternalReference(const ScAddress& rCell)
2175{
2176 ScFormulaCell* pCell = mrDoc.GetFormulaCell(rCell);
2177
2178 if (pCell)
2179 return std::any_of(maRefCells.begin(), maRefCells.end(),
2180 [&pCell](const RefCellMap::value_type& rEntry) { return rEntry.second.find(pCell) != rEntry.second.end(); });
2181
2182 return false;
2183}
2184
2185void ScExternalRefManager::enableDocTimer( bool bEnable )
2186{
2187 if (mbDocTimerEnabled == bEnable)
2188 return;
2189
2190 mbDocTimerEnabled = bEnable;
2191 if (mbDocTimerEnabled)
2192 {
2193 if (!maDocShells.empty())
2194 {
2195 for (auto& rEntry : maDocShells)
2196 rEntry.second.maLastAccess = tools::Time(tools::Time::SYSTEM);
2197
2198 maSrcDocTimer.Start();
2199 }
2200 }
2201 else
2202 maSrcDocTimer.Stop();
2203}
2204
2205void ScExternalRefManager::fillCellFormat(sal_uLong nFmtIndex, ScExternalRefCache::CellFormat* pFmt) const
2206{
2207 if (!pFmt)
2208 return;
2209
2210 SvNumFormatType nFmtType = mrDoc.GetFormatTable()->GetType(nFmtIndex);
2211 if (nFmtType != SvNumFormatType::UNDEFINED)
2212 {
2213 pFmt->mbIsSet = true;
2214 pFmt->mnIndex = nFmtIndex;
2215 pFmt->mnType = nFmtType;
2216 }
2217}
2218
2219ScExternalRefCache::TokenRef ScExternalRefManager::getSingleRefTokenFromSrcDoc(
2220 sal_uInt16 nFileId, ScDocument& rSrcDoc, const ScAddress& rPos,
2221 ScExternalRefCache::CellFormat* pFmt)
2222{
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));
2226
2227 if (!pToken)
2228 {
2229 // Generate an error for unresolvable cells.
2230 pToken.reset( new FormulaErrorToken( FormulaError::NoValue));
2231 }
2232
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);
2238 return pToken;
2239}
2240
2241ScExternalRefCache::TokenArrayRef ScExternalRefManager::getDoubleRefTokensFromSrcDoc(
2242 const ScDocument& rSrcDoc, const OUString& rTabName, ScRange& rRange,
2243 vector<ScExternalRefCache::SingleRangeData>& rCacheData)
2244{
2245 ScExternalRefCache::TokenArrayRef pArray;
2246 SCTAB nTab1;
2247
2248 if (!rSrcDoc.GetTable(rTabName, nTab1))
2249 {
2250 // specified table name doesn't exist in the source document.
2251 pArray = std::make_shared<ScTokenArray>(rSrcDoc);
2252 pArray->AddToken(FormulaErrorToken(FormulaError::NoRef));
2253 return pArray;
2254 }
2255
2256 ScRange aRange(rRange);
2257 aRange.PutInOrder();
2258 SCTAB nTabSpan = aRange.aEnd.Tab() - aRange.aStart.Tab();
2259
2260 vector<ScExternalRefCache::SingleRangeData> aCacheData;
2261 aCacheData.reserve(nTabSpan+1);
2262 aCacheData.emplace_back();
2263 aCacheData.back().maTableName = ScGlobal::getCharClassPtr()->uppercase(rTabName);
2264
2265 for (SCTAB i = 1; i < nTabSpan + 1; ++i)
2266 {
2267 OUString aTabName;
2268 if (!rSrcDoc.GetName(nTab1 + 1, aTabName))
2269 // source document doesn't have any table by the specified name.
2270 break;
2271
2272 aCacheData.emplace_back();
2273 aCacheData.back().maTableName = ScGlobal::getCharClassPtr()->uppercase(aTabName);
2274 }
2275
2276 aRange.aStart.SetTab(nTab1);
2277 aRange.aEnd.SetTab(nTab1 + nTabSpan);
2278
2279 pArray = convertToTokenArray(mrDoc, rSrcDoc, aRange, aCacheData);
2280 rRange = aRange;
2281 rCacheData.swap(aCacheData);
2282 return pArray;
2283}
2284
2285ScExternalRefCache::TokenArrayRef ScExternalRefManager::getRangeNameTokensFromSrcDoc(
2286 sal_uInt16 nFileId, const ScDocument& rSrcDoc, OUString& rName)
2287{
2288 ScRangeName* pExtNames = rSrcDoc.GetRangeName();
2289 OUString aUpperName = ScGlobal::getCharClassPtr()->uppercase(rName);
2290 const ScRangeData* pRangeData = pExtNames->findByUpperName(aUpperName);
2291 if (!pRangeData)
2292 return ScExternalRefCache::TokenArrayRef();
2293
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
2297 // source.
2298
2299 ScExternalRefCache::TokenArrayRef pNew = std::make_shared<ScTokenArray>(rSrcDoc);
2300
2301 ScTokenArray aCode(*pRangeData->GetCode());
2302 FormulaTokenArrayPlainIterator aIter(aCode);
2303 for (const FormulaToken* pToken = aIter.First(); pToken; pToken = aIter.Next())
2304 {
2305 bool bTokenAdded = false;
2306 switch (pToken->GetType())
2307 {
2308 case svSingleRef:
2309 {
2310 const ScSingleRefData& rRef = *pToken->GetSingleRef();
2311 OUString aTabName;
2312 rSrcDoc.GetName(rRef.Tab(), aTabName);
2313 ScExternalSingleRefToken aNewToken(nFileId, svl::SharedString( aTabName), // string not interned
2314 *pToken->GetSingleRef());
2315 pNew->AddToken(aNewToken);
2316 bTokenAdded = true;
2317 }
2318 break;
2319 case svDoubleRef:
2320 {
2321 const ScSingleRefData& rRef = *pToken->GetSingleRef();
2322 OUString aTabName;
2323 rSrcDoc.GetName(rRef.Tab(), aTabName);
2324 ScExternalDoubleRefToken aNewToken(nFileId, svl::SharedString( aTabName), // string not interned
2325 *pToken->GetDoubleRef());
2326 pNew->AddToken(aNewToken);
2327 bTokenAdded = true;
2328 }
2329 break;
2330 default:
2331 ; // nothing
2332 }
2333
2334 if (!bTokenAdded)
2335 pNew->AddToken(*pToken);
2336 }
2337
2338 rName = pRangeData->GetName(); // Get the correctly-cased name.
2339 return pNew;
2340}
2341
2342ScDocument* ScExternalRefManager::getInMemorySrcDocument(sal_uInt16 nFileId)
2343{
2344 const OUString* pFileName = getExternalFileName(nFileId);
2345 if (!pFileName)
2346 return nullptr;
2347
2348 // Do not load document until it was allowed.
2349 if (!isLinkUpdateAllowedInDoc(mrDoc))
2350 return nullptr;
2351
2352 ScDocument* pSrcDoc = nullptr;
2353 ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<ScDocShell>, false));
2354 while (pShell)
2355 {
2356 SfxMedium* pMedium = pShell->GetMedium();
2357 if (pMedium && !pMedium->GetName().isEmpty())
2358 {
2359 // TODO: We should make the case sensitivity platform dependent.
2360 if (pFileName->equalsIgnoreAsciiCase(pMedium->GetName()))
2361 {
2362 // Found !
2363 pSrcDoc = &pShell->GetDocument();
2364 break;
2365 }
2366 }
2367 else
2368 {
2369 // handle unsaved documents here
2370 OUString aName = pShell->GetName();
2371 if (pFileName->equalsIgnoreAsciiCase(aName))
2372 {
2373 // Found !
2374 SrcShell aSrcDoc;
2375 aSrcDoc.maShell = pShell;
2376 maUnsavedDocShells.emplace(nFileId, aSrcDoc);
2377 StartListening(*pShell);
2378 pSrcDoc = &pShell->GetDocument();
2379 break;
2380 }
2381 }
2382 pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, checkSfxObjectShell<ScDocShell>, false));
2383 }
2384
2385 initDocInCache(maRefCache, pSrcDoc, nFileId);
2386 return pSrcDoc;
2387}
2388
2389ScDocument* ScExternalRefManager::getSrcDocument(sal_uInt16 nFileId)
2390{
2391 if (!mrDoc.IsExecuteLinkEnabled())
4
Assuming the condition is false
5
Taking false branch
2392 return nullptr;
2393
2394 DocShellMap::iterator itrEnd = maDocShells.end();
2395 DocShellMap::iterator itr = maDocShells.find(nFileId);
2396
2397 if (itr != itrEnd)
6
Taking false branch
2398 {
2399 // document already loaded.
2400
2401 SfxObjectShell* p = itr->second.maShell.get();
2402 itr->second.maLastAccess = tools::Time( tools::Time::SYSTEM );
2403 return &static_cast<ScDocShell*>(p)->GetDocument();
2404 }
2405
2406 itrEnd = maUnsavedDocShells.end();
2407 itr = maUnsavedDocShells.find(nFileId);
2408 if (itr != itrEnd)
7
Taking false branch
2409 {
2410 //document is unsaved document
2411
2412 SfxObjectShell* p = itr->second.maShell.get();
2413 itr->second.maLastAccess = tools::Time( tools::Time::SYSTEM );
2414 return &static_cast<ScDocShell*>(p)->GetDocument();
2415 }
2416
2417 const OUString* pFile = getExternalFileName(nFileId);
2418 if (!pFile
7.1
'pFile' is non-null
7.1
'pFile' is non-null
)
8
Taking false branch
2419 // no file name associated with this ID.
2420 return nullptr;
2421
2422 SrcShell aSrcDoc;
2423 try
2424 {
2425 OUString aFilter;
2426 aSrcDoc.maShell = loadSrcDocument(nFileId, aFilter);
2427 }
2428 catch (const css::uno::Exception&)
2429 {
2430 }
2431 if (!aSrcDoc.maShell.is())
9
Taking false branch
2432 {
2433 // source document could not be loaded.
2434 return nullptr;
2435 }
2436
2437 return &cacheNewDocShell(nFileId, aSrcDoc);
10
Calling implicit destructor for 'SrcShell'
11
Calling '~SvRef'
21
Returning from '~SvRef'
22
Returning from destructor for 'SrcShell'
23
Use of memory after it is freed
2438}
2439
2440SfxObjectShellRef ScExternalRefManager::loadSrcDocument(sal_uInt16 nFileId, OUString& rFilter)
2441{
2442 // Do not load document until it was allowed.
2443 if (!isLinkUpdateAllowedInDoc(mrDoc))
2444 return nullptr;
2445
2446 const SrcFileData* pFileData = getExternalFileData(nFileId);
2447 if (!pFileData)
2448 return nullptr;
2449
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
2453 // given.
2454 OUString aFile = pFileData->maFileName;
2455 maybeCreateRealFileName(nFileId);
2456 if (!pFileData->maRealFileName.isEmpty())
2457 aFile = pFileData->maRealFileName;
2458
2459 if (!isFileLoadable(aFile))
2460 return nullptr;
2461
2462 OUString aOptions = pFileData->maFilterOptions;
2463 if ( !pFileData->maFilterName.isEmpty() )
2464 rFilter = pFileData->maFilterName; // don't overwrite stored filter with guessed filter
2465 else
2466 ScDocumentLoader::GetFilterName(aFile, rFilter, aOptions, true, false);
2467 std::shared_ptr<const SfxFilter> pFilter = ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName(rFilter);
2468
2469 if (pFileData->maRelativeName.isEmpty())
2470 {
2471 // Generate a relative file path.
2472 INetURLObject aBaseURL(getOwnDocumentName());
2473 aBaseURL.insertName("content.xml");
2474
2475 OUString aStr = URIHelper::simpleNormalizedMakeRelative(
2476 aBaseURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), aFile);
2477
2478 setRelativeFileName(nFileId, aStr);
2479 }
2480
2481 std::unique_ptr<SfxItemSet> pSet(new SfxAllItemSet(SfxGetpApp()->GetPool()));
2482 if (!aOptions.isEmpty())
2483 pSet->Put(SfxStringItem(SID_FILE_FILTEROPTIONS(5000 + 527), aOptions));
2484
2485 // make medium hidden to prevent assertion from progress bar
2486 pSet->Put( SfxBoolItem(SID_HIDDEN(5000 + 534), true) );
2487
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
2494 // trusted.
2495 SfxObjectShell* pShell = mrDoc.GetDocumentShell();
2496 if (pShell)
2497 {
2498 SfxMedium* pMedium = pShell->GetMedium();
2499 if (pMedium)
2500 {
2501 const SfxPoolItem* pItem;
2502 if (pMedium->GetItemSet()->GetItemState( SID_MACROEXECMODE(5000 + 1319), false, &pItem ) == SfxItemState::SET &&
2503 static_cast<const SfxUInt16Item*>(pItem)->GetValue() != css::document::MacroExecMode::NEVER_EXECUTE)
2504 pSet->Put( SfxUInt16Item( SID_MACROEXECMODE(5000 + 1319), css::document::MacroExecMode::USE_CONFIG));
2505 }
2506
2507 pSet->Put( SfxUInt16Item( SID_UPDATEDOCMODE(5000 + 1668), css::document::UpdateDocMode::FULL_UPDATE));
2508 }
2509
2510 unique_ptr<SfxMedium> pMedium(new SfxMedium(aFile, StreamMode::STD_READ, pFilter, std::move(pSet)));
2511 if (pMedium->GetError() != ERRCODE_NONEErrCode(0))
2512 return nullptr;
2513
2514 // To load encrypted documents with password, user interaction needs to be enabled.
2515 pMedium->UseInteractionHandler(mbUserInteractionEnabled);
2516
2517 ScDocShell* pNewShell = new ScDocShell(SfxModelFlags::EXTERNAL_LINK);
2518 SfxObjectShellRef aRef = pNewShell;
2519
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);
2528
2529 ScExtDocOptions* pExtOptNew = rSrcDoc.GetExtDocOptions();
2530 if (!pExtOptNew)
2531 {
2532 rSrcDoc.SetExtDocOptions(std::make_unique<ScExtDocOptions>());
2533 pExtOptNew = rSrcDoc.GetExtDocOptions();
2534 }
2535 pExtOptNew->GetDocSettings().mnLinkCnt = nLinkCount + 1;
2536
2537 if (!pNewShell->DoLoad(pMedium.release()))
2538 {
2539 aRef->DoClose();
2540 aRef.clear();
2541 return aRef;
2542 }
2543
2544 // with UseInteractionHandler, options may be set by dialog during DoLoad
2545 OUString aNew = ScDocumentLoader::GetOptions(*pNewShell->GetMedium());
2546 if (!aNew.isEmpty() && aNew != aOptions)
2547 aOptions = aNew;
2548 setFilterData(nFileId, rFilter, aOptions); // update the filter data, including the new options
2549
2550 return aRef;
2551}
2552
2553ScDocument& ScExternalRefManager::cacheNewDocShell( sal_uInt16 nFileId, SrcShell& rSrcShell )
2554{
2555 if (mbDocTimerEnabled && maDocShells.empty())
2556 // If this is the first source document insertion, start up the timer.
2557 maSrcDocTimer.Start();
2558
2559 maDocShells.emplace(nFileId, rSrcShell);
2560 SfxObjectShell& rShell = *rSrcShell.maShell;
2561 ScDocument& rSrcDoc = static_cast<ScDocShell&>(rShell).GetDocument();
2562 initDocInCache(maRefCache, &rSrcDoc, nFileId);
2563 return rSrcDoc;
2564}
2565
2566bool ScExternalRefManager::isFileLoadable(const OUString& rFile) const
2567{
2568 if (rFile.isEmpty())
2569 return false;
2570
2571 if (isOwnDocument(rFile))
2572 return false;
2573 OUString aPhysical;
2574 if (osl::FileBase::getSystemPathFromFileURL(rFile, aPhysical)
2575 == osl::FileBase::E_None)
2576 {
2577 // #i114504# try IsFolder/Exists only for file URLs
2578
2579 if (utl::UCBContentHelper::IsFolder(rFile))
2580 return false;
2581
2582 return utl::UCBContentHelper::Exists(rFile);
2583 }
2584 else
2585 return true; // for http and others, Exists doesn't work, but the URL can still be opened
2586}
2587
2588void ScExternalRefManager::maybeLinkExternalFile( sal_uInt16 nFileId, bool bDeferFilterDetection )
2589{
2590 if (maLinkedDocs.count(nFileId))
2591 // file already linked, or the link has been broken.
2592 return;
2593
2594 // Source document not linked yet. Link it now.
2595 const OUString* pFileName = getExternalFileName(nFileId);
2596 if (!pFileName)
2597 return;
2598
2599 OUString aFilter, aOptions;
2600 const SrcFileData* pFileData = getExternalFileData(nFileId);
2601 if (pFileData)
2602 {
2603 aFilter = pFileData->maFilterName;
2604 aOptions = pFileData->maFilterOptions;
2605 }
2606
2607 // Filter detection may access external links; defer it until we are allowed.
2608 if (!bDeferFilterDetection)
2609 bDeferFilterDetection = !isLinkUpdateAllowedInDoc(mrDoc);
2610
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();
2618 if (!pLinkMgr)
2619 {
2620 SAL_WARN( "sc.ui", "ScExternalRefManager::maybeLinkExternalFile: pLinkMgr==NULL")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "sc.ui")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "ScExternalRefManager::maybeLinkExternalFile: pLinkMgr==NULL"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "2620" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "ScExternalRefManager::maybeLinkExternalFile: pLinkMgr==NULL"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "ScExternalRefManager::maybeLinkExternalFile: pLinkMgr==NULL"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "2620" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "ScExternalRefManager::maybeLinkExternalFile: pLinkMgr==NULL"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "2620" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "ScExternalRefManager::maybeLinkExternalFile: pLinkMgr==NULL"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "ScExternalRefManager::maybeLinkExternalFile: pLinkMgr==NULL"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "2620" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
2621 return;
2622 }
2623 ScExternalRefLink* pLink = new ScExternalRefLink(mrDoc, nFileId);
2624 OSL_ENSURE(pFileName, "ScExternalRefManager::maybeLinkExternalFile: file name pointer is NULL")do { if (true && (!(pFileName))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "2624" ": "), "%s", "ScExternalRefManager::maybeLinkExternalFile: file name pointer is NULL"
); } } while (false)
;
2625 pLinkMgr->InsertFileLink(*pLink, sfx2::SvBaseLinkObjectType::ClientFile, *pFileName,
2626 (aFilter.isEmpty() && bDeferFilterDetection ? nullptr : &aFilter));
2627
2628 pLink->SetDoRefresh(false);
2629 pLink->Update();
2630 pLink->SetDoRefresh(true);
2631
2632 maLinkedDocs.emplace(nFileId, true);
2633}
2634
2635void ScExternalRefManager::addFilesToLinkManager()
2636{
2637 if (maSrcFiles.empty())
2638 return;
2639
2640 SAL_WARN_IF( maSrcFiles.size() >= SAL_MAX_UINT16,do { if (true && (maSrcFiles.size() >= ((sal_uInt16
) 0xFFFF))) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "sc.ui")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "ScExternalRefManager::addFilesToLinkManager: files overflow"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "2641" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "ScExternalRefManager::addFilesToLinkManager: files overflow"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "ScExternalRefManager::addFilesToLinkManager: files overflow"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "2641" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "ScExternalRefManager::addFilesToLinkManager: files overflow"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "2641" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "ScExternalRefManager::addFilesToLinkManager: files overflow"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "ScExternalRefManager::addFilesToLinkManager: files overflow"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "2641" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
2641 "sc.ui", "ScExternalRefManager::addFilesToLinkManager: files overflow")do { if (true && (maSrcFiles.size() >= ((sal_uInt16
) 0xFFFF))) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "sc.ui")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "ScExternalRefManager::addFilesToLinkManager: files overflow"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "2641" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "ScExternalRefManager::addFilesToLinkManager: files overflow"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "ScExternalRefManager::addFilesToLinkManager: files overflow"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "2641" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "ScExternalRefManager::addFilesToLinkManager: files overflow"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "2641" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "ScExternalRefManager::addFilesToLinkManager: files overflow"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "ScExternalRefManager::addFilesToLinkManager: files overflow"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc.ui"
), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "2641" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
2642 const sal_uInt16 nSize = static_cast<sal_uInt16>( std::min<size_t>( maSrcFiles.size(), SAL_MAX_UINT16((sal_uInt16) 0xFFFF)));
2643 for (sal_uInt16 nFileId = 0; nFileId < nSize; ++nFileId)
2644 maybeLinkExternalFile( nFileId, true);
2645}
2646
2647void ScExternalRefManager::SrcFileData::maybeCreateRealFileName(const OUString& rOwnDocName)
2648{
2649 if (maRelativeName.isEmpty())
2650 // No relative path given. Nothing to do.
2651 return;
2652
2653 if (!maRealFileName.isEmpty())
2654 // Real file name already created. Nothing to do.
2655 return;
2656
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);
2663}
2664
2665void ScExternalRefManager::maybeCreateRealFileName(sal_uInt16 nFileId)
2666{
2667 if (nFileId >= maSrcFiles.size())
2668 return;
2669
2670 maSrcFiles[nFileId].maybeCreateRealFileName(getOwnDocumentName());
2671}
2672
2673OUString ScExternalRefManager::getOwnDocumentName() const
2674{
2675 if (utl::ConfigManager::IsFuzzing())
2676 return "file:///tmp/document";
2677
2678 SfxObjectShell* pShell = mrDoc.GetDocumentShell();
2679 if (!pShell)
2680 // This should not happen!
2681 return OUString();
2682
2683 SfxMedium* pMed = pShell->GetMedium();
2684 if (!pMed)
2685 return OUString();
2686
2687 return pMed->GetName();
2688}
2689
2690bool ScExternalRefManager::isOwnDocument(const OUString& rFile) const
2691{
2692 return getOwnDocumentName() == rFile;
2693}
2694
2695void ScExternalRefManager::convertToAbsName(OUString& rFile) const
2696{
2697 // unsaved documents have no AbsName
2698 ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<ScDocShell>, false));
2699 while (pShell)
2700 {
2701 if (rFile == pShell->GetName())
2702 return;
2703
2704 pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, checkSfxObjectShell<ScDocShell>, false));
2705 }
2706
2707 SfxObjectShell* pDocShell = mrDoc.GetDocumentShell();
2708 rFile = ScGlobal::GetAbsDocName(rFile, pDocShell);
2709}
2710
2711sal_uInt16 ScExternalRefManager::getExternalFileId(const OUString& rFile)
2712{
2713 vector<SrcFileData>::const_iterator itrBeg = maSrcFiles.begin(), itrEnd = maSrcFiles.end();
2714 vector<SrcFileData>::const_iterator itr = find_if(itrBeg, itrEnd, FindSrcFileByName(rFile));
2715 if (itr != itrEnd)
2716 {
2717 size_t nId = distance(itrBeg, itr);
2718 return static_cast<sal_uInt16>(nId);
2719 }
2720
2721 SrcFileData aData;
2722 aData.maFileName = rFile;
2723 maSrcFiles.push_back(aData);
2724 return static_cast<sal_uInt16>(maSrcFiles.size() - 1);
2725}
2726
2727const OUString* ScExternalRefManager::getExternalFileName(sal_uInt16 nFileId, bool bForceOriginal)
2728{
2729 if (nFileId >= maSrcFiles.size())
2730 return nullptr;
2731
2732 if (bForceOriginal)
2733 return &maSrcFiles[nFileId].maFileName;
2734
2735 maybeCreateRealFileName(nFileId);
2736
2737 if (!maSrcFiles[nFileId].maRealFileName.isEmpty())
2738 return &maSrcFiles[nFileId].maRealFileName;
2739
2740 return &maSrcFiles[nFileId].maFileName;
2741}
2742
2743std::vector<OUString> ScExternalRefManager::getAllCachedExternalFileNames() const
2744{
2745 std::vector<OUString> aNames;
2746 aNames.reserve(maSrcFiles.size());
2747 for (const SrcFileData& rData : maSrcFiles)
2748 {
2749 aNames.push_back(rData.maFileName);
2750 }
2751
2752 return aNames;
2753}
2754
2755bool ScExternalRefManager::hasExternalFile(sal_uInt16 nFileId) const
2756{
2757 return nFileId < maSrcFiles.size();
2758}
2759
2760bool ScExternalRefManager::hasExternalFile(const OUString& rFile) const
2761{
2762 return ::std::any_of(maSrcFiles.begin(), maSrcFiles.end(), FindSrcFileByName(rFile));
2763}
2764
2765const ScExternalRefManager::SrcFileData* ScExternalRefManager::getExternalFileData(sal_uInt16 nFileId) const
2766{
2767 if (nFileId >= maSrcFiles.size())
2768 return nullptr;
2769
2770 return &maSrcFiles[nFileId];
2771}
2772
2773const OUString* ScExternalRefManager::getRealTableName(sal_uInt16 nFileId, const OUString& rTabName) const
2774{
2775 return maRefCache.getRealTableName(nFileId, rTabName);
2776}
2777
2778const OUString* ScExternalRefManager::getRealRangeName(sal_uInt16 nFileId, const OUString& rRangeName) const
2779{
2780 return maRefCache.getRealRangeName(nFileId, rRangeName);
2781}
2782
2783template<typename MapContainer>
2784static void lcl_removeByFileId(sal_uInt16 nFileId, MapContainer& rMap)
2785{
2786 typename MapContainer::iterator itr = rMap.find(nFileId);
2787 if (itr != rMap.end())
2788 {
2789 // Close this document shell.
2790 itr->second.maShell->DoClose();
2791 rMap.erase(itr);
2792 }
2793}
2794
2795void ScExternalRefManager::clearCache(sal_uInt16 nFileId)
2796{
2797 maRefCache.clearCache(nFileId);
2798}
2799
2800namespace {
2801
2802class RefCacheFiller : public sc::ColumnSpanSet::ColumnAction
2803{
2804 svl::SharedStringPool& mrStrPool;
2805
2806 ScExternalRefCache& mrRefCache;
2807 ScExternalRefCache::TableTypeRef mpRefTab;
2808 sal_uInt16 mnFileId;
2809 ScColumn* mpCurCol;
2810 sc::ColumnBlockConstPosition maBlockPos;
2811
2812public:
2813 RefCacheFiller( svl::SharedStringPool& rStrPool, ScExternalRefCache& rRefCache, sal_uInt16 nFileId ) :
2814 mrStrPool(rStrPool), mrRefCache(rRefCache), mnFileId(nFileId), mpCurCol(nullptr) {}
2815
2816 virtual void startColumn( ScColumn* pCol ) override
2817 {
2818 mpCurCol = pCol;
2819 if (!mpCurCol)
2820 return;
2821
2822 mpCurCol->InitBlockPosition(maBlockPos);
2823 mpRefTab = mrRefCache.getCacheTable(mnFileId, mpCurCol->GetTab());
2824 }
2825
2826 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
2827 {
2828 if (!mpCurCol || !bVal)
2829 return;
2830
2831 if (!mpRefTab)
2832 return;
2833
2834 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2835 {
2836 ScExternalRefCache::TokenRef pTok;
2837 ScRefCellValue aCell = mpCurCol->GetCellValue(maBlockPos, nRow);
2838 switch (aCell.meType)
2839 {
2840 case CELLTYPE_STRING:
2841 case CELLTYPE_EDIT:
2842 {
2843 OUString aStr = aCell.getString(&mpCurCol->GetDoc());
2844 svl::SharedString aSS = mrStrPool.intern(aStr);
2845 pTok.reset(new formula::FormulaStringToken(aSS));
2846 }
2847 break;
2848 case CELLTYPE_VALUE:
2849 pTok.reset(new formula::FormulaDoubleToken(aCell.mfValue));
2850 break;
2851 case CELLTYPE_FORMULA:
2852 {
2853 sc::FormulaResultValue aRes = aCell.mpFormula->GetResult();
2854 switch (aRes.meType)
2855 {
2856 case sc::FormulaResultValue::Value:
2857 pTok.reset(new formula::FormulaDoubleToken(aRes.mfValue));
2858 break;
2859 case sc::FormulaResultValue::String:
2860 {
2861 // Re-intern the string to the host document pool.
2862 svl::SharedString aInterned = mrStrPool.intern(aRes.maString.getString());
2863 pTok.reset(new formula::FormulaStringToken(aInterned));
2864 }
2865 break;
2866 case sc::FormulaResultValue::Error:
2867 case sc::FormulaResultValue::Invalid:
2868 default:
2869 pTok.reset(new FormulaErrorToken(FormulaError::NoValue));
2870 }
2871 }
2872 break;
2873 default:
2874 pTok.reset(new FormulaErrorToken(FormulaError::NoValue));
2875 }
2876
2877 if (pTok)
2878 {
2879 // Cache this cell.
2880 mpRefTab->setCell(mpCurCol->GetCol(), nRow, pTok, mpCurCol->GetNumberFormat(mpCurCol->GetDoc().GetNonThreadedContext(), nRow));
2881 mpRefTab->setCachedCell(mpCurCol->GetCol(), nRow);
2882 }
2883 }
2884 };
2885};
2886
2887}
2888
2889bool ScExternalRefManager::refreshSrcDocument(sal_uInt16 nFileId)
2890{
2891 SfxObjectShellRef xDocShell;
2892 try
2893 {
2894 OUString aFilter;
2895 xDocShell = loadSrcDocument(nFileId, aFilter);
2896 }
2897 catch ( const css::uno::Exception& ) {}
2898
2899 if (!xDocShell.is())
2900 // Failed to load the document. Bail out.
2901 return false;
2902
2903 ScDocShell& rDocSh = static_cast<ScDocShell&>(*xDocShell);
2904 ScDocument& rSrcDoc = rDocSh.GetDocument();
2905
2906 sc::ColumnSpanSet aCachedArea;
2907 maRefCache.getAllCachedDataSpans(rSrcDoc, nFileId, aCachedArea);
2908
2909 // Clear the existing cache, and refill it. Make sure we keep the
2910 // existing cache table instances here.
2911 maRefCache.clearCacheTables(nFileId);
2912 RefCacheFiller aAction(mrDoc.GetSharedStringPool(), maRefCache, nFileId);
2913 aCachedArea.executeColumnAction(rSrcDoc, aAction);
2914
2915 DocShellMap::iterator it = maDocShells.find(nFileId);
2916 if (it != maDocShells.end())
2917 {
2918 it->second.maShell->DoClose();
2919 it->second.maShell = xDocShell;
2920 it->second.maLastAccess = tools::Time(tools::Time::SYSTEM);
2921 }
2922 else
2923 {
2924 SrcShell aSrcDoc;
2925 aSrcDoc.maShell = xDocShell;
2926 aSrcDoc.maLastAccess = tools::Time(tools::Time::SYSTEM);
2927 cacheNewDocShell(nFileId, aSrcDoc);
2928 }
2929
2930 // Update all cells containing names from this source document.
2931 refreshAllRefCells(nFileId);
2932
2933 notifyAllLinkListeners(nFileId, LINK_MODIFIED);
2934
2935 return true;
2936}
2937
2938void ScExternalRefManager::breakLink(sal_uInt16 nFileId)
2939{
2940 // Turn all formula cells referencing this external document into static
2941 // cells.
2942 RefCellMap::iterator itrRefs = maRefCells.find(nFileId);
2943 if (itrRefs != maRefCells.end())
2944 {
2945 // Make a copy because removing the formula cells below will modify
2946 // the original container.
2947 RefCellSet aSet = itrRefs->second;
2948 for_each(aSet.begin(), aSet.end(), ConvertFormulaToStatic(&mrDoc));
2949 maRefCells.erase(nFileId);
2950 }
2951
2952 // Remove all named ranges that reference this document.
2953
2954 // Global named ranges.
2955 ScRangeName* pRanges = mrDoc.GetRangeName();
2956 if (pRanges)
2957 removeRangeNamesBySrcDoc(*pRanges, nFileId);
2958
2959 // Sheet-local named ranges.
2960 for (SCTAB i = 0, n = mrDoc.GetTableCount(); i < n; ++i)
2961 {
2962 pRanges = mrDoc.GetRangeName(i);
2963 if (pRanges)
2964 removeRangeNamesBySrcDoc(*pRanges, nFileId);
2965 }
2966
2967 clearCache(nFileId);
2968 lcl_removeByFileId(nFileId, maDocShells);
2969
2970 if (maDocShells.empty())
2971 maSrcDocTimer.Stop();
2972
2973 LinkedDocMap::iterator itr = maLinkedDocs.find(nFileId);
2974 if (itr != maLinkedDocs.end())
2975 itr->second = false;
2976
2977 notifyAllLinkListeners(nFileId, LINK_BROKEN);
2978}
2979
2980void ScExternalRefManager::switchSrcFile(sal_uInt16 nFileId, const OUString& rNewFile, const OUString& rNewFilter)
2981{
2982 maSrcFiles[nFileId].maFileName = rNewFile;
2983 maSrcFiles[nFileId].maRelativeName.clear();
2984 maSrcFiles[nFileId].maRealFileName.clear();
2985 if (maSrcFiles[nFileId].maFilterName != rNewFilter)
2986 {
2987 // Filter type has changed.
2988 maSrcFiles[nFileId].maFilterName = rNewFilter;
2989 maSrcFiles[nFileId].maFilterOptions.clear();
2990 }
2991 refreshSrcDocument(nFileId);
2992}
2993
2994void ScExternalRefManager::setRelativeFileName(sal_uInt16 nFileId, const OUString& rRelUrl)
2995{
2996 if (nFileId >= maSrcFiles.size())
2997 return;
2998 maSrcFiles[nFileId].maRelativeName = rRelUrl;
2999}
3000
3001void ScExternalRefManager::setFilterData(sal_uInt16 nFileId, const OUString& rFilterName, const OUString& rOptions)
3002{
3003 if (nFileId >= maSrcFiles.size())
3004 return;
3005 maSrcFiles[nFileId].maFilterName = rFilterName;
3006 maSrcFiles[nFileId].maFilterOptions = rOptions;
3007}
3008
3009void ScExternalRefManager::clear()
3010{
3011 for (auto& rEntry : maDocShells)
3012 rEntry.second.maShell->DoClose();
3013
3014 maDocShells.clear();
3015 maSrcDocTimer.Stop();
3016}
3017
3018bool ScExternalRefManager::hasExternalData() const
3019{
3020 return !maSrcFiles.empty();
3021}
3022
3023void ScExternalRefManager::resetSrcFileData(const OUString& rBaseFileUrl)
3024{
3025 for (auto& rSrcFile : maSrcFiles)
3026 {
3027 // Re-generate relative file name from the absolute file name.
3028 OUString aAbsName = rSrcFile.maRealFileName;
3029 if (aAbsName.isEmpty())
3030 aAbsName = rSrcFile.maFileName;
3031
3032 rSrcFile.maRelativeName = URIHelper::simpleNormalizedMakeRelative(
3033 rBaseFileUrl, aAbsName);
3034 }
3035}
3036
3037void ScExternalRefManager::updateAbsAfterLoad()
3038{
3039 OUString aOwn( getOwnDocumentName() );
3040 for (auto& rSrcFile : maSrcFiles)
3041 {
3042 // update maFileName to the real file name,
3043 // to be called when the original name is no longer needed (after CompileXML)
3044
3045 rSrcFile.maybeCreateRealFileName( aOwn );
3046 OUString aReal = rSrcFile.maRealFileName;
3047 if (!aReal.isEmpty())
3048 rSrcFile.maFileName = aReal;
3049 }
3050}
3051
3052void ScExternalRefManager::removeRefCell(ScFormulaCell* pCell)
3053{
3054 for_each(maRefCells.begin(), maRefCells.end(), RemoveFormulaCell(pCell));
3055}
3056
3057void ScExternalRefManager::addLinkListener(sal_uInt16 nFileId, LinkListener* pListener)
3058{
3059 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
3060 if (itr == maLinkListeners.end())
3061 {
3062 pair<LinkListenerMap::iterator, bool> r = maLinkListeners.emplace(
3063 nFileId, LinkListeners());
3064 if (!r.second)
3065 {
3066 OSL_FAIL("insertion of new link listener list failed")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/ui/docshell/externalrefmgr.cxx"
":" "3066" ": "), "%s", "insertion of new link listener list failed"
); } } while (false)
;
3067 return;
3068 }
3069
3070 itr = r.first;
3071 }
3072
3073 LinkListeners& rList = itr->second;
3074 rList.insert(pListener);
3075}
3076
3077void ScExternalRefManager::removeLinkListener(sal_uInt16 nFileId, LinkListener* pListener)
3078{
3079 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
3080 if (itr == maLinkListeners.end())
3081 // no listeners for a specified file.
3082 return;
3083
3084 LinkListeners& rList = itr->second;
3085 rList.erase(pListener);
3086
3087 if (rList.empty())
3088 // No more listeners for this file. Remove its entry.
3089 maLinkListeners.erase(itr);
3090}
3091
3092void ScExternalRefManager::removeLinkListener(LinkListener* pListener)
3093{
3094 for (auto& rEntry : maLinkListeners)
3095 rEntry.second.erase(pListener);
3096}
3097
3098void ScExternalRefManager::notifyAllLinkListeners(sal_uInt16 nFileId, LinkUpdateType eType)
3099{
3100 LinkListenerMap::iterator itr = maLinkListeners.find(nFileId);
3101 if (itr == maLinkListeners.end())
3102 // no listeners for a specified file.
3103 return;
3104
3105 LinkListeners& rList = itr->second;
3106 for_each(rList.begin(), rList.end(), NotifyLinkListener(nFileId, eType));
3107}
3108
3109void ScExternalRefManager::purgeStaleSrcDocument(sal_Int32 nTimeOut)
3110{
3111 // To avoid potentially freezing Calc, we close one stale document at a time.
3112 DocShellMap::iterator itr = std::find_if(maDocShells.begin(), maDocShells.end(),
3113 [nTimeOut](const DocShellMap::value_type& rEntry) {
3114 // in 100th of a second.
3115 sal_Int32 nSinceLastAccess = (tools::Time( tools::Time::SYSTEM ) - rEntry.second.maLastAccess).GetTime();
3116 return nSinceLastAccess >= nTimeOut;
3117 });
3118 if (itr != maDocShells.end())
3119 {
3120 // Timed out. Let's close this.
3121 itr->second.maShell->DoClose();
3122 maDocShells.erase(itr);
3123 }
3124
3125 if (maDocShells.empty())
3126 maSrcDocTimer.Stop();
3127}
3128
3129sal_uInt32 ScExternalRefManager::getMappedNumberFormat(sal_uInt16 nFileId, sal_uInt32 nNumFmt, const ScDocument& rSrcDoc)
3130{
3131 NumFmtMap::iterator itr = maNumFormatMap.find(nFileId);
3132 if (itr == maNumFormatMap.end())
3133 {
3134 // Number formatter map is not initialized for this external document.
3135 pair<NumFmtMap::iterator, bool> r = maNumFormatMap.emplace(
3136 nFileId, SvNumberFormatterMergeMap());
3137
3138 if (!r.second)
3139 // insertion failed.
3140 return nNumFmt;
3141
3142 itr = r.first;
3143 mrDoc.GetFormatTable()->MergeFormatter(*rSrcDoc.GetFormatTable());
3144 SvNumberFormatterMergeMap aMap = mrDoc.GetFormatTable()->ConvertMergeTableToMap();
3145 itr->second.swap(aMap);
3146 }
3147 const SvNumberFormatterMergeMap& rMap = itr->second;
3148 SvNumberFormatterMergeMap::const_iterator itrNumFmt = rMap.find(nNumFmt);
3149 if (itrNumFmt != rMap.end())
3150 // mapped value found.
3151 return itrNumFmt->second;
3152
3153 return nNumFmt;
3154}
3155
3156void ScExternalRefManager::transformUnsavedRefToSavedRef( SfxObjectShell* pShell )
3157{
3158 DocShellMap::iterator itr = maUnsavedDocShells.begin();
3159 while( itr != maUnsavedDocShells.end() )
3160 {
3161 if ( itr->second.maShell.get() == pShell )
3162 {
3163 // found that the shell is marked as unsaved
3164 OUString aFileURL = pShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
3165 switchSrcFile(itr->first, aFileURL, OUString());
3166 EndListening(*pShell);
3167 maUnsavedDocShells.erase(itr++);
3168 }
3169 }
3170}
3171
3172void ScExternalRefManager::Notify( SfxBroadcaster&, const SfxHint& rHint )
3173{
3174 const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&rHint);
3175 if ( !pEventHint )
3176 return;
3177
3178 SfxEventHintId nEventId = pEventHint->GetEventId();
3179 switch ( nEventId )
3180 {
3181 case SfxEventHintId::PrepareCloseDoc:
3182 {
3183 std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
3184 VclMessageType::Warning, VclButtonsType::Ok,
3185 ScResId(STR_CLOSE_WITH_UNSAVED_REFSreinterpret_cast<char const *>("STR_CLOSE_WITH_UNSAVED_REFS"
"\004" u8"This Document is referenced by another document and not yet saved. Closing it without saving will result in data loss."
)
)));
3186 xWarn->run();
3187 }
3188 break;
3189 case SfxEventHintId::SaveDocDone:
3190 case SfxEventHintId::SaveAsDocDone:
3191 {
3192 SfxObjectShell* pObjShell = static_cast<const SfxEventHint&>( rHint ).GetObjShell();
3193 transformUnsavedRefToSavedRef(pObjShell);
3194 }
3195 break;
3196 default:
3197 break;
3198 }
3199}
3200
3201IMPL_LINK(ScExternalRefManager, TimeOutHdl, Timer*, pTimer, void)void ScExternalRefManager::LinkStubTimeOutHdl(void * instance
, Timer* data) { return static_cast<ScExternalRefManager *
>(instance)->TimeOutHdl(data); } void ScExternalRefManager
::TimeOutHdl(Timer* pTimer)
3202{
3203 if (pTimer == &maSrcDocTimer)
3204 purgeStaleSrcDocument(SRCDOC_LIFE_SPAN30000);
3205}
3206
3207/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

/home/maarten/src/libreoffice/core/include/tools/ref.hxx

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#ifndef INCLUDED_TOOLS_REF_HXX
20#define INCLUDED_TOOLS_REF_HXX
21
22#include <sal/config.h>
23#include <cassert>
24#include <tools/toolsdllapi.h>
25#include <utility>
26
27/**
28 This implements similar functionality to boost::intrusive_ptr
29*/
30
31namespace tools {
32
33/** T must be a class that extends SvRefBase */
34template<typename T> class SAL_DLLPUBLIC_RTTI__attribute__ ((type_visibility("default"))) SvRef final {
35public:
36 SvRef(): pObj(nullptr) {}
37
38 SvRef(SvRef&& rObj) noexcept
39 {
40 pObj = rObj.pObj;
41 rObj.pObj = nullptr;
42 }
43
44 SvRef(SvRef const & rObj): pObj(rObj.pObj)
45 {
46 if (pObj != nullptr) pObj->AddNextRef();
47 }
48
49 SvRef(T * pObjP): pObj(pObjP)
50 {
51 if (pObj != nullptr) pObj->AddFirstRef();
52 }
53
54 ~SvRef()
55 {
56 if (pObj != nullptr) pObj->ReleaseRef();
12
Taking true branch
13
Calling 'SvRefBase::ReleaseRef'
20
Returning; memory was released
57 }
58
59 void clear()
60 {
61 if (pObj != nullptr) {
62 T * pRefObj = pObj;
63 pObj = nullptr;
64 pRefObj->ReleaseRef();
65 }
66 }
67
68 SvRef & operator =(SvRef const & rObj)
69 {
70 if (rObj.pObj != nullptr) {
71 rObj.pObj->AddNextRef();
72 }
73 T * pRefObj = pObj;
74 pObj = rObj.pObj;
75 if (pRefObj != nullptr) {
76 pRefObj->ReleaseRef();
77 }
78 return *this;
79 }
80
81 SvRef & operator =(SvRef && rObj)
82 {
83 if (pObj != nullptr) {
84 pObj->ReleaseRef();
85 }
86 pObj = rObj.pObj;
87 rObj.pObj = nullptr;
88 return *this;
89 }
90
91 bool is() const { return pObj != nullptr; }
92
93 explicit operator bool() const { return is(); }
94
95 T * get() const { return pObj; }
96
97 T * operator ->() const { assert(pObj != nullptr)(static_cast <bool> (pObj != nullptr) ? void (0) : __assert_fail
("pObj != nullptr", "/home/maarten/src/libreoffice/core/include/tools/ref.hxx"
, 97, __extension__ __PRETTY_FUNCTION__))
; return pObj; }
98
99 T & operator *() const { assert(pObj != nullptr)(static_cast <bool> (pObj != nullptr) ? void (0) : __assert_fail
("pObj != nullptr", "/home/maarten/src/libreoffice/core/include/tools/ref.hxx"
, 99, __extension__ __PRETTY_FUNCTION__))
; return *pObj; }
100
101 bool operator ==(const SvRef<T> &rhs) const { return pObj == rhs.pObj; }
102 bool operator !=(const SvRef<T> &rhs) const { return !(*this == rhs); }
103
104private:
105 T * pObj;
106};
107
108/**
109 * This implements similar functionality to std::make_shared.
110 */
111template<typename T, typename... Args>
112SvRef<T> make_ref(Args&& ... args)
113{
114 return SvRef<T>(new T(std::forward<Args>(args)...));
115}
116
117}
118
119/** Classes that want to be referenced-counted via SvRef<T>, should extend this base class */
120class TOOLS_DLLPUBLIC__attribute__ ((visibility("default"))) SvRefBase
121{
122 // work around a clang 3.5 optimization bug: if the bNoDelete is *first*
123 // it mis-compiles "if (--nRefCount == 0)" and never deletes any object
124 unsigned int nRefCount : 31;
125 // the only reason this is not bool is because MSVC cannot handle mixed type bitfields
126 unsigned int bNoDelete : 1;
127
128protected:
129 virtual ~SvRefBase() COVERITY_NOEXCEPT_FALSE;
130
131public:
132 SvRefBase() : nRefCount(0), bNoDelete(1) {}
133 SvRefBase(const SvRefBase &) : nRefCount(0), bNoDelete(1) {}
134
135 SvRefBase & operator=(const SvRefBase &) { return *this; }
136
137 void RestoreNoDelete()
138 { bNoDelete = 1; }
139
140 void AddNextRef()
141 {
142 assert( nRefCount < (1 << 30) && "Do not add refs to dead objects" )(static_cast <bool> (nRefCount < (1 << 30) &&
"Do not add refs to dead objects") ? void (0) : __assert_fail
("nRefCount < (1 << 30) && \"Do not add refs to dead objects\""
, "/home/maarten/src/libreoffice/core/include/tools/ref.hxx",
142, __extension__ __PRETTY_FUNCTION__))
;
143 ++nRefCount;
144 }
145
146 void AddFirstRef()
147 {
148 assert( nRefCount < (1 << 30) && "Do not add refs to dead objects" )(static_cast <bool> (nRefCount < (1 << 30) &&
"Do not add refs to dead objects") ? void (0) : __assert_fail
("nRefCount < (1 << 30) && \"Do not add refs to dead objects\""
, "/home/maarten/src/libreoffice/core/include/tools/ref.hxx",
148, __extension__ __PRETTY_FUNCTION__))
;
149 if( bNoDelete )
150 bNoDelete = 0;
151 ++nRefCount;
152 }
153
154 void ReleaseRef()
155 {
156 assert( nRefCount >= 1)(static_cast <bool> (nRefCount >= 1) ? void (0) : __assert_fail
("nRefCount >= 1", "/home/maarten/src/libreoffice/core/include/tools/ref.hxx"
, 156, __extension__ __PRETTY_FUNCTION__))
;
14
Assuming field 'nRefCount' is >= 1
15
'?' condition is true
157 if( --nRefCount == 0 && !bNoDelete)
16
Assuming the condition is true
17
Assuming field 'bNoDelete' is 0
18
Taking true branch
158 {
159 // I'm not sure about the original purpose of this line, but right now
160 // it serves the purpose that anything that attempts to do an AddRef()
161 // after an object is deleted will trip an assert.
162 nRefCount = 1 << 30;
163 delete this;
19
Memory is released
164 }
165 }
166
167 unsigned int GetRefCount() const
168 { return nRefCount; }
169};
170
171template<typename T>
172class SvCompatWeakBase;
173
174/** SvCompatWeakHdl acts as an intermediary between SvCompatWeakRef<T> and T.
175*/
176template<typename T>
177class SvCompatWeakHdl final : public SvRefBase
178{
179 friend class SvCompatWeakBase<T>;
180 T* _pObj;
181
182 SvCompatWeakHdl( T* pObj ) : _pObj( pObj ) {}
183
184public:
185 void ResetWeakBase( ) { _pObj = nullptr; }
186 T* GetObj() { return _pObj; }
187};
188
189/** We only have one place that extends this, in include/sfx2/frame.hxx, class SfxFrame.
190 Its function is to notify the SvCompatWeakHdl when an SfxFrame object is deleted.
191*/
192template<typename T>
193class SvCompatWeakBase
194{
195 tools::SvRef< SvCompatWeakHdl<T> > _xHdl;
196
197public:
198 /** Does not use initializer due to compiler warnings,
199 because the lifetime of the _xHdl object can exceed the lifetime of this class.
200 */
201 SvCompatWeakBase( T* pObj ) { _xHdl = new SvCompatWeakHdl<T>( pObj ); }
202
203 ~SvCompatWeakBase() { _xHdl->ResetWeakBase(); }
204
205 SvCompatWeakHdl<T>* GetHdl() { return _xHdl.get(); }
206};
207
208/** We only have one weak reference in LO, in include/sfx2/frame.hxx, class SfxFrameWeak.
209*/
210template<typename T>
211class SAL_WARN_UNUSED__attribute__((warn_unused)) SvCompatWeakRef
212{
213 tools::SvRef< SvCompatWeakHdl<T> > _xHdl;
214public:
215 SvCompatWeakRef( ) {}
216 SvCompatWeakRef( T* pObj )
217 { if( pObj ) _xHdl = pObj->GetHdl(); }
218#if defined(__COVERITY__)
219 ~SvCompatWeakRef() COVERITY_NOEXCEPT_FALSE {}
220#endif
221 SvCompatWeakRef& operator = ( T * pObj )
222 { _xHdl = pObj ? pObj->GetHdl() : nullptr; return *this; }
223 bool is() const
224 { return _xHdl.is() && _xHdl->GetObj(); }
225 explicit operator bool() const { return is(); }
226 T* operator -> () const
227 { return _xHdl.is() ? _xHdl->GetObj() : nullptr; }
228 operator T* () const
229 { return _xHdl.is() ? _xHdl->GetObj() : nullptr; }
230};
231
232#endif
233
234/* vim:set shiftwidth=4 softtabstop=4 expandtab: */