Coverage Report

Created: 2026-06-30 11:14

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