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