Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/unoobj/PivotTableDataProvider.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
10
#include <limits>
11
#include <memory>
12
#include <sal/config.h>
13
14
#include <PivotTableDataProvider.hxx>
15
#include <PivotTableDataSource.hxx>
16
#include <PivotTableDataSequence.hxx>
17
18
#include <miscuno.hxx>
19
#include <document.hxx>
20
#include <docsh.hxx>
21
#include <unonames.hxx>
22
#include <scresid.hxx>
23
#include <globstr.hrc>
24
#include <strings.hrc>
25
#include <dpobject.hxx>
26
#include <hints.hxx>
27
28
#include <o3tl/safeint.hxx>
29
#include <vcl/svapp.hxx>
30
#include <comphelper/propertysequence.hxx>
31
#include <comphelper/sequence.hxx>
32
#include <comphelper/processfactory.hxx>
33
34
#include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
35
#include <com/sun/star/chart/ChartDataRowSource.hpp>
36
#include <com/sun/star/frame/XModel.hpp>
37
38
#include <com/sun/star/sheet/XDataPilotResults.hpp>
39
#include <com/sun/star/sheet/DataResultFlags.hpp>
40
41
#include <com/sun/star/sheet/XDimensionsSupplier.hpp>
42
#include <com/sun/star/sheet/XHierarchiesSupplier.hpp>
43
#include <com/sun/star/sheet/XLevelsSupplier.hpp>
44
#include <com/sun/star/sheet/XDataPilotMemberResults.hpp>
45
#include <com/sun/star/sheet/MemberResultFlags.hpp>
46
#include <com/sun/star/sheet/XMembersSupplier.hpp>
47
48
#include <com/sun/star/chart/ChartDataChangeEvent.hpp>
49
#include <com/sun/star/container/XNamed.hpp>
50
51
#include <unordered_map>
52
53
using namespace css;
54
55
namespace sc
56
{
57
namespace
58
{
59
constexpr OUStringLiteral constIdCategories(u"categories");
60
constexpr OUStringLiteral constIdLabel(u"label");
61
constexpr OUStringLiteral constIdData(u"data");
62
63
std::span<const SfxItemPropertyMapEntry> lcl_GetDataProviderPropertyMap()
64
0
{
65
0
    static const SfxItemPropertyMapEntry aDataProviderPropertyMap_Impl[] =
66
0
    {
67
0
        { SC_UNONAME_INCLUDEHIDDENCELLS, 0, cppu::UnoType<bool>::get(), 0, 0 },
68
0
        { SC_UNONAME_USE_INTERNAL_DATA_PROVIDER, 0, cppu::UnoType<bool>::get(), 0, 0 },
69
0
    };
70
0
    return aDataProviderPropertyMap_Impl;
71
0
}
72
73
uno::Reference<frame::XModel> lcl_GetXModel(const ScDocument * pDoc)
74
0
{
75
0
    uno::Reference<frame::XModel> xModel;
76
0
    ScDocShell* pObjSh(pDoc ? pDoc->GetDocumentShell() : nullptr);
77
0
    if (pObjSh)
78
0
        xModel.set(pObjSh->GetModel());
79
0
    return xModel;
80
0
}
81
82
OUString lcl_identifierForData(sal_Int32 index)
83
0
{
84
0
    return "PT@" + constIdData + " " + OUString::number(index);
85
0
}
86
87
OUString lcl_identifierForLabel(sal_Int32 index)
88
0
{
89
0
    return "PT@" + constIdLabel + " " + OUString::number(index);
90
0
}
91
92
OUString lcl_identifierForCategories()
93
0
{
94
0
    return "PT@" + constIdCategories;
95
0
}
96
97
std::vector<OUString> lcl_getVisiblePageMembers(const uno::Reference<uno::XInterface> & xLevel)
98
0
{
99
0
    std::vector<OUString> aResult;
100
0
    if (!xLevel.is())
101
0
        return aResult;
102
103
0
    uno::Reference<sheet::XMembersSupplier> xMembersSupplier(xLevel, uno::UNO_QUERY);
104
0
    if (!xMembersSupplier.is())
105
0
        return aResult;
106
107
0
    uno::Reference<sheet::XMembersAccess> xMembersAccess = xMembersSupplier->getMembers();
108
0
    if (!xMembersAccess.is())
109
0
        return aResult;
110
111
0
    const css::uno::Sequence<OUString> aMembersNames = xMembersAccess->getElementNames();
112
0
    for (OUString const & rMemberNames : aMembersNames)
113
0
    {
114
0
        uno::Reference<beans::XPropertySet> xProperties(xMembersAccess->getByName(rMemberNames), uno::UNO_QUERY);
115
0
        if (!xProperties.is())
116
0
            continue;
117
118
0
        OUString aCaption = ScUnoHelpFunctions::GetStringProperty(xProperties, SC_UNO_DP_LAYOUTNAME, OUString());
119
0
        if (aCaption.isEmpty())
120
0
            aCaption = rMemberNames;
121
122
0
        bool bVisible = ScUnoHelpFunctions::GetBoolProperty(xProperties, SC_UNO_DP_ISVISIBLE);
123
124
0
        if (bVisible)
125
0
            aResult.push_back(aCaption);
126
0
    }
127
128
0
    return aResult;
129
0
}
130
131
} // end anonymous namespace
132
133
SC_SIMPLE_SERVICE_INFO(PivotTableDataProvider, u"PivotTableDataProvider"_ustr, SC_SERVICENAME_CHART_PIVOTTABLE_DATAPROVIDER)
134
135
// DataProvider ==============================================================
136
137
PivotTableDataProvider::PivotTableDataProvider(ScDocument& rDoc)
138
0
    : m_pDocument(&rDoc)
139
0
    , m_aPropSet(lcl_GetDataProviderPropertyMap())
140
0
    , m_bIncludeHiddenCells(true)
141
0
    , m_bNeedsUpdate(true)
142
0
    , m_xContext(comphelper::getProcessComponentContext())
143
0
{
144
0
    rDoc.AddUnoObject(*this);
145
0
}
146
147
PivotTableDataProvider::~PivotTableDataProvider()
148
0
{
149
0
    SolarMutexGuard g;
150
151
0
    if (m_pDocument)
152
0
        m_pDocument->RemoveUnoObject( *this);
153
0
}
154
155
void PivotTableDataProvider::Notify(SfxBroadcaster& /*rBroadcaster*/, const SfxHint& rHint)
156
0
{
157
0
    if (rHint.GetId() == SfxHintId::Dying)
158
0
    {
159
0
        m_pDocument = nullptr;
160
0
    }
161
0
    else if (m_pDocument)
162
0
    {
163
0
        if (rHint.GetId() == SfxHintId::ScDataPilotModified)
164
0
        {
165
0
            auto pDataPilotHint = static_cast<const ScDataPilotModifiedHint*>(&rHint);
166
0
            if (pDataPilotHint->GetName() == m_sPivotTableName)
167
0
            {
168
0
                m_bNeedsUpdate = true;
169
0
                for (uno::Reference<util::XModifyListener> const & xListener : m_aValueListeners)
170
0
                {
171
0
                    css::chart::ChartDataChangeEvent aEvent(getXWeak(),
172
0
                                                            css::chart::ChartDataChangeType_ALL,
173
0
                                                            0, 0, 0, 0);
174
0
                    xListener->modified(aEvent);
175
0
                }
176
0
            }
177
0
        }
178
0
    }
179
0
}
180
181
sal_Bool SAL_CALL PivotTableDataProvider::createDataSourcePossible(const uno::Sequence<beans::PropertyValue>& /*aArguments*/)
182
0
{
183
0
    SolarMutexGuard aGuard;
184
0
    if (!m_pDocument)
185
0
        return false;
186
187
0
    if (m_sPivotTableName.isEmpty())
188
0
        return false;
189
190
0
    ScDPCollection* pDPCollection = m_pDocument->GetDPCollection();
191
0
    return bool(pDPCollection->GetByName(m_sPivotTableName));
192
0
}
193
194
uno::Reference<chart2::data::XDataSource> SAL_CALL
195
    PivotTableDataProvider::createDataSource(const uno::Sequence<beans::PropertyValue>& aArguments)
196
0
{
197
0
    SolarMutexGuard aGuard;
198
199
0
    if (!m_pDocument)
200
0
        throw uno::RuntimeException();
201
202
0
    bool bOrientCol = true;
203
0
    OUString aRangeRepresentation;
204
205
0
    for (beans::PropertyValue const & rProperty : aArguments)
206
0
    {
207
0
        if (rProperty.Name == "DataRowSource")
208
0
        {
209
0
            chart::ChartDataRowSource eSource = chart::ChartDataRowSource_COLUMNS;
210
0
            if (!(rProperty.Value >>= eSource))
211
0
            {
212
0
                sal_Int32 nSource(0);
213
0
                if (rProperty.Value >>= nSource)
214
0
                    eSource = chart::ChartDataRowSource(nSource);
215
0
            }
216
0
            bOrientCol = (eSource == chart::ChartDataRowSource_COLUMNS);
217
0
        }
218
0
        else if (rProperty.Name == "CellRangeRepresentation")
219
0
            rProperty.Value >>= aRangeRepresentation;
220
0
    }
221
222
0
    uno::Reference<chart2::data::XDataSource> xResult;
223
224
0
    if (aRangeRepresentation == lcl_identifierForCategories())
225
0
        xResult = createCategoriesDataSource(bOrientCol);
226
0
    else
227
0
        xResult = createValuesDataSource();
228
229
0
    return xResult;
230
0
}
231
232
uno::Reference<chart2::data::XLabeledDataSequence>
233
    PivotTableDataProvider::newLabeledDataSequence()
234
0
{
235
0
    uno::Reference<chart2::data::XLabeledDataSequence> xResult;
236
0
    if (!m_xContext.is())
237
0
        return xResult;
238
0
    xResult.set(chart2::data::LabeledDataSequence::create(m_xContext), uno::UNO_QUERY_THROW);
239
0
    return xResult;
240
0
}
241
242
uno::Reference<chart2::data::XDataSource>
243
PivotTableDataProvider::createCategoriesDataSource(bool bOrientationIsColumn)
244
0
{
245
0
    if (m_bNeedsUpdate)
246
0
        collectPivotTableData();
247
248
0
    uno::Reference<chart2::data::XDataSource> xDataSource;
249
0
    std::vector<uno::Reference<chart2::data::XLabeledDataSequence>> aLabeledSequences;
250
251
0
    std::vector<std::vector<ValueAndFormat>> const & rCategoriesVector = bOrientationIsColumn ? m_aCategoriesColumnOrientation
252
0
                                                                                              : m_aCategoriesRowOrientation;
253
254
0
    for (std::vector<ValueAndFormat> const & rCategories : rCategoriesVector)
255
0
    {
256
0
        uno::Reference<chart2::data::XLabeledDataSequence> xResult = newLabeledDataSequence();
257
0
        rtl::Reference<PivotTableDataSequence> pSequence(new PivotTableDataSequence(m_pDocument,
258
0
                                                   lcl_identifierForCategories(), std::vector(rCategories)));
259
0
        pSequence->setRole(u"categories"_ustr);
260
0
        xResult->setValues(uno::Reference<chart2::data::XDataSequence>(pSequence));
261
262
0
        aLabeledSequences.push_back(xResult);
263
0
    }
264
265
0
    xDataSource.set(new PivotTableDataSource(std::move(aLabeledSequences)));
266
0
    return xDataSource;
267
0
}
268
269
void PivotTableDataProvider::collectPivotTableData()
270
0
{
271
0
    ScDPCollection* pDPCollection = m_pDocument->GetDPCollection();
272
0
    ScDPObject* pDPObject = pDPCollection->GetByName(m_sPivotTableName);
273
0
    if (!pDPObject)
274
0
        return;
275
276
0
    m_aCategoriesColumnOrientation.clear();
277
0
    m_aCategoriesRowOrientation.clear();
278
0
    m_aLabels.clear();
279
0
    m_aDataRowVector.clear();
280
0
    m_aColumnFields.clear();
281
0
    m_aRowFields.clear();
282
0
    m_aPageFields.clear();
283
0
    m_aDataFields.clear();
284
0
    m_aFieldOutputDescriptionMap.clear();
285
286
0
    uno::Reference<sheet::XDataPilotResults> xDPResults(pDPObject->GetSource(), uno::UNO_QUERY);
287
0
    if (!xDPResults.is())
288
0
        return;
289
0
    const uno::Sequence<uno::Sequence<sheet::DataResult>> xDataResultsSequence = xDPResults->getResults();
290
291
0
    std::unordered_set<size_t> aValidRowIndex;
292
293
0
    size_t nRowIndex = 0;
294
0
    for (uno::Sequence<sheet::DataResult> const & xDataResults : xDataResultsSequence)
295
0
    {
296
0
        std::vector<ValueAndFormat> aRow;
297
0
        bool bRowEmpty = true;
298
        // First pass - collect a row of valid data and track if the row is empty
299
0
        for (sheet::DataResult const & rDataResult : xDataResults)
300
0
        {
301
0
            if (rDataResult.Flags & css::sheet::DataResultFlags::SUBTOTAL)
302
0
                continue;
303
0
            if (rDataResult.Flags == 0 || rDataResult.Flags & css::sheet::DataResultFlags::HASDATA)
304
0
            {
305
0
                aRow.emplace_back(rDataResult.Flags ? rDataResult.Value
306
0
                                                    : std::numeric_limits<double>::quiet_NaN(), 0);
307
0
                if (rDataResult.Flags != 0) // set as valid only if we have data
308
0
                {
309
0
                    bRowEmpty = false;
310
                    // We need to remember all valid (non-empty) row indices
311
0
                    aValidRowIndex.insert(nRowIndex);
312
0
                }
313
0
            }
314
0
        }
315
        // Second pass: add to collection only non-empty rows
316
0
        if (!bRowEmpty)
317
0
        {
318
0
            size_t nColumnIndex = 0;
319
0
            for (ValueAndFormat const & aValue : aRow)
320
0
            {
321
0
                if (nColumnIndex >= m_aDataRowVector.size())
322
0
                    m_aDataRowVector.resize(nColumnIndex + 1);
323
0
                m_aDataRowVector[nColumnIndex].push_back(aValue);
324
0
                nColumnIndex++;
325
0
            }
326
0
        }
327
0
        nRowIndex++;
328
0
    }
329
330
0
    uno::Reference<sheet::XDimensionsSupplier> xDimensionsSupplier(pDPObject->GetSource());
331
0
    uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess(xDimensionsSupplier->getDimensions());
332
333
0
    std::unordered_map<OUString, sal_Int32> aDataFieldNumberFormatMap;
334
0
    std::vector<OUString> aDataFieldNamesVectors;
335
336
0
    std::unordered_map<OUString, OUString> aDataFieldCaptionNames;
337
338
0
    sheet::DataPilotFieldOrientation eDataFieldOrientation = sheet::DataPilotFieldOrientation_HIDDEN;
339
340
0
    for (sal_Int32 nDim = 0; nDim < xDims->getCount(); nDim++)
341
0
    {
342
0
        uno::Reference<uno::XInterface> xDim(xDims->getByIndex(nDim), uno::UNO_QUERY);
343
0
        uno::Reference<beans::XPropertySet> xDimProp(xDim, uno::UNO_QUERY);
344
0
        uno::Reference<sheet::XHierarchiesSupplier> xDimSupp(xDim, uno::UNO_QUERY);
345
346
0
        if (!xDimProp.is() || !xDimSupp.is())
347
0
            continue;
348
349
0
        sheet::DataPilotFieldOrientation eDimOrient =
350
0
            ScUnoHelpFunctions::GetEnumProperty(xDimProp, SC_UNO_DP_ORIENTATION,
351
0
                                                sheet::DataPilotFieldOrientation_HIDDEN);
352
353
0
        if (eDimOrient == sheet::DataPilotFieldOrientation_HIDDEN)
354
0
            continue;
355
356
0
        uno::Reference<container::XIndexAccess> xHierarchies = new ScNameToIndexAccess(xDimSupp->getHierarchies());
357
0
        sal_Int32 nHierarchy = ScUnoHelpFunctions::GetLongProperty(xDimProp, SC_UNO_DP_USEDHIERARCHY);
358
0
        if (nHierarchy >= xHierarchies->getCount())
359
0
            nHierarchy = 0;
360
361
0
        uno::Reference<sheet::XLevelsSupplier> xLevelsSupplier(xHierarchies->getByIndex(nHierarchy),
362
0
                                                               uno::UNO_QUERY);
363
364
0
        if (!xLevelsSupplier.is())
365
0
            continue;
366
367
0
        uno::Reference<container::XIndexAccess> xLevels = new ScNameToIndexAccess(xLevelsSupplier->getLevels());
368
369
0
        for (tools::Long nLevel = 0; nLevel < xLevels->getCount(); nLevel++)
370
0
        {
371
0
            uno::Reference<uno::XInterface> xLevel(xLevels->getByIndex(nLevel), uno::UNO_QUERY);
372
0
            uno::Reference<container::XNamed> xLevelName(xLevel, uno::UNO_QUERY);
373
0
            uno::Reference<sheet::XDataPilotMemberResults> xLevelResult(xLevel, uno::UNO_QUERY );
374
375
0
            if (xLevelName.is() && xLevelResult.is())
376
0
            {
377
0
                bool bIsDataLayout = ScUnoHelpFunctions::GetBoolProperty(xDimProp, SC_UNO_DP_ISDATALAYOUT);
378
0
                sal_Int32 nDimPos = ScUnoHelpFunctions::GetLongProperty(xDimProp, SC_UNO_DP_POSITION);
379
0
                sal_Int32 nNumberFormat = ScUnoHelpFunctions::GetLongProperty(xDimProp, SC_UNO_DP_NUMBERFO);
380
0
                bool bHasHiddenMember = ScUnoHelpFunctions::GetBoolProperty(xDimProp, SC_UNO_DP_HAS_HIDDEN_MEMBER);
381
                // TODO: wire up calculated field info to chart data provider
382
                /*bool bCalculatedField = ScUnoHelpFunctions::GetBoolProperty(xDimProp, SC_UNO_DP_CALCULATEDFIELD);
383
                std::optional<OUString> aCalculation;
384
                if (bCalculatedField)
385
                    aCalculation = ScUnoHelpFunctions::GetStringProperty(xDimProp, SC_UNO_DP_CALCULATION, u""_ustr);*/
386
387
0
                switch (eDimOrient)
388
0
                {
389
0
                    case sheet::DataPilotFieldOrientation_COLUMN:
390
0
                    {
391
0
                        m_aColumnFields.emplace_back(xLevelName->getName(), nDim, nDimPos, bHasHiddenMember);
392
393
0
                        const uno::Sequence<sheet::MemberResult> aSequence = xLevelResult->getResults();
394
0
                        size_t i = 0;
395
0
                        OUString sCaption;
396
0
                        OUString sName;
397
0
                        for (sheet::MemberResult const & rMember : aSequence)
398
0
                        {
399
                            // Skip grandtotals and subtotals
400
0
                            if (rMember.Flags & sheet::MemberResultFlags::SUBTOTAL ||
401
0
                                rMember.Flags & sheet::MemberResultFlags::GRANDTOTAL)
402
0
                                    continue;
403
0
                            if (rMember.Flags & sheet::MemberResultFlags::HASMEMBER ||
404
0
                                rMember.Flags & sheet::MemberResultFlags::CONTINUE)
405
0
                            {
406
0
                                if (!(rMember.Flags & sheet::MemberResultFlags::CONTINUE))
407
0
                                {
408
0
                                    sCaption = rMember.Caption;
409
0
                                    sName = rMember.Name;
410
0
                                }
411
412
0
                                if (i >= m_aLabels.size())
413
0
                                    m_aLabels.resize(i + 1);
414
415
0
                                if (o3tl::make_unsigned(nDimPos) >= m_aLabels[i].size())
416
0
                                    m_aLabels[i].resize(nDimPos + 1);
417
0
                                m_aLabels[i][nDimPos] = ValueAndFormat(sCaption);
418
419
0
                                if (bIsDataLayout)
420
0
                                {
421
                                    // Remember data fields to determine the number format of data
422
0
                                    aDataFieldNamesVectors.push_back(sName);
423
0
                                    eDataFieldOrientation = sheet::DataPilotFieldOrientation_COLUMN;
424
                                    // Remember the caption name
425
0
                                    aDataFieldCaptionNames[rMember.Name] = rMember.Caption;
426
0
                                }
427
0
                                i++;
428
0
                            }
429
0
                        }
430
0
                    }
431
0
                    break;
432
433
0
                    case sheet::DataPilotFieldOrientation_ROW:
434
0
                    {
435
0
                        m_aRowFields.emplace_back(xLevelName->getName(), nDim, nDimPos, bHasHiddenMember);
436
437
0
                        const uno::Sequence<sheet::MemberResult> aSequence = xLevelResult->getResults();
438
439
0
                        size_t i = 0;
440
0
                        size_t nEachIndex = 0;
441
0
                        std::unique_ptr<ValueAndFormat> pItem;
442
443
0
                        for (sheet::MemberResult const & rMember : aSequence)
444
0
                        {
445
0
                            bool bFound = aValidRowIndex.find(nEachIndex) != aValidRowIndex.end();
446
447
0
                            nEachIndex++;
448
449
0
                            bool bHasContinueFlag = rMember.Flags & sheet::MemberResultFlags::CONTINUE;
450
451
0
                            if (rMember.Flags & sheet::MemberResultFlags::HASMEMBER || bHasContinueFlag)
452
0
                            {
453
0
                                if (!bHasContinueFlag)
454
0
                                {
455
                                    // Chart2 does not use number format for labels, so use the display string.
456
0
                                    pItem.reset(new ValueAndFormat(rMember.Caption));
457
0
                                }
458
459
0
                                if (bFound)
460
0
                                {
461
0
                                    assert(pItem && "bHasContinueFlag must be false on this or some preceding element");
462
463
0
                                    if (i >= m_aCategoriesRowOrientation.size())
464
0
                                        m_aCategoriesRowOrientation.resize(i + 1);
465
466
0
                                    if (o3tl::make_unsigned(nDimPos) >= m_aCategoriesColumnOrientation.size())
467
0
                                        m_aCategoriesColumnOrientation.resize(nDimPos + 1);
468
0
                                    m_aCategoriesColumnOrientation[nDimPos].push_back(*pItem);
469
470
0
                                    if (o3tl::make_unsigned(nDimPos) >= m_aCategoriesRowOrientation[i].size())
471
0
                                        m_aCategoriesRowOrientation[i].resize(nDimPos + 1);
472
0
                                    m_aCategoriesRowOrientation[i][nDimPos] = *pItem;
473
474
0
                                    if (bIsDataLayout)
475
0
                                    {
476
                                        // Remember data fields to determine the number format of data
477
0
                                        aDataFieldNamesVectors.push_back(rMember.Name);
478
0
                                        eDataFieldOrientation = sheet::DataPilotFieldOrientation_ROW;
479
480
                                        // Remember the caption name
481
0
                                        aDataFieldCaptionNames[rMember.Name] = rMember.Caption;
482
0
                                    }
483
484
                                    // Set to empty so the sub categories are set to empty when they continue
485
0
                                    pItem.reset(new ValueAndFormat);
486
0
                                    i++;
487
0
                                }
488
0
                            }
489
0
                        }
490
0
                    }
491
0
                    break;
492
493
0
                    case sheet::DataPilotFieldOrientation_PAGE:
494
0
                    {
495
0
                        m_aPageFields.emplace_back(xLevelName->getName(), nDim, nDimPos, bHasHiddenMember);
496
497
                        // Resolve filtering
498
0
                        OUString aFieldOutputDescription;
499
0
                        if (bHasHiddenMember)
500
0
                        {
501
0
                            std::vector<OUString> aMembers = lcl_getVisiblePageMembers(xLevel);
502
503
0
                            if (aMembers.size() == 1)
504
0
                                aFieldOutputDescription = aMembers[0];
505
0
                            else
506
0
                                aFieldOutputDescription = ScResId(SCSTR_MULTIPLE);
507
0
                        }
508
0
                        else
509
0
                        {
510
0
                            aFieldOutputDescription = ScResId(SCSTR_ALL);
511
0
                        }
512
0
                        m_aFieldOutputDescriptionMap[nDim] = aFieldOutputDescription;
513
0
                    }
514
0
                    break;
515
516
0
                    case sheet::DataPilotFieldOrientation_DATA:
517
0
                    {
518
0
                        aDataFieldNumberFormatMap[xLevelName->getName()] = nNumberFormat;
519
0
                        m_aDataFields.emplace_back(xLevelName->getName(), nDim, nDimPos, bHasHiddenMember);
520
0
                    }
521
0
                    break;
522
523
0
                    default:
524
0
                        break;
525
0
                }
526
0
            }
527
0
        }
528
0
    }
529
530
    // Transform the name of data fields
531
0
    for (chart2::data::PivotTableFieldEntry& rDataFields : m_aDataFields)
532
0
    {
533
0
        rDataFields.Name = aDataFieldCaptionNames[rDataFields.Name];
534
0
    }
535
536
    // Apply number format to the data
537
0
    if (eDataFieldOrientation == sheet::DataPilotFieldOrientation_ROW)
538
0
    {
539
0
        for (std::vector<ValueAndFormat> & rDataRow : m_aDataRowVector)
540
0
        {
541
0
            size_t i = 0;
542
0
            for (ValueAndFormat & rItem : rDataRow)
543
0
            {
544
0
                const OUString& sName = aDataFieldNamesVectors[i];
545
0
                sal_Int32 nNumberFormat = aDataFieldNumberFormatMap[sName];
546
0
                rItem.m_nNumberFormat = nNumberFormat;
547
0
                i++;
548
0
            }
549
0
        }
550
0
    }
551
0
    else if (eDataFieldOrientation == sheet::DataPilotFieldOrientation_COLUMN)
552
0
    {
553
0
        size_t i = 0;
554
0
        for (std::vector<ValueAndFormat> & rDataRow : m_aDataRowVector)
555
0
        {
556
0
            const OUString& sName = aDataFieldNamesVectors[i];
557
0
            sal_Int32 nNumberFormat = aDataFieldNumberFormatMap[sName];
558
0
            for (ValueAndFormat & rItem : rDataRow)
559
0
            {
560
0
                rItem.m_nNumberFormat = nNumberFormat;
561
0
            }
562
0
            i++;
563
0
        }
564
0
    }
565
566
    // Sort fields so it respects the order of how it is represented in the pivot table
567
568
0
    auto funcDimensionPositionSortCompare = [] (chart2::data::PivotTableFieldEntry const & entry1,
569
0
                                                chart2::data::PivotTableFieldEntry const & entry2)
570
0
    {
571
0
        return entry1.DimensionPositionIndex < entry2.DimensionPositionIndex;
572
0
    };
573
574
0
    std::sort(m_aColumnFields.begin(), m_aColumnFields.end(), funcDimensionPositionSortCompare);
575
0
    std::sort(m_aRowFields.begin(),    m_aRowFields.end(),    funcDimensionPositionSortCompare);
576
0
    std::sort(m_aPageFields.begin(),   m_aPageFields.end(),   funcDimensionPositionSortCompare);
577
0
    std::sort(m_aDataFields.begin(),   m_aDataFields.end(),   funcDimensionPositionSortCompare);
578
579
    // Mark that we updated the data
580
0
    m_bNeedsUpdate = false;
581
0
}
582
583
rtl::Reference<PivotTableDataSequence>
584
PivotTableDataProvider::assignValuesToDataSequence(size_t nIndex)
585
0
{
586
0
    if (nIndex >= m_aDataRowVector.size())
587
0
        return nullptr;
588
589
0
    OUString sDataID = lcl_identifierForData(nIndex);
590
591
0
    std::vector<ValueAndFormat> const & rRowOfData = m_aDataRowVector[nIndex];
592
0
    rtl::Reference<PivotTableDataSequence> pSequence(new PivotTableDataSequence(m_pDocument, sDataID, std::vector(rRowOfData)));
593
0
    pSequence->setRole(u"values-y"_ustr);
594
0
    return pSequence;
595
0
}
596
597
rtl::Reference<PivotTableDataSequence>
598
PivotTableDataProvider::assignLabelsToDataSequence(size_t nIndex)
599
0
{
600
0
    OUString sLabelID = lcl_identifierForLabel(nIndex);
601
602
0
    OUStringBuffer aLabel;
603
0
    bool bFirst = true;
604
605
0
    if (m_aLabels.empty())
606
0
    {
607
0
        aLabel = ScResId(STR_PIVOT_TOTAL);
608
0
    }
609
0
    else if (nIndex < m_aLabels.size())
610
0
    {
611
0
        for (ValueAndFormat const & rItem : m_aLabels[nIndex])
612
0
        {
613
0
            if (bFirst)
614
0
            {
615
0
                aLabel.append(rItem.m_aString);
616
0
                bFirst = false;
617
0
            }
618
0
            else
619
0
            {
620
0
                aLabel.append(" - " + rItem.m_aString);
621
0
            }
622
0
        }
623
0
    }
624
625
0
    std::vector<ValueAndFormat> aLabelVector { ValueAndFormat(aLabel.makeStringAndClear()) };
626
627
0
    rtl::Reference<PivotTableDataSequence> pSequence(new PivotTableDataSequence(m_pDocument,
628
0
                                               std::move(sLabelID), std::move(aLabelVector)));
629
0
    pSequence->setRole(u"values-y"_ustr);
630
0
    return pSequence;
631
0
}
632
633
rtl::Reference<PivotTableDataSequence>
634
    PivotTableDataProvider::assignFirstCategoriesToDataSequence()
635
0
{
636
0
    if (m_aCategoriesColumnOrientation.empty())
637
0
        return nullptr;
638
639
0
    std::vector<ValueAndFormat> const & rCategories = m_aCategoriesColumnOrientation.back();
640
641
0
    rtl::Reference<PivotTableDataSequence> pSequence(new PivotTableDataSequence(m_pDocument,
642
0
                                               lcl_identifierForCategories(), std::vector(rCategories)));
643
0
    pSequence->setRole(u"categories"_ustr);
644
0
    return pSequence;
645
0
}
646
647
uno::Reference<chart2::data::XDataSource>
648
    PivotTableDataProvider::createValuesDataSource()
649
0
{
650
0
    if (m_bNeedsUpdate)
651
0
        collectPivotTableData();
652
653
0
    uno::Reference<chart2::data::XDataSource> xDataSource;
654
0
    std::vector<uno::Reference<chart2::data::XLabeledDataSequence>> aLabeledSequences;
655
656
    // Fill first sequence of categories
657
0
    {
658
0
        uno::Reference<chart2::data::XLabeledDataSequence> xResult = newLabeledDataSequence();
659
0
        xResult->setValues(assignFirstCategoriesToDataSequence());
660
0
        aLabeledSequences.push_back(xResult);
661
0
    }
662
663
    // Fill values and labels
664
0
    {
665
0
        for (size_t i = 0; i < m_aDataRowVector.size(); ++i)
666
0
        {
667
0
            uno::Reference<chart2::data::XLabeledDataSequence> xResult = newLabeledDataSequence();
668
0
            xResult->setValues(assignValuesToDataSequence(i));
669
0
            xResult->setLabel(assignLabelsToDataSequence(i));
670
0
            aLabeledSequences.push_back(xResult);
671
0
        }
672
0
    }
673
674
0
    xDataSource.set(new PivotTableDataSource(std::move(aLabeledSequences)));
675
0
    return xDataSource;
676
0
}
677
678
679
uno::Sequence<beans::PropertyValue> SAL_CALL PivotTableDataProvider::detectArguments(
680
            const uno::Reference<chart2::data::XDataSource> & xDataSource)
681
0
{
682
0
    if (!m_pDocument ||!xDataSource.is())
683
0
        return uno::Sequence<beans::PropertyValue>();
684
685
0
    return comphelper::InitPropertySequence({
686
0
        { "CellRangeRepresentation", uno::Any(u"PivotChart"_ustr) },
687
0
        { "DataRowSource", uno::Any(chart::ChartDataRowSource_COLUMNS) },
688
0
        { "FirstCellAsLabel", uno::Any(false) },
689
0
        { "HasCategories", uno::Any(true) }
690
0
    });
691
0
}
692
693
sal_Bool SAL_CALL PivotTableDataProvider::createDataSequenceByRangeRepresentationPossible(const OUString& /*aRangeRepresentation*/)
694
0
{
695
0
    return false;
696
0
}
697
698
uno::Reference<chart2::data::XDataSequence> SAL_CALL
699
    PivotTableDataProvider::createDataSequenceByRangeRepresentation(const OUString& /*rRangeRepresentation*/)
700
0
{
701
0
    uno::Reference<chart2::data::XDataSequence> xDataSequence;
702
0
    return xDataSequence;
703
0
}
704
705
uno::Reference<chart2::data::XDataSequence> SAL_CALL
706
    PivotTableDataProvider::createDataSequenceByValueArray(const OUString& /*aRole*/,
707
                                                           const OUString& /*aRangeRepresentation*/,
708
                                                           const OUString& /*aRoleQualifier*/)
709
0
{
710
0
    return uno::Reference<chart2::data::XDataSequence>();
711
0
}
712
713
uno::Reference<sheet::XRangeSelection> SAL_CALL PivotTableDataProvider::getRangeSelection()
714
0
{
715
0
    uno::Reference<sheet::XRangeSelection> xResult;
716
717
0
    uno::Reference<frame::XModel> xModel(lcl_GetXModel(m_pDocument));
718
0
    if (xModel.is())
719
0
        xResult.set(xModel->getCurrentController(), uno::UNO_QUERY);
720
721
0
    return xResult;
722
0
}
723
724
// XPivotTableDataProvider ========================================================
725
726
const std::vector<chart2::data::PivotTableFieldEntry>& PivotTableDataProvider::getColumnFields() const
727
0
{
728
0
    return m_aColumnFields;
729
0
}
730
731
const std::vector<chart2::data::PivotTableFieldEntry>& PivotTableDataProvider::getRowFields() const
732
0
{
733
0
    return m_aRowFields;
734
0
}
735
736
const std::vector<chart2::data::PivotTableFieldEntry>& PivotTableDataProvider::getPageFields() const
737
0
{
738
0
    return m_aPageFields;
739
0
}
740
741
const std::vector<chart2::data::PivotTableFieldEntry>& PivotTableDataProvider::getDataFields() const
742
0
{
743
0
    return m_aDataFields;
744
0
}
745
746
const OUString & PivotTableDataProvider::getPivotTableName() const
747
0
{
748
0
    return m_sPivotTableName;
749
0
}
750
751
void PivotTableDataProvider::setPivotTableName(const OUString& sPivotTableName)
752
0
{
753
0
    ScDPCollection* pDPCollection = m_pDocument->GetDPCollection();
754
0
    ScDPObject* pDPObject = pDPCollection->GetByName(sPivotTableName);
755
0
    if (pDPObject)
756
0
        m_sPivotTableName = sPivotTableName;
757
0
}
758
759
bool PivotTableDataProvider::hasPivotTable() const
760
0
{
761
0
    if (m_sPivotTableName.isEmpty())
762
0
        return false;
763
764
0
    ScDPCollection* pDPCollection = m_pDocument->GetDPCollection();
765
0
    ScDPObject* pDPObject = pDPCollection->GetByName(m_sPivotTableName);
766
767
0
    if (pDPObject)
768
0
        return true;
769
770
0
    return false;
771
0
}
772
773
uno::Reference<chart2::data::XDataSequence>
774
    PivotTableDataProvider::createDataSequenceOfValuesByIndex(sal_Int32 nIndex)
775
0
{
776
0
    SolarMutexGuard aGuard;
777
778
0
    if (m_bNeedsUpdate)
779
0
        collectPivotTableData();
780
781
0
    return assignValuesToDataSequence(size_t(nIndex));
782
0
}
783
784
uno::Reference<css::chart2::data::XDataSequence>
785
    PivotTableDataProvider::createDataSequenceOfLabelsByIndex(sal_Int32 nIndex)
786
0
{
787
0
    SolarMutexGuard aGuard;
788
789
0
    if (m_bNeedsUpdate)
790
0
        collectPivotTableData();
791
792
0
    return assignLabelsToDataSequence(size_t(nIndex));
793
0
}
794
795
uno::Reference<css::chart2::data::XDataSequence>
796
    PivotTableDataProvider::createDataSequenceOfCategories()
797
0
{
798
0
    SolarMutexGuard aGuard;
799
800
0
    if (m_bNeedsUpdate)
801
0
        collectPivotTableData();
802
803
0
    return assignFirstCategoriesToDataSequence();
804
0
}
805
806
OUString PivotTableDataProvider::getFieldOutputDescription(sal_Int32 nDimensionIndex) const
807
0
{
808
0
    if (nDimensionIndex < 0)
809
0
        return OUString();
810
0
    return m_aFieldOutputDescriptionMap.at(size_t(nDimensionIndex));
811
0
}
812
813
// XModifyBroadcaster ========================================================
814
815
void SAL_CALL PivotTableDataProvider::addModifyListener(const uno::Reference< util::XModifyListener>& aListener)
816
0
{
817
0
    SolarMutexGuard aGuard;
818
819
0
    m_aValueListeners.emplace_back(aListener);
820
0
}
821
822
void SAL_CALL PivotTableDataProvider::removeModifyListener(const uno::Reference<util::XModifyListener>& aListener )
823
0
{
824
0
    SolarMutexGuard aGuard;
825
826
0
    sal_uInt16 nCount = m_aValueListeners.size();
827
0
    for (sal_uInt16 n = nCount; n--;)
828
0
    {
829
0
        uno::Reference<util::XModifyListener>& rObject = m_aValueListeners[n];
830
0
        if (rObject == aListener)
831
0
        {
832
0
            m_aValueListeners.erase(m_aValueListeners.begin() + n);
833
0
        }
834
0
    }
835
0
}
836
837
// DataProvider XPropertySet ========================================================
838
839
uno::Reference< beans::XPropertySetInfo> SAL_CALL
840
    PivotTableDataProvider::getPropertySetInfo()
841
0
{
842
0
    SolarMutexGuard aGuard;
843
0
    static uno::Reference<beans::XPropertySetInfo> aRef =
844
0
        new SfxItemPropertySetInfo( m_aPropSet.getPropertyMap() );
845
0
    return aRef;
846
0
}
847
848
void SAL_CALL PivotTableDataProvider::setPropertyValue(const OUString& rPropertyName, const uno::Any& rValue)
849
0
{
850
0
    if (rPropertyName != SC_UNONAME_INCLUDEHIDDENCELLS)
851
0
        throw beans::UnknownPropertyException(rPropertyName);
852
853
0
    if (!(rValue >>= m_bIncludeHiddenCells))
854
0
        throw lang::IllegalArgumentException();
855
0
}
856
857
uno::Any SAL_CALL PivotTableDataProvider::getPropertyValue(const OUString& rPropertyName)
858
0
{
859
0
    uno::Any aRet;
860
0
    if (rPropertyName == SC_UNONAME_INCLUDEHIDDENCELLS)
861
0
        aRet <<= m_bIncludeHiddenCells;
862
0
    else if (rPropertyName == SC_UNONAME_USE_INTERNAL_DATA_PROVIDER)
863
0
    {
864
        // This is a read-only property.
865
0
        aRet <<= m_pDocument->PastingDrawFromOtherDoc();
866
0
    }
867
0
    else
868
0
        throw beans::UnknownPropertyException(rPropertyName);
869
0
    return aRet;
870
0
}
871
872
void SAL_CALL PivotTableDataProvider::addPropertyChangeListener(
873
        const OUString& /*rPropertyName*/,
874
        const uno::Reference<beans::XPropertyChangeListener>& /*xListener*/)
875
0
{
876
0
    OSL_FAIL("Not yet implemented");
877
0
}
878
879
void SAL_CALL PivotTableDataProvider::removePropertyChangeListener(
880
        const OUString& /*rPropertyName*/,
881
        const uno::Reference<beans::XPropertyChangeListener>& /*rListener*/)
882
0
{
883
0
    OSL_FAIL("Not yet implemented");
884
0
}
885
886
void SAL_CALL PivotTableDataProvider::addVetoableChangeListener(
887
        const OUString& /*rPropertyName*/,
888
        const uno::Reference<beans::XVetoableChangeListener>& /*rListener*/)
889
0
{
890
0
    OSL_FAIL("Not yet implemented");
891
0
}
892
893
void SAL_CALL PivotTableDataProvider::removeVetoableChangeListener(
894
        const OUString& /*rPropertyName*/,
895
        const uno::Reference<beans::XVetoableChangeListener>& /*rListener*/ )
896
0
{
897
0
    OSL_FAIL("Not yet implemented");
898
0
}
899
900
} // end sc namespace
901
902
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */