Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/unoobj/chart2uno.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 <memory>
21
#include <sal/config.h>
22
#include <sal/log.hxx>
23
24
#include <algorithm>
25
#include <utility>
26
27
#include <chart2uno.hxx>
28
#include <miscuno.hxx>
29
#include <document.hxx>
30
#include <docsh.hxx>
31
#include <formulacell.hxx>
32
#include <unonames.hxx>
33
#include <globstr.hrc>
34
#include <scresid.hxx>
35
#include <rangeutl.hxx>
36
#include <hints.hxx>
37
#include <unoreflist.hxx>
38
#include <compiler.hxx>
39
#include <reftokenhelper.hxx>
40
#include <chartlis.hxx>
41
#include <tokenuno.hxx>
42
#include <cellvalue.hxx>
43
#include <tokenarray.hxx>
44
#include <scmatrix.hxx>
45
#include <brdcst.hxx>
46
#include <mtvelements.hxx>
47
48
#include <formula/opcode.hxx>
49
#include <o3tl/safeint.hxx>
50
#include <svl/numformat.hxx>
51
#include <svl/sharedstring.hxx>
52
53
#include <vcl/svapp.hxx>
54
55
#include <com/sun/star/beans/UnknownPropertyException.hpp>
56
#include <com/sun/star/chart/ChartDataRowSource.hpp>
57
#include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
58
#include <com/sun/star/frame/XModel.hpp>
59
#include <comphelper/extract.hxx>
60
#include <comphelper/processfactory.hxx>
61
#include <comphelper/sequence.hxx>
62
63
#include <limits>
64
65
SC_SIMPLE_SERVICE_INFO( ScChart2DataProvider, u"ScChart2DataProvider"_ustr,
66
        u"com.sun.star.chart2.data.DataProvider"_ustr)
67
SC_SIMPLE_SERVICE_INFO( ScChart2DataSource, u"ScChart2DataSource"_ustr,
68
        u"com.sun.star.chart2.data.DataSource"_ustr)
69
SC_SIMPLE_SERVICE_INFO( ScChart2DataSequence, u"ScChart2DataSequence"_ustr,
70
        u"com.sun.star.chart2.data.DataSequence"_ustr)
71
72
using namespace ::com::sun::star;
73
using namespace ::formula;
74
75
namespace
76
{
77
std::span<const SfxItemPropertyMapEntry> lcl_GetDataProviderPropertyMap()
78
0
{
79
0
    static const SfxItemPropertyMapEntry aDataProviderPropertyMap_Impl[] =
80
0
    {
81
0
        { SC_UNONAME_INCLUDEHIDDENCELLS, 0, cppu::UnoType<bool>::get(), 0, 0 },
82
0
        { SC_UNONAME_USE_INTERNAL_DATA_PROVIDER, 0, cppu::UnoType<bool>::get(), 0, 0 },
83
0
    };
84
0
    return aDataProviderPropertyMap_Impl;
85
0
}
86
87
std::span<const SfxItemPropertyMapEntry> lcl_GetDataSequencePropertyMap()
88
0
{
89
0
    static const SfxItemPropertyMapEntry aDataSequencePropertyMap_Impl[] =
90
0
    {
91
0
        { SC_UNONAME_HIDDENVALUES, 0, cppu::UnoType<uno::Sequence<sal_Int32>>::get(),                 0, 0 },
92
0
        { SC_UNONAME_ROLE, 0, cppu::UnoType<css::chart2::data::DataSequenceRole>::get(),                  0, 0 },
93
0
        { SC_UNONAME_INCLUDEHIDDENCELLS, 0,        cppu::UnoType<bool>::get(),                  0, 0 },
94
0
    };
95
0
    return aDataSequencePropertyMap_Impl;
96
0
}
97
98
struct lcl_appendTableNumber
99
{
100
    explicit lcl_appendTableNumber( OUStringBuffer & rBuffer ) :
101
0
            m_rBuffer( rBuffer )
102
0
    {}
103
    void operator() ( SCTAB nTab )
104
0
    {
105
        // there is no append with SCTAB or sal_Int16
106
0
        m_rBuffer.append( static_cast< sal_Int32 >( nTab ));
107
0
        m_rBuffer.append( ' ' );
108
0
    }
109
private:
110
    OUStringBuffer & m_rBuffer;
111
};
112
113
OUString lcl_createTableNumberList(const std::vector< SCTAB > & rTableVector )
114
0
{
115
0
    OUStringBuffer aBuffer;
116
0
    std::for_each( rTableVector.begin(), rTableVector.end(), lcl_appendTableNumber( aBuffer ));
117
    // remove last trailing ' '
118
0
    if( !aBuffer.isEmpty() )
119
0
        aBuffer.setLength( aBuffer.getLength() - 1 );
120
0
    return aBuffer.makeStringAndClear();
121
0
}
122
123
uno::Reference< frame::XModel > lcl_GetXModel( const ScDocument * pDoc )
124
0
{
125
0
    uno::Reference< frame::XModel > xModel;
126
0
    ScDocShell * pObjSh( pDoc ? pDoc->GetDocumentShell() : nullptr );
127
0
    if( pObjSh )
128
0
        xModel.set( pObjSh->GetModel());
129
0
    return xModel;
130
0
}
131
132
struct TokenTable
133
{
134
    SCROW mnRowCount;
135
    SCCOL mnColCount;
136
    std::vector<std::unique_ptr<FormulaToken>> maTokens;
137
138
    // noncopyable
139
    TokenTable(const TokenTable&) = delete;
140
    const TokenTable& operator=(const TokenTable&) = delete;
141
142
    TokenTable()
143
0
        : mnRowCount(0)
144
0
        , mnColCount(0)
145
0
    {
146
0
    }
147
148
    void init( SCCOL nColCount, SCROW nRowCount )
149
0
    {
150
0
        mnColCount = nColCount;
151
0
        mnRowCount = nRowCount;
152
0
        maTokens.reserve(mnColCount*mnRowCount);
153
0
    }
154
    void clear()
155
0
    {
156
0
        for (auto & rToken : maTokens)
157
0
            rToken.reset();
158
0
    }
159
160
    void push_back( std::unique_ptr<FormulaToken> pToken )
161
0
    {
162
0
        maTokens.push_back( std::move(pToken) );
163
0
        OSL_ENSURE( maTokens.size()<= o3tl::make_unsigned( mnColCount*mnRowCount ), "too many tokens" );
164
0
    }
165
166
    sal_uInt32 getIndex(SCCOL nCol, SCROW nRow) const
167
0
    {
168
0
        OSL_ENSURE( nCol<mnColCount, "wrong column index" );
169
0
        OSL_ENSURE( nRow<mnRowCount, "wrong row index" );
170
0
        sal_uInt32 nRet = static_cast<sal_uInt32>(nCol*mnRowCount + nRow);
171
0
        OSL_ENSURE( maTokens.size()>= o3tl::make_unsigned( mnColCount*mnRowCount ), "too few tokens" );
172
0
        return nRet;
173
0
    }
174
175
    std::vector<ScTokenRef> getColRanges(const ScDocument* pDoc, SCCOL nCol) const;
176
    std::vector<ScTokenRef> getRowRanges(const ScDocument* pDoc, SCROW nRow) const;
177
    std::vector<ScTokenRef> getAllRanges(const ScDocument* pDoc) const;
178
};
179
180
std::vector<ScTokenRef> TokenTable::getColRanges(const ScDocument* pDoc, SCCOL nCol) const
181
0
{
182
0
    if (nCol >= mnColCount)
183
0
        return std::vector<ScTokenRef>();
184
0
    if( mnRowCount<=0 )
185
0
        return std::vector<ScTokenRef>();
186
187
0
    std::vector<ScTokenRef> aTokens;
188
0
    sal_uInt32 nLast = getIndex(nCol, mnRowCount-1);
189
0
    for (sal_uInt32 i = getIndex(nCol, 0); i <= nLast; ++i)
190
0
    {
191
0
        FormulaToken* p = maTokens[i].get();
192
0
        if (!p)
193
0
            continue;
194
195
0
        ScTokenRef pCopy(p->Clone());
196
0
        ScRefTokenHelper::join(pDoc, aTokens, pCopy, ScAddress());
197
0
    }
198
0
    return aTokens;
199
0
}
200
201
std::vector<ScTokenRef> TokenTable::getRowRanges(const ScDocument* pDoc, SCROW nRow) const
202
0
{
203
0
    if (nRow >= mnRowCount)
204
0
        return std::vector<ScTokenRef>();
205
0
    if( mnColCount<=0 )
206
0
        return std::vector<ScTokenRef>();
207
208
0
    std::vector<ScTokenRef> aTokens;
209
0
    sal_uInt32 nLast = getIndex(mnColCount-1, nRow);
210
0
    for (sal_uInt32 i = getIndex(0, nRow); i <= nLast; i += mnRowCount)
211
0
    {
212
0
        FormulaToken* p = maTokens[i].get();
213
0
        if (!p)
214
0
            continue;
215
216
0
        ScTokenRef p2(p->Clone());
217
0
        ScRefTokenHelper::join(pDoc, aTokens, p2, ScAddress());
218
0
    }
219
0
    return aTokens;
220
0
}
221
222
std::vector<ScTokenRef> TokenTable::getAllRanges(const ScDocument* pDoc) const
223
0
{
224
0
    std::vector<ScTokenRef> aTokens;
225
0
    sal_uInt32 nStop = mnColCount*mnRowCount;
226
0
    for (sal_uInt32 i = 0; i < nStop; i++)
227
0
    {
228
0
        FormulaToken* p = maTokens[i].get();
229
0
        if (!p)
230
0
            continue;
231
232
0
        ScTokenRef p2(p->Clone());
233
0
        ScRefTokenHelper::join(pDoc, aTokens, p2, ScAddress());
234
0
    }
235
0
    return aTokens;
236
0
}
237
238
typedef std::map<SCROW, std::unique_ptr<FormulaToken>> FormulaTokenMap;
239
typedef std::map<sal_uInt32, FormulaTokenMap> FormulaTokenMapMap;
240
241
class Chart2PositionMap
242
{
243
public:
244
    Chart2PositionMap(SCCOL nColCount, SCROW nRowCount,
245
                      bool bFillRowHeader, bool bFillColumnHeader, FormulaTokenMapMap& rCols,
246
                      ScDocument* pDoc );
247
    ~Chart2PositionMap();
248
249
0
    SCCOL getDataColCount() const { return mnDataColCount; }
250
0
    SCROW getDataRowCount() const { return mnDataRowCount; }
251
252
    std::vector<ScTokenRef> getLeftUpperCornerRanges() const;
253
    std::vector<ScTokenRef> getAllColHeaderRanges() const;
254
    std::vector<ScTokenRef> getAllRowHeaderRanges() const;
255
256
    std::vector<ScTokenRef> getColHeaderRanges(SCCOL nChartCol) const;
257
    std::vector<ScTokenRef> getRowHeaderRanges(SCROW nChartRow) const;
258
259
    std::vector<ScTokenRef> getDataColRanges(SCCOL nCol) const;
260
    std::vector<ScTokenRef> getDataRowRanges(SCROW nRow) const;
261
262
private:
263
    const ScDocument* mpDoc;
264
    SCCOL mnDataColCount;
265
    SCROW mnDataRowCount;
266
267
    TokenTable maLeftUpperCorner; //nHeaderColCount*nHeaderRowCount
268
    TokenTable maColHeaders; //mnDataColCount*nHeaderRowCount
269
    TokenTable maRowHeaders; //nHeaderColCount*mnDataRowCount
270
    TokenTable maData;//mnDataColCount*mnDataRowCount
271
};
272
273
Chart2PositionMap::Chart2PositionMap(SCCOL nAllColCount,  SCROW nAllRowCount,
274
                                     bool bFillRowHeader, bool bFillColumnHeader, FormulaTokenMapMap& rCols, ScDocument* pDoc)
275
0
{
276
0
    mpDoc = pDoc;
277
    // if bFillRowHeader is true, at least the first column serves as a row header.
278
    //  If more than one column is pure text all the first pure text columns are used as header.
279
    // Likewise, if bFillColumnHeader is true, at least the first row serves as a column header.
280
    //  If more than one row is pure text all the first pure text rows are used as header.
281
282
0
    SCROW nHeaderRowCount = (bFillColumnHeader && nAllColCount && nAllRowCount) ? 1 : 0;
283
0
    SCCOL nHeaderColCount = (bFillRowHeader && nAllColCount && nAllRowCount) ? 1 : 0;
284
285
0
    if( pDoc && (nHeaderColCount || nHeaderRowCount ) )
286
0
    {
287
        //check whether there is more than one text column or row that should be added to the headers
288
0
        SCROW nMaxHeaderRow = nAllRowCount;
289
0
        SCCOL nCol = 0;
290
0
        for (auto it = rCols.begin(); it != rCols.end(); ++it, ++nCol)
291
0
        {
292
            // Skip header columns
293
0
            if (nCol < nHeaderColCount)
294
0
                continue;
295
296
0
            const auto& rCol = *it;
297
298
0
            bool bFoundValuesInCol = false;
299
0
            bool bFoundAnythingInCol = false;
300
0
            SCROW nRow = 0;
301
0
            for (auto it2 = rCol.second.begin(); it2 != rCol.second.end(); ++it2, ++nRow)
302
0
            {
303
0
                const auto& rCell = *it2;
304
305
                // Skip header rows
306
0
                if (nRow < nHeaderRowCount || !rCell.second)
307
0
                    continue;
308
309
0
                ScRange aRange;
310
0
                bool bExternal = false;
311
0
                StackVar eType = rCell.second->GetType();
312
0
                if( eType==svExternal || eType==svExternalSingleRef || eType==svExternalDoubleRef || eType==svExternalName )
313
0
                    bExternal = true;//lllll todo correct?
314
0
                ScTokenRef pSharedToken(rCell.second->Clone());
315
0
                ScRefTokenHelper::getRangeFromToken(pDoc, aRange, pSharedToken, ScAddress(), bExternal);
316
0
                SCCOL nCol1=0, nCol2=0;
317
0
                SCROW nRow1=0, nRow2=0;
318
0
                SCTAB nTab1=0, nTab2=0;
319
0
                aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
320
0
                if ( pDoc->HasValueData( nCol1, nRow1, nTab1 ) )
321
0
                {
322
                    // Found some numeric data
323
0
                    bFoundValuesInCol = true;
324
0
                    nMaxHeaderRow = std::min(nMaxHeaderRow, nRow);
325
0
                    break;
326
0
                }
327
0
                if ( pDoc->HasData( nCol1, nRow1, nTab1 ) )
328
0
                {
329
                    // Found some other data (non-numeric)
330
0
                    bFoundAnythingInCol = true;
331
0
                }
332
0
                else
333
0
                {
334
                    // If cell is empty, it belongs to data
335
0
                    nMaxHeaderRow = std::min(nMaxHeaderRow, nRow);
336
0
                }
337
0
            }
338
339
0
            if (nHeaderColCount && !bFoundValuesInCol && bFoundAnythingInCol && nCol == nHeaderColCount)
340
0
            {
341
                // There is no values in row, but some data. And this column is next to header
342
                // So let's put it to header
343
0
                nHeaderColCount++;
344
0
            }
345
0
        }
346
347
0
        if (nHeaderRowCount)
348
0
        {
349
0
            nHeaderRowCount = nMaxHeaderRow;
350
0
        }
351
0
    }
352
353
0
    mnDataColCount = nAllColCount - nHeaderColCount;
354
0
    mnDataRowCount = nAllRowCount - nHeaderRowCount;
355
356
0
    maLeftUpperCorner.init(nHeaderColCount,nHeaderRowCount);
357
0
    maColHeaders.init(mnDataColCount,nHeaderRowCount);
358
0
    maRowHeaders.init(nHeaderColCount,mnDataRowCount);
359
0
    maData.init(mnDataColCount,mnDataRowCount);
360
361
0
    FormulaTokenMapMap::iterator it1 = rCols.begin();
362
0
    for (SCCOL nCol = 0; nCol < nAllColCount; ++nCol)
363
0
    {
364
0
        if (it1 != rCols.end())
365
0
        {
366
0
            FormulaTokenMap& rCol = it1->second;
367
0
            FormulaTokenMap::iterator it2 = rCol.begin();
368
0
            for (SCROW nRow = 0; nRow < nAllRowCount; ++nRow)
369
0
            {
370
0
                std::unique_ptr<FormulaToken> pToken;
371
0
                if (it2 != rCol.end())
372
0
                {
373
0
                    pToken = std::move(it2->second);
374
0
                    ++it2;
375
0
                }
376
377
0
                if( nCol < nHeaderColCount )
378
0
                {
379
0
                    if( nRow < nHeaderRowCount )
380
0
                        maLeftUpperCorner.push_back(std::move(pToken));
381
0
                    else
382
0
                        maRowHeaders.push_back(std::move(pToken));
383
0
                }
384
0
                else if( nRow < nHeaderRowCount )
385
0
                    maColHeaders.push_back(std::move(pToken));
386
0
                else
387
0
                    maData.push_back(std::move(pToken));
388
0
            }
389
0
            ++it1;
390
0
        }
391
0
    }
392
0
}
393
394
Chart2PositionMap::~Chart2PositionMap()
395
0
{
396
0
    maLeftUpperCorner.clear();
397
0
    maColHeaders.clear();
398
0
    maRowHeaders.clear();
399
0
    maData.clear();
400
0
}
401
402
std::vector<ScTokenRef> Chart2PositionMap::getLeftUpperCornerRanges() const
403
0
{
404
0
    return maLeftUpperCorner.getAllRanges(mpDoc);
405
0
}
406
407
std::vector<ScTokenRef> Chart2PositionMap::getAllColHeaderRanges() const
408
0
{
409
0
    return maColHeaders.getAllRanges(mpDoc);
410
0
}
411
412
std::vector<ScTokenRef> Chart2PositionMap::getAllRowHeaderRanges() const
413
0
{
414
0
    return maRowHeaders.getAllRanges(mpDoc);
415
0
}
416
417
std::vector<ScTokenRef> Chart2PositionMap::getColHeaderRanges(SCCOL nCol) const
418
0
{
419
0
    return maColHeaders.getColRanges(mpDoc, nCol);
420
0
}
421
422
std::vector<ScTokenRef> Chart2PositionMap::getRowHeaderRanges(SCROW nRow) const
423
0
{
424
0
    return maRowHeaders.getRowRanges(mpDoc, nRow);
425
0
}
426
427
std::vector<ScTokenRef> Chart2PositionMap::getDataColRanges(SCCOL nCol) const
428
0
{
429
0
    return maData.getColRanges(mpDoc, nCol);
430
0
}
431
432
std::vector<ScTokenRef> Chart2PositionMap::getDataRowRanges(SCROW nRow) const
433
0
{
434
0
    return maData.getRowRanges(mpDoc, nRow);
435
0
}
436
437
/**
438
 * Designed to be a drop-in replacement for ScChartPositioner, in order to
439
 * handle external references.
440
 */
441
class Chart2Positioner
442
{
443
    enum GlueType
444
    {
445
        GLUETYPE_NA,
446
        GLUETYPE_NONE,
447
        GLUETYPE_COLS,
448
        GLUETYPE_ROWS,
449
        GLUETYPE_BOTH
450
    };
451
452
public:
453
    Chart2Positioner(const Chart2Positioner&) = delete;
454
    const Chart2Positioner& operator=(const Chart2Positioner&) = delete;
455
456
    Chart2Positioner(ScDocument* pDoc, const std::vector<ScTokenRef>& rRefTokens) :
457
0
        mrRefTokens(rRefTokens),
458
0
        meGlue(GLUETYPE_NA),
459
0
        mnStartCol(0),
460
0
        mnStartRow(0),
461
0
        mpDoc(pDoc),
462
0
        mbColHeaders(false),
463
0
        mbRowHeaders(false),
464
0
        mbDummyUpperLeft(false)
465
0
    {
466
0
    }
467
468
    void setHeaders(bool bColHeaders, bool bRowHeaders)
469
0
    {
470
0
        mbColHeaders = bColHeaders;
471
0
        mbRowHeaders = bRowHeaders;
472
0
    }
473
474
    Chart2PositionMap* getPositionMap()
475
0
    {
476
0
        createPositionMap();
477
0
        return mpPositionMap.get();
478
0
    }
479
480
private:
481
    void invalidateGlue();
482
    void glueState();
483
    void calcGlueState(SCCOL nCols, SCROW nRows);
484
    void createPositionMap();
485
486
private:
487
    const std::vector<ScTokenRef>& mrRefTokens;
488
    std::unique_ptr<Chart2PositionMap> mpPositionMap;
489
    GlueType    meGlue;
490
    SCCOL       mnStartCol;
491
    SCROW       mnStartRow;
492
    ScDocument* mpDoc;
493
    bool mbColHeaders:1;
494
    bool mbRowHeaders:1;
495
    bool mbDummyUpperLeft:1;
496
};
497
498
void Chart2Positioner::invalidateGlue()
499
0
{
500
0
    meGlue = GLUETYPE_NA;
501
0
    mpPositionMap.reset();
502
0
}
503
504
void Chart2Positioner::glueState()
505
0
{
506
0
    if (meGlue != GLUETYPE_NA)
507
0
        return;
508
509
0
    mbDummyUpperLeft = false;
510
0
    if (mrRefTokens.size() <= 1)
511
0
    {
512
        // Source data consists of only one data range.
513
0
        const ScTokenRef& p = mrRefTokens.front();
514
0
        ScComplexRefData aData;
515
0
        if (ScRefTokenHelper::getDoubleRefDataFromToken(aData, p))
516
0
        {
517
0
            if (aData.Ref1.Tab() == aData.Ref2.Tab())
518
0
                meGlue = GLUETYPE_NONE;
519
0
            else
520
0
                meGlue = GLUETYPE_COLS;
521
0
            mnStartCol = aData.Ref1.Col();
522
0
            mnStartRow = aData.Ref1.Row();
523
0
        }
524
0
        else
525
0
        {
526
0
            invalidateGlue();
527
0
            mnStartCol = 0;
528
0
            mnStartRow = 0;
529
0
        }
530
0
        return;
531
0
    }
532
533
0
    ScComplexRefData aData;
534
0
    if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData, mrRefTokens.front()))
535
0
    {
536
0
        SAL_WARN("sc", "Chart2Positioner::glueState getDoubleRefDataFromToken failed");
537
0
        invalidateGlue();
538
0
        mnStartCol = 0;
539
0
        mnStartRow = 0;
540
0
        return;
541
0
    }
542
0
    mnStartCol = aData.Ref1.Col();
543
0
    mnStartRow = aData.Ref1.Row();
544
545
0
    SCCOL nEndCol = 0;
546
0
    SCROW nEndRow = 0;
547
0
    for (const auto& rxToken : mrRefTokens)
548
0
    {
549
0
        ScRefTokenHelper::getDoubleRefDataFromToken(aData, rxToken);
550
0
        SCCOLROW n1 = aData.Ref1.Col();
551
0
        SCCOLROW n2 = aData.Ref2.Col();
552
0
        if (n1 > mpDoc->MaxCol())
553
0
            n1 = mpDoc->MaxCol();
554
0
        if (n2 > mpDoc->MaxCol())
555
0
            n2 = mpDoc->MaxCol();
556
0
        if (n1 < mnStartCol)
557
0
            mnStartCol = static_cast<SCCOL>(n1);
558
0
        if (n2 > nEndCol)
559
0
            nEndCol = static_cast<SCCOL>(n2);
560
561
0
        n1 = aData.Ref1.Row();
562
0
        n2 = aData.Ref2.Row();
563
0
        if (n1 > mpDoc->MaxRow())
564
0
            n1 = mpDoc->MaxRow();
565
0
        if (n2 > mpDoc->MaxRow())
566
0
            n2 = mpDoc->MaxRow();
567
568
0
        if (n1 < mnStartRow)
569
0
            mnStartRow = static_cast<SCROW>(n1);
570
0
        if (n2 > nEndRow)
571
0
            nEndRow = static_cast<SCROW>(n2);
572
0
    }
573
574
0
    if (mnStartCol == nEndCol)
575
0
    {
576
        // All source data is in a single column.
577
0
        meGlue = GLUETYPE_ROWS;
578
0
        return;
579
0
    }
580
581
0
    if (mnStartRow == nEndRow)
582
0
    {
583
        // All source data is in a single row.
584
0
        meGlue = GLUETYPE_COLS;
585
0
        return;
586
0
    }
587
588
    // total column size
589
0
    SCCOL nC = nEndCol - mnStartCol + 1;
590
591
    // total row size
592
0
    SCROW nR = nEndRow - mnStartRow + 1;
593
594
    // #i103540# prevent invalid vector size
595
0
    if ((nC <= 0) || (nR <= 0))
596
0
    {
597
0
        invalidateGlue();
598
0
        mnStartCol = 0;
599
0
        mnStartRow = 0;
600
0
        return;
601
0
    }
602
603
0
    calcGlueState(nC, nR);
604
0
}
605
606
enum State { Hole = 0, Occupied = 1, Free = 2, Glue = 3 };
607
608
void Chart2Positioner::calcGlueState(SCCOL nColSize, SCROW nRowSize)
609
0
{
610
    // TODO: This code can use some space optimization.  Using an array to
611
    // store individual cell's states is terribly inefficient esp for large
612
    // data ranges; let's use flat_segment_tree to reduce memory usage here.
613
614
0
    sal_uInt32 nCR = static_cast<sal_uInt32>(nColSize*nRowSize);
615
616
0
    std::vector<State> aCellStates(nCR, Hole);
617
618
    // Mark all referenced cells "occupied".
619
0
    for (const auto& rxToken : mrRefTokens)
620
0
    {
621
0
        ScComplexRefData aData;
622
0
        ScRefTokenHelper::getDoubleRefDataFromToken(aData, rxToken);
623
0
        auto nCol1 = aData.Ref1.Col() - mnStartCol;
624
0
        auto nCol2 = aData.Ref2.Col() - mnStartCol;
625
0
        SCROW nRow1 = aData.Ref1.Row() - mnStartRow;
626
0
        SCROW nRow2 = aData.Ref2.Row() - mnStartRow;
627
0
        for (SCCOLROW nCol = nCol1; nCol <= nCol2 && nCol >= 0; ++nCol)
628
0
            for (SCCOLROW nRow = nRow1; nRow <= nRow2 && nRow >= 0; ++nRow)
629
0
            {
630
0
                size_t i = nCol*nRowSize + nRow;
631
0
                aCellStates[i] = Occupied;
632
0
            }
633
0
    }
634
635
    // If at least one cell in either the first column or first row is empty,
636
    // we don't glue at all unless the whole column or row is empty; we expect
637
    // all cells in the first column / row to be fully populated.  If we have
638
    // empty column or row, then we do glue by the column or row,
639
    // respectively.
640
641
0
    bool bGlue = true;
642
0
    bool bGlueCols = false;
643
0
    for (auto nCol = 0; bGlue && nCol < nColSize; ++nCol)
644
0
    {
645
0
        for (SCROW nRow = 0; bGlue && nRow < nRowSize; ++nRow)
646
0
        {
647
0
            size_t i = nCol*nRowSize + nRow;
648
0
            if (aCellStates[i] == Occupied)
649
0
            {
650
0
                if (nCol == 0 || nRow == 0)
651
0
                    break;
652
653
0
                bGlue = false;
654
0
            }
655
0
            else
656
0
                aCellStates[i] = Free;
657
0
        }
658
0
        size_t nLast = (nCol+1)*nRowSize - 1; // index for the last cell in the column.
659
0
        if (bGlue && aCellStates[nLast] == Free)
660
0
        {
661
            // Whole column is empty.
662
0
            aCellStates[nLast] = Glue;
663
0
            bGlueCols = true;
664
0
        }
665
0
    }
666
667
0
    bool bGlueRows = false;
668
0
    for (SCROW nRow = 0; bGlue && nRow < nRowSize; ++nRow)
669
0
    {
670
0
        size_t i = nRow;
671
0
        for (SCCOL nCol = 0; bGlue && nCol < nColSize; ++nCol, i += nRowSize)
672
0
        {
673
0
            if (aCellStates[i] == Occupied)
674
0
            {
675
0
                if (nCol == 0 || nRow == 0)
676
0
                    break;
677
678
0
                bGlue = false;
679
0
            }
680
0
            else
681
0
                aCellStates[i] = Free;
682
0
        }
683
0
        i = (nColSize-1)*nRowSize + nRow; // index for the row position in the last column.
684
0
        if (bGlue && aCellStates[i] == Free)
685
0
        {
686
            // Whole row is empty.
687
0
            aCellStates[i] = Glue;
688
0
            bGlueRows = true;
689
0
        }
690
0
    }
691
692
0
    size_t i = 1;
693
0
    for (sal_uInt32 n = 1; bGlue && n < nCR; ++n, ++i)
694
0
        if (aCellStates[i] == Hole)
695
0
            bGlue = false;
696
697
0
    if (bGlue)
698
0
    {
699
0
        if (bGlueCols && bGlueRows)
700
0
            meGlue = GLUETYPE_BOTH;
701
0
        else if (bGlueRows)
702
0
            meGlue = GLUETYPE_ROWS;
703
0
        else
704
0
            meGlue = GLUETYPE_COLS;
705
0
        if (aCellStates.front() != Occupied)
706
0
            mbDummyUpperLeft = true;
707
0
    }
708
0
    else
709
0
        meGlue = GLUETYPE_NONE;
710
0
}
711
712
void Chart2Positioner::createPositionMap()
713
0
{
714
0
    if (meGlue == GLUETYPE_NA && mpPositionMap)
715
0
        mpPositionMap.reset();
716
717
0
    if (mpPositionMap)
718
0
        return;
719
720
0
    glueState();
721
722
0
    bool bNoGlue = (meGlue == GLUETYPE_NONE);
723
0
    FormulaTokenMapMap aCols;
724
0
    SCROW nNoGlueRow = 0;
725
0
    for (const ScTokenRef& pToken : mrRefTokens)
726
0
    {
727
0
        bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
728
0
        sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
729
0
        svl::SharedString aTabName = svl::SharedString::getEmptyString();
730
0
        if (bExternal)
731
0
            aTabName = pToken->GetString();
732
733
0
        ScComplexRefData aData;
734
0
        if( !ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken) )
735
0
            break;
736
0
        const ScSingleRefData& s = aData.Ref1;
737
0
        const ScSingleRefData& e = aData.Ref2;
738
0
        SCCOL nCol1 = s.Col(), nCol2 = e.Col();
739
0
        SCROW nRow1 = s.Row(), nRow2 = e.Row();
740
0
        SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
741
742
0
        for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab)
743
0
        {
744
0
            assert (nCol1 >= 0);
745
0
            sal_uInt32 nInsCol = bNoGlue ? 0 : static_cast<sal_uInt32>(nCol1) & 0xFFFF;
746
747
0
            for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol, ++nInsCol)
748
0
            {
749
                // columns on secondary sheets are appended; we treat them as if
750
                // all columns are on the same sheet.  TODO: We can't assume that
751
                // the column range is 16-bit; remove that restriction.
752
0
                sal_uInt32 nInsKey = (static_cast<sal_uInt32>(nTab) << 16) | nInsCol;
753
0
                FormulaTokenMap& rCol = aCols[nInsKey];
754
755
0
                auto nInsRow = bNoGlue ? nNoGlueRow : nRow1;
756
0
                for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow, ++nInsRow)
757
0
                {
758
0
                    ScSingleRefData aCellData;
759
0
                    aCellData.InitFlags();
760
0
                    aCellData.SetFlag3D(true);
761
0
                    aCellData.SetColRel(false);
762
0
                    aCellData.SetRowRel(false);
763
0
                    aCellData.SetTabRel(false);
764
0
                    aCellData.SetAbsCol(nCol);
765
0
                    aCellData.SetAbsRow(nRow);
766
0
                    aCellData.SetAbsTab(nTab);
767
768
0
                    auto& rCell = rCol[nInsRow];
769
0
                    if (!rCell)
770
0
                    {
771
0
                        if (bExternal)
772
0
                            rCell.reset(new ScExternalSingleRefToken(nFileId, aTabName, aCellData));
773
0
                        else
774
0
                            rCell.reset(new ScSingleRefToken(mpDoc->GetSheetLimits(), aCellData));
775
0
                    }
776
0
                }
777
0
            }
778
0
        }
779
0
        nNoGlueRow += nRow2 - nRow1 + 1;
780
0
    }
781
782
0
    bool bFillRowHeader = mbRowHeaders;
783
0
    bool bFillColumnHeader = mbColHeaders;
784
785
0
    SCSIZE nAllColCount = static_cast<SCSIZE>(aCols.size());
786
0
    SCSIZE nAllRowCount = 0;
787
0
    if (!aCols.empty())
788
0
    {
789
0
        FormulaTokenMap& rCol = aCols.begin()->second;
790
0
        if (mbDummyUpperLeft)
791
0
            rCol.try_emplace( 0, nullptr );        // dummy for labeling
792
0
        nAllRowCount = static_cast<SCSIZE>(rCol.size());
793
0
    }
794
795
0
    if( nAllColCount!=0 && nAllRowCount!=0 )
796
0
    {
797
0
        if (bNoGlue)
798
0
        {
799
0
            FormulaTokenMap& rFirstCol = aCols.begin()->second;
800
0
            for (const auto& rFirstColEntry : rFirstCol)
801
0
            {
802
0
                SCROW nKey = rFirstColEntry.first;
803
0
                for (auto& rEntry : aCols)
804
0
                {
805
0
                    FormulaTokenMap& rCol = rEntry.second;
806
0
                    rCol.try_emplace( nKey, nullptr );
807
0
                }
808
0
            }
809
0
        }
810
0
    }
811
0
    mpPositionMap.reset(
812
0
        new Chart2PositionMap(
813
0
            static_cast<SCCOL>(nAllColCount), static_cast<SCROW>(nAllRowCount),
814
0
            bFillRowHeader, bFillColumnHeader, aCols, mpDoc));
815
0
}
816
817
/**
818
 * Function object to create a range string from a token list.
819
 */
820
class Tokens2RangeString
821
{
822
public:
823
    Tokens2RangeString(ScDocument& rDoc, FormulaGrammar::Grammar eGram, sal_Unicode cRangeSep) :
824
0
        mpRangeStr(std::make_shared<OUStringBuffer>()),
825
0
        mpDoc(&rDoc),
826
0
        meGrammar(eGram),
827
0
        mcRangeSep(cRangeSep),
828
0
        mbFirst(true)
829
0
    {
830
0
    }
831
832
    void operator() (const ScTokenRef& rToken)
833
0
    {
834
0
        ScCompiler aCompiler(*mpDoc, ScAddress(0,0,0), meGrammar);
835
0
        OUString aStr;
836
0
        aCompiler.CreateStringFromToken(aStr, rToken.get());
837
0
        if (mbFirst)
838
0
            mbFirst = false;
839
0
        else
840
0
            mpRangeStr->append(mcRangeSep);
841
0
        mpRangeStr->append(aStr);
842
0
    }
843
844
    void getString(OUString& rStr)
845
0
    {
846
0
        rStr = mpRangeStr->makeStringAndClear();
847
0
    }
848
849
private:
850
    std::shared_ptr<OUStringBuffer>  mpRangeStr;
851
    ScDocument*         mpDoc;
852
    FormulaGrammar::Grammar  meGrammar;
853
    sal_Unicode         mcRangeSep;
854
    bool                mbFirst;
855
};
856
857
/**
858
 * Function object to convert a list of tokens into a string form suitable
859
 * for ODF export.  In ODF, a range is expressed as
860
 *
861
 *   (start cell address):(end cell address)
862
 *
863
 * and each address doesn't include any '$' symbols.
864
 */
865
class Tokens2RangeStringXML
866
{
867
public:
868
    explicit Tokens2RangeStringXML(ScDocument& rDoc) :
869
0
        mpRangeStr(std::make_shared<OUStringBuffer>()),
870
0
        mpDoc(&rDoc),
871
0
        mbFirst(true)
872
0
    {
873
0
    }
874
875
    void operator() (const ScTokenRef& rToken)
876
0
    {
877
0
        if (mbFirst)
878
0
            mbFirst = false;
879
0
        else
880
0
            mpRangeStr->append(mcRangeSep);
881
882
0
        ScTokenRef aStart, aEnd;
883
0
        bool bValidToken = splitRangeToken(*mpDoc, rToken, aStart, aEnd);
884
        // Check there is a valid reference in named range
885
0
        if (!bValidToken && rToken->GetType() == svIndex && rToken->GetOpCode() == ocName)
886
0
        {
887
0
            ScRangeData* pNameRange = mpDoc->FindRangeNameBySheetAndIndex(rToken->GetSheet(), rToken->GetIndex());
888
0
            if (pNameRange->HasReferences())
889
0
            {
890
0
                const ScTokenRef aTempToken = pNameRange->GetCode()->FirstToken();
891
0
                bValidToken = splitRangeToken(*mpDoc, aTempToken, aStart, aEnd);
892
0
            }
893
0
        }
894
895
0
        OSL_ENSURE(bValidToken, "invalid token");
896
0
        if (!bValidToken)
897
0
            return;
898
899
0
        ScCompiler aCompiler(*mpDoc, ScAddress(0,0,0), FormulaGrammar::GRAM_ENGLISH);
900
0
        {
901
0
            OUString aStr;
902
0
            aCompiler.CreateStringFromToken(aStr, aStart.get());
903
0
            mpRangeStr->append(aStr);
904
0
        }
905
0
        mpRangeStr->append(mcAddrSep);
906
0
        {
907
0
            OUString aStr;
908
0
            aCompiler.CreateStringFromToken(aStr, aEnd.get());
909
0
            mpRangeStr->append(aStr);
910
0
        }
911
0
    }
912
913
    void getString(OUString& rStr)
914
0
    {
915
0
        rStr = mpRangeStr->makeStringAndClear();
916
0
    }
917
918
private:
919
    static bool splitRangeToken(const ScDocument& rDoc, const ScTokenRef& pToken, ScTokenRef& rStart, ScTokenRef& rEnd)
920
0
    {
921
0
        ScComplexRefData aData;
922
0
        bool bIsRefToken = ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken);
923
0
        OSL_ENSURE(bIsRefToken, "invalid token");
924
0
        if (!bIsRefToken)
925
0
            return false;
926
0
        bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
927
0
        sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
928
0
        const svl::SharedString aTabName = bExternal ? pToken->GetString() : svl::SharedString::getEmptyString();
929
930
        // In saving to XML, we don't prepend address with '$'.
931
0
        setRelative(aData.Ref1);
932
0
        setRelative(aData.Ref2);
933
934
        // In XML, the range must explicitly specify sheet name.
935
0
        aData.Ref1.SetFlag3D(true);
936
0
        aData.Ref2.SetFlag3D(true);
937
938
0
        if (bExternal)
939
0
            rStart.reset(new ScExternalSingleRefToken(nFileId, aTabName, aData.Ref1));
940
0
        else
941
0
            rStart.reset(new ScSingleRefToken(rDoc.GetSheetLimits(), aData.Ref1));
942
943
0
        if (bExternal)
944
0
            rEnd.reset(new ScExternalSingleRefToken(nFileId, aTabName, aData.Ref2));
945
0
        else
946
0
            rEnd.reset(new ScSingleRefToken(rDoc.GetSheetLimits(), aData.Ref2));
947
0
        return true;
948
0
    }
949
950
    static void setRelative(ScSingleRefData& rData)
951
0
    {
952
0
        rData.SetColRel(true);
953
0
        rData.SetRowRel(true);
954
0
        rData.SetTabRel(true);
955
0
    }
956
957
private:
958
    std::shared_ptr<OUStringBuffer>  mpRangeStr;
959
    ScDocument*                 mpDoc;
960
    static const sal_Unicode    mcRangeSep = ' ';
961
    static const sal_Unicode    mcAddrSep = ':';
962
    bool                        mbFirst;
963
};
964
965
void lcl_convertTokensToString(OUString& rStr, const std::vector<ScTokenRef>& rTokens, ScDocument& rDoc)
966
0
{
967
0
    const sal_Unicode cRangeSep = ScCompiler::GetNativeSymbolChar(ocSep);
968
0
    FormulaGrammar::Grammar eGrammar = rDoc.GetGrammar();
969
0
    Tokens2RangeString func(rDoc, eGrammar, cRangeSep);
970
0
    func = ::std::for_each(rTokens.begin(), rTokens.end(), func);
971
0
    func.getString(rStr);
972
0
}
973
974
} // anonymous namespace
975
976
// DataProvider ==============================================================
977
978
ScChart2DataProvider::ScChart2DataProvider( ScDocument* pDoc )
979
0
    : m_pDocument( pDoc)
980
0
    , m_aPropSet(lcl_GetDataProviderPropertyMap())
981
0
    , m_bIncludeHiddenCells( true)
982
0
{
983
0
    if ( m_pDocument )
984
0
        m_pDocument->AddUnoObject( *this);
985
0
}
986
987
ScChart2DataProvider::~ScChart2DataProvider()
988
0
{
989
0
    SolarMutexGuard g;
990
991
0
    if ( m_pDocument )
992
0
        m_pDocument->RemoveUnoObject( *this);
993
0
}
994
995
void ScChart2DataProvider::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
996
0
{
997
0
    if ( rHint.GetId() == SfxHintId::Dying )
998
0
    {
999
0
        m_pDocument = nullptr;
1000
0
    }
1001
0
}
1002
1003
sal_Bool SAL_CALL ScChart2DataProvider::createDataSourcePossible( const uno::Sequence< beans::PropertyValue >& aArguments )
1004
0
{
1005
0
    SolarMutexGuard aGuard;
1006
0
    if( ! m_pDocument )
1007
0
        return false;
1008
1009
0
    OUString aRangeRepresentation;
1010
0
    for(const auto& rArgument : aArguments)
1011
0
    {
1012
0
        if ( rArgument.Name == "CellRangeRepresentation" )
1013
0
        {
1014
0
            rArgument.Value >>= aRangeRepresentation;
1015
0
        }
1016
0
    }
1017
1018
0
    std::vector<ScTokenRef> aTokens;
1019
0
    const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
1020
0
    ScRefTokenHelper::compileRangeRepresentation(
1021
0
        aTokens, aRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
1022
0
    return !aTokens.empty();
1023
0
}
1024
1025
namespace
1026
{
1027
1028
uno::Reference< chart2::data::XLabeledDataSequence > lcl_createLabeledDataSequenceFromTokens(
1029
    std::vector< ScTokenRef > && aValueTokens, std::vector< ScTokenRef > && aLabelTokens,
1030
    ScDocument* pDoc, bool bIncludeHiddenCells )
1031
0
{
1032
0
    uno::Reference< chart2::data::XLabeledDataSequence >  xResult;
1033
0
    bool bHasValues = !aValueTokens.empty();
1034
0
    bool bHasLabel = !aLabelTokens.empty();
1035
0
    if( bHasValues || bHasLabel )
1036
0
    {
1037
0
        try
1038
0
        {
1039
0
            const uno::Reference< uno::XComponentContext >& xContext( ::comphelper::getProcessComponentContext() );
1040
0
            if ( xContext.is() )
1041
0
            {
1042
0
                xResult.set( chart2::data::LabeledDataSequence::create(xContext), uno::UNO_QUERY_THROW );
1043
0
            }
1044
0
            if ( bHasValues )
1045
0
            {
1046
0
                uno::Reference< chart2::data::XDataSequence > xSeq( new ScChart2DataSequence( pDoc, std::move(aValueTokens), bIncludeHiddenCells ) );
1047
0
                xResult->setValues( xSeq );
1048
0
            }
1049
0
            if ( bHasLabel )
1050
0
            {
1051
                //Labels should always include hidden cells, regardless of the bIncludeHiddenCells setting
1052
0
                uno::Reference< chart2::data::XDataSequence > xLabelSeq( new ScChart2DataSequence( pDoc, std::move(aLabelTokens), true ) );
1053
0
                xResult->setLabel( xLabelSeq );
1054
0
            }
1055
0
        }
1056
0
        catch( const uno::Exception& )
1057
0
        {
1058
0
        }
1059
0
    }
1060
0
    return xResult;
1061
0
}
1062
1063
/**
1064
 * Check the current list of reference tokens, and add the upper left
1065
 * corner of the minimum range that encloses all ranges if certain
1066
 * conditions are met.
1067
 *
1068
 * @param rRefTokens list of reference tokens
1069
 *
1070
 * @return true if the corner was added, false otherwise.
1071
 */
1072
bool lcl_addUpperLeftCornerIfMissing(const ScDocument* pDoc, std::vector<ScTokenRef>& rRefTokens,
1073
            SCROW nCornerRowCount, SCCOL nCornerColumnCount)
1074
0
{
1075
0
    if (rRefTokens.empty())
1076
0
        return false;
1077
1078
0
    SCCOL nMinCol = pDoc->GetSheetLimits().GetMaxColCount();
1079
0
    SCROW nMinRow = pDoc->GetSheetLimits().GetMaxRowCount();
1080
0
    SCCOL nMaxCol = 0;
1081
0
    SCROW nMaxRow = 0;
1082
0
    SCTAB nTab    = 0;
1083
1084
0
    sal_uInt16 nFileId = 0;
1085
0
    svl::SharedString aExtTabName;
1086
0
    bool bExternal = false;
1087
1088
0
    std::vector<ScTokenRef>::const_iterator itr = rRefTokens.begin(), itrEnd = rRefTokens.end();
1089
1090
    // Get the first ref token.
1091
0
    ScTokenRef pToken = *itr;
1092
0
    switch (pToken->GetType())
1093
0
    {
1094
0
        case svSingleRef:
1095
0
        {
1096
0
            const ScSingleRefData& rData = *pToken->GetSingleRef();
1097
0
            nMinCol = rData.Col();
1098
0
            nMinRow = rData.Row();
1099
0
            nMaxCol = rData.Col();
1100
0
            nMaxRow = rData.Row();
1101
0
            nTab = rData.Tab();
1102
0
        }
1103
0
        break;
1104
0
        case svDoubleRef:
1105
0
        {
1106
0
            const ScComplexRefData& rData = *pToken->GetDoubleRef();
1107
0
            nMinCol = std::min(rData.Ref1.Col(), rData.Ref2.Col());
1108
0
            nMinRow = std::min(rData.Ref1.Row(), rData.Ref2.Row());
1109
0
            nMaxCol = std::max(rData.Ref1.Col(), rData.Ref2.Col());
1110
0
            nMaxRow = std::max(rData.Ref1.Row(), rData.Ref2.Row());
1111
0
            nTab = rData.Ref1.Tab();
1112
0
        }
1113
0
        break;
1114
0
        case svExternalSingleRef:
1115
0
        {
1116
0
            const ScSingleRefData& rData = *pToken->GetSingleRef();
1117
0
            nMinCol = rData.Col();
1118
0
            nMinRow = rData.Row();
1119
0
            nMaxCol = rData.Col();
1120
0
            nMaxRow = rData.Row();
1121
0
            nTab = rData.Tab();
1122
0
            nFileId = pToken->GetIndex();
1123
0
            aExtTabName = pToken->GetString();
1124
0
            bExternal = true;
1125
0
        }
1126
0
        break;
1127
0
        case svExternalDoubleRef:
1128
0
        {
1129
0
            const ScComplexRefData& rData = *pToken->GetDoubleRef();
1130
0
            nMinCol = std::min(rData.Ref1.Col(), rData.Ref2.Col());
1131
0
            nMinRow = std::min(rData.Ref1.Row(), rData.Ref2.Row());
1132
0
            nMaxCol = std::max(rData.Ref1.Col(), rData.Ref2.Col());
1133
0
            nMaxRow = std::max(rData.Ref1.Row(), rData.Ref2.Row());
1134
0
            nTab = rData.Ref1.Tab();
1135
0
            nFileId = pToken->GetIndex();
1136
0
            aExtTabName = pToken->GetString();
1137
0
            bExternal = true;
1138
0
        }
1139
0
        break;
1140
0
        default:
1141
0
            ;
1142
0
    }
1143
1144
    // Determine the minimum range enclosing all data ranges.  Also make sure
1145
    // that they are all on the same table.
1146
1147
0
    for (++itr; itr != itrEnd; ++itr)
1148
0
    {
1149
0
        pToken = *itr;
1150
0
        switch (pToken->GetType())
1151
0
        {
1152
0
            case svSingleRef:
1153
0
            {
1154
0
                const ScSingleRefData& rData = *pToken->GetSingleRef();
1155
1156
0
                nMinCol = std::min(nMinCol, rData.Col());
1157
0
                nMinRow = std::min(nMinRow, rData.Row());
1158
0
                nMaxCol = std::max(nMaxCol, rData.Col());
1159
0
                nMaxRow = std::max(nMaxRow, rData.Row());
1160
0
                if (nTab != rData.Tab() || bExternal)
1161
0
                    return false;
1162
0
            }
1163
0
            break;
1164
0
            case svDoubleRef:
1165
0
            {
1166
0
                const ScComplexRefData& rData = *pToken->GetDoubleRef();
1167
1168
0
                nMinCol = std::min(nMinCol, rData.Ref1.Col());
1169
0
                nMinCol = std::min(nMinCol, rData.Ref2.Col());
1170
0
                nMinRow = std::min(nMinRow, rData.Ref1.Row());
1171
0
                nMinRow = std::min(nMinRow, rData.Ref2.Row());
1172
1173
0
                nMaxCol = std::max(nMaxCol, rData.Ref1.Col());
1174
0
                nMaxCol = std::max(nMaxCol, rData.Ref2.Col());
1175
0
                nMaxRow = std::max(nMaxRow, rData.Ref1.Row());
1176
0
                nMaxRow = std::max(nMaxRow, rData.Ref2.Row());
1177
1178
0
                if (nTab != rData.Ref1.Tab() || bExternal)
1179
0
                    return false;
1180
0
            }
1181
0
            break;
1182
0
            case svExternalSingleRef:
1183
0
            {
1184
0
                if (!bExternal)
1185
0
                    return false;
1186
1187
0
                if (nFileId != pToken->GetIndex() || aExtTabName != pToken->GetString())
1188
0
                    return false;
1189
1190
0
                const ScSingleRefData& rData = *pToken->GetSingleRef();
1191
1192
0
                nMinCol = std::min(nMinCol, rData.Col());
1193
0
                nMinRow = std::min(nMinRow, rData.Row());
1194
0
                nMaxCol = std::max(nMaxCol, rData.Col());
1195
0
                nMaxRow = std::max(nMaxRow, rData.Row());
1196
0
            }
1197
0
            break;
1198
0
            case svExternalDoubleRef:
1199
0
            {
1200
0
                if (!bExternal)
1201
0
                    return false;
1202
1203
0
                if (nFileId != pToken->GetIndex() || aExtTabName != pToken->GetString())
1204
0
                    return false;
1205
1206
0
                const ScComplexRefData& rData = *pToken->GetDoubleRef();
1207
1208
0
                nMinCol = std::min(nMinCol, rData.Ref1.Col());
1209
0
                nMinCol = std::min(nMinCol, rData.Ref2.Col());
1210
0
                nMinRow = std::min(nMinRow, rData.Ref1.Row());
1211
0
                nMinRow = std::min(nMinRow, rData.Ref2.Row());
1212
1213
0
                nMaxCol = std::max(nMaxCol, rData.Ref1.Col());
1214
0
                nMaxCol = std::max(nMaxCol, rData.Ref2.Col());
1215
0
                nMaxRow = std::max(nMaxRow, rData.Ref1.Row());
1216
0
                nMaxRow = std::max(nMaxRow, rData.Ref2.Row());
1217
0
            }
1218
0
            break;
1219
0
            default:
1220
0
                ;
1221
0
        }
1222
0
    }
1223
1224
0
    const auto & rSheetLimits = pDoc->GetSheetLimits();
1225
0
    if (nMinRow >= nMaxRow || nMinCol >= nMaxCol ||
1226
0
        nMinRow >= rSheetLimits.GetMaxRowCount() || nMinCol >= rSheetLimits.GetMaxColCount() ||
1227
0
        nMaxRow >= rSheetLimits.GetMaxRowCount() || nMaxCol >= rSheetLimits.GetMaxColCount())
1228
0
    {
1229
        // Invalid range.  Bail out.
1230
0
        return false;
1231
0
    }
1232
1233
    // Check if the following conditions are met:
1234
1235
    // 1) The upper-left corner cell is not included.
1236
    // 2) The three adjacent cells of that corner cell are included.
1237
1238
0
    bool bRight = false, bBottom = false, bDiagonal = false;
1239
0
    for (const auto& rxToken : rRefTokens)
1240
0
    {
1241
0
        switch (rxToken->GetType())
1242
0
        {
1243
0
            case svSingleRef:
1244
0
            case svExternalSingleRef:
1245
0
            {
1246
0
                const ScSingleRefData& rData = *rxToken->GetSingleRef();
1247
0
                if (rData.Col() == nMinCol && rData.Row() == nMinRow)
1248
                    // The corner cell is contained.
1249
0
                    return false;
1250
1251
0
                if (rData.Col() == nMinCol+nCornerColumnCount && rData.Row() == nMinRow)
1252
0
                    bRight = true;
1253
1254
0
                if (rData.Col() == nMinCol && rData.Row() == nMinRow+nCornerRowCount)
1255
0
                    bBottom = true;
1256
1257
0
                if (rData.Col() == nMinCol+nCornerColumnCount && rData.Row() == nMinRow+nCornerRowCount)
1258
0
                    bDiagonal = true;
1259
0
            }
1260
0
            break;
1261
0
            case svDoubleRef:
1262
0
            case svExternalDoubleRef:
1263
0
            {
1264
0
                const ScComplexRefData& rData = *rxToken->GetDoubleRef();
1265
0
                const ScSingleRefData& r1 = rData.Ref1;
1266
0
                const ScSingleRefData& r2 = rData.Ref2;
1267
0
                if (r1.Col() <= nMinCol && nMinCol <= r2.Col() &&
1268
0
                    r1.Row() <= nMinRow && nMinRow <= r2.Row())
1269
                    // The corner cell is contained.
1270
0
                    return false;
1271
1272
0
                if (r1.Col() <= nMinCol+nCornerColumnCount && nMinCol+nCornerColumnCount <= r2.Col() &&
1273
0
                    r1.Row() <= nMinRow && nMinRow <= r2.Row())
1274
0
                    bRight = true;
1275
1276
0
                if (r1.Col() <= nMinCol && nMinCol <= r2.Col() &&
1277
0
                    r1.Row() <= nMinRow+nCornerRowCount && nMinRow+nCornerRowCount <= r2.Row())
1278
0
                    bBottom = true;
1279
1280
0
                if (r1.Col() <= nMinCol+nCornerColumnCount && nMinCol+nCornerColumnCount <= r2.Col() &&
1281
0
                    r1.Row() <= nMinRow+nCornerRowCount && nMinRow+nCornerRowCount <= r2.Row())
1282
0
                    bDiagonal = true;
1283
0
            }
1284
0
            break;
1285
0
            default:
1286
0
                ;
1287
0
        }
1288
0
    }
1289
1290
0
    if (!bRight || !bBottom || !bDiagonal)
1291
        // Not all the adjacent cells are included.  Bail out.
1292
0
        return false;
1293
1294
0
    ScSingleRefData aData;
1295
0
    aData.InitFlags();
1296
0
    aData.SetFlag3D(true);
1297
0
    aData.SetAbsCol(nMinCol);
1298
0
    aData.SetAbsRow(nMinRow);
1299
0
    aData.SetAbsTab(nTab);
1300
1301
0
    if( nCornerRowCount==1 && nCornerColumnCount==1 )
1302
0
    {
1303
0
        if (bExternal)
1304
0
        {
1305
0
            ScTokenRef pCorner(
1306
0
                new ScExternalSingleRefToken(nFileId, std::move(aExtTabName), aData));
1307
0
            ScRefTokenHelper::join(pDoc, rRefTokens, pCorner, ScAddress());
1308
0
        }
1309
0
        else
1310
0
        {
1311
0
            ScTokenRef pCorner(new ScSingleRefToken(pDoc->GetSheetLimits(), aData));
1312
0
            ScRefTokenHelper::join(pDoc, rRefTokens, pCorner, ScAddress());
1313
0
        }
1314
0
    }
1315
0
    else
1316
0
    {
1317
0
        ScSingleRefData aDataEnd(aData);
1318
0
        aDataEnd.IncCol(nCornerColumnCount-1);
1319
0
        aDataEnd.IncRow(nCornerRowCount-1);
1320
0
        ScComplexRefData r;
1321
0
        r.Ref1=aData;
1322
0
        r.Ref2=aDataEnd;
1323
0
        if (bExternal)
1324
0
        {
1325
0
            ScTokenRef pCorner(
1326
0
                new ScExternalDoubleRefToken(nFileId, std::move(aExtTabName), r));
1327
0
            ScRefTokenHelper::join(pDoc, rRefTokens, pCorner, ScAddress());
1328
0
        }
1329
0
        else
1330
0
        {
1331
0
            ScTokenRef pCorner(new ScDoubleRefToken(pDoc->GetSheetLimits(), r));
1332
0
            ScRefTokenHelper::join(pDoc, rRefTokens, pCorner, ScAddress());
1333
0
        }
1334
0
    }
1335
1336
0
    return true;
1337
0
}
1338
1339
0
#define SHRINK_RANGE_THRESHOLD 10000
1340
1341
class ShrinkRefTokenToDataRange
1342
{
1343
    ScDocument* mpDoc;
1344
public:
1345
0
    explicit ShrinkRefTokenToDataRange(ScDocument* pDoc) : mpDoc(pDoc) {}
1346
    void operator() (const ScTokenRef& rRef)
1347
0
    {
1348
0
        if (ScRefTokenHelper::isExternalRef(rRef))
1349
0
            return;
1350
1351
        // Don't assume an ScDoubleRefToken if it isn't. It can be at least an
1352
        // ScSingleRefToken, then there isn't anything to shrink.
1353
0
        if (rRef->GetType() != svDoubleRef)
1354
0
            return;
1355
1356
0
        ScComplexRefData& rData = *rRef->GetDoubleRef();
1357
0
        ScSingleRefData& s = rData.Ref1;
1358
0
        ScSingleRefData& e = rData.Ref2;
1359
1360
0
        if(abs((e.Col()-s.Col())*(e.Row()-s.Row())) < SHRINK_RANGE_THRESHOLD)
1361
0
            return;
1362
1363
0
        SCCOL nMinCol = mpDoc->MaxCol(), nMaxCol = 0;
1364
0
        SCROW nMinRow = mpDoc->MaxRow(), nMaxRow = 0;
1365
1366
        // Determine the smallest range that encompasses the data ranges of all sheets.
1367
0
        SCTAB nTab1 = s.Tab(), nTab2 = e.Tab();
1368
0
        for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab)
1369
0
        {
1370
0
            SCCOL nCol1 = 0, nCol2 = mpDoc->MaxCol();
1371
0
            SCROW nRow1 = 0, nRow2 = mpDoc->MaxRow();
1372
0
            mpDoc->ShrinkToDataArea(nTab, nCol1, nRow1, nCol2, nRow2);
1373
0
            nMinCol = std::min(nMinCol, nCol1);
1374
0
            nMinRow = std::min(nMinRow, nRow1);
1375
0
            nMaxCol = std::max(nMaxCol, nCol2);
1376
0
            nMaxRow = std::max(nMaxRow, nRow2);
1377
0
        }
1378
1379
        // Shrink range to the data range if applicable.
1380
0
        if (s.Col() < nMinCol)
1381
0
            s.SetAbsCol(nMinCol);
1382
0
        if (s.Row() < nMinRow)
1383
0
            s.SetAbsRow(nMinRow);
1384
0
        if (e.Col() > nMaxCol)
1385
0
            e.SetAbsCol(nMaxCol);
1386
0
        if (e.Row() > nMaxRow)
1387
0
            e.SetAbsRow(nMaxRow);
1388
0
    }
1389
};
1390
1391
void shrinkToDataRange(ScDocument* pDoc, std::vector<ScTokenRef>& rRefTokens)
1392
0
{
1393
0
    std::for_each(rRefTokens.begin(), rRefTokens.end(), ShrinkRefTokenToDataRange(pDoc));
1394
0
}
1395
1396
}
1397
1398
uno::Reference< chart2::data::XDataSource> SAL_CALL
1399
ScChart2DataProvider::createDataSource(
1400
    const uno::Sequence< beans::PropertyValue >& aArguments )
1401
0
{
1402
0
    SolarMutexGuard aGuard;
1403
0
    if ( ! m_pDocument )
1404
0
        throw uno::RuntimeException();
1405
1406
0
    uno::Reference< chart2::data::XDataSource> xResult;
1407
0
    bool bLabel = true;
1408
0
    bool bCategories = false;
1409
0
    bool bOrientCol = true;
1410
0
    OUString aRangeRepresentation;
1411
0
    uno::Sequence< sal_Int32 > aSequenceMapping;
1412
0
    bool bTimeBased = false;
1413
0
    for(const auto& rArgument : aArguments)
1414
0
    {
1415
0
        if ( rArgument.Name == "DataRowSource" )
1416
0
        {
1417
0
            chart::ChartDataRowSource eSource = chart::ChartDataRowSource_COLUMNS;
1418
0
            if( ! (rArgument.Value >>= eSource))
1419
0
            {
1420
0
                sal_Int32 nSource(0);
1421
0
                if( rArgument.Value >>= nSource )
1422
0
                    eSource = static_cast< chart::ChartDataRowSource >( nSource );
1423
0
            }
1424
0
            bOrientCol = (eSource == chart::ChartDataRowSource_COLUMNS);
1425
0
        }
1426
0
        else if ( rArgument.Name == "FirstCellAsLabel" )
1427
0
        {
1428
0
            bLabel = ::cppu::any2bool(rArgument.Value);
1429
0
        }
1430
0
        else if ( rArgument.Name == "HasCategories" )
1431
0
        {
1432
0
            bCategories = ::cppu::any2bool(rArgument.Value);
1433
0
        }
1434
0
        else if ( rArgument.Name == "CellRangeRepresentation" )
1435
0
        {
1436
0
            rArgument.Value >>= aRangeRepresentation;
1437
0
        }
1438
0
        else if ( rArgument.Name == "SequenceMapping" )
1439
0
        {
1440
0
            rArgument.Value >>= aSequenceMapping;
1441
0
        }
1442
0
        else if ( rArgument.Name == "TimeBased" )
1443
0
        {
1444
0
            rArgument.Value >>= bTimeBased;
1445
0
        }
1446
0
    }
1447
1448
0
    std::vector<ScTokenRef> aRefTokens;
1449
0
    const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
1450
0
    ScRefTokenHelper::compileRangeRepresentation(
1451
0
        aRefTokens, aRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
1452
0
    if (aRefTokens.empty())
1453
        // Invalid range representation.  Bail out.
1454
0
        throw lang::IllegalArgumentException();
1455
1456
0
    SCTAB nTimeBasedStart = MAXTAB;
1457
0
    SCTAB nTimeBasedEnd = 0;
1458
0
    if(bTimeBased)
1459
0
    {
1460
        // limit to first sheet
1461
0
        for(const auto& rxToken : aRefTokens)
1462
0
        {
1463
0
            if (rxToken->GetType() != svDoubleRef)
1464
0
                continue;
1465
1466
0
            ScComplexRefData& rData = *rxToken->GetDoubleRef();
1467
0
            ScSingleRefData& s = rData.Ref1;
1468
0
            ScSingleRefData& e = rData.Ref2;
1469
1470
0
            nTimeBasedStart = std::min(nTimeBasedStart, s.Tab());
1471
0
            nTimeBasedEnd = std::min(nTimeBasedEnd, e.Tab());
1472
1473
0
            if(s.Tab() != e.Tab())
1474
0
                e.SetAbsTab(s.Tab());
1475
0
        }
1476
0
    }
1477
1478
0
    if(!bTimeBased)
1479
0
        shrinkToDataRange(m_pDocument, aRefTokens);
1480
1481
0
    if (bLabel)
1482
0
        lcl_addUpperLeftCornerIfMissing(m_pDocument, aRefTokens, 1, 1); //#i90669#
1483
1484
0
    bool bColHeaders = (bOrientCol ? bLabel : bCategories );
1485
0
    bool bRowHeaders = (bOrientCol ? bCategories : bLabel );
1486
1487
0
    Chart2Positioner aChPositioner(m_pDocument, aRefTokens);
1488
0
    aChPositioner.setHeaders(bColHeaders, bRowHeaders);
1489
1490
0
    const Chart2PositionMap* pChartMap = aChPositioner.getPositionMap();
1491
0
    if (!pChartMap)
1492
        // No chart position map instance.  Bail out.
1493
0
        return xResult;
1494
1495
0
    rtl::Reference<ScChart2DataSource> pDS;
1496
0
    std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSeqs;
1497
1498
    // Fill Categories
1499
0
    if( bCategories )
1500
0
    {
1501
0
        std::vector<ScTokenRef> aValueTokens;
1502
0
        if (bOrientCol)
1503
0
            aValueTokens = pChartMap->getAllRowHeaderRanges();
1504
0
        else
1505
0
            aValueTokens = pChartMap->getAllColHeaderRanges();
1506
1507
0
        std::vector<ScTokenRef> aLabelTokens(
1508
0
                pChartMap->getLeftUpperCornerRanges());
1509
1510
0
        uno::Reference< chart2::data::XLabeledDataSequence > xCategories = lcl_createLabeledDataSequenceFromTokens(
1511
0
            std::move(aValueTokens), std::move(aLabelTokens), m_pDocument, m_bIncludeHiddenCells ); //ownership of pointers is transferred!
1512
0
        if ( xCategories.is() )
1513
0
        {
1514
0
            aSeqs.push_back( xCategories );
1515
0
        }
1516
0
    }
1517
1518
    // Fill Series (values and label)
1519
0
    sal_Int32 nCount = bOrientCol ? pChartMap->getDataColCount() : pChartMap->getDataRowCount();
1520
0
    for (sal_Int32 i = 0; i < nCount; ++i)
1521
0
    {
1522
0
        std::vector<ScTokenRef> aValueTokens;
1523
0
        std::vector<ScTokenRef> aLabelTokens;
1524
0
        if (bOrientCol)
1525
0
        {
1526
0
            aValueTokens = pChartMap->getDataColRanges(static_cast<SCCOL>(i));
1527
0
            aLabelTokens = pChartMap->getColHeaderRanges(static_cast<SCCOL>(i));
1528
0
        }
1529
0
        else
1530
0
        {
1531
0
            aValueTokens = pChartMap->getDataRowRanges(static_cast<SCROW>(i));
1532
0
            aLabelTokens = pChartMap->getRowHeaderRanges(static_cast<SCROW>(i));
1533
0
        }
1534
0
        uno::Reference< chart2::data::XLabeledDataSequence > xChartSeries = lcl_createLabeledDataSequenceFromTokens(
1535
0
            std::move(aValueTokens), std::move(aLabelTokens), m_pDocument, m_bIncludeHiddenCells ); //ownership of pointers is transferred!
1536
0
        if ( xChartSeries.is() && xChartSeries->getValues().is() && xChartSeries->getValues()->getData().hasElements() )
1537
0
        {
1538
0
            aSeqs.push_back( xChartSeries );
1539
0
        }
1540
0
    }
1541
1542
0
    pDS = new ScChart2DataSource(m_pDocument);
1543
1544
    //reorder labeled sequences according to aSequenceMapping
1545
0
    std::vector< uno::Reference< chart2::data::XLabeledDataSequence > > aSeqVector;
1546
0
    aSeqVector.reserve(aSeqs.size());
1547
0
    for (auto const& aSeq : aSeqs)
1548
0
    {
1549
0
        aSeqVector.push_back(aSeq);
1550
0
    }
1551
1552
0
    for (const sal_Int32 nNewIndex : aSequenceMapping)
1553
0
    {
1554
        // note: assuming that the values in the sequence mapping are always non-negative
1555
0
        std::vector< uno::Reference< chart2::data::XLabeledDataSequence > >::size_type nOldIndex( static_cast< sal_uInt32 >( nNewIndex ) );
1556
0
        if( nOldIndex < aSeqVector.size() )
1557
0
        {
1558
0
            pDS->AddLabeledSequence( aSeqVector[nOldIndex] );
1559
0
            aSeqVector[nOldIndex] = nullptr;
1560
0
        }
1561
0
    }
1562
1563
0
    for(const uno::Reference< chart2::data::XLabeledDataSequence >& xSeq : aSeqVector)
1564
0
    {
1565
0
        if ( xSeq.is() )
1566
0
        {
1567
0
            pDS->AddLabeledSequence( xSeq );
1568
0
        }
1569
0
    }
1570
1571
0
    xResult.set( pDS );
1572
0
    return xResult;
1573
0
}
1574
1575
namespace
1576
{
1577
1578
/**
1579
 * Function object to create a list of table numbers from a token list.
1580
 */
1581
class InsertTabNumber
1582
{
1583
public:
1584
    InsertTabNumber() :
1585
0
        mpTabNumVector(std::make_shared<std::vector<SCTAB>>())
1586
0
    {
1587
0
    }
1588
1589
    void operator() (const ScTokenRef& pToken) const
1590
0
    {
1591
0
        if (!ScRefTokenHelper::isRef(pToken))
1592
0
            return;
1593
1594
0
        const ScSingleRefData& r = *pToken->GetSingleRef();
1595
0
        mpTabNumVector->push_back(r.Tab());
1596
0
    }
1597
1598
    void getVector(std::vector<SCTAB>& rVector)
1599
0
    {
1600
0
        mpTabNumVector->swap(rVector);
1601
0
    }
1602
private:
1603
    std::shared_ptr<std::vector<SCTAB>> mpTabNumVector;
1604
};
1605
1606
class RangeAnalyzer
1607
{
1608
public:
1609
    RangeAnalyzer();
1610
    void initRangeAnalyzer( const ScDocument* pDoc, const std::vector<ScTokenRef>& rTokens );
1611
    void analyzeRange( sal_Int32& rnDataInRows, sal_Int32& rnDataInCols,
1612
            bool& rbRowSourceAmbiguous ) const;
1613
    bool inSameSingleRow( const RangeAnalyzer& rOther );
1614
    bool inSameSingleColumn( const RangeAnalyzer& rOther );
1615
0
    SCROW getRowCount() const { return mnRowCount; }
1616
0
    SCCOL getColumnCount() const { return mnColumnCount; }
1617
1618
private:
1619
    bool mbEmpty;
1620
    bool mbAmbiguous;
1621
    SCROW mnRowCount;
1622
    SCCOL mnColumnCount;
1623
1624
    SCCOL mnStartColumn;
1625
    SCROW mnStartRow;
1626
};
1627
1628
RangeAnalyzer::RangeAnalyzer()
1629
0
    : mbEmpty(true)
1630
0
    , mbAmbiguous(false)
1631
0
    , mnRowCount(0)
1632
0
    , mnColumnCount(0)
1633
0
    , mnStartColumn(-1)
1634
0
    , mnStartRow(-1)
1635
0
{
1636
0
}
1637
1638
void RangeAnalyzer::initRangeAnalyzer( const ScDocument* pDoc, const std::vector<ScTokenRef>& rTokens )
1639
0
{
1640
0
    mnRowCount=0;
1641
0
    mnColumnCount=0;
1642
0
    mnStartColumn = -1;
1643
0
    mnStartRow = -1;
1644
0
    mbAmbiguous=false;
1645
0
    if( rTokens.empty() )
1646
0
    {
1647
0
        mbEmpty=true;
1648
0
        return;
1649
0
    }
1650
0
    mbEmpty=false;
1651
1652
0
    for (const ScTokenRef& aRefToken : rTokens)
1653
0
    {
1654
0
        StackVar eVar = aRefToken->GetType();
1655
0
        if (eVar == svDoubleRef || eVar == svExternalDoubleRef)
1656
0
        {
1657
0
            const ScComplexRefData& r = *aRefToken->GetDoubleRef();
1658
0
            if (r.Ref1.Tab() == r.Ref2.Tab())
1659
0
            {
1660
0
                mnColumnCount = std::max<SCCOL>(mnColumnCount, static_cast<SCCOL>(abs(r.Ref2.Col() - r.Ref1.Col())+1));
1661
0
                mnRowCount = std::max<SCROW>(mnRowCount, static_cast<SCROW>(abs(r.Ref2.Row() - r.Ref1.Row())+1));
1662
0
                if( mnStartColumn == -1 )
1663
0
                {
1664
0
                    mnStartColumn = r.Ref1.Col();
1665
0
                    mnStartRow = r.Ref1.Row();
1666
0
                }
1667
0
                else
1668
0
                {
1669
0
                    if (mnStartColumn != r.Ref1.Col() && mnStartRow != r.Ref1.Row())
1670
0
                        mbAmbiguous=true;
1671
0
                }
1672
0
            }
1673
0
            else
1674
0
                mbAmbiguous=true;
1675
0
        }
1676
0
        else if (eVar == svSingleRef || eVar == svExternalSingleRef)
1677
0
        {
1678
0
            const ScSingleRefData& r = *aRefToken->GetSingleRef();
1679
0
            mnColumnCount = std::max<SCCOL>( mnColumnCount, 1);
1680
0
            mnRowCount = std::max<SCROW>( mnRowCount, 1);
1681
0
            if( mnStartColumn == -1 )
1682
0
            {
1683
0
                mnStartColumn = r.Col();
1684
0
                mnStartRow = r.Row();
1685
0
            }
1686
0
            else
1687
0
            {
1688
0
                if (mnStartColumn != r.Col() && mnStartRow != r.Row())
1689
0
                    mbAmbiguous=true;
1690
0
            }
1691
0
        }
1692
0
        else if (eVar == svIndex && aRefToken->GetOpCode() == ocName)
1693
0
        {
1694
0
            ScRangeData* pNameRange = pDoc->FindRangeNameBySheetAndIndex(aRefToken->GetSheet(), aRefToken->GetIndex());
1695
0
            ScRange aRange;
1696
0
            if (pNameRange->IsReference(aRange))
1697
0
            {
1698
0
                mnColumnCount = std::max<SCCOL>(mnColumnCount, static_cast<SCCOL>(abs(aRange.aEnd.Col() - aRange.aStart.Col()) + 1));
1699
0
                mnRowCount = std::max<SCROW>(mnRowCount, static_cast<SCROW>(abs(aRange.aEnd.Row() - aRange.aStart.Row()) + 1));
1700
0
                if (mnStartColumn == -1)
1701
0
                {
1702
0
                    mnStartColumn = aRange.aStart.Col();
1703
0
                    mnStartRow = aRange.aStart.Row();
1704
0
                }
1705
0
                else
1706
0
                {
1707
0
                    if (mnStartColumn != aRange.aStart.Col() && mnStartRow != aRange.aStart.Row())
1708
0
                        mbAmbiguous = true;
1709
0
                }
1710
0
            }
1711
0
            else
1712
0
                mbAmbiguous = true;
1713
0
        }
1714
0
        else
1715
0
            mbAmbiguous=true;
1716
0
    }
1717
0
}
1718
1719
void RangeAnalyzer::analyzeRange( sal_Int32& rnDataInRows,
1720
                                     sal_Int32& rnDataInCols,
1721
                                     bool& rbRowSourceAmbiguous ) const
1722
0
{
1723
0
    if(!mbEmpty && !mbAmbiguous)
1724
0
    {
1725
0
        if( mnRowCount==1 && mnColumnCount>1 )
1726
0
            ++rnDataInRows;
1727
0
        else if( mnColumnCount==1 && mnRowCount>1 )
1728
0
            ++rnDataInCols;
1729
0
        else if( mnRowCount>1 && mnColumnCount>1 )
1730
0
            rbRowSourceAmbiguous = true;
1731
0
    }
1732
0
    else if( !mbEmpty )
1733
0
        rbRowSourceAmbiguous = true;
1734
0
}
1735
1736
bool RangeAnalyzer::inSameSingleRow( const RangeAnalyzer& rOther )
1737
0
{
1738
0
    return mnStartRow==rOther.mnStartRow &&
1739
0
        mnRowCount==1 && rOther.mnRowCount==1;
1740
0
}
1741
1742
bool RangeAnalyzer::inSameSingleColumn( const RangeAnalyzer& rOther )
1743
0
{
1744
0
    return mnStartColumn==rOther.mnStartColumn &&
1745
0
        mnColumnCount==1 && rOther.mnColumnCount==1;
1746
0
}
1747
1748
std::pair<OUString, OUString> constructKey(const uno::Reference< chart2::data::XLabeledDataSequence>& xNew)
1749
0
{
1750
0
    std::pair<OUString, OUString> aKey;
1751
0
    if( xNew->getLabel().is() )
1752
0
        aKey.first = xNew->getLabel()->getSourceRangeRepresentation();
1753
0
    if( xNew->getValues().is() )
1754
0
        aKey.second = xNew->getValues()->getSourceRangeRepresentation();
1755
0
    return aKey;
1756
0
}
1757
1758
1759
} //end anonymous namespace
1760
1761
uno::Sequence< beans::PropertyValue > SAL_CALL ScChart2DataProvider::detectArguments(
1762
    const uno::Reference< chart2::data::XDataSource >& xDataSource )
1763
0
{
1764
0
    std::vector< beans::PropertyValue > aResult;
1765
0
    bool bRowSourceDetected = false;
1766
0
    bool bFirstCellAsLabel = false;
1767
0
    bool bHasCategories = false;
1768
0
    OUString sRangeRep;
1769
1770
0
    bool bHasCategoriesLabels = false;
1771
0
    std::vector<ScTokenRef> aAllCategoriesValuesTokens;
1772
0
    std::vector<ScTokenRef> aAllSeriesLabelTokens;
1773
1774
0
    chart::ChartDataRowSource eRowSource = chart::ChartDataRowSource_COLUMNS;
1775
1776
0
    std::vector<ScTokenRef> aAllTokens;
1777
1778
    // parse given data source and collect infos
1779
0
    {
1780
0
        SolarMutexGuard aGuard;
1781
0
        OSL_ENSURE( m_pDocument, "No Document -> no detectArguments" );
1782
0
        if(!m_pDocument ||!xDataSource.is())
1783
0
            return comphelper::containerToSequence( aResult );
1784
1785
0
        sal_Int32 nDataInRows = 0;
1786
0
        sal_Int32 nDataInCols = 0;
1787
0
        bool bRowSourceAmbiguous = false;
1788
1789
0
        const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aSequences( xDataSource->getDataSequences());
1790
0
        const sal_Int32 nCount( aSequences.getLength());
1791
0
        RangeAnalyzer aPrevLabel,aPrevValues;
1792
0
        for( const uno::Reference< chart2::data::XLabeledDataSequence >& xLS : aSequences )
1793
0
        {
1794
0
            if( xLS.is() )
1795
0
            {
1796
0
                bool bThisIsCategories = false;
1797
0
                if(!bHasCategories)
1798
0
                {
1799
0
                    uno::Reference< beans::XPropertySet > xSeqProp( xLS->getValues(), uno::UNO_QUERY );
1800
0
                    OUString aRole;
1801
0
                    if( xSeqProp.is() && (xSeqProp->getPropertyValue(u"Role"_ustr) >>= aRole) &&
1802
0
                        aRole == "categories" )
1803
0
                        bThisIsCategories = bHasCategories = true;
1804
0
                }
1805
1806
0
                RangeAnalyzer aLabel,aValues;
1807
                // label
1808
0
                uno::Reference< chart2::data::XDataSequence > xLabel( xLS->getLabel());
1809
0
                if( xLabel.is())
1810
0
                {
1811
0
                    bFirstCellAsLabel = true;
1812
0
                    std::vector<ScTokenRef> aTokens;
1813
0
                    const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
1814
0
                    ScRefTokenHelper::compileRangeRepresentation(
1815
0
                        aTokens, xLabel->getSourceRangeRepresentation(), *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
1816
0
                    aLabel.initRangeAnalyzer(m_pDocument, aTokens);
1817
0
                    for (const auto& rxToken : aTokens)
1818
0
                    {
1819
0
                        if (rxToken->GetType() == svIndex && rxToken->GetOpCode() == ocName)
1820
0
                        {
1821
0
                            ScRangeData* pNameRange = m_pDocument->FindRangeNameBySheetAndIndex(rxToken->GetSheet(), rxToken->GetIndex());
1822
0
                            if (pNameRange->HasReferences())
1823
0
                            {
1824
0
                                const ScTokenRef aTempToken = pNameRange->GetCode()->FirstToken();
1825
0
                                ScRefTokenHelper::join(m_pDocument, aAllTokens, aTempToken, ScAddress());
1826
0
                            }
1827
0
                            else
1828
0
                                ScRefTokenHelper::join(m_pDocument, aAllTokens, rxToken, ScAddress());
1829
0
                        }
1830
0
                        else
1831
0
                            ScRefTokenHelper::join(m_pDocument, aAllTokens, rxToken, ScAddress());
1832
0
                        if(!bThisIsCategories)
1833
0
                            ScRefTokenHelper::join(m_pDocument, aAllSeriesLabelTokens, rxToken, ScAddress());
1834
0
                    }
1835
0
                    if(bThisIsCategories)
1836
0
                        bHasCategoriesLabels=true;
1837
0
                }
1838
                // values
1839
0
                uno::Reference< chart2::data::XDataSequence > xValues( xLS->getValues());
1840
0
                if( xValues.is())
1841
0
                {
1842
0
                    std::vector<ScTokenRef> aTokens;
1843
0
                    const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
1844
0
                    ScRefTokenHelper::compileRangeRepresentation(
1845
0
                        aTokens, xValues->getSourceRangeRepresentation(), *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
1846
0
                    aValues.initRangeAnalyzer(m_pDocument, aTokens);
1847
0
                    for (const auto& rxToken : aTokens)
1848
0
                    {
1849
0
                        if (rxToken->GetType() == svIndex && rxToken->GetOpCode() == ocName)
1850
0
                        {
1851
0
                            ScRangeData* pNameRange = m_pDocument->FindRangeNameBySheetAndIndex(rxToken->GetSheet(), rxToken->GetIndex());
1852
0
                            if (pNameRange->HasReferences())
1853
0
                            {
1854
0
                                const ScTokenRef aTempToken = pNameRange->GetCode()->FirstToken();
1855
0
                                ScRefTokenHelper::join(m_pDocument, aAllTokens, aTempToken, ScAddress());
1856
0
                            }
1857
0
                            else
1858
0
                                ScRefTokenHelper::join(m_pDocument, aAllTokens, rxToken, ScAddress());
1859
0
                        }
1860
0
                        else
1861
0
                            ScRefTokenHelper::join(m_pDocument, aAllTokens, rxToken, ScAddress());
1862
0
                        if(bThisIsCategories)
1863
0
                            ScRefTokenHelper::join(m_pDocument, aAllCategoriesValuesTokens, rxToken, ScAddress());
1864
0
                    }
1865
0
                }
1866
                //detect row source
1867
0
                if(!bThisIsCategories || nCount==1) //categories might span multiple rows *and* columns, so they should be used for detection only if nothing else is available
1868
0
                {
1869
0
                    if (!bRowSourceAmbiguous)
1870
0
                    {
1871
0
                        aValues.analyzeRange(nDataInRows,nDataInCols,bRowSourceAmbiguous);
1872
0
                        aLabel.analyzeRange(nDataInRows,nDataInCols,bRowSourceAmbiguous);
1873
0
                        if (nDataInRows > 1 && nDataInCols > 1)
1874
0
                            bRowSourceAmbiguous = true;
1875
0
                        else if( !bRowSourceAmbiguous && !nDataInRows && !nDataInCols )
1876
0
                        {
1877
0
                            if( aValues.inSameSingleColumn( aLabel ) )
1878
0
                                nDataInCols++;
1879
0
                            else if( aValues.inSameSingleRow( aLabel ) )
1880
0
                                nDataInRows++;
1881
0
                            else
1882
0
                            {
1883
                                //#i86188# also detect a single column split into rows correctly
1884
0
                                if( aValues.inSameSingleColumn( aPrevValues ) )
1885
0
                                    nDataInRows++;
1886
0
                                else if( aValues.inSameSingleRow( aPrevValues ) )
1887
0
                                    nDataInCols++;
1888
0
                                else if( aLabel.inSameSingleColumn( aPrevLabel ) )
1889
0
                                    nDataInRows++;
1890
0
                                else if( aLabel.inSameSingleRow( aPrevLabel ) )
1891
0
                                    nDataInCols++;
1892
0
                            }
1893
0
                        }
1894
0
                    }
1895
0
                }
1896
0
                aPrevValues=aValues;
1897
0
                aPrevLabel=aLabel;
1898
0
            }
1899
0
        }
1900
1901
0
        if (!bRowSourceAmbiguous)
1902
0
        {
1903
0
            bRowSourceDetected = true;
1904
0
            eRowSource = ( nDataInCols > 0
1905
0
                           ? chart::ChartDataRowSource_COLUMNS
1906
0
                           : chart::ChartDataRowSource_ROWS );
1907
0
        }
1908
0
        else
1909
0
        {
1910
            // set DataRowSource to the better of the two ambiguities
1911
0
            eRowSource = ( nDataInRows > nDataInCols
1912
0
                           ? chart::ChartDataRowSource_ROWS
1913
0
                           : chart::ChartDataRowSource_COLUMNS );
1914
0
        }
1915
1916
0
    }
1917
1918
    // TableNumberList
1919
0
    {
1920
0
        std::vector<SCTAB> aTableNumVector;
1921
0
        InsertTabNumber func;
1922
0
        func = ::std::for_each(aAllTokens.begin(), aAllTokens.end(), func);
1923
0
        func.getVector(aTableNumVector);
1924
0
        aResult.emplace_back( "TableNumberList", -1,
1925
0
                                  uno::Any( lcl_createTableNumberList( aTableNumVector ) ),
1926
0
                                  beans::PropertyState_DIRECT_VALUE );
1927
0
    }
1928
1929
0
    if( bRowSourceDetected )
1930
0
    {
1931
        // DataRowSource (calculated before)
1932
0
        aResult.emplace_back( "DataRowSource", -1,
1933
0
                                  uno::Any( eRowSource ), beans::PropertyState_DIRECT_VALUE );
1934
        // HasCategories
1935
0
        aResult.emplace_back( "HasCategories", -1,
1936
0
                                  uno::Any( bHasCategories ), beans::PropertyState_DIRECT_VALUE );
1937
        // FirstCellAsLabel
1938
0
        aResult.emplace_back( "FirstCellAsLabel", -1,
1939
0
                                  uno::Any( bFirstCellAsLabel ), beans::PropertyState_DIRECT_VALUE );
1940
0
    }
1941
1942
    // Add the left upper corner to the range if it is missing.
1943
0
    if (bRowSourceDetected && bFirstCellAsLabel && bHasCategories && !bHasCategoriesLabels )
1944
0
    {
1945
0
        RangeAnalyzer aTop,aLeft;
1946
0
        if( eRowSource==chart::ChartDataRowSource_COLUMNS )
1947
0
        {
1948
0
            aTop.initRangeAnalyzer(m_pDocument, aAllSeriesLabelTokens);
1949
0
            aLeft.initRangeAnalyzer(m_pDocument, aAllCategoriesValuesTokens);
1950
0
        }
1951
0
        else
1952
0
        {
1953
0
            aTop.initRangeAnalyzer(m_pDocument, aAllCategoriesValuesTokens);
1954
0
            aLeft.initRangeAnalyzer(m_pDocument, aAllSeriesLabelTokens);
1955
0
        }
1956
0
        lcl_addUpperLeftCornerIfMissing(m_pDocument, aAllTokens, aTop.getRowCount(), aLeft.getColumnCount());//e.g. #i91212#
1957
0
    }
1958
1959
    // Get range string.
1960
0
    lcl_convertTokensToString(sRangeRep, aAllTokens, *m_pDocument);
1961
1962
    // add cell range property
1963
0
    aResult.emplace_back( "CellRangeRepresentation", -1,
1964
0
                              uno::Any( sRangeRep ), beans::PropertyState_DIRECT_VALUE );
1965
1966
    //Sequence Mapping
1967
0
    bool const bSequencesReordered = true;//todo detect this above or detect this sequence mapping cheaper ...
1968
0
    if( bSequencesReordered && bRowSourceDetected )
1969
0
    {
1970
0
        bool bDifferentIndexes = false;
1971
1972
0
        std::vector< sal_Int32 > aSequenceMappingVector;
1973
1974
0
        uno::Reference< chart2::data::XDataSource > xCompareDataSource;
1975
0
        try
1976
0
        {
1977
0
            xCompareDataSource.set( createDataSource( comphelper::containerToSequence( aResult ) ) );
1978
0
        }
1979
0
        catch( const lang::IllegalArgumentException & )
1980
0
        {
1981
            // creation of data source to compare didn't work, so we cannot
1982
            // create a sequence mapping
1983
0
        }
1984
1985
0
        if( xDataSource.is() && xCompareDataSource.is() )
1986
0
        {
1987
0
            const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> > aOldSequences =
1988
0
                xCompareDataSource->getDataSequences();
1989
0
            const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> > aNewSequences =
1990
0
                xDataSource->getDataSequences();
1991
1992
0
            std::map<std::pair<OUString, OUString>,sal_Int32> aOldEntryToIndex;
1993
0
            for( sal_Int32 nIndex = 0, n = aOldSequences.getLength(); nIndex < n; nIndex++ )
1994
0
            {
1995
0
                const uno::Reference< chart2::data::XLabeledDataSequence>& xOld( aOldSequences[nIndex] );
1996
0
                if( xOld.is() )
1997
0
                {
1998
0
                    std::pair<OUString, OUString> aKey = constructKey(xOld);
1999
0
                    aOldEntryToIndex[aKey] = nIndex;
2000
0
                }
2001
0
            }
2002
2003
0
            for( sal_Int32 nNewIndex = 0, n = aNewSequences.getLength(); nNewIndex < n; nNewIndex++ )
2004
0
            {
2005
0
                const uno::Reference< chart2::data::XLabeledDataSequence>& xNew( aNewSequences[nNewIndex] );
2006
0
                if( !xNew.is() )
2007
0
                    continue;
2008
2009
0
                std::pair<OUString, OUString> aKey = constructKey(xNew);
2010
0
                if (aOldEntryToIndex.find(aKey) == aOldEntryToIndex.end())
2011
0
                    continue;
2012
2013
0
                sal_Int32 nOldIndex = aOldEntryToIndex[aKey];
2014
0
                if( nOldIndex != nNewIndex )
2015
0
                    bDifferentIndexes = true;
2016
2017
0
                aSequenceMappingVector.push_back(nOldIndex);
2018
0
            }
2019
0
        }
2020
2021
0
        if( bDifferentIndexes && !aSequenceMappingVector.empty() )
2022
0
        {
2023
0
            aResult.emplace_back( "SequenceMapping", -1,
2024
0
                    uno::Any( comphelper::containerToSequence(aSequenceMappingVector) )
2025
0
                    , beans::PropertyState_DIRECT_VALUE );
2026
0
        }
2027
0
    }
2028
2029
0
    return comphelper::containerToSequence( aResult );
2030
0
}
2031
2032
sal_Bool SAL_CALL ScChart2DataProvider::createDataSequenceByRangeRepresentationPossible( const OUString& aRangeRepresentation )
2033
0
{
2034
0
    SolarMutexGuard aGuard;
2035
0
    if( ! m_pDocument )
2036
0
        return false;
2037
2038
0
    std::vector<ScTokenRef> aTokens;
2039
0
    const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
2040
0
    ScRefTokenHelper::compileRangeRepresentation(
2041
0
        aTokens, aRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
2042
0
    return !aTokens.empty();
2043
0
}
2044
2045
uno::Reference< chart2::data::XDataSequence > SAL_CALL
2046
    ScChart2DataProvider::createDataSequenceByRangeRepresentation(
2047
    const OUString& aRangeRepresentation )
2048
0
{
2049
0
    SolarMutexGuard aGuard;
2050
0
    uno::Reference< chart2::data::XDataSequence > xResult;
2051
2052
0
    OSL_ENSURE( m_pDocument, "No Document -> no createDataSequenceByRangeRepresentation" );
2053
0
    if(!m_pDocument || aRangeRepresentation.isEmpty())
2054
0
        return xResult;
2055
2056
0
    std::vector<ScTokenRef> aRefTokens;
2057
0
    const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
2058
0
    ScRefTokenHelper::compileRangeRepresentation(
2059
0
        aRefTokens, aRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
2060
0
    if (aRefTokens.empty())
2061
0
        return xResult;
2062
2063
0
    shrinkToDataRange(m_pDocument, aRefTokens);
2064
2065
0
    xResult.set(new ScChart2DataSequence(m_pDocument, std::move(aRefTokens), m_bIncludeHiddenCells));
2066
2067
0
    return xResult;
2068
0
}
2069
2070
uno::Reference<chart2::data::XDataSequence> SAL_CALL
2071
ScChart2DataProvider::createDataSequenceByValueArray(
2072
    const OUString& /*aRole*/, const OUString& /*aRangeRepresentation*/,
2073
    const OUString& /*aRoleQualifier*/ )
2074
0
{
2075
0
    return uno::Reference<chart2::data::XDataSequence>();
2076
0
}
2077
2078
uno::Reference< sheet::XRangeSelection > SAL_CALL ScChart2DataProvider::getRangeSelection()
2079
0
{
2080
0
    uno::Reference< sheet::XRangeSelection > xResult;
2081
2082
0
    uno::Reference< frame::XModel > xModel( lcl_GetXModel( m_pDocument ));
2083
0
    if( xModel.is())
2084
0
        xResult.set( xModel->getCurrentController(), uno::UNO_QUERY );
2085
2086
0
    return xResult;
2087
0
}
2088
2089
sal_Bool SAL_CALL ScChart2DataProvider::createDataSequenceByFormulaTokensPossible(
2090
    const uno::Sequence<sheet::FormulaToken>& aTokens )
2091
0
{
2092
0
    if (!aTokens.hasElements())
2093
0
        return false;
2094
2095
0
    ScTokenArray aCode(*m_pDocument);
2096
0
    if (!ScTokenConversion::ConvertToTokenArray(*m_pDocument, aCode, aTokens))
2097
0
        return false;
2098
2099
0
    sal_uInt16 n = aCode.GetLen();
2100
0
    if (!n)
2101
0
        return false;
2102
2103
0
    formula::FormulaTokenArrayPlainIterator aIter(aCode);
2104
0
    const formula::FormulaToken* pFirst = aIter.First();
2105
0
    const formula::FormulaToken* pLast = aCode.GetArray()[n-1];
2106
0
    for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
2107
0
    {
2108
0
        switch (p->GetType())
2109
0
        {
2110
0
            case svSep:
2111
0
            {
2112
0
                switch (p->GetOpCode())
2113
0
                {
2114
0
                    case ocSep:
2115
                        // separators are allowed.
2116
0
                    break;
2117
0
                    case ocOpen:
2118
0
                        if (p != pFirst)
2119
                            // open paran is allowed only as the first token.
2120
0
                            return false;
2121
0
                    break;
2122
0
                    case ocClose:
2123
0
                        if (p != pLast)
2124
                            // close paren is allowed only as the last token.
2125
0
                            return false;
2126
0
                    break;
2127
0
                    default:
2128
0
                        return false;
2129
0
                }
2130
0
            }
2131
0
            break;
2132
0
            case svSingleRef:
2133
0
            case svDoubleRef:
2134
0
            case svExternalSingleRef:
2135
0
            case svExternalDoubleRef:
2136
0
            break;
2137
0
            default:
2138
0
                return false;
2139
0
        }
2140
0
    }
2141
2142
0
    return true;
2143
0
}
2144
2145
uno::Reference<chart2::data::XDataSequence> SAL_CALL
2146
ScChart2DataProvider::createDataSequenceByFormulaTokens(
2147
    const uno::Sequence<sheet::FormulaToken>& aTokens )
2148
0
{
2149
0
    uno::Reference<chart2::data::XDataSequence> xResult;
2150
0
    if (!aTokens.hasElements())
2151
0
        return xResult;
2152
2153
0
    ScTokenArray aCode(*m_pDocument);
2154
0
    if (!ScTokenConversion::ConvertToTokenArray(*m_pDocument, aCode, aTokens))
2155
0
        return xResult;
2156
2157
0
    sal_uInt16 n = aCode.GetLen();
2158
0
    if (!n)
2159
0
        return xResult;
2160
2161
0
    std::vector<ScTokenRef> aRefTokens;
2162
0
    formula::FormulaTokenArrayPlainIterator aIter(aCode);
2163
0
    const formula::FormulaToken* pFirst = aIter.First();
2164
0
    const formula::FormulaToken* pLast = aCode.GetArray()[n-1];
2165
0
    for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
2166
0
    {
2167
0
        switch (p->GetType())
2168
0
        {
2169
0
            case svSep:
2170
0
            {
2171
0
                switch (p->GetOpCode())
2172
0
                {
2173
0
                    case ocSep:
2174
                        // separators are allowed.
2175
0
                    break;
2176
0
                    case ocOpen:
2177
0
                        if (p != pFirst)
2178
                            // open paran is allowed only as the first token.
2179
0
                            throw lang::IllegalArgumentException();
2180
0
                    break;
2181
0
                    case ocClose:
2182
0
                        if (p != pLast)
2183
                            // close paren is allowed only as the last token.
2184
0
                            throw lang::IllegalArgumentException();
2185
0
                    break;
2186
0
                    default:
2187
0
                        throw lang::IllegalArgumentException();
2188
0
                }
2189
0
            }
2190
0
            break;
2191
0
            case svIndex:
2192
0
            case svString:
2193
0
            case svSingleRef:
2194
0
            case svDoubleRef:
2195
0
            case svExternalSingleRef:
2196
0
            case svExternalDoubleRef:
2197
0
            {
2198
0
                ScTokenRef pNew(p->Clone());
2199
0
                aRefTokens.push_back(pNew);
2200
0
            }
2201
0
            break;
2202
0
            default:
2203
0
                throw lang::IllegalArgumentException();
2204
0
        }
2205
0
    }
2206
2207
0
    if (aRefTokens.empty())
2208
0
        return xResult;
2209
2210
0
    shrinkToDataRange(m_pDocument, aRefTokens);
2211
2212
0
    xResult.set(new ScChart2DataSequence(m_pDocument, std::move(aRefTokens), m_bIncludeHiddenCells));
2213
0
    return xResult;
2214
0
}
2215
2216
// XRangeXMLConversion ---------------------------------------------------
2217
2218
OUString SAL_CALL ScChart2DataProvider::convertRangeToXML( const OUString& sRangeRepresentation )
2219
0
{
2220
0
    OUString aRet;
2221
0
    if (!m_pDocument)
2222
0
        return aRet;
2223
2224
0
    if (sRangeRepresentation.isEmpty())
2225
        // Empty data range is allowed.
2226
0
        return aRet;
2227
2228
0
    std::vector<ScTokenRef> aRefTokens;
2229
0
    const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
2230
0
    ScRefTokenHelper::compileRangeRepresentation(
2231
0
        aRefTokens, sRangeRepresentation, *m_pDocument, cSep, m_pDocument->GetGrammar(), true);
2232
0
    if (aRefTokens.empty())
2233
0
    {
2234
0
        SAL_WARN("sc", "convertRangeToXML throw IllegalArgumentException from input of: " << sRangeRepresentation);
2235
0
        throw lang::IllegalArgumentException();
2236
0
    }
2237
2238
0
    Tokens2RangeStringXML converter(*m_pDocument);
2239
0
    converter = ::std::for_each(aRefTokens.begin(), aRefTokens.end(), converter);
2240
0
    converter.getString(aRet);
2241
2242
0
    return aRet;
2243
0
}
2244
2245
OUString SAL_CALL ScChart2DataProvider::convertRangeFromXML( const OUString& sXMLRange )
2246
0
{
2247
0
    if (!m_pDocument)
2248
0
    {
2249
        // #i74062# When loading flat XML, this is called before the referenced sheets are in the document,
2250
        // so the conversion has to take place directly with the strings, without looking up the sheets.
2251
2252
0
        OUStringBuffer sRet;
2253
0
        sal_Int32 nOffset = 0;
2254
0
        while( nOffset >= 0 )
2255
0
        {
2256
0
            OUString sToken;
2257
0
            ScRangeStringConverter::GetTokenByOffset( sToken, sXMLRange, nOffset );
2258
0
            if( nOffset >= 0 )
2259
0
            {
2260
                // convert one address (remove dots)
2261
2262
0
                OUString aUIString(sToken);
2263
2264
0
                sal_Int32 nIndex = ScRangeStringConverter::IndexOf( sToken, ':', 0 );
2265
0
                if ( nIndex >= 0 && nIndex < aUIString.getLength() - 1 &&
2266
0
                        aUIString[nIndex + 1] == '.' )
2267
0
                    aUIString = aUIString.replaceAt( nIndex + 1, 1, u"" );
2268
2269
0
                if ( aUIString[0] == '.' )
2270
0
                    aUIString = aUIString.copy( 1 );
2271
2272
0
                if( !sRet.isEmpty() )
2273
0
                    sRet.append( ';' );
2274
0
                sRet.append( aUIString );
2275
0
            }
2276
0
        }
2277
2278
0
        return sRet.makeStringAndClear();
2279
0
    }
2280
2281
0
    OUString aRet;
2282
0
    ScRangeStringConverter::GetStringFromXMLRangeString(aRet, sXMLRange, *m_pDocument);
2283
0
    return aRet;
2284
0
}
2285
2286
// DataProvider XPropertySet -------------------------------------------------
2287
2288
uno::Reference< beans::XPropertySetInfo> SAL_CALL
2289
ScChart2DataProvider::getPropertySetInfo()
2290
0
{
2291
0
    SolarMutexGuard aGuard;
2292
0
    static uno::Reference<beans::XPropertySetInfo> aRef =
2293
0
        new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() );
2294
0
    return aRef;
2295
0
}
2296
2297
void SAL_CALL ScChart2DataProvider::setPropertyValue(
2298
        const OUString& rPropertyName, const uno::Any& rValue)
2299
0
{
2300
0
    if ( rPropertyName != SC_UNONAME_INCLUDEHIDDENCELLS )
2301
0
        throw beans::UnknownPropertyException(rPropertyName);
2302
2303
0
    if ( !(rValue >>= m_bIncludeHiddenCells))
2304
0
        throw lang::IllegalArgumentException();
2305
2306
0
}
2307
2308
uno::Any SAL_CALL ScChart2DataProvider::getPropertyValue(
2309
        const OUString& rPropertyName)
2310
0
{
2311
0
    uno::Any aRet;
2312
0
    if ( rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS )
2313
0
        aRet <<= m_bIncludeHiddenCells;
2314
0
    else if (rPropertyName == SC_UNONAME_USE_INTERNAL_DATA_PROVIDER)
2315
0
    {
2316
        // This is a read-only property.
2317
0
        aRet <<= m_pDocument->PastingDrawFromOtherDoc();
2318
0
    }
2319
0
    else
2320
0
        throw beans::UnknownPropertyException(rPropertyName);
2321
0
    return aRet;
2322
0
}
2323
2324
void SAL_CALL ScChart2DataProvider::addPropertyChangeListener(
2325
        const OUString& /*rPropertyName*/,
2326
        const uno::Reference< beans::XPropertyChangeListener>& /*xListener*/)
2327
0
{
2328
0
    OSL_FAIL( "Not yet implemented" );
2329
0
}
2330
2331
void SAL_CALL ScChart2DataProvider::removePropertyChangeListener(
2332
        const OUString& /*rPropertyName*/,
2333
        const uno::Reference< beans::XPropertyChangeListener>& /*rListener*/)
2334
0
{
2335
0
    OSL_FAIL( "Not yet implemented" );
2336
0
}
2337
2338
void SAL_CALL ScChart2DataProvider::addVetoableChangeListener(
2339
        const OUString& /*rPropertyName*/,
2340
        const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/)
2341
0
{
2342
0
    OSL_FAIL( "Not yet implemented" );
2343
0
}
2344
2345
void SAL_CALL ScChart2DataProvider::removeVetoableChangeListener(
2346
        const OUString& /*rPropertyName*/,
2347
        const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/ )
2348
0
{
2349
0
    OSL_FAIL( "Not yet implemented" );
2350
0
}
2351
2352
// DataSource ================================================================
2353
2354
ScChart2DataSource::ScChart2DataSource( ScDocument* pDoc)
2355
0
    : m_pDocument( pDoc)
2356
0
{
2357
0
    if ( m_pDocument )
2358
0
        m_pDocument->AddUnoObject( *this);
2359
0
}
2360
2361
ScChart2DataSource::~ScChart2DataSource()
2362
0
{
2363
0
    SolarMutexGuard g;
2364
2365
0
    if ( m_pDocument )
2366
0
        m_pDocument->RemoveUnoObject( *this);
2367
0
}
2368
2369
void ScChart2DataSource::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
2370
0
{
2371
0
    if ( rHint.GetId() == SfxHintId::Dying )
2372
0
    {
2373
0
        m_pDocument = nullptr;
2374
0
    }
2375
0
}
2376
2377
uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence> > SAL_CALL
2378
ScChart2DataSource::getDataSequences()
2379
0
{
2380
0
    SolarMutexGuard aGuard;
2381
0
    return comphelper::containerToSequence(m_aLabeledSequences);
2382
0
}
2383
2384
void ScChart2DataSource::AddLabeledSequence(const uno::Reference < chart2::data::XLabeledDataSequence >& xNew)
2385
0
{
2386
0
    m_aLabeledSequences.push_back(xNew);
2387
0
}
2388
2389
// DataSequence ==============================================================
2390
2391
ScChart2DataSequence::Item::Item()
2392
0
    : mfValue(std::numeric_limits<double>::quiet_NaN())
2393
0
    , mbIsValue(false)
2394
0
{
2395
0
}
2396
2397
ScChart2DataSequence::HiddenRangeListener::HiddenRangeListener(ScChart2DataSequence& rParent) :
2398
0
    mrParent(rParent)
2399
0
{
2400
0
}
2401
2402
ScChart2DataSequence::HiddenRangeListener::~HiddenRangeListener()
2403
0
{
2404
0
}
2405
2406
void ScChart2DataSequence::HiddenRangeListener::notify()
2407
0
{
2408
0
    mrParent.setDataChangedHint(true);
2409
0
}
2410
2411
ScChart2DataSequence::ScChart2DataSequence( ScDocument* pDoc,
2412
        std::vector<ScTokenRef>&& rTokens,
2413
        bool bIncludeHiddenCells )
2414
0
    : m_xDataArray(new std::vector<Item>)
2415
0
    , m_bIncludeHiddenCells( bIncludeHiddenCells)
2416
0
    , m_nObjectId( 0 )
2417
0
    , m_pDocument( pDoc)
2418
0
    , m_aTokens(std::move(rTokens))
2419
0
    , m_aPropSet(lcl_GetDataSequencePropertyMap())
2420
0
    , m_bGotDataChangedHint(false)
2421
0
    , m_bExtDataRebuildQueued(false)
2422
0
    , mbTimeBased(false)
2423
0
    , mnTimeBasedStart(0)
2424
0
    , mnTimeBasedEnd(0)
2425
0
    , mnCurrentTab(0)
2426
0
{
2427
0
    if ( m_pDocument )
2428
0
    {
2429
0
        m_pDocument->AddUnoObject( *this);
2430
0
        m_nObjectId = m_pDocument->GetNewUnoId();
2431
0
    }
2432
    // FIXME: real implementation of identifier and it's mapping to ranges.
2433
    // Reuse ScChartListener?
2434
2435
    // BM: don't use names of named ranges but the UI range strings
2436
//  String  aStr;
2437
//  rRangeList->Format( aStr, ScRefFlags::RANGE_ABS_3D, m_pDocument );
2438
//    m_aIdentifier = aStr;
2439
2440
//      m_aIdentifier = "ID_";
2441
//      static sal_Int32 nID = 0;
2442
//      m_aIdentifier += OUString::valueOf( ++nID);
2443
0
}
2444
2445
/** called from Clone() */
2446
ScChart2DataSequence::ScChart2DataSequence(ScDocument* pDoc, const ScChart2DataSequence& r)
2447
0
    : m_xDataArray(r.m_xDataArray)
2448
0
    , m_aHiddenValues(r.m_aHiddenValues)
2449
0
    , m_aRole(r.m_aRole)
2450
0
    , m_bIncludeHiddenCells( r.m_bIncludeHiddenCells)
2451
0
    , m_nObjectId( 0 )
2452
0
    , m_pDocument( pDoc)
2453
0
    , m_aPropSet(lcl_GetDataSequencePropertyMap())
2454
0
    , m_bGotDataChangedHint(false)
2455
0
    , m_bExtDataRebuildQueued(false)
2456
0
    , mbTimeBased(false)
2457
0
    , mnTimeBasedStart(0)
2458
0
    , mnTimeBasedEnd(0)
2459
0
    , mnCurrentTab(0)
2460
0
{
2461
0
    assert(pDoc);
2462
2463
    // Clone tokens.
2464
0
    m_aTokens.reserve(r.m_aTokens.size());
2465
0
    for (const auto& rxToken : r.m_aTokens)
2466
0
    {
2467
0
        ScTokenRef p(rxToken->Clone());
2468
0
        m_aTokens.push_back(p);
2469
0
    }
2470
2471
0
    m_pDocument->AddUnoObject( *this);
2472
0
    m_nObjectId = m_pDocument->GetNewUnoId();
2473
2474
0
    if (r.m_oRangeIndices)
2475
0
        m_oRangeIndices = *r.m_oRangeIndices;
2476
2477
0
    if (!r.m_pExtRefListener)
2478
0
        return;
2479
2480
    // Re-register all external files that the old instance was
2481
    // listening to.
2482
2483
0
    ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager();
2484
0
    m_pExtRefListener.reset(new ExternalRefListener(*this, m_pDocument));
2485
0
    const std::unordered_set<sal_uInt16>& rFileIds = r.m_pExtRefListener->getAllFileIds();
2486
0
    for (const auto& rFileId : rFileIds)
2487
0
    {
2488
0
        pRefMgr->addLinkListener(rFileId, m_pExtRefListener.get());
2489
0
        m_pExtRefListener->addFileId(rFileId);
2490
0
    }
2491
0
}
2492
2493
ScChart2DataSequence::~ScChart2DataSequence()
2494
0
{
2495
0
    SolarMutexGuard g;
2496
2497
0
    if ( m_pDocument )
2498
0
    {
2499
0
        m_pDocument->RemoveUnoObject( *this);
2500
0
        if (m_pHiddenListener)
2501
0
        {
2502
0
            ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection();
2503
0
            if (pCLC)
2504
0
                pCLC->EndListeningHiddenRange(m_pHiddenListener.get());
2505
0
        }
2506
0
        StopListeningToAllExternalRefs();
2507
0
    }
2508
2509
0
    m_pValueListener.reset();
2510
0
}
2511
2512
void ScChart2DataSequence::RefChanged()
2513
0
{
2514
0
    if( !m_pValueListener || m_aValueListeners.empty() )
2515
0
        return;
2516
2517
0
    m_pValueListener->EndListeningAll();
2518
2519
0
    if( !m_pDocument )
2520
0
        return;
2521
2522
0
    ScChartListenerCollection* pCLC = nullptr;
2523
0
    if (m_pHiddenListener)
2524
0
    {
2525
0
        pCLC = m_pDocument->GetChartListenerCollection();
2526
0
        if (pCLC)
2527
0
            pCLC->EndListeningHiddenRange(m_pHiddenListener.get());
2528
0
    }
2529
2530
0
    for (const auto& rxToken : m_aTokens)
2531
0
    {
2532
0
        ScRange aRange;
2533
0
        if (!ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, rxToken, ScAddress()))
2534
0
            continue;
2535
2536
0
        m_pDocument->StartListeningArea(aRange, false, m_pValueListener.get());
2537
0
        if (pCLC)
2538
0
            pCLC->StartListeningHiddenRange(aRange, m_pHiddenListener.get());
2539
0
    }
2540
0
}
2541
2542
void ScChart2DataSequence::BuildDataCache()
2543
0
{
2544
0
    m_bExtDataRebuildQueued = false;
2545
2546
0
    if (!m_xDataArray->empty())
2547
0
        return;
2548
2549
0
    StopListeningToAllExternalRefs();
2550
2551
0
    std::vector<sal_Int32> aHiddenValues;
2552
0
    sal_Int32 nDataCount = 0;
2553
2554
0
    for (const auto& rxToken : m_aTokens)
2555
0
    {
2556
0
        if (ScRefTokenHelper::isExternalRef(rxToken))
2557
0
        {
2558
0
            nDataCount += FillCacheFromExternalRef(rxToken);
2559
0
        }
2560
0
        else
2561
0
        {
2562
0
            ScRange aRange;
2563
0
            if (!ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, rxToken, ScAddress()))
2564
0
                continue;
2565
2566
0
            SCCOL nLastCol = -1;
2567
0
            SCROW nLastRow = -1;
2568
0
            for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab)
2569
0
            {
2570
0
                for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol)
2571
0
                {
2572
0
                    sc::ColumnBlockPosition hint;
2573
0
                    m_pDocument->InitColumnBlockPosition( hint, nTab, nCol );
2574
0
                    for (SCROW nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow)
2575
0
                    {
2576
0
                        if (nRow == aRange.aEnd.Row())
2577
0
                        {
2578
                            // Excel behavior: if the last row is the totals row, the data
2579
                            // is not added to the chart. If it's not the last row, the data
2580
                            // is added like normal.
2581
0
                            const auto* pData = m_pDocument->GetDBAtCursor(
2582
0
                                nCol, nRow, nTab,
2583
0
                                ScDBDataPortion::AREA
2584
0
                            );
2585
0
                            if (pData && pData->HasTotals())
2586
0
                            {
2587
0
                                ScRange aTempRange;
2588
0
                                pData->GetArea(aTempRange);
2589
0
                                if (aTempRange.aEnd.Row() == nRow)
2590
0
                                {
2591
                                    // Current row is totals row, skip
2592
0
                                    break;
2593
0
                                }
2594
0
                            }
2595
0
                        }
2596
0
                        bool bColHidden = m_pDocument->ColHidden(nCol, nTab, nullptr, &nLastCol);
2597
0
                        bool bRowHidden = m_pDocument->RowHidden(nRow, nTab, nullptr, &nLastRow);
2598
2599
0
                        if (bColHidden || bRowHidden)
2600
0
                        {
2601
                            // hidden cell
2602
0
                            aHiddenValues.push_back(nDataCount-1);
2603
2604
0
                            if( !m_bIncludeHiddenCells )
2605
0
                                continue;
2606
0
                        }
2607
2608
0
                        Item aItem;
2609
2610
0
                        ScAddress aAdr(nCol, nRow, nTab);
2611
0
                        aItem.maString = m_pDocument->GetString(aAdr);
2612
2613
0
                        ScRefCellValue aCell(*m_pDocument, aAdr, hint);
2614
0
                        switch (aCell.getType())
2615
0
                        {
2616
0
                            case CELLTYPE_VALUE:
2617
0
                                aItem.mfValue = aCell.getValue();
2618
0
                                aItem.mbIsValue = true;
2619
0
                            break;
2620
0
                            case CELLTYPE_FORMULA:
2621
0
                            {
2622
0
                                ScFormulaCell* pFCell = aCell.getFormula();
2623
0
                                FormulaError nErr = pFCell->GetErrCode();
2624
0
                                if (nErr != FormulaError::NONE)
2625
0
                                    break;
2626
2627
0
                                if (pFCell->IsValue())
2628
0
                                {
2629
0
                                    aItem.mfValue = pFCell->GetValue();
2630
0
                                    aItem.mbIsValue = true;
2631
0
                                }
2632
0
                            }
2633
0
                            break;
2634
0
                            case CELLTYPE_EDIT:
2635
0
                            case CELLTYPE_NONE:
2636
0
                            case CELLTYPE_STRING:
2637
0
                            default:
2638
0
                                ; // do nothing
2639
0
                        }
2640
2641
0
                        aItem.mAddress = ScAddress(nCol, nRow, nTab);
2642
2643
0
                        m_xDataArray->push_back(std::move(aItem));
2644
0
                        ++nDataCount;
2645
0
                    }
2646
0
                }
2647
0
            }
2648
0
        }
2649
0
    }
2650
2651
    // convert the hidden cell list to sequence.
2652
0
    m_aHiddenValues.realloc(aHiddenValues.size());
2653
0
    std::copy(
2654
0
        aHiddenValues.begin(), aHiddenValues.end(), m_aHiddenValues.getArray());
2655
2656
    // Clear the data series cache when the array is re-built.
2657
0
    m_aMixedDataCache.realloc(0);
2658
0
}
2659
2660
void ScChart2DataSequence::RebuildDataCache()
2661
0
{
2662
0
    if (!m_bExtDataRebuildQueued)
2663
0
    {
2664
0
        m_xDataArray.reset(new std::vector<Item>);
2665
0
        m_pDocument->BroadcastUno(ScHint(SfxHintId::ScDataChanged, ScAddress()));
2666
0
        m_bExtDataRebuildQueued = true;
2667
0
        m_bGotDataChangedHint = true;
2668
0
    }
2669
0
}
2670
2671
sal_Int32 ScChart2DataSequence::FillCacheFromExternalRef(const ScTokenRef& pToken)
2672
0
{
2673
0
    ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager();
2674
0
    ScRange aRange;
2675
0
    if (!ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, pToken, ScAddress(), true))
2676
0
        return 0;
2677
2678
0
    sal_uInt16 nFileId = pToken->GetIndex();
2679
0
    OUString aTabName = pToken->GetString().getString();
2680
0
    ScExternalRefCache::TokenArrayRef pArray = pRefMgr->getDoubleRefTokens(nFileId, aTabName, aRange, nullptr);
2681
0
    if (!pArray)
2682
        // no external data exists for this range.
2683
0
        return 0;
2684
2685
    // Start listening for this external document.
2686
0
    ExternalRefListener* pExtRefListener = GetExtRefListener();
2687
0
    pRefMgr->addLinkListener(nFileId, pExtRefListener);
2688
0
    pExtRefListener->addFileId(nFileId);
2689
2690
0
    m_xDataArray.reset(new std::vector<Item>);
2691
0
    ScExternalRefCache::TableTypeRef pTable = pRefMgr->getCacheTable(nFileId, aTabName, false);
2692
0
    sal_Int32 nDataCount = 0;
2693
0
    FormulaTokenArrayPlainIterator aIter(*pArray);
2694
0
    for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
2695
0
    {
2696
        // Cached external range is always represented as a single
2697
        // matrix token, although that might change in the future when
2698
        // we introduce a new token type to store multi-table range
2699
        // data.
2700
2701
0
        if (p->GetType() != svMatrix)
2702
0
        {
2703
0
            OSL_FAIL("Cached array is not a matrix token.");
2704
0
            continue;
2705
0
        }
2706
2707
0
        const ScMatrix* pMat = p->GetMatrix();
2708
0
        SCSIZE nCSize, nRSize;
2709
0
        pMat->GetDimensions(nCSize, nRSize);
2710
0
        for (SCSIZE nC = 0; nC < nCSize; ++nC)
2711
0
        {
2712
0
            for (SCSIZE nR = 0; nR < nRSize; ++nR)
2713
0
            {
2714
0
                if (pMat->IsValue(nC, nR) || pMat->IsBoolean(nC, nR))
2715
0
                {
2716
0
                    Item aItem;
2717
2718
0
                    aItem.mbIsValue = true;
2719
0
                    aItem.mfValue = pMat->GetDouble(nC, nR);
2720
2721
0
                    SvNumberFormatter* pFormatter = m_pDocument->GetFormatTable();
2722
0
                    if (pFormatter)
2723
0
                    {
2724
0
                        const double fVal = aItem.mfValue;
2725
0
                        const Color* pColor = nullptr;
2726
0
                        sal_uInt32 nFmt = 0;
2727
0
                        if (pTable)
2728
0
                        {
2729
                            // Get the correct format index from the cache.
2730
0
                            SCCOL nCol = aRange.aStart.Col() + static_cast<SCCOL>(nC);
2731
0
                            SCROW nRow = aRange.aStart.Row() + static_cast<SCROW>(nR);
2732
0
                            pTable->getCell(nCol, nRow, &nFmt);
2733
0
                        }
2734
0
                        pFormatter->GetOutputString(fVal, nFmt, aItem.maString, &pColor);
2735
0
                    }
2736
2737
0
                    m_xDataArray->push_back(std::move(aItem));
2738
0
                    ++nDataCount;
2739
0
                }
2740
0
                else if (pMat->IsStringOrEmpty(nC, nR))
2741
0
                {
2742
0
                    Item aItem;
2743
2744
0
                    aItem.mbIsValue = false;
2745
0
                    aItem.maString = pMat->GetString(nC, nR).getString();
2746
2747
0
                    m_xDataArray->push_back(std::move(aItem));
2748
0
                    ++nDataCount;
2749
0
                }
2750
0
            }
2751
0
        }
2752
0
    }
2753
0
    return nDataCount;
2754
0
}
2755
2756
void ScChart2DataSequence::UpdateTokensFromRanges(const ScRangeList& rRanges)
2757
0
{
2758
0
    if (!m_oRangeIndices)
2759
0
        return;
2760
2761
0
    for ( size_t i = 0, nCount = rRanges.size(); i < nCount; ++i )
2762
0
    {
2763
0
        ScTokenRef pToken;
2764
0
        const ScRange & rRange = rRanges[i];
2765
2766
0
        ScRefTokenHelper::getTokenFromRange(m_pDocument, pToken, rRange);
2767
0
        sal_uInt32 nOrigPos = (*m_oRangeIndices)[i];
2768
0
        m_aTokens[nOrigPos] = std::move(pToken);
2769
0
    }
2770
2771
0
    RefChanged();
2772
2773
    // any change of the range address is broadcast to value (modify) listeners
2774
0
    if ( !m_aValueListeners.empty() )
2775
0
        m_bGotDataChangedHint = true;
2776
0
}
2777
2778
ScChart2DataSequence::ExternalRefListener* ScChart2DataSequence::GetExtRefListener()
2779
0
{
2780
0
    if (!m_pExtRefListener)
2781
0
        m_pExtRefListener.reset(new ExternalRefListener(*this, m_pDocument));
2782
2783
0
    return m_pExtRefListener.get();
2784
0
}
2785
2786
void ScChart2DataSequence::StopListeningToAllExternalRefs()
2787
0
{
2788
0
    if (!m_pExtRefListener)
2789
0
        return;
2790
2791
0
    const std::unordered_set<sal_uInt16>& rFileIds = m_pExtRefListener->getAllFileIds();
2792
0
    ScExternalRefManager* pRefMgr = m_pDocument->GetExternalRefManager();
2793
0
    for (const auto& rFileId : rFileIds)
2794
0
        pRefMgr->removeLinkListener(rFileId, m_pExtRefListener.get());
2795
2796
0
    m_pExtRefListener.reset();
2797
0
}
2798
2799
2800
void ScChart2DataSequence::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
2801
0
{
2802
0
    if ( rHint.GetId() == SfxHintId::ScUpdateRef )
2803
0
    {
2804
        // Create a range list from the token list, have the range list
2805
        // updated, and bring the change back to the token list.
2806
2807
0
        ScRangeList aRanges;
2808
0
        m_oRangeIndices.emplace();
2809
0
        std::vector<ScTokenRef>::const_iterator itrBeg = m_aTokens.begin(), itrEnd = m_aTokens.end();
2810
0
        for (std::vector<ScTokenRef>::const_iterator itr = itrBeg ;itr != itrEnd; ++itr)
2811
0
        {
2812
0
            if (!ScRefTokenHelper::isExternalRef(*itr))
2813
0
            {
2814
0
                ScRange aRange;
2815
0
                ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, *itr, ScAddress());
2816
0
                aRanges.push_back(aRange);
2817
0
                sal_uInt32 nPos = std::distance(itrBeg, itr);
2818
0
                m_oRangeIndices->push_back(nPos);
2819
0
            }
2820
0
        }
2821
2822
0
        assert(m_oRangeIndices->size() == aRanges.size() &&
2823
0
                   "range list and range index list have different sizes.");
2824
2825
0
        std::unique_ptr<ScRangeList> pUndoRanges;
2826
0
        if ( m_pDocument->HasUnoRefUndo() )
2827
0
            pUndoRanges.reset(new ScRangeList(aRanges));
2828
2829
0
        const ScUpdateRefHint& rRef = static_cast<const ScUpdateRefHint&>(rHint);
2830
0
        bool bChanged = aRanges.UpdateReference(
2831
0
            rRef.GetMode(), *m_pDocument, rRef.GetRange(), rRef.GetDx(), rRef.GetDy(), rRef.GetDz());
2832
2833
0
        if (bChanged)
2834
0
        {
2835
            // TODO: This should be an assert, but tdf#144537 triggers it.
2836
0
            SAL_WARN_IF(m_oRangeIndices->size() == aRanges.size(),
2837
0
                        "sc.ui", "range list and range index list have different sizes after the reference update.");
2838
2839
            // Bring the change back from the range list to the token list.
2840
0
            UpdateTokensFromRanges(aRanges);
2841
2842
0
            if (pUndoRanges)
2843
0
                m_pDocument->AddUnoRefChange(m_nObjectId, *pUndoRanges);
2844
0
        }
2845
0
    }
2846
0
    else if ( rHint.GetId() == SfxHintId::ScUnoRefUndo )
2847
0
    {
2848
0
        auto pUndoHint = static_cast<const ScUnoRefUndoHint*>(&rHint);
2849
0
        do
2850
0
        {
2851
0
            if (pUndoHint->GetObjectId() != m_nObjectId)
2852
0
                break;
2853
2854
            // The hint object provides the old ranges.  Restore the old state
2855
            // from these ranges.
2856
2857
0
            if (!m_oRangeIndices || m_oRangeIndices->empty())
2858
0
            {
2859
0
                assert(false && " faulty range indices");
2860
0
                break;
2861
0
            }
2862
2863
0
            const ScRangeList& rRanges = pUndoHint->GetRanges();
2864
2865
0
            size_t nCount = rRanges.size();
2866
0
            if (nCount != m_oRangeIndices->size())
2867
0
            {
2868
0
                assert(false && "range count and range index count differ.");
2869
0
                break;
2870
0
            }
2871
2872
0
            UpdateTokensFromRanges(rRanges);
2873
0
        }
2874
0
        while (false);
2875
0
    }
2876
0
    else
2877
0
    {
2878
0
        const SfxHintId nId = rHint.GetId();
2879
0
        if ( nId ==SfxHintId::Dying )
2880
0
        {
2881
0
            m_pDocument = nullptr;
2882
0
        }
2883
0
        else if ( nId == SfxHintId::DataChanged )
2884
0
        {
2885
            // delayed broadcast as in ScCellRangesBase
2886
2887
0
            if ( m_bGotDataChangedHint && m_pDocument )
2888
0
            {
2889
0
                m_xDataArray.reset(new std::vector<Item>);
2890
0
                lang::EventObject aEvent;
2891
0
                aEvent.Source = getXWeak();
2892
2893
0
                if( m_pDocument )
2894
0
                {
2895
0
                    for (const uno::Reference<util::XModifyListener> & xListener: m_aValueListeners)
2896
0
                        m_pDocument->AddUnoListenerCall( xListener, aEvent );
2897
0
                }
2898
2899
0
                m_bGotDataChangedHint = false;
2900
0
            }
2901
0
        }
2902
0
        else if ( nId == SfxHintId::ScCalcAll )
2903
0
        {
2904
            // broadcast from DoHardRecalc - set m_bGotDataChangedHint
2905
            // (SfxHintId::DataChanged follows separately)
2906
2907
0
            if ( !m_aValueListeners.empty() )
2908
0
                m_bGotDataChangedHint = true;
2909
0
        }
2910
0
        else if (nId == SfxHintId::ScClearCache)
2911
0
        {
2912
            // necessary after import
2913
0
            m_xDataArray.reset(new std::vector<Item>);
2914
0
        }
2915
0
    }
2916
0
}
2917
2918
IMPL_LINK( ScChart2DataSequence, ValueListenerHdl, const SfxHint&, rHint, void )
2919
0
{
2920
0
    if ( m_pDocument && (rHint.GetId() == SfxHintId::ScDataChanged) )
2921
0
    {
2922
        //  This may be called several times for a single change, if several formulas
2923
        //  in the range are notified. So only a flag is set that is checked when
2924
        //  SfxHintId::DataChanged is received.
2925
2926
0
        setDataChangedHint(true);
2927
0
    }
2928
0
}
2929
2930
ScChart2DataSequence::ExternalRefListener::ExternalRefListener(
2931
    ScChart2DataSequence& rParent, ScDocument* pDoc) :
2932
0
    mrParent(rParent),
2933
0
    mpDoc(pDoc)
2934
0
{
2935
0
}
2936
2937
ScChart2DataSequence::ExternalRefListener::~ExternalRefListener()
2938
0
{
2939
0
    if (!mpDoc || mpDoc->IsInDtorClear())
2940
        // The document is being destroyed.  Do nothing.
2941
0
        return;
2942
2943
    // Make sure to remove all pointers to this object.
2944
0
    mpDoc->GetExternalRefManager()->removeLinkListener(this);
2945
0
}
2946
2947
void ScChart2DataSequence::ExternalRefListener::notify(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType)
2948
0
{
2949
0
    switch (eType)
2950
0
    {
2951
0
        case ScExternalRefManager::LINK_MODIFIED:
2952
0
        {
2953
0
            if (maFileIds.count(nFileId))
2954
                // We are listening to this external document.
2955
0
                mrParent.RebuildDataCache();
2956
0
        }
2957
0
        break;
2958
0
        case ScExternalRefManager::LINK_BROKEN:
2959
0
            maFileIds.erase(nFileId);
2960
0
        break;
2961
0
        case ScExternalRefManager::OH_NO_WE_ARE_GOING_TO_DIE:
2962
0
            mpDoc = nullptr;
2963
0
        break;
2964
0
    }
2965
0
}
2966
2967
void ScChart2DataSequence::ExternalRefListener::addFileId(sal_uInt16 nFileId)
2968
0
{
2969
0
    maFileIds.insert(nFileId);
2970
0
}
2971
2972
uno::Sequence< uno::Any> SAL_CALL ScChart2DataSequence::getData()
2973
0
{
2974
0
    SolarMutexGuard aGuard;
2975
0
    if ( !m_pDocument)
2976
0
        throw uno::RuntimeException();
2977
2978
0
    BuildDataCache();
2979
2980
0
    if (!m_aMixedDataCache.hasElements())
2981
0
    {
2982
        // Build a cache for the 1st time...
2983
2984
0
        sal_Int32 nCount = m_xDataArray->size();
2985
0
        m_aMixedDataCache.realloc(nCount);
2986
0
        uno::Any* pArr = m_aMixedDataCache.getArray();
2987
0
        for (const Item &rItem : *m_xDataArray)
2988
0
        {
2989
0
            if (rItem.mbIsValue)
2990
0
                *pArr <<= rItem.mfValue;
2991
0
            else if (rItem.maString.isEmpty())
2992
0
            {
2993
0
                ScRefCellValue aCell(*m_pDocument, rItem.mAddress);
2994
0
                if (aCell.isEmpty())
2995
0
                   *pArr = uno::Any();
2996
0
                else
2997
0
                   *pArr <<= rItem.maString;
2998
0
            }
2999
0
            else
3000
0
                *pArr <<= rItem.maString;
3001
0
            ++pArr;
3002
0
        }
3003
0
    }
3004
0
    return m_aMixedDataCache;
3005
0
}
3006
3007
// XNumericalDataSequence --------------------------------------------------
3008
3009
uno::Sequence< double > SAL_CALL ScChart2DataSequence::getNumericalData()
3010
0
{
3011
0
    SolarMutexGuard aGuard;
3012
0
    if ( !m_pDocument)
3013
0
        throw uno::RuntimeException();
3014
3015
0
    BuildDataCache();
3016
3017
0
    sal_Int32 nCount = m_xDataArray->size();
3018
0
    uno::Sequence<double> aSeq(nCount);
3019
0
    double* pArr = aSeq.getArray();
3020
0
    for (const Item& rItem : *m_xDataArray)
3021
0
    {
3022
0
        *pArr = rItem.mbIsValue ? rItem.mfValue : std::numeric_limits<double>::quiet_NaN();
3023
0
        ++pArr;
3024
0
    }
3025
3026
0
    return aSeq;
3027
0
}
3028
3029
// XTextualDataSequence --------------------------------------------------
3030
3031
uno::Sequence< OUString > SAL_CALL ScChart2DataSequence::getTextualData()
3032
0
{
3033
0
    SolarMutexGuard aGuard;
3034
0
    uno::Sequence<OUString> aSeq;
3035
0
    if ( !m_pDocument )
3036
0
        throw uno::RuntimeException();
3037
3038
0
    BuildDataCache();
3039
3040
0
    sal_Int32 nCount = m_xDataArray->size();
3041
0
    if ( nCount > 0 )
3042
0
    {
3043
0
        aSeq =  uno::Sequence<OUString>(nCount);
3044
0
        OUString* pArr = aSeq.getArray();
3045
0
        for (const Item& rItem : *m_xDataArray)
3046
0
        {
3047
0
            *pArr = rItem.maString;
3048
0
            ++pArr;
3049
0
        }
3050
0
    }
3051
0
    else if ( m_aTokens.front() )
3052
0
    {
3053
0
        if( m_aTokens.front()->GetType() == svString )
3054
0
        {
3055
0
            aSeq = uno::Sequence<OUString> { m_aTokens.front()->GetString().getString() };
3056
0
        }
3057
0
    }
3058
3059
0
    return aSeq;
3060
0
}
3061
3062
OUString SAL_CALL ScChart2DataSequence::getSourceRangeRepresentation()
3063
0
{
3064
0
    SolarMutexGuard aGuard;
3065
0
    OUString aStr;
3066
0
    OSL_ENSURE( m_pDocument, "No Document -> no SourceRangeRepresentation" );
3067
0
    if (m_pDocument)
3068
0
        lcl_convertTokensToString(aStr, m_aTokens, *m_pDocument);
3069
3070
0
    return aStr;
3071
0
}
3072
3073
namespace {
3074
3075
/**
3076
 * This function object is used to accumulatively count the numbers of
3077
 * columns and rows in all reference tokens.
3078
 */
3079
class AccumulateRangeSize
3080
{
3081
public:
3082
    AccumulateRangeSize(const ScDocument* pDoc) :
3083
0
        mpDoc(pDoc), mnCols(0), mnRows(0) {}
3084
3085
    void operator() (const ScTokenRef& pToken)
3086
0
    {
3087
0
        ScRange r;
3088
0
        bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
3089
0
        ScRefTokenHelper::getRangeFromToken(mpDoc, r, pToken, ScAddress(), bExternal);
3090
0
        r.PutInOrder();
3091
0
        mnCols += r.aEnd.Col() - r.aStart.Col() + 1;
3092
0
        mnRows += r.aEnd.Row() - r.aStart.Row() + 1;
3093
0
    }
3094
3095
0
    SCCOL getCols() const { return mnCols; }
3096
0
    SCROW getRows() const { return mnRows; }
3097
private:
3098
    const ScDocument* mpDoc;
3099
    SCCOL mnCols;
3100
    SCROW mnRows;
3101
};
3102
3103
/**
3104
 * This function object is used to generate label strings from a list of
3105
 * reference tokens.
3106
 */
3107
class GenerateLabelStrings
3108
{
3109
public:
3110
    GenerateLabelStrings(const ScDocument* pDoc, sal_Int32 nSize, chart2::data::LabelOrigin eOrigin, bool bColumn) :
3111
0
        mpDoc(pDoc),
3112
0
        mpLabels(std::make_shared<uno::Sequence<OUString>>(nSize)),
3113
0
        meOrigin(eOrigin),
3114
0
        mnCount(0),
3115
0
        mbColumn(bColumn) {}
3116
3117
    void operator() (const ScTokenRef& pToken)
3118
0
    {
3119
0
        bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
3120
0
        ScRange aRange;
3121
0
        ScRefTokenHelper::getRangeFromToken(mpDoc, aRange, pToken, ScAddress(), bExternal);
3122
0
        OUString* pArr = mpLabels->getArray();
3123
0
        if (mbColumn)
3124
0
        {
3125
0
            for (SCCOL nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); ++nCol)
3126
0
            {
3127
0
                if ( meOrigin != chart2::data::LabelOrigin_LONG_SIDE)
3128
0
                {
3129
0
                    OUString aString = ScResId(STR_COLUMN) + " ";
3130
0
                    ScAddress aPos( nCol, 0, 0 );
3131
0
                    OUString aColStr(aPos.Format(ScRefFlags::COL_VALID));
3132
0
                    aString += aColStr;
3133
0
                    pArr[mnCount] = aString;
3134
0
                }
3135
0
                else //only indices for categories
3136
0
                    pArr[mnCount] = OUString::number( mnCount+1 );
3137
0
                ++mnCount;
3138
0
            }
3139
0
        }
3140
0
        else
3141
0
        {
3142
0
            for (sal_Int32 nRow = aRange.aStart.Row(); nRow <= aRange.aEnd.Row(); ++nRow)
3143
0
            {
3144
0
                if (meOrigin != chart2::data::LabelOrigin_LONG_SIDE)
3145
0
                    pArr[mnCount] = ScResId(STR_ROW) + " " + OUString::number(nRow + 1);
3146
0
                else //only indices for categories
3147
0
                    pArr[mnCount] = OUString::number( mnCount+1 );
3148
0
                ++mnCount;
3149
0
            }
3150
0
        }
3151
0
    }
3152
3153
0
    const uno::Sequence<OUString>& getLabels() const { return *mpLabels; }
3154
3155
private:
3156
    const ScDocument*                   mpDoc;
3157
    std::shared_ptr<uno::Sequence<OUString>>    mpLabels;
3158
    chart2::data::LabelOrigin           meOrigin;
3159
    sal_Int32                           mnCount;
3160
    bool                                mbColumn;
3161
};
3162
3163
}
3164
3165
uno::Sequence< OUString > SAL_CALL ScChart2DataSequence::generateLabel(chart2::data::LabelOrigin eOrigin)
3166
0
{
3167
0
    SolarMutexGuard aGuard;
3168
0
    if ( !m_pDocument)
3169
0
        throw uno::RuntimeException();
3170
3171
    // Determine the total size of all ranges.
3172
0
    AccumulateRangeSize func(m_pDocument);
3173
0
    func = ::std::for_each(m_aTokens.begin(), m_aTokens.end(), func);
3174
0
    SCCOL nCols = func.getCols();
3175
0
    SCROW nRows = func.getRows();
3176
3177
    // Determine whether this is column-major or row-major.
3178
0
    bool bColumn = true;
3179
0
    if ((eOrigin == chart2::data::LabelOrigin_SHORT_SIDE) ||
3180
0
        (eOrigin == chart2::data::LabelOrigin_LONG_SIDE))
3181
0
    {
3182
0
        if (nRows > nCols)
3183
0
        {
3184
0
            bColumn = eOrigin == chart2::data::LabelOrigin_SHORT_SIDE;
3185
0
        }
3186
0
        else if (nCols > nRows)
3187
0
        {
3188
0
            bColumn = eOrigin != chart2::data::LabelOrigin_SHORT_SIDE;
3189
0
        }
3190
0
        else
3191
0
            return uno::Sequence<OUString>();
3192
0
    }
3193
3194
    // Generate label strings based on the info so far.
3195
0
    sal_Int32 nCount = bColumn ? nCols : nRows;
3196
0
    GenerateLabelStrings genLabels(m_pDocument, nCount, eOrigin, bColumn);
3197
0
    genLabels = ::std::for_each(m_aTokens.begin(), m_aTokens.end(), genLabels);
3198
0
    uno::Sequence<OUString> aSeq = genLabels.getLabels();
3199
3200
0
    return aSeq;
3201
0
}
3202
3203
namespace {
3204
3205
sal_uInt32 getDisplayNumberFormat(const ScDocument* pDoc, const ScAddress& rPos)
3206
0
{
3207
0
    sal_uInt32 nFormat = pDoc->GetNumberFormat(ScRange(rPos)); // original format from cell.
3208
0
    return nFormat;
3209
0
}
3210
3211
}
3212
3213
::sal_Int32 SAL_CALL ScChart2DataSequence::getNumberFormatKeyByIndex( ::sal_Int32 nIndex )
3214
0
{
3215
0
    SolarMutexGuard aGuard;
3216
0
    BuildDataCache();
3217
3218
0
    if (nIndex == -1)
3219
0
    {
3220
        // return format of first non-empty cell
3221
        // TODO: use nicer heuristic
3222
0
        for (const Item& rItem : *m_xDataArray)
3223
0
        {
3224
0
            ScRefCellValue aCell(*m_pDocument, rItem.mAddress);
3225
0
            if (!aCell.isEmpty() && aCell.hasNumeric())
3226
0
            {
3227
0
                return static_cast<sal_Int32>(getDisplayNumberFormat(m_pDocument, rItem.mAddress));
3228
0
            }
3229
0
        }
3230
3231
        // we could not find a non-empty cell
3232
0
        return 0;
3233
0
    }
3234
3235
0
    if (nIndex < 0 || o3tl::make_unsigned(nIndex) >= m_xDataArray->size())
3236
0
    {
3237
0
        SAL_WARN("sc.ui", "Passed invalid index to getNumberFormatKeyByIndex(). Will return default value '0'.");
3238
0
        return 0;
3239
0
    }
3240
3241
0
    return static_cast<sal_Int32>(getDisplayNumberFormat(m_pDocument, m_xDataArray->at(nIndex).mAddress));
3242
0
}
3243
3244
// XCloneable ================================================================
3245
3246
uno::Reference< util::XCloneable > SAL_CALL ScChart2DataSequence::createClone()
3247
0
{
3248
0
    SolarMutexGuard aGuard;
3249
3250
0
    rtl::Reference<ScChart2DataSequence> p(new ScChart2DataSequence(m_pDocument, *this));
3251
0
    return p;
3252
0
}
3253
3254
// XModifyBroadcaster ========================================================
3255
3256
void SAL_CALL ScChart2DataSequence::addModifyListener( const uno::Reference< util::XModifyListener >& aListener )
3257
0
{
3258
    // like ScCellRangesBase::addModifyListener
3259
0
    SolarMutexGuard aGuard;
3260
0
    if (m_aTokens.empty())
3261
0
        return;
3262
3263
0
    ScRangeList aRanges;
3264
0
    ScRefTokenHelper::getRangeListFromTokens(m_pDocument, aRanges, m_aTokens, ScAddress());
3265
0
    m_aValueListeners.emplace_back( aListener );
3266
3267
0
    if ( m_aValueListeners.size() != 1 )
3268
0
        return;
3269
3270
0
    if (!m_pValueListener)
3271
0
        m_pValueListener.reset(new ScLinkListener( LINK( this, ScChart2DataSequence, ValueListenerHdl ) ));
3272
3273
0
    if (!m_pHiddenListener)
3274
0
        m_pHiddenListener.reset(new HiddenRangeListener(*this));
3275
3276
0
    if( m_pDocument )
3277
0
    {
3278
0
        ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection();
3279
0
        for (const auto& rxToken : m_aTokens)
3280
0
        {
3281
0
            ScRange aRange;
3282
0
            if (!ScRefTokenHelper::getRangeFromToken(m_pDocument, aRange, rxToken, ScAddress()))
3283
0
                continue;
3284
3285
0
            m_pDocument->StartListeningArea( aRange, false, m_pValueListener.get() );
3286
0
            if (pCLC)
3287
0
                pCLC->StartListeningHiddenRange(aRange, m_pHiddenListener.get());
3288
0
        }
3289
0
    }
3290
3291
0
    acquire();  // don't lose this object (one ref for all listeners)
3292
0
}
3293
3294
void SAL_CALL ScChart2DataSequence::removeModifyListener( const uno::Reference< util::XModifyListener >& aListener )
3295
0
{
3296
    // like ScCellRangesBase::removeModifyListener
3297
3298
0
    SolarMutexGuard aGuard;
3299
0
    if (m_aTokens.empty())
3300
0
        return;
3301
3302
0
    rtl::Reference<ScChart2DataSequence> xSelfHold(this);      // in case the listeners have the last ref
3303
3304
0
    sal_uInt16 nCount = m_aValueListeners.size();
3305
0
    for ( sal_uInt16 n=nCount; n--; )
3306
0
    {
3307
0
        uno::Reference<util::XModifyListener>& rObj = m_aValueListeners[n];
3308
0
        if ( rObj == aListener )
3309
0
        {
3310
0
            m_aValueListeners.erase( m_aValueListeners.begin() + n );
3311
3312
0
            if ( m_aValueListeners.empty() )
3313
0
            {
3314
0
                if (m_pValueListener)
3315
0
                    m_pValueListener->EndListeningAll();
3316
3317
0
                if (m_pHiddenListener && m_pDocument)
3318
0
                {
3319
0
                    ScChartListenerCollection* pCLC = m_pDocument->GetChartListenerCollection();
3320
0
                    if (pCLC)
3321
0
                        pCLC->EndListeningHiddenRange(m_pHiddenListener.get());
3322
0
                }
3323
3324
0
                release();      // release the ref for the listeners
3325
0
            }
3326
3327
0
            break;
3328
0
        }
3329
0
    }
3330
0
}
3331
3332
// DataSequence XPropertySet -------------------------------------------------
3333
3334
uno::Reference< beans::XPropertySetInfo> SAL_CALL
3335
ScChart2DataSequence::getPropertySetInfo()
3336
0
{
3337
0
    SolarMutexGuard aGuard;
3338
0
    static uno::Reference<beans::XPropertySetInfo> aRef =
3339
0
        new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() );
3340
0
    return aRef;
3341
0
}
3342
3343
void SAL_CALL ScChart2DataSequence::setPropertyValue(
3344
        const OUString& rPropertyName, const uno::Any& rValue)
3345
0
{
3346
0
    if ( rPropertyName == SC_UNONAME_ROLE )
3347
0
    {
3348
0
        if ( !(rValue >>= m_aRole))
3349
0
            throw lang::IllegalArgumentException();
3350
0
    }
3351
0
    else if ( rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS )
3352
0
    {
3353
0
        bool bOldValue = m_bIncludeHiddenCells;
3354
0
        if ( !(rValue >>= m_bIncludeHiddenCells))
3355
0
            throw lang::IllegalArgumentException();
3356
0
        if( bOldValue != m_bIncludeHiddenCells )
3357
0
            m_xDataArray.reset(new std::vector<Item>);//data array is dirty now
3358
0
    }
3359
0
    else if( rPropertyName == "TimeBased" )
3360
0
    {
3361
0
        bool bTimeBased = mbTimeBased;
3362
0
        rValue>>= bTimeBased;
3363
0
        mbTimeBased = bTimeBased;
3364
0
    }
3365
0
    else
3366
0
        throw beans::UnknownPropertyException(rPropertyName);
3367
    // TODO: support optional properties
3368
0
}
3369
3370
uno::Any SAL_CALL ScChart2DataSequence::getPropertyValue(const OUString& rPropertyName)
3371
0
{
3372
0
    uno::Any aRet;
3373
0
    if ( rPropertyName == SC_UNONAME_ROLE )
3374
0
        aRet <<= m_aRole;
3375
0
    else if ( rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS )
3376
0
        aRet <<= m_bIncludeHiddenCells;
3377
0
    else if ( rPropertyName == SC_UNONAME_HIDDENVALUES )
3378
0
    {
3379
        // This property is read-only thus cannot be set externally via
3380
        // setPropertyValue(...).
3381
0
        BuildDataCache();
3382
0
        aRet <<= m_aHiddenValues;
3383
0
    }
3384
0
    else if (rPropertyName == SC_UNONAME_TIME_BASED)
3385
0
    {
3386
0
        aRet <<= mbTimeBased;
3387
0
    }
3388
0
    else if (rPropertyName == SC_UNONAME_HAS_STRING_LABEL)
3389
0
    {
3390
        // Read-only property.  It returns whether or not the label value is a
3391
        // direct user input, rather than an indirect reference.
3392
0
        bool bHasStringLabel = false;
3393
0
        if (m_aTokens.size() == 1)
3394
0
        {
3395
0
            const formula::FormulaToken& rToken = *m_aTokens[0];
3396
0
            bHasStringLabel = rToken.GetType() == formula::svString;
3397
0
        }
3398
0
        aRet <<= bHasStringLabel;
3399
0
    }
3400
0
    else
3401
0
        throw beans::UnknownPropertyException(rPropertyName);
3402
    // TODO: support optional properties
3403
0
    return aRet;
3404
0
}
3405
3406
void SAL_CALL ScChart2DataSequence::addPropertyChangeListener(
3407
        const OUString& /*rPropertyName*/,
3408
        const uno::Reference< beans::XPropertyChangeListener>& /*xListener*/)
3409
0
{
3410
    // FIXME: real implementation
3411
0
    OSL_FAIL( "Not yet implemented" );
3412
0
}
3413
3414
void SAL_CALL ScChart2DataSequence::removePropertyChangeListener(
3415
        const OUString& /*rPropertyName*/,
3416
        const uno::Reference< beans::XPropertyChangeListener>& /*rListener*/)
3417
0
{
3418
    // FIXME: real implementation
3419
0
    OSL_FAIL( "Not yet implemented" );
3420
0
}
3421
3422
void SAL_CALL ScChart2DataSequence::addVetoableChangeListener(
3423
        const OUString& /*rPropertyName*/,
3424
        const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/)
3425
0
{
3426
    // FIXME: real implementation
3427
0
    OSL_FAIL( "Not yet implemented" );
3428
0
}
3429
3430
void SAL_CALL ScChart2DataSequence::removeVetoableChangeListener(
3431
        const OUString& /*rPropertyName*/,
3432
        const uno::Reference< beans::XVetoableChangeListener>& /*rListener*/)
3433
0
{
3434
    // FIXME: real implementation
3435
0
    OSL_FAIL( "Not yet implemented" );
3436
0
}
3437
3438
void ScChart2DataSequence::setDataChangedHint(bool b)
3439
0
{
3440
0
    m_bGotDataChangedHint = b;
3441
0
}
3442
3443
sal_Bool ScChart2DataSequence::switchToNext(sal_Bool bWrap)
3444
0
{
3445
0
    if(!mbTimeBased)
3446
0
        return true;
3447
3448
0
    if(mnCurrentTab >= mnTimeBasedEnd)
3449
0
    {
3450
0
        if(bWrap)
3451
0
            setToPointInTime(0);
3452
0
        return false;
3453
0
    }
3454
3455
0
    for(const auto& rxToken : m_aTokens)
3456
0
    {
3457
0
        if (rxToken->GetType() != svDoubleRef)
3458
0
            continue;
3459
3460
0
        ScComplexRefData& rData = *rxToken->GetDoubleRef();
3461
0
        ScSingleRefData& s = rData.Ref1;
3462
0
        ScSingleRefData& e = rData.Ref2;
3463
3464
0
        s.IncTab(1);
3465
0
        e.IncTab(1);
3466
0
    }
3467
3468
0
    ++mnCurrentTab;
3469
3470
0
    RebuildDataCache();
3471
3472
0
    return true;
3473
0
}
3474
3475
void ScChart2DataSequence::setRange(sal_Int32 nStart, sal_Int32 nEnd)
3476
0
{
3477
0
    mnTimeBasedStart = nStart;
3478
0
    mnTimeBasedEnd = nEnd;
3479
0
    mnCurrentTab = mnTimeBasedStart;
3480
0
}
3481
3482
sal_Bool ScChart2DataSequence::setToPointInTime(sal_Int32 nPoint)
3483
0
{
3484
0
    if(nPoint > mnTimeBasedEnd - mnTimeBasedStart)
3485
0
        return false;
3486
3487
0
    SCTAB nTab = mnTimeBasedStart + nPoint;
3488
0
    for(const auto& rxToken : m_aTokens)
3489
0
    {
3490
0
        if (rxToken->GetType() != svDoubleRef)
3491
0
            continue;
3492
3493
0
        ScComplexRefData& rData = *rxToken->GetDoubleRef();
3494
0
        ScSingleRefData& s = rData.Ref1;
3495
0
        ScSingleRefData& e = rData.Ref2;
3496
3497
0
        s.SetAbsTab(nTab);
3498
0
        e.SetAbsTab(nTab);
3499
0
    }
3500
3501
0
    mnCurrentTab = nTab;
3502
3503
0
    RebuildDataCache();
3504
3505
0
    return true;
3506
0
}
3507
3508
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */