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 <dptabsrc.hxx>
21 
22 #include <algorithm>
23 #include <vector>
24 
25 #include <comphelper/sequence.hxx>
26 #include <o3tl/any.hxx>
27 #include <o3tl/safeint.hxx>
28 #include <osl/diagnose.h>
29 #include <rtl/math.hxx>
30 #include <sal/log.hxx>
31 #include <svl/itemprop.hxx>
32 #include <vcl/svapp.hxx>
33 
34 #include <dpcache.hxx>
35 #include <dptabres.hxx>
36 #include <dptabdat.hxx>
37 #include <global.hxx>
38 #include <miscuno.hxx>
39 #include <unonames.hxx>
40 #include <dpitemdata.hxx>
41 #include <dputil.hxx>
42 #include <dpresfilter.hxx>
43 #include <calcmacros.hxx>
44 #include <generalfunction.hxx>
45 
46 #include <com/sun/star/beans/PropertyAttribute.hpp>
47 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
48 #include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
49 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
50 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
51 #include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
52 #include <com/sun/star/sheet/GeneralFunction2.hpp>
53 #include <com/sun/star/sheet/TableFilterField.hpp>
54 
55 #include <unotools/calendarwrapper.hxx>
56 #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
57 
58 using namespace com::sun::star;
59 using ::std::vector;
60 using ::com::sun::star::uno::Sequence;
61 using ::com::sun::star::uno::Any;
62 using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
63 
64 #define SC_MINCOUNT_LIMIT   1000000
65 
66 SC_SIMPLE_SERVICE_INFO( ScDPSource,      "ScDPSource",      "com.sun.star.sheet.DataPilotSource" )
67 SC_SIMPLE_SERVICE_INFO( ScDPDimensions,  "ScDPDimensions",  "com.sun.star.sheet.DataPilotSourceDimensions" )
68 SC_SIMPLE_SERVICE_INFO( ScDPDimension,   "ScDPDimension",   "com.sun.star.sheet.DataPilotSourceDimension" )
69 
70 // Typos are on purpose here, quote from Eike Rathke (see https://gerrit.libreoffice.org/c/core/+/101116):
71 // "The typo is exactly why the SC_SIMPLE_SERVICE_INFO_COMPAT() lists both service names,
72 // the old with the typo and the new corrected one. Correcting the typo in the old name
73 // will make all extensions fail that use it. This is not to be changed."
74 SC_SIMPLE_SERVICE_INFO_COMPAT( ScDPHierarchies, "ScDPHierarchies",
75         "com.sun.star.sheet.DataPilotSourceHierarchies", "com.sun.star.sheet.DataPilotSourceHierarcies" )
76 SC_SIMPLE_SERVICE_INFO_COMPAT( ScDPHierarchy,   "ScDPHierarchy",
77         "com.sun.star.sheet.DataPilotSourceHierarchy", "com.sun.star.sheet.DataPilotSourceHierarcy" )
78 
79 SC_SIMPLE_SERVICE_INFO( ScDPLevels,      "ScDPLevels",      "com.sun.star.sheet.DataPilotSourceLevels" )
80 SC_SIMPLE_SERVICE_INFO( ScDPLevel,       "ScDPLevel",       "com.sun.star.sheet.DataPilotSourceLevel" )
81 SC_SIMPLE_SERVICE_INFO( ScDPMembers,     "ScDPMembers",     "com.sun.star.sheet.DataPilotSourceMembers" )
82 SC_SIMPLE_SERVICE_INFO( ScDPMember,      "ScDPMember",      "com.sun.star.sheet.DataPilotSourceMember" )
83 
84 // property maps for PropertySetInfo
85 //  DataDescription / NumberFormat are internal
86 
87 //TODO: move to a header?
lcl_GetBoolFromAny(const uno::Any & aAny)88 static bool lcl_GetBoolFromAny( const uno::Any& aAny )
89 {
90     auto b = o3tl::tryAccess<bool>(aAny);
91     return b && *b;
92 }
93 
ScDPSource(ScDPTableData * pD)94 ScDPSource::ScDPSource( ScDPTableData* pD ) :
95     pData( pD ),
96     bColumnGrand( true ),       // default is true
97     bRowGrand( true ),
98     bIgnoreEmptyRows( false ),
99     bRepeatIfEmpty( false ),
100     nDupCount( 0 ),
101     bResultOverflow( false ),
102     bPageFiltered( false )
103 {
104     pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
105 }
106 
~ScDPSource()107 ScDPSource::~ScDPSource()
108 {
109     // free lists
110 
111     pColResults.reset();
112     pRowResults.reset();
113 
114     pColResRoot.reset();
115     pRowResRoot.reset();
116     pResData.reset();
117 }
118 
GetGrandTotalName() const119 const std::optional<OUString> & ScDPSource::GetGrandTotalName() const
120 {
121     return mpGrandTotalName;
122 }
123 
GetOrientation(sal_Int32 nColumn)124 sheet::DataPilotFieldOrientation ScDPSource::GetOrientation(sal_Int32 nColumn)
125 {
126     if (std::find(maColDims.begin(), maColDims.end(), nColumn) != maColDims.end())
127         return sheet::DataPilotFieldOrientation_COLUMN;
128 
129     if (std::find(maRowDims.begin(), maRowDims.end(), nColumn) != maRowDims.end())
130         return sheet::DataPilotFieldOrientation_ROW;
131 
132     if (std::find(maDataDims.begin(), maDataDims.end(), nColumn) != maDataDims.end())
133         return sheet::DataPilotFieldOrientation_DATA;
134 
135     if (std::find(maPageDims.begin(), maPageDims.end(), nColumn) != maPageDims.end())
136         return sheet::DataPilotFieldOrientation_PAGE;
137 
138     return sheet::DataPilotFieldOrientation_HIDDEN;
139 }
140 
GetDataDimensionCount() const141 sal_Int32 ScDPSource::GetDataDimensionCount() const
142 {
143     return maDataDims.size();
144 }
145 
GetDataDimension(sal_Int32 nIndex)146 ScDPDimension* ScDPSource::GetDataDimension(sal_Int32 nIndex)
147 {
148     if (nIndex < 0 || o3tl::make_unsigned(nIndex) >= maDataDims.size())
149         return nullptr;
150 
151     sal_Int32 nDimIndex = maDataDims[nIndex];
152     return GetDimensionsObject()->getByIndex(nDimIndex);
153 }
154 
GetDataDimName(sal_Int32 nIndex)155 OUString ScDPSource::GetDataDimName(sal_Int32 nIndex)
156 {
157     OUString aRet;
158     ScDPDimension* pDim = GetDataDimension(nIndex);
159     if (pDim)
160         aRet = pDim->getName();
161     return aRet;
162 }
163 
GetPosition(sal_Int32 nColumn)164 sal_Int32 ScDPSource::GetPosition(sal_Int32 nColumn)
165 {
166     std::vector<sal_Int32>::const_iterator it, itBeg = maColDims.begin(), itEnd = maColDims.end();
167     it = std::find(itBeg, itEnd, nColumn);
168     if (it != itEnd)
169         return std::distance(itBeg, it);
170 
171     itBeg = maRowDims.begin();
172     itEnd = maRowDims.end();
173     it = std::find(itBeg, itEnd, nColumn);
174     if (it != itEnd)
175         return std::distance(itBeg, it);
176 
177     itBeg = maDataDims.begin();
178     itEnd = maDataDims.end();
179     it = std::find(itBeg, itEnd, nColumn);
180     if (it != itEnd)
181         return std::distance(itBeg, it);
182 
183     itBeg = maPageDims.begin();
184     itEnd = maPageDims.end();
185     it = std::find(itBeg, itEnd, nColumn);
186     if (it != itEnd)
187         return std::distance(itBeg, it);
188 
189     return 0;
190 }
191 
192 namespace {
193 
testSubTotal(bool & rAllowed,sal_Int32 nColumn,const std::vector<sal_Int32> & rDims,ScDPSource * pSource)194 bool testSubTotal( bool& rAllowed, sal_Int32 nColumn, const std::vector<sal_Int32>& rDims, ScDPSource* pSource )
195 {
196     rAllowed = true;
197     std::vector<sal_Int32>::const_iterator it = rDims.begin(), itEnd = rDims.end();
198     for (; it != itEnd; ++it)
199     {
200         if (*it != nColumn)
201             continue;
202 
203         if ( pSource->IsDataLayoutDimension(nColumn) )
204         {
205             //  no subtotals for data layout dim, no matter where
206             rAllowed = false;
207             return true;
208         }
209 
210         //  no subtotals if no other dim but data layout follows
211         ++it;
212         if (it != itEnd && pSource->IsDataLayoutDimension(*it))
213             ++it;
214         if (it == itEnd)
215             rAllowed = false;
216 
217         return true;    // found
218     }
219 
220     return false;
221 }
222 
removeDim(sal_Int32 nRemove,std::vector<sal_Int32> & rDims)223 void removeDim( sal_Int32 nRemove, std::vector<sal_Int32>& rDims )
224 {
225     std::vector<sal_Int32>::iterator it = std::find(rDims.begin(), rDims.end(), nRemove);
226     if (it != rDims.end())
227         rDims.erase(it);
228 }
229 
230 }
231 
SubTotalAllowed(sal_Int32 nColumn)232 bool ScDPSource::SubTotalAllowed(sal_Int32 nColumn)
233 {
234     //TODO: cache this at ScDPResultData
235     bool bAllowed = true;
236     if ( testSubTotal(bAllowed, nColumn, maColDims, this) )
237         return bAllowed;
238     if ( testSubTotal(bAllowed, nColumn, maRowDims, this) )
239         return bAllowed;
240     return bAllowed;
241 }
242 
SetOrientation(sal_Int32 nColumn,sheet::DataPilotFieldOrientation nNew)243 void ScDPSource::SetOrientation(sal_Int32 nColumn, sheet::DataPilotFieldOrientation nNew)
244 {
245     //TODO: change to no-op if new orientation is equal to old?
246 
247     // remove from old list
248     removeDim(nColumn, maColDims);
249     removeDim(nColumn, maRowDims);
250     removeDim(nColumn, maDataDims);
251     removeDim(nColumn, maPageDims);
252 
253     // add to new list
254     switch (nNew)
255     {
256         case sheet::DataPilotFieldOrientation_COLUMN:
257             maColDims.push_back(nColumn);
258             break;
259         case sheet::DataPilotFieldOrientation_ROW:
260             maRowDims.push_back(nColumn);
261             break;
262         case sheet::DataPilotFieldOrientation_DATA:
263             maDataDims.push_back(nColumn);
264             break;
265         case sheet::DataPilotFieldOrientation_PAGE:
266             maPageDims.push_back(nColumn);
267             break;
268             // DataPilot Migration - Cache&&Performance
269         case sheet::DataPilotFieldOrientation_HIDDEN:
270             break;
271         default:
272             OSL_FAIL( "ScDPSource::SetOrientation: unexpected orientation" );
273             break;
274     }
275 }
276 
IsDataLayoutDimension(sal_Int32 nDim)277 bool ScDPSource::IsDataLayoutDimension(sal_Int32 nDim)
278 {
279     return nDim == pData->GetColumnCount();
280 }
281 
GetDataLayoutOrientation()282 sheet::DataPilotFieldOrientation ScDPSource::GetDataLayoutOrientation()
283 {
284     return GetOrientation(pData->GetColumnCount());
285 }
286 
IsDateDimension(sal_Int32 nDim)287 bool ScDPSource::IsDateDimension(sal_Int32 nDim)
288 {
289     return pData->IsDateDimension(nDim);
290 }
291 
GetDimensionsObject()292 ScDPDimensions* ScDPSource::GetDimensionsObject()
293 {
294     if (!pDimensions.is())
295     {
296         pDimensions = new ScDPDimensions(this);
297     }
298     return pDimensions.get();
299 }
300 
getDimensions()301 uno::Reference<container::XNameAccess> SAL_CALL ScDPSource::getDimensions()
302 {
303     return GetDimensionsObject();
304 }
305 
SetDupCount(tools::Long nNew)306 void ScDPSource::SetDupCount( tools::Long nNew )
307 {
308     nDupCount = nNew;
309 }
310 
AddDuplicated(std::u16string_view rNewName)311 ScDPDimension* ScDPSource::AddDuplicated(std::u16string_view rNewName)
312 {
313     OSL_ENSURE( pDimensions.is(), "AddDuplicated without dimensions?" );
314 
315     //  re-use
316 
317     tools::Long nOldDimCount = pDimensions->getCount();
318     for (tools::Long i=0; i<nOldDimCount; i++)
319     {
320         ScDPDimension* pDim = pDimensions->getByIndex(i);
321         if (pDim && pDim->getName() == rNewName)
322         {
323             //TODO: test if pDim is a duplicate of source
324             return pDim;
325         }
326     }
327 
328     SetDupCount( nDupCount + 1 );
329     pDimensions->CountChanged();        // uses nDupCount
330 
331     return pDimensions->getByIndex( pDimensions->getCount() - 1 );
332 }
333 
GetSourceDim(sal_Int32 nDim)334 sal_Int32 ScDPSource::GetSourceDim(sal_Int32 nDim)
335 {
336     //  original source dimension or data layout dimension?
337     if ( nDim <= pData->GetColumnCount() )
338         return nDim;
339 
340     if ( nDim < pDimensions->getCount() )
341     {
342         ScDPDimension* pDimObj = pDimensions->getByIndex( nDim );
343         if ( pDimObj )
344         {
345             tools::Long nSource = pDimObj->GetSourceDim();
346             if ( nSource >= 0 )
347                 return nSource;
348         }
349     }
350 
351     OSL_FAIL("GetSourceDim: wrong dim");
352     return nDim;
353 }
354 
getResults()355 uno::Sequence< uno::Sequence<sheet::DataResult> > SAL_CALL ScDPSource::getResults()
356 {
357     CreateRes_Impl();       // create pColResRoot and pRowResRoot
358 
359     if ( bResultOverflow )      // set in CreateRes_Impl
360     {
361         //  no results available
362         throw uno::RuntimeException();
363     }
364 
365     sal_Int32 nColCount = pColResRoot->GetSize(pResData->GetColStartMeasure());
366     sal_Int32 nRowCount = pRowResRoot->GetSize(pResData->GetRowStartMeasure());
367 
368     //  allocate full sequence
369     //TODO: leave out empty rows???
370 
371     uno::Sequence< uno::Sequence<sheet::DataResult> > aSeq( nRowCount );
372     uno::Sequence<sheet::DataResult>* pRowAry = aSeq.getArray();
373     for (sal_Int32 nRow = 0; nRow < nRowCount; nRow++)
374     {
375         uno::Sequence<sheet::DataResult> aColSeq( nColCount );
376         //  use default values of DataResult
377         pRowAry[nRow] = aColSeq;
378     }
379 
380     ScDPResultFilterContext aFilterCxt;
381     pRowResRoot->FillDataResults(
382         pColResRoot.get(), aFilterCxt, aSeq, pResData->GetRowStartMeasure());
383 
384     maResFilterSet.swap(aFilterCxt.maFilterSet); // Keep this data for GETPIVOTDATA.
385 
386     return aSeq;
387 }
388 
getFilteredResults(const uno::Sequence<sheet::DataPilotFieldFilter> & aFilters)389 uno::Sequence<double> ScDPSource::getFilteredResults(
390             const uno::Sequence<sheet::DataPilotFieldFilter>& aFilters )
391 {
392     if (maResFilterSet.empty())
393         getResults(); // Build result tree first.
394 
395     // Get result values from the tree.
396     const ScDPResultTree::ValuesType* pVals = maResFilterSet.getResults(aFilters);
397     if (pVals && !pVals->empty())
398     {
399         size_t n = pVals->size();
400         uno::Sequence<double> aRet(n);
401         for (size_t i = 0; i < n; ++i)
402             aRet[i] = (*pVals)[i];
403 
404         return aRet;
405     }
406 
407     if (aFilters.getLength() == 1)
408     {
409         // Try to get result from the leaf nodes.
410         double fVal = maResFilterSet.getLeafResult(aFilters[0]);
411         if (!std::isnan(fVal))
412         {
413             return { fVal };
414         }
415     }
416 
417     return uno::Sequence<double>();
418 }
419 
refresh()420 void SAL_CALL ScDPSource::refresh()
421 {
422     disposeData();
423 }
424 
addRefreshListener(const uno::Reference<util::XRefreshListener> &)425 void SAL_CALL ScDPSource::addRefreshListener( const uno::Reference<util::XRefreshListener >& )
426 {
427     OSL_FAIL("not implemented");    //TODO: exception?
428 }
429 
removeRefreshListener(const uno::Reference<util::XRefreshListener> &)430 void SAL_CALL ScDPSource::removeRefreshListener( const uno::Reference<util::XRefreshListener >& )
431 {
432     OSL_FAIL("not implemented");    //TODO: exception?
433 }
434 
getDrillDownData(const Sequence<sheet::DataPilotFieldFilter> & aFilters)435 Sequence< Sequence<Any> > SAL_CALL ScDPSource::getDrillDownData(const Sequence<sheet::DataPilotFieldFilter>& aFilters)
436 {
437     sal_Int32 nColumnCount = GetData()->GetColumnCount();
438 
439     vector<ScDPFilteredCache::Criterion> aFilterCriteria;
440     for (const sheet::DataPilotFieldFilter& rFilter : aFilters)
441     {
442         const OUString& aFieldName = rFilter.FieldName;
443         for (sal_Int32 nCol = 0; nCol < nColumnCount; ++nCol)
444         {
445             if (aFieldName == pData->getDimensionName(nCol))
446             {
447                 ScDPDimension* pDim = GetDimensionsObject()->getByIndex( nCol );
448                 ScDPMembers* pMembers = pDim->GetHierarchiesObject()->getByIndex(0)->
449                                         GetLevelsObject()->getByIndex(0)->GetMembersObject();
450                 sal_Int32 nIndex = pMembers->GetIndexFromName( rFilter.MatchValueName );
451                 if ( nIndex >= 0 )
452                 {
453                     ScDPItemData aItem(pMembers->getByIndex(nIndex)->FillItemData());
454                     aFilterCriteria.emplace_back( );
455                     aFilterCriteria.back().mnFieldIndex = nCol;
456                     aFilterCriteria.back().mpFilter =
457                         std::make_shared<ScDPFilteredCache::SingleFilter>(aItem);
458                 }
459             }
460         }
461     }
462 
463     // Take into account the visibilities of field members.
464     ScDPResultVisibilityData aResVisData(this);
465     pRowResRoot->FillVisibilityData(aResVisData);
466     pColResRoot->FillVisibilityData(aResVisData);
467     aResVisData.fillFieldFilters(aFilterCriteria);
468 
469     Sequence< Sequence<Any> > aTabData;
470     std::unordered_set<sal_Int32> aCatDims;
471     GetCategoryDimensionIndices(aCatDims);
472     pData->GetDrillDownData(aFilterCriteria, aCatDims, aTabData);
473     return aTabData;
474 }
475 
getDataDescription()476 OUString ScDPSource::getDataDescription()
477 {
478     CreateRes_Impl();       // create pResData
479 
480     OUString aRet;
481     if ( pResData->GetMeasureCount() == 1 )
482     {
483         bool bTotalResult = false;
484         aRet = pResData->GetMeasureString(0, true, SUBTOTAL_FUNC_NONE, bTotalResult);
485     }
486 
487     //  empty for more than one measure
488 
489     return aRet;
490 }
491 
setIgnoreEmptyRows(bool bSet)492 void ScDPSource::setIgnoreEmptyRows(bool bSet)
493 {
494     bIgnoreEmptyRows = bSet;
495     pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
496 }
497 
setRepeatIfEmpty(bool bSet)498 void ScDPSource::setRepeatIfEmpty(bool bSet)
499 {
500     bRepeatIfEmpty = bSet;
501     pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
502 }
503 
disposeData()504 void ScDPSource::disposeData()
505 {
506     maResFilterSet.clear();
507 
508     if ( pResData )
509     {
510         //  reset all data...
511 
512         pColResRoot.reset();
513         pRowResRoot.reset();
514         pResData.reset();
515         pColResults.reset();
516         pRowResults.reset();
517         aColLevelList.clear();
518         aRowLevelList.clear();
519     }
520 
521     pDimensions.clear(); // settings have to be applied (from SaveData) again!
522     SetDupCount( 0 );
523 
524     maColDims.clear();
525     maRowDims.clear();
526     maDataDims.clear();
527     maPageDims.clear();
528 
529     pData->DisposeData();   // cached entries etc.
530     bPageFiltered = false;
531     bResultOverflow = false;
532 }
533 
lcl_CountMinMembers(const vector<ScDPDimension * > & ppDim,const vector<ScDPLevel * > & ppLevel,tools::Long nLevels)534 static tools::Long lcl_CountMinMembers(const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLevel, tools::Long nLevels )
535 {
536     //  Calculate the product of the member count for those consecutive levels that
537     //  have the "show all" flag, one following level, and the data layout dimension.
538 
539     tools::Long nTotal = 1;
540     tools::Long nDataCount = 1;
541     bool bWasShowAll = true;
542     tools::Long nPos = nLevels;
543     while ( nPos > 0 )
544     {
545         --nPos;
546 
547         if ( nPos+1 < nLevels && ppDim[nPos] == ppDim[nPos+1] )
548         {
549             OSL_FAIL("lcl_CountMinMembers: multiple levels from one dimension not implemented");
550             return 0;
551         }
552 
553         bool bDo = false;
554         if ( ppDim[nPos]->getIsDataLayoutDimension() )
555         {
556             //  data layout dim doesn't interfere with "show all" flags
557             nDataCount = ppLevel[nPos]->GetMembersObject()->getCount();
558             if ( nDataCount == 0 )
559                 nDataCount = 1;
560         }
561         else if ( bWasShowAll )     // "show all" set for all following levels?
562         {
563             bDo = true;
564             if ( !ppLevel[nPos]->getShowEmpty() )
565             {
566                 //  this level is counted, following ones are not
567                 bWasShowAll = false;
568             }
569         }
570         if ( bDo )
571         {
572             tools::Long nThisCount = ppLevel[nPos]->GetMembersObject()->getMinMembers();
573             if ( nThisCount == 0 )
574             {
575                 nTotal = 1;         //  empty level -> start counting from here
576                                     //TODO: start with visible elements in this level?
577             }
578             else
579             {
580                 if ( nTotal >= LONG_MAX / nThisCount )
581                     return LONG_MAX;                        //  overflow
582                 nTotal *= nThisCount;
583             }
584         }
585     }
586 
587     //  always include data layout dim, even after restarting
588     if ( nTotal >= LONG_MAX / nDataCount )
589         return LONG_MAX;                        //  overflow
590     nTotal *= nDataCount;
591 
592     return nTotal;
593 }
594 
FillCalcInfo(bool bIsRow,ScDPTableData::CalcInfo & rInfo,bool & rHasAutoShow)595 void ScDPSource::FillCalcInfo(bool bIsRow, ScDPTableData::CalcInfo& rInfo, bool &rHasAutoShow)
596 {
597     const std::vector<sal_Int32>& rDims = bIsRow ? maRowDims : maColDims;
598     for (const auto& rDimIndex : rDims)
599     {
600         ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
601         tools::Long nHierarchy = ScDPDimension::getUsedHierarchy();
602         if ( nHierarchy >= ScDPHierarchies::getCount() )
603             nHierarchy = 0;
604         ScDPLevels* pLevels = pDim->GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
605         sal_Int32 nCount = pLevels->getCount();
606 
607         //TODO: Test
608         if (pDim->getIsDataLayoutDimension() && maDataDims.size() < 2)
609             nCount = 0;
610         //TODO: Test
611 
612         for (sal_Int32 j = 0; j < nCount; ++j)
613         {
614             ScDPLevel* pLevel = pLevels->getByIndex(j);
615             pLevel->EvaluateSortOrder();
616 
617             // no layout flags for column fields, only for row fields
618             pLevel->SetEnableLayout( bIsRow );
619 
620             if ( pLevel->GetAutoShow().IsEnabled )
621                 rHasAutoShow = true;
622 
623             if (bIsRow)
624             {
625                 rInfo.aRowLevelDims.push_back(rDimIndex);
626                 rInfo.aRowDims.push_back(pDim);
627                 rInfo.aRowLevels.push_back(pLevel);
628             }
629             else
630             {
631                 rInfo.aColLevelDims.push_back(rDimIndex);
632                 rInfo.aColDims.push_back(pDim);
633                 rInfo.aColLevels.push_back(pLevel);
634             }
635 
636             pLevel->GetMembersObject();                 // initialize for groups
637         }
638     }
639 }
640 
641 namespace {
642 
643 class CategoryDimInserter
644 {
645     ScDPSource& mrSource;
646     std::unordered_set<sal_Int32>& mrCatDims;
647 public:
CategoryDimInserter(ScDPSource & rSource,std::unordered_set<sal_Int32> & rCatDims)648     CategoryDimInserter(ScDPSource& rSource, std::unordered_set<sal_Int32>& rCatDims) :
649         mrSource(rSource),
650         mrCatDims(rCatDims) {}
651 
operator ()(tools::Long nDim)652     void operator() (tools::Long nDim)
653     {
654         if (!mrSource.IsDataLayoutDimension(nDim))
655             mrCatDims.insert(nDim);
656     }
657 };
658 
659 }
660 
GetCategoryDimensionIndices(std::unordered_set<sal_Int32> & rCatDims)661 void ScDPSource::GetCategoryDimensionIndices(std::unordered_set<sal_Int32>& rCatDims)
662 {
663     std::unordered_set<sal_Int32> aCatDims;
664 
665     CategoryDimInserter aInserter(*this, aCatDims);
666     std::for_each(maColDims.begin(), maColDims.end(), aInserter);
667     std::for_each(maRowDims.begin(), maRowDims.end(), aInserter);
668     std::for_each(maPageDims.begin(), maPageDims.end(), aInserter);
669 
670     rCatDims.swap(aCatDims);
671 }
672 
FilterCacheByPageDimensions()673 void ScDPSource::FilterCacheByPageDimensions()
674 {
675     // #i117661# Repeated calls to ScDPFilteredCache::filterByPageDimension
676     // are invalid because rows are only hidden, never shown again. If
677     // FilterCacheByPageDimensions is called again, the cache table must
678     // be re-initialized. Currently, CreateRes_Impl always uses a fresh cache
679     // because ScDBDocFunc::DataPilotUpdate calls InvalidateData.
680 
681     if (bPageFiltered)
682     {
683         SAL_WARN( "sc.core","tried to apply page field filters several times");
684 
685         pData->DisposeData();
686         pData->CreateCacheTable();  // re-initialize the cache table
687         bPageFiltered = false;
688     }
689 
690     // filter table by page dimensions.
691     vector<ScDPFilteredCache::Criterion> aCriteria;
692     for (const auto& rDimIndex : maPageDims)
693     {
694         ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
695         tools::Long nField = pDim->GetDimension();
696 
697         ScDPMembers* pMems = pDim->GetHierarchiesObject()->getByIndex(0)->
698             GetLevelsObject()->getByIndex(0)->GetMembersObject();
699 
700         tools::Long nMemCount = pMems->getCount();
701         ScDPFilteredCache::Criterion aFilter;
702         aFilter.mnFieldIndex = static_cast<sal_Int32>(nField);
703         aFilter.mpFilter = std::make_shared<ScDPFilteredCache::GroupFilter>();
704         ScDPFilteredCache::GroupFilter* pGrpFilter =
705             static_cast<ScDPFilteredCache::GroupFilter*>(aFilter.mpFilter.get());
706         for (tools::Long j = 0; j < nMemCount; ++j)
707         {
708             ScDPMember* pMem = pMems->getByIndex(j);
709             if (pMem->isVisible())
710             {
711                 ScDPItemData aData(pMem->FillItemData());
712                 pGrpFilter->addMatchItem(aData);
713             }
714         }
715         if (pGrpFilter->getMatchItemCount() < o3tl::make_unsigned(nMemCount))
716             // there is at least one invisible item.  Add this filter criterion to the mix.
717             aCriteria.push_back(aFilter);
718 
719         if (!pDim->HasSelectedPage())
720             continue;
721 
722         const ScDPItemData& rData = pDim->GetSelectedData();
723         aCriteria.emplace_back();
724         ScDPFilteredCache::Criterion& r = aCriteria.back();
725         r.mnFieldIndex = static_cast<sal_Int32>(nField);
726         r.mpFilter = std::make_shared<ScDPFilteredCache::SingleFilter>(rData);
727     }
728     if (!aCriteria.empty())
729     {
730         std::unordered_set<sal_Int32> aCatDims;
731         GetCategoryDimensionIndices(aCatDims);
732         pData->FilterCacheTable(aCriteria, aCatDims);
733         bPageFiltered = true;
734     }
735 }
736 
CreateRes_Impl()737 void ScDPSource::CreateRes_Impl()
738 {
739     if (pResData)
740         return;
741 
742     sheet::DataPilotFieldOrientation nDataOrient = GetDataLayoutOrientation();
743     if (maDataDims.size() > 1 && ( nDataOrient != sheet::DataPilotFieldOrientation_COLUMN &&
744                                 nDataOrient != sheet::DataPilotFieldOrientation_ROW ) )
745     {
746         //  if more than one data dimension, data layout orientation must be set
747         SetOrientation( pData->GetColumnCount(), sheet::DataPilotFieldOrientation_ROW );
748         nDataOrient = sheet::DataPilotFieldOrientation_ROW;
749     }
750 
751     // TODO: Aggregate pDataNames, pDataRefValues, nDataRefOrient, and
752     // eDataFunctions into a structure and use vector instead of static
753     // or pointer arrays.
754     vector<OUString> aDataNames;
755     vector<sheet::DataPilotFieldReference> aDataRefValues;
756     vector<ScSubTotalFunc> aDataFunctions;
757     vector<sheet::DataPilotFieldOrientation> aDataRefOrient;
758 
759     ScDPTableData::CalcInfo aInfo;
760 
761     //  LateInit (initialize only those rows/children that are used) can be used unless
762     //  any data dimension needs reference values from column/row dimensions
763     bool bLateInit = true;
764 
765     // Go through all data dimensions (i.e. fields) and build their meta data
766     // so that they can be passed on to ScDPResultData instance later.
767     // TODO: aggregate all of data dimension info into a structure.
768     for (const tools::Long nDimIndex : maDataDims)
769     {
770         // Get function for each data field.
771         ScDPDimension* pDim = GetDimensionsObject()->getByIndex(nDimIndex);
772         ScGeneralFunction eUser = pDim->getFunction();
773         if (eUser == ScGeneralFunction::AUTO)
774         {
775             //TODO: test for numeric data
776             eUser = ScGeneralFunction::SUM;
777         }
778 
779         // Map UNO's enum to internal enum ScSubTotalFunc.
780         aDataFunctions.push_back(ScDPUtil::toSubTotalFunc(eUser));
781 
782         // Get reference field/item information.
783         aDataRefValues.push_back(pDim->GetReferenceValue());
784         sheet::DataPilotFieldOrientation nDataRefOrient = sheet::DataPilotFieldOrientation_HIDDEN;    // default if not used
785         sal_Int32 eRefType = aDataRefValues.back().ReferenceType;
786         if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
787              eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
788              eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE ||
789              eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL )
790         {
791             sal_Int32 nColumn = comphelper::findValue(
792                 GetDimensionsObject()->getElementNames(), aDataRefValues.back().ReferenceField);
793             if ( nColumn >= 0 )
794             {
795                 nDataRefOrient = GetOrientation(nColumn);
796                 //  need fully initialized results to find reference values
797                 //  (both in column or row dimensions), so updated values or
798                 //  differences to 0 can be displayed even for empty results.
799                 bLateInit = false;
800             }
801         }
802 
803         aDataRefOrient.push_back(nDataRefOrient);
804 
805         aDataNames.push_back(pDim->getName());
806 
807         //TODO: modify user visible strings as in ScDPResultData::GetMeasureString instead!
808 
809         aDataNames.back() = ScDPUtil::getSourceDimensionName(aDataNames.back());
810 
811         //TODO: if the name is overridden by user, a flag must be set
812         //TODO: so the user defined name replaces the function string and field name.
813 
814         //TODO: the complete name (function and field) must be stored at the dimension
815 
816         tools::Long nSource = pDim->GetSourceDim();
817         if (nSource >= 0)
818             aInfo.aDataSrcCols.push_back(nSource);
819         else
820             aInfo.aDataSrcCols.push_back(nDimIndex);
821     }
822 
823     pResData.reset( new ScDPResultData(*this) );
824     pResData->SetMeasureData(aDataFunctions, aDataRefValues, aDataRefOrient, aDataNames);
825     pResData->SetDataLayoutOrientation(nDataOrient);
826     pResData->SetLateInit( bLateInit );
827 
828     bool bHasAutoShow = false;
829 
830     ScDPInitState aInitState;
831 
832     // Page field selections restrict the members shown in related fields
833     // (both in column and row fields). aInitState is filled with the page
834     // field selections, they are kept across the data iterator loop.
835 
836     for (const auto& rDimIndex : maPageDims)
837     {
838         ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
839         if ( pDim->HasSelectedPage() )
840             aInitState.AddMember(rDimIndex, GetCache()->GetIdByItemData(rDimIndex, pDim->GetSelectedData()));
841     }
842 
843     // Show grand total columns only when the option is set *and* there is at
844     // least one column field.  Same for the grand total rows.
845     sheet::DataPilotFieldOrientation nDataLayoutOrient = GetDataLayoutOrientation();
846     tools::Long nColDimCount2 = maColDims.size() - (nDataLayoutOrient == sheet::DataPilotFieldOrientation_COLUMN ? 1 : 0);
847     tools::Long nRowDimCount2 = maRowDims.size() - (nDataLayoutOrient == sheet::DataPilotFieldOrientation_ROW ? 1 : 0);
848     bool bShowColGrand = bColumnGrand && nColDimCount2 > 0;
849     bool bShowRowGrand = bRowGrand && nRowDimCount2 > 0;
850     pColResRoot.reset( new ScDPResultMember(pResData.get(), bShowColGrand) );
851     pRowResRoot.reset( new ScDPResultMember(pResData.get(), bShowRowGrand) );
852 
853     FillCalcInfo(false, aInfo, bHasAutoShow);
854     tools::Long nColLevelCount = aInfo.aColLevels.size();
855 
856     pColResRoot->InitFrom( aInfo.aColDims, aInfo.aColLevels, 0, aInitState );
857     pColResRoot->SetHasElements();
858 
859     FillCalcInfo(true, aInfo, bHasAutoShow);
860     tools::Long nRowLevelCount = aInfo.aRowLevels.size();
861 
862     if ( nRowLevelCount > 0 )
863     {
864         // disable layout flags for the innermost row field (level)
865         aInfo.aRowLevels[nRowLevelCount-1]->SetEnableLayout( false );
866     }
867 
868     pRowResRoot->InitFrom( aInfo.aRowDims, aInfo.aRowLevels, 0, aInitState );
869     pRowResRoot->SetHasElements();
870 
871     // initialize members object also for all page dimensions (needed for numeric groups)
872     for (const auto& rDimIndex : maPageDims)
873     {
874         ScDPDimension* pDim = GetDimensionsObject()->getByIndex(rDimIndex);
875         tools::Long nHierarchy = ScDPDimension::getUsedHierarchy();
876         if ( nHierarchy >= ScDPHierarchies::getCount() )
877             nHierarchy = 0;
878 
879         ScDPLevels* pLevels = pDim->GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
880         tools::Long nCount = pLevels->getCount();
881         for (tools::Long j=0; j<nCount; j++)
882             pLevels->getByIndex(j)->GetMembersObject();             // initialize for groups
883     }
884 
885     //  pre-check: calculate minimum number of result columns / rows from
886     //  levels that have the "show all" flag set
887 
888     tools::Long nMinColMembers = lcl_CountMinMembers( aInfo.aColDims, aInfo.aColLevels, nColLevelCount );
889     tools::Long nMinRowMembers = lcl_CountMinMembers( aInfo.aRowDims, aInfo.aRowLevels, nRowLevelCount );
890 
891     if ( nMinColMembers > MAXCOLCOUNT/*SC_MINCOUNT_LIMIT*/ || nMinRowMembers > SC_MINCOUNT_LIMIT )
892     {
893         //  resulting table is too big -> abort before calculating
894         //  (this relies on late init, so no members are allocated in InitFrom above)
895 
896         bResultOverflow = true;
897         return;
898     }
899 
900     FilterCacheByPageDimensions();
901 
902     aInfo.aPageDims  = maPageDims;
903     aInfo.pInitState = &aInitState;
904     aInfo.pColRoot   = pColResRoot.get();
905     aInfo.pRowRoot   = pRowResRoot.get();
906     pData->CalcResults(aInfo, false);
907 
908     pColResRoot->CheckShowEmpty();
909     pRowResRoot->CheckShowEmpty();
910 
911     //  With all data processed, calculate the final results:
912 
913     //  UpdateDataResults calculates all original results from the collected values,
914     //  and stores them as reference values if needed.
915     pRowResRoot->UpdateDataResults( pColResRoot.get(), pResData->GetRowStartMeasure() );
916 
917     if ( bHasAutoShow )     // do the double calculation only if AutoShow is used
918     {
919         //  Find the desired members and set bAutoHidden flag for the others
920         pRowResRoot->DoAutoShow( pColResRoot.get() );
921 
922         //  Reset all results to empty, so they can be built again with data for the
923         //  desired members only.
924         pColResRoot->ResetResults();
925         pRowResRoot->ResetResults();
926         pData->CalcResults(aInfo, true);
927 
928         //  Call UpdateDataResults again, with the new (limited) values.
929         pRowResRoot->UpdateDataResults( pColResRoot.get(), pResData->GetRowStartMeasure() );
930     }
931 
932     //  SortMembers does the sorting by a result dimension, using the original results,
933     //  but not running totals etc.
934     pRowResRoot->SortMembers( pColResRoot.get() );
935 
936     //  UpdateRunningTotals calculates running totals along column/row dimensions,
937     //  differences from other members (named or relative), and column/row percentages
938     //  or index values.
939     //  Running totals and relative differences need to be done using the sorted values.
940     //  Column/row percentages and index values must be done after sorting, because the
941     //  results may no longer be in the right order (row total for percentage of row is
942     //  always 1).
943     ScDPRunningTotalState aRunning( pColResRoot.get(), pRowResRoot.get() );
944     ScDPRowTotals aTotals;
945     pRowResRoot->UpdateRunningTotals( pColResRoot.get(), pResData->GetRowStartMeasure(), aRunning, aTotals );
946 
947 #if DUMP_PIVOT_TABLE
948     DumpResults();
949 #endif
950 }
951 
FillLevelList(sheet::DataPilotFieldOrientation nOrientation,std::vector<ScDPLevel * > & rList)952 void ScDPSource::FillLevelList( sheet::DataPilotFieldOrientation nOrientation, std::vector<ScDPLevel*> &rList )
953 {
954     rList.clear();
955 
956     std::vector<sal_Int32>* pDimIndex = nullptr;
957     switch (nOrientation)
958     {
959         case sheet::DataPilotFieldOrientation_COLUMN:
960             pDimIndex = &maColDims;
961             break;
962         case sheet::DataPilotFieldOrientation_ROW:
963             pDimIndex = &maRowDims;
964             break;
965         case sheet::DataPilotFieldOrientation_DATA:
966             pDimIndex = &maDataDims;
967             break;
968         case sheet::DataPilotFieldOrientation_PAGE:
969             pDimIndex = &maPageDims;
970             break;
971         default:
972             OSL_FAIL( "ScDPSource::FillLevelList: unexpected orientation" );
973             break;
974     }
975     if (!pDimIndex)
976     {
977         OSL_FAIL("invalid orientation");
978         return;
979     }
980 
981     ScDPDimensions* pDims = GetDimensionsObject();
982     for (const auto& rIndex : *pDimIndex)
983     {
984         ScDPDimension* pDim = pDims->getByIndex(rIndex);
985         OSL_ENSURE( pDim->getOrientation() == nOrientation, "orientations are wrong" );
986 
987         ScDPHierarchies* pHiers = pDim->GetHierarchiesObject();
988         sal_Int32 nHierarchy = ScDPDimension::getUsedHierarchy();
989         if ( nHierarchy >= ScDPHierarchies::getCount() )
990             nHierarchy = 0;
991         ScDPHierarchy* pHier = pHiers->getByIndex(nHierarchy);
992         ScDPLevels* pLevels = pHier->GetLevelsObject();
993         sal_Int32 nLevCount = pLevels->getCount();
994         for (sal_Int32 nLev=0; nLev<nLevCount; nLev++)
995         {
996             ScDPLevel* pLevel = pLevels->getByIndex(nLev);
997             rList.push_back(pLevel);
998         }
999     }
1000 }
1001 
FillMemberResults()1002 void ScDPSource::FillMemberResults()
1003 {
1004     if ( pColResults || pRowResults )
1005         return;
1006 
1007     CreateRes_Impl();
1008 
1009     if ( bResultOverflow )      // set in CreateRes_Impl
1010     {
1011         //  no results available -> abort (leave empty)
1012         //  exception is thrown in ScDPSource::getResults
1013         return;
1014     }
1015 
1016     FillLevelList( sheet::DataPilotFieldOrientation_COLUMN, aColLevelList );
1017     sal_Int32 nColLevelCount = aColLevelList.size();
1018     if (nColLevelCount)
1019     {
1020         tools::Long nColDimSize = pColResRoot->GetSize(pResData->GetColStartMeasure());
1021         pColResults.reset(new uno::Sequence<sheet::MemberResult>[nColLevelCount]);
1022         for (tools::Long i=0; i<nColLevelCount; i++)
1023             pColResults[i].realloc(nColDimSize);
1024 
1025         tools::Long nPos = 0;
1026         pColResRoot->FillMemberResults( pColResults.get(), nPos, pResData->GetColStartMeasure(),
1027                                         true, nullptr, nullptr );
1028     }
1029 
1030     FillLevelList( sheet::DataPilotFieldOrientation_ROW, aRowLevelList );
1031     tools::Long nRowLevelCount = aRowLevelList.size();
1032     if (nRowLevelCount)
1033     {
1034         tools::Long nRowDimSize = pRowResRoot->GetSize(pResData->GetRowStartMeasure());
1035         pRowResults.reset( new uno::Sequence<sheet::MemberResult>[nRowLevelCount] );
1036         for (tools::Long i=0; i<nRowLevelCount; i++)
1037             pRowResults[i].realloc(nRowDimSize);
1038 
1039         tools::Long nPos = 0;
1040         pRowResRoot->FillMemberResults( pRowResults.get(), nPos, pResData->GetRowStartMeasure(),
1041                                         true, nullptr, nullptr );
1042     }
1043 }
1044 
GetMemberResults(const ScDPLevel * pLevel)1045 const uno::Sequence<sheet::MemberResult>* ScDPSource::GetMemberResults( const ScDPLevel* pLevel )
1046 {
1047     FillMemberResults();
1048 
1049     sal_Int32 i = 0;
1050     sal_Int32 nColCount = aColLevelList.size();
1051     for (i=0; i<nColCount; i++)
1052     {
1053         ScDPLevel* pColLevel = aColLevelList[i];
1054         if ( pColLevel == pLevel )
1055             return &pColResults[i];
1056     }
1057     sal_Int32 nRowCount = aRowLevelList.size();
1058     for (i=0; i<nRowCount; i++)
1059     {
1060         ScDPLevel* pRowLevel = aRowLevelList[i];
1061         if ( pRowLevel == pLevel )
1062             return &pRowResults[i];
1063     }
1064     return nullptr;
1065 }
1066 
1067 // XPropertySet
1068 
getPropertySetInfo()1069 uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPSource::getPropertySetInfo()
1070 {
1071     SolarMutexGuard aGuard;
1072     using beans::PropertyAttribute::READONLY;
1073 
1074     static const SfxItemPropertyMapEntry aDPSourceMap_Impl[] =
1075     {
1076         { u"" SC_UNO_DP_COLGRAND, 0,  cppu::UnoType<bool>::get(),              0, 0 },
1077         { u"" SC_UNO_DP_DATADESC, 0,  cppu::UnoType<OUString>::get(),    beans::PropertyAttribute::READONLY, 0 },
1078         { u"" SC_UNO_DP_IGNOREEMPTY, 0,  cppu::UnoType<bool>::get(),              0, 0 },     // for sheet data only
1079         { u"" SC_UNO_DP_REPEATEMPTY, 0,  cppu::UnoType<bool>::get(),              0, 0 },     // for sheet data only
1080         { u"" SC_UNO_DP_ROWGRAND, 0,  cppu::UnoType<bool>::get(),              0, 0 },
1081         { u"" SC_UNO_DP_ROWFIELDCOUNT,    0, cppu::UnoType<sal_Int32>::get(), READONLY, 0 },
1082         { u"" SC_UNO_DP_COLUMNFIELDCOUNT, 0, cppu::UnoType<sal_Int32>::get(), READONLY, 0 },
1083         { u"" SC_UNO_DP_DATAFIELDCOUNT,   0, cppu::UnoType<sal_Int32>::get(), READONLY, 0 },
1084         { u"" SC_UNO_DP_GRANDTOTAL_NAME,  0, cppu::UnoType<OUString>::get(), 0, 0 },
1085         { u"", 0, css::uno::Type(), 0, 0 }
1086     };
1087     static uno::Reference<beans::XPropertySetInfo> aRef =
1088         new SfxItemPropertySetInfo( aDPSourceMap_Impl );
1089     return aRef;
1090 }
1091 
setPropertyValue(const OUString & aPropertyName,const uno::Any & aValue)1092 void SAL_CALL ScDPSource::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
1093 {
1094     if (aPropertyName == SC_UNO_DP_COLGRAND)
1095         bColumnGrand = lcl_GetBoolFromAny(aValue);
1096     else if (aPropertyName == SC_UNO_DP_ROWGRAND)
1097         bRowGrand = lcl_GetBoolFromAny(aValue);
1098     else if (aPropertyName == SC_UNO_DP_IGNOREEMPTY)
1099         setIgnoreEmptyRows( lcl_GetBoolFromAny( aValue ) );
1100     else if (aPropertyName == SC_UNO_DP_REPEATEMPTY)
1101         setRepeatIfEmpty( lcl_GetBoolFromAny( aValue ) );
1102     else if (aPropertyName == SC_UNO_DP_GRANDTOTAL_NAME)
1103     {
1104         OUString aName;
1105         if (aValue >>= aName)
1106             mpGrandTotalName = aName;
1107     }
1108     else
1109     {
1110         OSL_FAIL("unknown property");
1111         //TODO: THROW( UnknownPropertyException() );
1112     }
1113 }
1114 
getPropertyValue(const OUString & aPropertyName)1115 uno::Any SAL_CALL ScDPSource::getPropertyValue( const OUString& aPropertyName )
1116 {
1117     uno::Any aRet;
1118     if ( aPropertyName == SC_UNO_DP_COLGRAND )
1119         aRet <<= bColumnGrand;
1120     else if ( aPropertyName == SC_UNO_DP_ROWGRAND )
1121         aRet <<= bRowGrand;
1122     else if ( aPropertyName == SC_UNO_DP_IGNOREEMPTY )
1123         aRet <<= bIgnoreEmptyRows;
1124     else if ( aPropertyName == SC_UNO_DP_REPEATEMPTY )
1125         aRet <<= bRepeatIfEmpty;
1126     else if ( aPropertyName == SC_UNO_DP_DATADESC )             // read-only
1127         aRet <<= getDataDescription();
1128     else if ( aPropertyName == SC_UNO_DP_ROWFIELDCOUNT )        // read-only
1129         aRet <<= static_cast<sal_Int32>(maRowDims.size());
1130     else if ( aPropertyName == SC_UNO_DP_COLUMNFIELDCOUNT )     // read-only
1131         aRet <<= static_cast<sal_Int32>(maColDims.size());
1132     else if ( aPropertyName == SC_UNO_DP_DATAFIELDCOUNT )       // read-only
1133         aRet <<= static_cast<sal_Int32>(maDataDims.size());
1134     else if (aPropertyName == SC_UNO_DP_GRANDTOTAL_NAME)
1135     {
1136         if (mpGrandTotalName)
1137             aRet <<= *mpGrandTotalName;
1138     }
1139     else
1140     {
1141         OSL_FAIL("unknown property");
1142         //TODO: THROW( UnknownPropertyException() );
1143     }
1144     return aRet;
1145 }
1146 
1147 #if DUMP_PIVOT_TABLE
DumpResults() const1148 void ScDPSource::DumpResults() const
1149 {
1150     std::cout << "+++++ column root" << std::endl;
1151     pColResRoot->Dump(1);
1152     std::cout << "+++++ row root" << std::endl;
1153     pRowResRoot->Dump(1);
1154 }
1155 #endif
1156 
SC_IMPL_DUMMY_PROPERTY_LISTENER(ScDPSource)1157 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPSource )
1158 
1159 ScDPDimensions::ScDPDimensions( ScDPSource* pSrc ) :
1160     pSource( pSrc )
1161 {
1162     //TODO: hold pSource
1163 
1164     // include data layout dimension and duplicated dimensions
1165     nDimCount = pSource->GetData()->GetColumnCount() + 1 + pSource->GetDupCount();
1166 }
1167 
~ScDPDimensions()1168 ScDPDimensions::~ScDPDimensions()
1169 {
1170     //TODO: release pSource
1171 }
1172 
CountChanged()1173 void ScDPDimensions::CountChanged()
1174 {
1175     // include data layout dimension and duplicated dimensions
1176     sal_Int32 nNewCount = pSource->GetData()->GetColumnCount() + 1 + pSource->GetDupCount();
1177     if ( ppDims )
1178     {
1179         sal_Int32 i;
1180         sal_Int32 nCopy = std::min( nNewCount, nDimCount );
1181         rtl::Reference<ScDPDimension>* ppNew = new rtl::Reference<ScDPDimension>[nNewCount];
1182 
1183         for (i=0; i<nCopy; i++)             // copy existing dims
1184             ppNew[i] = ppDims[i];
1185         for (i=nCopy; i<nNewCount; i++)     // clear additional pointers
1186             ppNew[i] = nullptr;
1187 
1188         ppDims.reset( ppNew );
1189     }
1190     nDimCount = nNewCount;
1191 }
1192 
1193 // very simple XNameAccess implementation using getCount/getByIndex
1194 
getByName(const OUString & aName)1195 uno::Any SAL_CALL ScDPDimensions::getByName( const OUString& aName )
1196 {
1197     sal_Int32 nCount = getCount();
1198     for (sal_Int32 i=0; i<nCount; i++)
1199         if ( getByIndex(i)->getName() == aName )
1200         {
1201             uno::Reference<container::XNamed> xNamed = getByIndex(i);
1202             uno::Any aRet;
1203             aRet <<= xNamed;
1204             return aRet;
1205         }
1206 
1207     throw container::NoSuchElementException();
1208 //    return uno::Any();
1209 }
1210 
getElementNames()1211 uno::Sequence<OUString> SAL_CALL ScDPDimensions::getElementNames()
1212 {
1213     tools::Long nCount = getCount();
1214     uno::Sequence<OUString> aSeq(nCount);
1215     OUString* pArr = aSeq.getArray();
1216     for (tools::Long i=0; i<nCount; i++)
1217         pArr[i] = getByIndex(i)->getName();
1218     return aSeq;
1219 }
1220 
hasByName(const OUString & aName)1221 sal_Bool SAL_CALL ScDPDimensions::hasByName( const OUString& aName )
1222 {
1223     tools::Long nCount = getCount();
1224     for (tools::Long i=0; i<nCount; i++)
1225         if ( getByIndex(i)->getName() == aName )
1226             return true;
1227     return false;
1228 }
1229 
getElementType()1230 uno::Type SAL_CALL ScDPDimensions::getElementType()
1231 {
1232     return cppu::UnoType<container::XNamed>::get();
1233 }
1234 
hasElements()1235 sal_Bool SAL_CALL ScDPDimensions::hasElements()
1236 {
1237     return ( getCount() > 0 );
1238 }
1239 
1240 // end of XNameAccess implementation
1241 
getCount() const1242 tools::Long ScDPDimensions::getCount() const
1243 {
1244     //  in tabular data, every column of source data is a dimension
1245 
1246     return nDimCount;
1247 }
1248 
getByIndex(tools::Long nIndex) const1249 ScDPDimension* ScDPDimensions::getByIndex(tools::Long nIndex) const
1250 {
1251     if ( nIndex >= 0 && nIndex < nDimCount )
1252     {
1253         if ( !ppDims )
1254         {
1255             const_cast<ScDPDimensions*>(this)->ppDims.reset(new rtl::Reference<ScDPDimension>[nDimCount] );
1256             for (tools::Long i=0; i<nDimCount; i++)
1257                 ppDims[i] = nullptr;
1258         }
1259         if ( !ppDims[nIndex].is() )
1260         {
1261             ppDims[nIndex] = new ScDPDimension( pSource, nIndex );
1262         }
1263 
1264         return ppDims[nIndex].get();
1265     }
1266 
1267     return nullptr;    //TODO: exception?
1268 }
1269 
ScDPDimension(ScDPSource * pSrc,tools::Long nD)1270 ScDPDimension::ScDPDimension( ScDPSource* pSrc, tools::Long nD ) :
1271     pSource( pSrc ),
1272     nDim( nD ),
1273     nFunction( ScGeneralFunction::SUM ),     // sum is default
1274     nSourceDim( -1 ),
1275     bHasSelectedPage( false ),
1276     mbHasHiddenMember(false)
1277 {
1278     //TODO: hold pSource
1279 }
1280 
~ScDPDimension()1281 ScDPDimension::~ScDPDimension()
1282 {
1283     //TODO: release pSource
1284 }
1285 
GetHierarchiesObject()1286 ScDPHierarchies* ScDPDimension::GetHierarchiesObject()
1287 {
1288     if (!mxHierarchies.is())
1289     {
1290         mxHierarchies = new ScDPHierarchies( pSource, nDim );
1291     }
1292     return mxHierarchies.get();
1293 }
1294 
GetLayoutName() const1295 const std::optional<OUString> & ScDPDimension::GetLayoutName() const
1296 {
1297     return mpLayoutName;
1298 }
1299 
GetSubtotalName() const1300 const std::optional<OUString> & ScDPDimension::GetSubtotalName() const
1301 {
1302     return mpSubtotalName;
1303 }
1304 
getHierarchies()1305 uno::Reference<container::XNameAccess> SAL_CALL ScDPDimension::getHierarchies()
1306 {
1307     return GetHierarchiesObject();
1308 }
1309 
getName()1310 OUString SAL_CALL ScDPDimension::getName()
1311 {
1312     if (!aName.isEmpty())
1313         return aName;
1314     else
1315         return pSource->GetData()->getDimensionName( nDim );
1316 }
1317 
setName(const OUString & rNewName)1318 void SAL_CALL ScDPDimension::setName( const OUString& rNewName )
1319 {
1320     //  used after cloning
1321     aName = rNewName;
1322 }
1323 
getOrientation() const1324 sheet::DataPilotFieldOrientation ScDPDimension::getOrientation() const
1325 {
1326     return pSource->GetOrientation( nDim );
1327 }
1328 
getIsDataLayoutDimension() const1329 bool ScDPDimension::getIsDataLayoutDimension() const
1330 {
1331     return pSource->GetData()->getIsDataLayoutDimension( nDim );
1332 }
1333 
setFunction(ScGeneralFunction nNew)1334 void ScDPDimension::setFunction(ScGeneralFunction nNew)
1335 {
1336     nFunction = nNew;
1337 }
1338 
CreateCloneObject()1339 ScDPDimension* ScDPDimension::CreateCloneObject()
1340 {
1341     OSL_ENSURE( nSourceDim < 0, "recursive duplicate - not implemented" );
1342 
1343     //TODO: set new name here, or temporary name ???
1344     OUString aNewName = aName;
1345 
1346     ScDPDimension* pNew = pSource->AddDuplicated( aNewName );
1347 
1348     pNew->aName = aNewName;             //TODO: here or in source?
1349     pNew->nSourceDim = nDim;            //TODO: recursive?
1350 
1351     return pNew;
1352 }
1353 
createClone()1354 uno::Reference<util::XCloneable> SAL_CALL ScDPDimension::createClone()
1355 {
1356     return CreateCloneObject();
1357 }
1358 
GetSelectedData()1359 const ScDPItemData& ScDPDimension::GetSelectedData()
1360 {
1361     if ( !pSelectedData )
1362     {
1363         // find the named member to initialize pSelectedData from it, with name and value
1364 
1365         tools::Long nLevel = 0;
1366 
1367         tools::Long nHierarchy = getUsedHierarchy();
1368         if ( nHierarchy >= ScDPHierarchies::getCount() )
1369             nHierarchy = 0;
1370         ScDPLevels* pLevels = GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject();
1371         tools::Long nLevCount = pLevels->getCount();
1372         if ( nLevel < nLevCount )
1373         {
1374             ScDPMembers* pMembers = pLevels->getByIndex(nLevel)->GetMembersObject();
1375 
1376             //TODO: merge with ScDPMembers::getByName
1377             tools::Long nCount = pMembers->getCount();
1378             for (tools::Long i=0; i<nCount && !pSelectedData; i++)
1379             {
1380                 ScDPMember* pMember = pMembers->getByIndex(i);
1381                 if (aSelectedPage == pMember->GetNameStr(false))
1382                 {
1383                     pSelectedData.reset( new ScDPItemData(pMember->FillItemData()) );
1384                 }
1385             }
1386         }
1387 
1388         if ( !pSelectedData )
1389             pSelectedData.reset( new ScDPItemData(aSelectedPage) );      // default - name only
1390     }
1391 
1392     return *pSelectedData;
1393 }
1394 
1395 // XPropertySet
1396 
getPropertySetInfo()1397 uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPDimension::getPropertySetInfo()
1398 {
1399     SolarMutexGuard aGuard;
1400 
1401     static const SfxItemPropertyMapEntry aDPDimensionMap_Impl[] =
1402     {
1403         { u"" SC_UNO_DP_FILTER,   0,  cppu::UnoType<uno::Sequence<sheet::TableFilterField>>::get(), 0, 0 },
1404         { u"" SC_UNO_DP_FLAGS,    0,  cppu::UnoType<sal_Int32>::get(),                beans::PropertyAttribute::READONLY, 0 },
1405         { u"" SC_UNO_DP_FUNCTION, 0,  cppu::UnoType<sheet::GeneralFunction>::get(),   0, 0 },
1406         { u"" SC_UNO_DP_FUNCTION2, 0,  cppu::UnoType<sal_Int16>::get(),   0, 0 },
1407         { u"" SC_UNO_DP_ISDATALAYOUT, 0,  cppu::UnoType<bool>::get(),                      beans::PropertyAttribute::READONLY, 0 },
1408         { u"" SC_UNO_DP_NUMBERFO, 0,  cppu::UnoType<sal_Int32>::get(),                beans::PropertyAttribute::READONLY, 0 },
1409         { u"" SC_UNO_DP_ORIENTATION, 0,  cppu::UnoType<sheet::DataPilotFieldOrientation>::get(), 0, 0 },
1410         { u"" SC_UNO_DP_ORIGINAL, 0,  cppu::UnoType<container::XNamed>::get(), beans::PropertyAttribute::READONLY, 0 },
1411         { u"" SC_UNO_DP_ORIGINAL_POS, 0, cppu::UnoType<sal_Int32>::get(),             0, 0 },
1412         { u"" SC_UNO_DP_POSITION, 0,  cppu::UnoType<sal_Int32>::get(),                0, 0 },
1413         { u"" SC_UNO_DP_REFVALUE, 0,  cppu::UnoType<sheet::DataPilotFieldReference>::get(), 0, 0 },
1414         { u"" SC_UNO_DP_USEDHIERARCHY, 0,  cppu::UnoType<sal_Int32>::get(),                0, 0 },
1415         { u"" SC_UNO_DP_LAYOUTNAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
1416         { u"" SC_UNO_DP_FIELD_SUBTOTALNAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
1417         { u"" SC_UNO_DP_HAS_HIDDEN_MEMBER, 0, cppu::UnoType<bool>::get(), 0, 0 },
1418         { u"", 0, css::uno::Type(), 0, 0 }
1419     };
1420     static uno::Reference<beans::XPropertySetInfo> aRef =
1421         new SfxItemPropertySetInfo( aDPDimensionMap_Impl );
1422     return aRef;
1423 }
1424 
setPropertyValue(const OUString & aPropertyName,const uno::Any & aValue)1425 void SAL_CALL ScDPDimension::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
1426 {
1427     if ( aPropertyName == SC_UNO_DP_USEDHIERARCHY )
1428     {
1429         // #i52547# don't use the incomplete date hierarchy implementation - ignore the call
1430     }
1431     else if ( aPropertyName == SC_UNO_DP_ORIENTATION )
1432     {
1433         sheet::DataPilotFieldOrientation eEnum;
1434         if (aValue >>= eEnum)
1435             pSource->SetOrientation( nDim, eEnum );
1436     }
1437     else if ( aPropertyName == SC_UNO_DP_FUNCTION )
1438     {
1439         sheet::GeneralFunction eEnum;
1440         if (aValue >>= eEnum)
1441             setFunction( static_cast<ScGeneralFunction>(eEnum) );
1442     }
1443     else if ( aPropertyName == SC_UNO_DP_FUNCTION2 )
1444     {
1445         sal_Int16 eEnum;
1446         if (aValue >>= eEnum)
1447             setFunction( static_cast<ScGeneralFunction>(eEnum) );
1448     }
1449     else if ( aPropertyName == SC_UNO_DP_REFVALUE )
1450         aValue >>= aReferenceValue;
1451     else if ( aPropertyName == SC_UNO_DP_FILTER )
1452     {
1453         bool bDone = false;
1454         uno::Sequence<sheet::TableFilterField> aSeq;
1455         if (aValue >>= aSeq)
1456         {
1457             sal_Int32 nLength = aSeq.getLength();
1458             if ( nLength == 0 )
1459             {
1460                 aSelectedPage.clear();
1461                 bHasSelectedPage = false;
1462                 bDone = true;
1463             }
1464             else if ( nLength == 1 )
1465             {
1466                 const sheet::TableFilterField& rField = aSeq[0];
1467                 if ( rField.Field == 0 && rField.Operator == sheet::FilterOperator_EQUAL && !rField.IsNumeric )
1468                 {
1469                     aSelectedPage = rField.StringValue;
1470                     bHasSelectedPage = true;
1471                     bDone = true;
1472                 }
1473             }
1474         }
1475         if ( !bDone )
1476         {
1477             OSL_FAIL("Filter property is not a single string");
1478             throw lang::IllegalArgumentException();
1479         }
1480         pSelectedData.reset();       // invalid after changing aSelectedPage
1481     }
1482     else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
1483     {
1484         OUString aTmpName;
1485         if (aValue >>= aTmpName)
1486             mpLayoutName = aTmpName;
1487     }
1488     else if (aPropertyName == SC_UNO_DP_FIELD_SUBTOTALNAME)
1489     {
1490         OUString aTmpName;
1491         if (aValue >>= aTmpName)
1492             mpSubtotalName = aTmpName;
1493     }
1494     else if (aPropertyName == SC_UNO_DP_HAS_HIDDEN_MEMBER)
1495     {
1496         bool b = false;
1497         aValue >>= b;
1498         mbHasHiddenMember = b;
1499     }
1500     else
1501     {
1502         OSL_FAIL("unknown property");
1503         //TODO: THROW( UnknownPropertyException() );
1504     }
1505 }
1506 
getPropertyValue(const OUString & aPropertyName)1507 uno::Any SAL_CALL ScDPDimension::getPropertyValue( const OUString& aPropertyName )
1508 {
1509     uno::Any aRet;
1510     if ( aPropertyName == SC_UNO_DP_POSITION )
1511         aRet <<= pSource->GetPosition( nDim );
1512     else if ( aPropertyName == SC_UNO_DP_USEDHIERARCHY )
1513         aRet <<= static_cast<sal_Int32>(getUsedHierarchy());
1514     else if ( aPropertyName == SC_UNO_DP_ORIENTATION )
1515     {
1516         sheet::DataPilotFieldOrientation eVal = getOrientation();
1517         aRet <<= eVal;
1518     }
1519     else if ( aPropertyName == SC_UNO_DP_FUNCTION )
1520     {
1521         ScGeneralFunction nVal = getFunction();
1522         if (nVal == ScGeneralFunction::MEDIAN)
1523             nVal = ScGeneralFunction::NONE;
1524         const int nValAsInt = static_cast<int>(nVal);
1525         assert(nValAsInt >= int(css::sheet::GeneralFunction_NONE) &&
1526                nValAsInt <= int(css::sheet::GeneralFunction_VARP));
1527         aRet <<= static_cast<sheet::GeneralFunction>(nValAsInt);
1528     }
1529     else if ( aPropertyName == SC_UNO_DP_FUNCTION2 )
1530     {
1531         ScGeneralFunction eVal = getFunction();
1532         aRet <<= static_cast<sal_Int16>(eVal);
1533     }
1534     else if ( aPropertyName == SC_UNO_DP_REFVALUE )
1535         aRet <<= aReferenceValue;
1536     else if ( aPropertyName == SC_UNO_DP_ISDATALAYOUT )                 // read-only properties
1537         aRet <<= getIsDataLayoutDimension();
1538     else if ( aPropertyName == SC_UNO_DP_NUMBERFO )
1539     {
1540         sal_Int32 nFormat = 0;
1541         ScGeneralFunction eFunc = getFunction();
1542         // #i63745# don't use source format for "count"
1543         if ( eFunc != ScGeneralFunction::COUNT && eFunc != ScGeneralFunction::COUNTNUMS )
1544             nFormat = pSource->GetData()->GetNumberFormat( ( nSourceDim >= 0 ) ? nSourceDim : nDim );
1545 
1546         switch ( aReferenceValue.ReferenceType )
1547         {
1548         case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE:
1549         case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
1550         case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE:
1551         case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE:
1552         case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE:
1553             nFormat = pSource->GetData()->GetNumberFormatByIdx( NF_PERCENT_DEC2 );
1554             break;
1555         case sheet::DataPilotFieldReferenceType::INDEX:
1556             nFormat = pSource->GetData()->GetNumberFormatByIdx( NF_NUMBER_SYSTEM );
1557             break;
1558         default:
1559             break;
1560         }
1561 
1562         aRet <<= nFormat;
1563     }
1564     else if ( aPropertyName == SC_UNO_DP_ORIGINAL )
1565     {
1566         uno::Reference<container::XNamed> xOriginal;
1567         if (nSourceDim >= 0)
1568             xOriginal = pSource->GetDimensionsObject()->getByIndex(nSourceDim);
1569         aRet <<= xOriginal;
1570     }
1571     else if (aPropertyName == SC_UNO_DP_ORIGINAL_POS)
1572     {
1573         aRet <<= nSourceDim;
1574     }
1575     else if ( aPropertyName == SC_UNO_DP_FILTER )
1576     {
1577         if ( bHasSelectedPage )
1578         {
1579             // single filter field: first field equal to selected string
1580             sheet::TableFilterField aField( sheet::FilterConnection_AND, 0,
1581                     sheet::FilterOperator_EQUAL, false, 0.0, aSelectedPage );
1582             aRet <<= uno::Sequence<sheet::TableFilterField>( &aField, 1 );
1583         }
1584         else
1585             aRet <<= uno::Sequence<sheet::TableFilterField>(0);
1586     }
1587     else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
1588         aRet <<= mpLayoutName ? *mpLayoutName : OUString();
1589     else if (aPropertyName == SC_UNO_DP_FIELD_SUBTOTALNAME)
1590         aRet <<= mpSubtotalName ? *mpSubtotalName : OUString();
1591     else if (aPropertyName == SC_UNO_DP_HAS_HIDDEN_MEMBER)
1592         aRet <<= mbHasHiddenMember;
1593     else if (aPropertyName == SC_UNO_DP_FLAGS)
1594     {
1595         aRet <<= sal_Int32(0); // tabular data: all orientations are possible
1596     }
1597     else
1598     {
1599         OSL_FAIL("unknown property");
1600         //TODO: THROW( UnknownPropertyException() );
1601     }
1602     return aRet;
1603 }
1604 
SC_IMPL_DUMMY_PROPERTY_LISTENER(ScDPDimension)1605 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPDimension )
1606 
1607 ScDPHierarchies::ScDPHierarchies( ScDPSource* pSrc, tools::Long nD ) :
1608     pSource( pSrc ),
1609     nDim( nD )
1610 {
1611     //TODO: hold pSource
1612 }
1613 
~ScDPHierarchies()1614 ScDPHierarchies::~ScDPHierarchies()
1615 {
1616     //TODO: release pSource
1617 }
1618 
1619 // very simple XNameAccess implementation using getCount/getByIndex
1620 
getByName(const OUString & aName)1621 uno::Any SAL_CALL ScDPHierarchies::getByName( const OUString& aName )
1622 {
1623     tools::Long nCount = getCount();
1624     for (tools::Long i=0; i<nCount; i++)
1625         if ( getByIndex(i)->getName() == aName )
1626         {
1627             uno::Reference<container::XNamed> xNamed = getByIndex(i);
1628             uno::Any aRet;
1629             aRet <<= xNamed;
1630             return aRet;
1631         }
1632 
1633     throw container::NoSuchElementException();
1634 }
1635 
getElementNames()1636 uno::Sequence<OUString> SAL_CALL ScDPHierarchies::getElementNames()
1637 {
1638     tools::Long nCount = getCount();
1639     uno::Sequence<OUString> aSeq(nCount);
1640     OUString* pArr = aSeq.getArray();
1641     for (tools::Long i=0; i<nCount; i++)
1642         pArr[i] = getByIndex(i)->getName();
1643     return aSeq;
1644 }
1645 
hasByName(const OUString & aName)1646 sal_Bool SAL_CALL ScDPHierarchies::hasByName( const OUString& aName )
1647 {
1648     tools::Long nCount = getCount();
1649     for (tools::Long i=0; i<nCount; i++)
1650         if ( getByIndex(i)->getName() == aName )
1651             return true;
1652     return false;
1653 }
1654 
getElementType()1655 uno::Type SAL_CALL ScDPHierarchies::getElementType()
1656 {
1657     return cppu::UnoType<container::XNamed>::get();
1658 }
1659 
hasElements()1660 sal_Bool SAL_CALL ScDPHierarchies::hasElements()
1661 {
1662     return ( getCount() > 0 );
1663 }
1664 
1665 // end of XNameAccess implementation
1666 
getCount()1667 sal_Int32 ScDPHierarchies::getCount()
1668 {
1669     return nHierCount;
1670 }
1671 
getByIndex(tools::Long nIndex) const1672 ScDPHierarchy* ScDPHierarchies::getByIndex(tools::Long nIndex) const
1673 {
1674     //  pass hierarchy index to new object in case the implementation
1675     //  will be extended to more than one hierarchy
1676 
1677     if ( nIndex >= 0 && nIndex < nHierCount )
1678     {
1679         if ( !ppHiers )
1680         {
1681             const_cast<ScDPHierarchies*>(this)->ppHiers.reset( new rtl::Reference<ScDPHierarchy>[nHierCount] );
1682             for (sal_Int32 i=0; i<nHierCount; i++)
1683                 ppHiers[i] = nullptr;
1684         }
1685         if ( !ppHiers[nIndex].is() )
1686         {
1687             ppHiers[nIndex] = new ScDPHierarchy( pSource, nDim, nIndex );
1688         }
1689 
1690         return ppHiers[nIndex].get();
1691     }
1692 
1693     return nullptr;    //TODO: exception?
1694 }
1695 
ScDPHierarchy(ScDPSource * pSrc,sal_Int32 nD,sal_Int32 nH)1696 ScDPHierarchy::ScDPHierarchy( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH ) :
1697     pSource( pSrc ),
1698     nDim( nD ),
1699     nHier( nH )
1700 {
1701     //TODO: hold pSource
1702 }
1703 
~ScDPHierarchy()1704 ScDPHierarchy::~ScDPHierarchy()
1705 {
1706     //TODO: release pSource
1707 }
1708 
GetLevelsObject()1709 ScDPLevels* ScDPHierarchy::GetLevelsObject()
1710 {
1711     if (!mxLevels.is())
1712     {
1713         mxLevels = new ScDPLevels( pSource, nDim, nHier );
1714     }
1715     return mxLevels.get();
1716 }
1717 
getLevels()1718 uno::Reference<container::XNameAccess> SAL_CALL ScDPHierarchy::getLevels()
1719 {
1720     return GetLevelsObject();
1721 }
1722 
getName()1723 OUString SAL_CALL ScDPHierarchy::getName()
1724 {
1725     OUString aRet;        //TODO: globstr-ID !!!!
1726     switch (nHier)
1727     {
1728         case SC_DAPI_HIERARCHY_FLAT:
1729             aRet = "flat";
1730             break;  //TODO: name ???????
1731         case SC_DAPI_HIERARCHY_QUARTER:
1732             aRet = "Quarter";
1733             break;  //TODO: name ???????
1734         case SC_DAPI_HIERARCHY_WEEK:
1735             aRet = "Week";
1736             break;  //TODO: name ???????
1737         default:
1738             OSL_FAIL( "ScDPHierarchy::getName: unexpected hierarchy" );
1739             break;
1740     }
1741     return aRet;
1742 }
1743 
setName(const OUString &)1744 void SAL_CALL ScDPHierarchy::setName( const OUString& /* rNewName */ )
1745 {
1746     OSL_FAIL("not implemented");        //TODO: exception?
1747 }
1748 
ScDPLevels(ScDPSource * pSrc,sal_Int32 nD,sal_Int32 nH)1749 ScDPLevels::ScDPLevels( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH ) :
1750     pSource( pSrc ),
1751     nDim( nD ),
1752     nHier( nH )
1753 {
1754     //TODO: hold pSource
1755 
1756     //  text columns have only one level
1757 
1758     tools::Long nSrcDim = pSource->GetSourceDim( nDim );
1759     if ( pSource->IsDateDimension( nSrcDim ) )
1760     {
1761         switch ( nHier )
1762         {
1763             case SC_DAPI_HIERARCHY_FLAT:    nLevCount = SC_DAPI_FLAT_LEVELS;    break;
1764             case SC_DAPI_HIERARCHY_QUARTER: nLevCount = SC_DAPI_QUARTER_LEVELS; break;
1765             case SC_DAPI_HIERARCHY_WEEK:    nLevCount = SC_DAPI_WEEK_LEVELS;    break;
1766             default:
1767                 OSL_FAIL("wrong hierarchy");
1768                 nLevCount = 0;
1769         }
1770     }
1771     else
1772         nLevCount = 1;
1773 }
1774 
~ScDPLevels()1775 ScDPLevels::~ScDPLevels()
1776 {
1777     //TODO: release pSource
1778 }
1779 
1780 // very simple XNameAccess implementation using getCount/getByIndex
1781 
getByName(const OUString & aName)1782 uno::Any SAL_CALL ScDPLevels::getByName( const OUString& aName )
1783 {
1784     tools::Long nCount = getCount();
1785     for (tools::Long i=0; i<nCount; i++)
1786         if ( getByIndex(i)->getName() == aName )
1787         {
1788             uno::Reference<container::XNamed> xNamed = getByIndex(i);
1789             uno::Any aRet;
1790             aRet <<= xNamed;
1791             return aRet;
1792         }
1793 
1794     throw container::NoSuchElementException();
1795 }
1796 
getElementNames()1797 uno::Sequence<OUString> SAL_CALL ScDPLevels::getElementNames()
1798 {
1799     tools::Long nCount = getCount();
1800     uno::Sequence<OUString> aSeq(nCount);
1801     OUString* pArr = aSeq.getArray();
1802     for (tools::Long i=0; i<nCount; i++)
1803         pArr[i] = getByIndex(i)->getName();
1804     return aSeq;
1805 }
1806 
hasByName(const OUString & aName)1807 sal_Bool SAL_CALL ScDPLevels::hasByName( const OUString& aName )
1808 {
1809     tools::Long nCount = getCount();
1810     for (tools::Long i=0; i<nCount; i++)
1811         if ( getByIndex(i)->getName() == aName )
1812             return true;
1813     return false;
1814 }
1815 
getElementType()1816 uno::Type SAL_CALL ScDPLevels::getElementType()
1817 {
1818     return cppu::UnoType<container::XNamed>::get();
1819 }
1820 
hasElements()1821 sal_Bool SAL_CALL ScDPLevels::hasElements()
1822 {
1823     return ( getCount() > 0 );
1824 }
1825 
1826 // end of XNameAccess implementation
1827 
getCount() const1828 sal_Int32 ScDPLevels::getCount() const
1829 {
1830     return nLevCount;
1831 }
1832 
getByIndex(sal_Int32 nIndex) const1833 ScDPLevel* ScDPLevels::getByIndex(sal_Int32 nIndex) const
1834 {
1835     if ( nIndex >= 0 && nIndex < nLevCount )
1836     {
1837         if ( !ppLevs )
1838         {
1839             const_cast<ScDPLevels*>(this)->ppLevs.reset(new rtl::Reference<ScDPLevel>[nLevCount] );
1840             for (tools::Long i=0; i<nLevCount; i++)
1841                 ppLevs[i] = nullptr;
1842         }
1843         if ( !ppLevs[nIndex].is() )
1844         {
1845             ppLevs[nIndex] = new ScDPLevel( pSource, nDim, nHier, nIndex );
1846         }
1847 
1848         return ppLevs[nIndex].get();
1849     }
1850 
1851     return nullptr;    //TODO: exception?
1852 }
1853 
1854 namespace {
1855 
1856 class ScDPGlobalMembersOrder
1857 {
1858     ScDPLevel&  rLevel;
1859     bool        bAscending;
1860 
1861 public:
ScDPGlobalMembersOrder(ScDPLevel & rLev,bool bAsc)1862             ScDPGlobalMembersOrder( ScDPLevel& rLev, bool bAsc ) :
1863                 rLevel(rLev),
1864                 bAscending(bAsc)
1865             {}
1866 
1867     bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
1868 };
1869 
1870 }
1871 
operator ()(sal_Int32 nIndex1,sal_Int32 nIndex2) const1872 bool ScDPGlobalMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
1873 {
1874     sal_Int32 nCompare = 0;
1875     // seems that some ::std::sort() implementations pass the same index twice
1876     if( nIndex1 != nIndex2 )
1877     {
1878         ScDPMembers* pMembers = rLevel.GetMembersObject();
1879         ScDPMember* pMember1 = pMembers->getByIndex(nIndex1);
1880         ScDPMember* pMember2 = pMembers->getByIndex(nIndex2);
1881         nCompare = pMember1->Compare( *pMember2 );
1882     }
1883     return bAscending ? (nCompare < 0) : (nCompare > 0);
1884 }
1885 
ScDPLevel(ScDPSource * pSrc,sal_Int32 nD,sal_Int32 nH,sal_Int32 nL)1886 ScDPLevel::ScDPLevel( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH, sal_Int32 nL ) :
1887     pSource( pSrc ),
1888     nDim( nD ),
1889     nHier( nH ),
1890     nLev( nL ),
1891     aSortInfo( EMPTY_OUSTRING, true, sheet::DataPilotFieldSortMode::NAME ),   // default: sort by name
1892     nSortMeasure( 0 ),
1893     nAutoMeasure( 0 ),
1894     bShowEmpty( false ),
1895     bEnableLayout( false ),
1896     bRepeatItemLabels( false )
1897 {
1898     //TODO: hold pSource
1899     //  aSubTotals is empty
1900 }
1901 
~ScDPLevel()1902 ScDPLevel::~ScDPLevel()
1903 {
1904     //TODO: release pSource
1905 }
1906 
EvaluateSortOrder()1907 void ScDPLevel::EvaluateSortOrder()
1908 {
1909     switch (aSortInfo.Mode)
1910     {
1911         case sheet::DataPilotFieldSortMode::DATA:
1912             {
1913                 // find index of measure (index among data dimensions)
1914 
1915                 tools::Long nMeasureCount = pSource->GetDataDimensionCount();
1916                 for (tools::Long nMeasure=0; nMeasure<nMeasureCount; nMeasure++)
1917                 {
1918                     if (pSource->GetDataDimName(nMeasure) == aSortInfo.Field)
1919                     {
1920                         nSortMeasure = nMeasure;
1921                         break;
1922                     }
1923                 }
1924 
1925                 //TODO: error if not found?
1926             }
1927             break;
1928         case sheet::DataPilotFieldSortMode::MANUAL:
1929         case sheet::DataPilotFieldSortMode::NAME:
1930             {
1931                 ScDPMembers* pLocalMembers = GetMembersObject();
1932                 tools::Long nCount = pLocalMembers->getCount();
1933 
1934                 aGlobalOrder.resize( nCount );
1935                 for (tools::Long nPos=0; nPos<nCount; nPos++)
1936                     aGlobalOrder[nPos] = nPos;
1937 
1938                 // allow manual or name (manual is always ascending)
1939                 bool bAscending = ( aSortInfo.Mode == sheet::DataPilotFieldSortMode::MANUAL || aSortInfo.IsAscending );
1940                 ScDPGlobalMembersOrder aComp( *this, bAscending );
1941                 ::std::sort( aGlobalOrder.begin(), aGlobalOrder.end(), aComp );
1942             }
1943             break;
1944     }
1945 
1946     if ( !aAutoShowInfo.IsEnabled )
1947         return;
1948 
1949     // find index of measure (index among data dimensions)
1950 
1951     tools::Long nMeasureCount = pSource->GetDataDimensionCount();
1952     for (tools::Long nMeasure=0; nMeasure<nMeasureCount; nMeasure++)
1953     {
1954         if (pSource->GetDataDimName(nMeasure) == aAutoShowInfo.DataField)
1955         {
1956             nAutoMeasure = nMeasure;
1957             break;
1958         }
1959     }
1960 
1961     //TODO: error if not found?
1962 }
1963 
SetEnableLayout(bool bSet)1964 void ScDPLevel::SetEnableLayout(bool bSet)
1965 {
1966     bEnableLayout = bSet;
1967 }
1968 
GetMembersObject()1969 ScDPMembers* ScDPLevel::GetMembersObject()
1970 {
1971     if (!mxMembers.is())
1972     {
1973         mxMembers = new ScDPMembers( pSource, nDim, nHier, nLev );
1974     }
1975     return mxMembers.get();
1976 }
1977 
getMembers()1978 uno::Reference<sheet::XMembersAccess> SAL_CALL ScDPLevel::getMembers()
1979 {
1980     return GetMembersObject();
1981 }
1982 
getResults()1983 uno::Sequence<sheet::MemberResult> SAL_CALL ScDPLevel::getResults()
1984 {
1985     const uno::Sequence<sheet::MemberResult>* pRes = pSource->GetMemberResults( this );
1986     if (pRes)
1987         return *pRes;
1988 
1989     return {};       //TODO: Error?
1990 }
1991 
getName()1992 OUString SAL_CALL ScDPLevel::getName()
1993 {
1994     tools::Long nSrcDim = pSource->GetSourceDim( nDim );
1995     if ( pSource->IsDateDimension( nSrcDim ) )
1996     {
1997         OUString aRet;        //TODO: globstr-ID !!!!
1998 
1999         if ( nHier == SC_DAPI_HIERARCHY_QUARTER )
2000         {
2001             switch ( nLev )
2002             {
2003                 case SC_DAPI_LEVEL_YEAR:
2004                     aRet = "Year";
2005                     break;
2006                 case SC_DAPI_LEVEL_QUARTER:
2007                     aRet = "Quarter";
2008                     break;
2009                 case SC_DAPI_LEVEL_MONTH:
2010                     aRet = "Month";
2011                     break;
2012                 case SC_DAPI_LEVEL_DAY:
2013                     aRet = "Day";
2014                     break;
2015                 default:
2016                     OSL_FAIL( "ScDPLevel::getName: unexpected level" );
2017                     break;
2018             }
2019         }
2020         else if ( nHier == SC_DAPI_HIERARCHY_WEEK )
2021         {
2022             switch ( nLev )
2023             {
2024                 case SC_DAPI_LEVEL_YEAR:
2025                     aRet = "Year";
2026                     break;
2027                 case SC_DAPI_LEVEL_WEEK:
2028                     aRet = "Week";
2029                     break;
2030                 case SC_DAPI_LEVEL_WEEKDAY:
2031                     aRet = "Weekday";
2032                     break;
2033                 default:
2034                     OSL_FAIL( "ScDPLevel::getName: unexpected level" );
2035                     break;
2036             }
2037         }
2038         if (!aRet.isEmpty())
2039             return aRet;
2040     }
2041 
2042     ScDPDimension* pDim = pSource->GetDimensionsObject()->getByIndex(nSrcDim);
2043     if (!pDim)
2044         return OUString();
2045 
2046     return pDim->getName();
2047 }
2048 
setName(const OUString &)2049 void SAL_CALL ScDPLevel::setName( const OUString& /* rNewName */ )
2050 {
2051     OSL_FAIL("not implemented");        //TODO: exception?
2052 }
2053 
getSubTotals() const2054 uno::Sequence<sal_Int16> ScDPLevel::getSubTotals() const
2055 {
2056     //TODO: separate functions for settings and evaluation?
2057 
2058     tools::Long nSrcDim = pSource->GetSourceDim( nDim );
2059     if ( !pSource->SubTotalAllowed( nSrcDim ) )
2060         return {};
2061 
2062     return aSubTotals;
2063 }
2064 
2065 // XPropertySet
2066 
getPropertySetInfo()2067 uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPLevel::getPropertySetInfo()
2068 {
2069     SolarMutexGuard aGuard;
2070 
2071     static const SfxItemPropertyMapEntry aDPLevelMap_Impl[] =
2072     {
2073         //TODO: change type of AutoShow/Layout/Sorting to API struct when available
2074         { u"" SC_UNO_DP_AUTOSHOW, 0,  cppu::UnoType<sheet::DataPilotFieldAutoShowInfo>::get(),     0, 0 },
2075         { u"" SC_UNO_DP_LAYOUT,   0,  cppu::UnoType<sheet::DataPilotFieldLayoutInfo>::get(),       0, 0 },
2076         { u"" SC_UNO_DP_SHOWEMPTY, 0, cppu::UnoType<bool>::get(),                                   0, 0 },
2077         { u"" SC_UNO_DP_REPEATITEMLABELS, 0, cppu::UnoType<bool>::get(),                                   0, 0 },
2078         { u"" SC_UNO_DP_SORTING,  0,  cppu::UnoType<sheet::DataPilotFieldSortInfo>::get(),         0, 0 },
2079         { u"" SC_UNO_DP_SUBTOTAL, 0,  cppu::UnoType<uno::Sequence<sheet::GeneralFunction>>::get(), 0, 0 },
2080         { u"" SC_UNO_DP_SUBTOTAL2, 0, cppu::UnoType<uno::Sequence<sal_Int16>>::get(), 0, 0 },
2081         { u"", 0, css::uno::Type(), 0, 0 }
2082     };
2083     static uno::Reference<beans::XPropertySetInfo> aRef =
2084         new SfxItemPropertySetInfo( aDPLevelMap_Impl );
2085     return aRef;
2086 }
2087 
setPropertyValue(const OUString & aPropertyName,const uno::Any & aValue)2088 void SAL_CALL ScDPLevel::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
2089 {
2090     if ( aPropertyName == SC_UNO_DP_SHOWEMPTY )
2091         bShowEmpty = lcl_GetBoolFromAny(aValue);
2092     else if ( aPropertyName == SC_UNO_DP_REPEATITEMLABELS )
2093         bRepeatItemLabels = lcl_GetBoolFromAny(aValue);
2094     else if ( aPropertyName == SC_UNO_DP_SUBTOTAL )
2095     {
2096         uno::Sequence<sheet::GeneralFunction> aSeq;
2097         aValue >>= aSeq;
2098         aSubTotals.realloc(aSeq.getLength());
2099         std::transform(aSeq.begin(), aSeq.end(), aSubTotals.begin(),
2100             [](const sheet::GeneralFunction& rFunc) -> sal_Int16 {
2101                 return static_cast<sal_Int16>(rFunc); });
2102     }
2103     else if ( aPropertyName == SC_UNO_DP_SUBTOTAL2 )
2104         aValue >>= aSubTotals;
2105     else if ( aPropertyName == SC_UNO_DP_SORTING )
2106         aValue >>= aSortInfo;
2107     else if ( aPropertyName == SC_UNO_DP_AUTOSHOW )
2108         aValue >>= aAutoShowInfo;
2109     else if ( aPropertyName == SC_UNO_DP_LAYOUT )
2110         aValue >>= aLayoutInfo;
2111     else
2112     {
2113         OSL_FAIL("unknown property");
2114     }
2115 }
2116 
getPropertyValue(const OUString & aPropertyName)2117 uno::Any SAL_CALL ScDPLevel::getPropertyValue( const OUString& aPropertyName )
2118 {
2119     uno::Any aRet;
2120     if ( aPropertyName == SC_UNO_DP_SHOWEMPTY )
2121         aRet <<= bShowEmpty;
2122     else if ( aPropertyName == SC_UNO_DP_REPEATITEMLABELS )
2123         aRet <<= bRepeatItemLabels;
2124     else if ( aPropertyName == SC_UNO_DP_SUBTOTAL )
2125     {
2126         const uno::Sequence<sal_Int16> aSeq = getSubTotals();
2127         uno::Sequence<sheet::GeneralFunction> aNewSeq;
2128         aNewSeq.realloc(aSeq.getLength());
2129         std::transform(aSeq.begin(), aSeq.end(), aNewSeq.begin(),
2130             [](const sal_Int16 nFunc) -> sheet::GeneralFunction {
2131                 if (nFunc == sheet::GeneralFunction2::MEDIAN)
2132                     return sheet::GeneralFunction_NONE;
2133                 return static_cast<sheet::GeneralFunction>(nFunc);
2134             });
2135 
2136         aRet <<= aNewSeq;
2137     }
2138     else if ( aPropertyName == SC_UNO_DP_SUBTOTAL2 )
2139     {
2140         uno::Sequence<sal_Int16> aSeq = getSubTotals();        //TODO: avoid extra copy?
2141         aRet <<= aSeq;
2142     }
2143     else if ( aPropertyName == SC_UNO_DP_SORTING )
2144         aRet <<= aSortInfo;
2145     else if ( aPropertyName == SC_UNO_DP_AUTOSHOW )
2146         aRet <<= aAutoShowInfo;
2147     else if ( aPropertyName == SC_UNO_DP_LAYOUT )
2148         aRet <<= aLayoutInfo;
2149     else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
2150     {
2151         // read only property
2152         tools::Long nSrcDim = pSource->GetSourceDim(nDim);
2153         ScDPDimension* pDim = pSource->GetDimensionsObject()->getByIndex(nSrcDim);
2154         if (!pDim)
2155             return aRet;
2156 
2157         const std::optional<OUString> & pLayoutName = pDim->GetLayoutName();
2158         if (!pLayoutName)
2159             return aRet;
2160 
2161         aRet <<= *pLayoutName;
2162     }
2163     else
2164     {
2165         OSL_FAIL("unknown property");
2166     }
2167     return aRet;
2168 }
2169 
SC_IMPL_DUMMY_PROPERTY_LISTENER(ScDPLevel)2170 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPLevel )
2171 
2172 ScDPMembers::ScDPMembers( ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH, sal_Int32 nL ) :
2173     pSource( pSrc ),
2174     nDim( nD ),
2175     nHier( nH ),
2176     nLev( nL )
2177 {
2178     //TODO: hold pSource
2179 
2180     tools::Long nSrcDim = pSource->GetSourceDim( nDim );
2181     if ( pSource->IsDataLayoutDimension(nSrcDim) )
2182         nMbrCount = pSource->GetDataDimensionCount();
2183     else if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
2184     {
2185         nMbrCount = 0;
2186         if ( nHier == SC_DAPI_HIERARCHY_QUARTER )
2187         {
2188             switch (nLev)
2189             {
2190                 case SC_DAPI_LEVEL_YEAR:
2191                     {
2192                         const ScDPItemData* pLastNumData = nullptr;
2193                         for ( SCROW n = 0; n < static_cast<SCROW>(pSource->GetData()->GetColumnEntries(nDim).size()); n-- )
2194                         {
2195                             const ScDPItemData* pData  = GetSrcItemDataByIndex( n );
2196                             if ( pData && pData->HasStringData() )
2197                                 break;
2198                             else
2199                                 pLastNumData = pData;
2200                         }
2201 
2202                         if ( pLastNumData )
2203                         {
2204                             const ScDPItemData*  pFirstData = GetSrcItemDataByIndex( 0 );
2205                             double fFirstVal = pFirstData->GetValue();
2206                             double fLastVal = pLastNumData->GetValue();
2207 
2208                             tools::Long nFirstYear = pSource->GetData()->GetDatePart(
2209                                         static_cast<tools::Long>(::rtl::math::approxFloor( fFirstVal )),
2210                                         nHier, nLev );
2211                             tools::Long nLastYear = pSource->GetData()->GetDatePart(
2212                                         static_cast<tools::Long>(::rtl::math::approxFloor( fLastVal )),
2213                                         nHier, nLev );
2214 
2215                             nMbrCount = nLastYear + 1 - nFirstYear;
2216                         }
2217                         else
2218                             nMbrCount = 0;      // no values
2219                     }
2220                     break;
2221                 case SC_DAPI_LEVEL_QUARTER: nMbrCount = 4;  break;
2222                 case SC_DAPI_LEVEL_MONTH:   nMbrCount = 12; break;
2223                 case SC_DAPI_LEVEL_DAY:     nMbrCount = 31; break;
2224                 default:
2225                     OSL_FAIL( "ScDPMembers::ScDPMembers: unexpected level" );
2226                     break;
2227             }
2228         }
2229         else if ( nHier == SC_DAPI_HIERARCHY_WEEK )
2230         {
2231             switch (nLev)
2232             {
2233                 case SC_DAPI_LEVEL_YEAR:    nMbrCount = 1;  break;      //TODO: get years from source
2234                 case SC_DAPI_LEVEL_WEEK:    nMbrCount = 53; break;
2235                 case SC_DAPI_LEVEL_WEEKDAY: nMbrCount = 7;  break;
2236                 default:
2237                     OSL_FAIL( "ScDPMembers::ScDPMembers: unexpected level" );
2238                     break;
2239             }
2240         }
2241     }
2242     else
2243         nMbrCount = pSource->GetData()->GetMembersCount( nSrcDim );
2244 }
2245 
~ScDPMembers()2246 ScDPMembers::~ScDPMembers()
2247 {
2248 }
2249 
2250 // XNameAccess implementation using getCount/getByIndex
2251 
GetIndexFromName(const OUString & rName) const2252 sal_Int32 ScDPMembers::GetIndexFromName( const OUString& rName ) const
2253 {
2254     if ( aHashMap.empty() )
2255     {
2256         // store the index for each name
2257 
2258         sal_Int32 nCount = getCount();
2259         for (sal_Int32 i=0; i<nCount; i++)
2260             aHashMap[ getByIndex(i)->getName() ] = i;
2261     }
2262 
2263     ScDPMembersHashMap::const_iterator aIter = aHashMap.find( rName );
2264     if ( aIter != aHashMap.end() )
2265         return aIter->second;           // found index
2266     else
2267         return -1;                      // not found
2268 }
2269 
getByName(const OUString & aName)2270 uno::Any SAL_CALL ScDPMembers::getByName( const OUString& aName )
2271 {
2272     sal_Int32 nIndex = GetIndexFromName( aName );
2273     if ( nIndex >= 0 )
2274     {
2275         uno::Reference<container::XNamed> xNamed = getByIndex(nIndex);
2276         uno::Any aRet;
2277         aRet <<= xNamed;
2278         return aRet;
2279     }
2280 
2281     throw container::NoSuchElementException();
2282 }
2283 
getElementNames()2284 uno::Sequence<OUString> SAL_CALL ScDPMembers::getElementNames()
2285 {
2286     return getElementNames( false );
2287 }
2288 
hasByName(const OUString & aName)2289 sal_Bool SAL_CALL ScDPMembers::hasByName( const OUString& aName )
2290 {
2291     return ( GetIndexFromName( aName ) >= 0 );
2292 }
2293 
getElementType()2294 uno::Type SAL_CALL ScDPMembers::getElementType()
2295 {
2296     return cppu::UnoType<container::XNamed>::get();
2297 }
2298 
hasElements()2299 sal_Bool SAL_CALL ScDPMembers::hasElements()
2300 {
2301     return ( getCount() > 0 );
2302 }
2303 
2304 // end of XNameAccess implementation
2305 
2306 // XMembersAccess implementation
2307 
getLocaleIndependentElementNames()2308 uno::Sequence<OUString> SAL_CALL ScDPMembers::getLocaleIndependentElementNames()
2309 {
2310     return getElementNames( true );
2311 }
2312 
2313 // end of XMembersAccess implementation
2314 
getElementNames(bool bLocaleIndependent) const2315 uno::Sequence<OUString> ScDPMembers::getElementNames( bool bLocaleIndependent ) const
2316 {
2317     // Return list of names in sorted order,
2318     // so it's displayed in that order in the field options dialog.
2319     // Sorting is done at the level object (parent of this).
2320 
2321     ScDPLevel* pLevel = pSource->GetDimensionsObject()->getByIndex(nDim)->
2322         GetHierarchiesObject()->getByIndex(nHier)->GetLevelsObject()->getByIndex(nLev);
2323     pLevel->EvaluateSortOrder();
2324     const std::vector<sal_Int32>& rGlobalOrder = pLevel->GetGlobalOrder();
2325     bool bSort = !rGlobalOrder.empty();
2326 
2327     tools::Long nCount = getCount();
2328     uno::Sequence<OUString> aSeq(nCount);
2329     OUString* pArr = aSeq.getArray();
2330     for (tools::Long i=0; i<nCount; i++)
2331         pArr[i] = getByIndex(bSort ? rGlobalOrder[i] : i)->GetNameStr( bLocaleIndependent);
2332     return aSeq;
2333 }
2334 
getMinMembers() const2335 sal_Int32 ScDPMembers::getMinMembers() const
2336 {
2337     // used in lcl_CountMinMembers
2338 
2339     sal_Int32 nVisCount = 0;
2340     if (!maMembers.empty())
2341     {
2342         nVisCount = std::count_if(maMembers.begin(), maMembers.end(), [](const rtl::Reference<ScDPMember>& pMbr) {
2343             //  count only visible with details (default is true for both)
2344             return !pMbr || (pMbr->isVisible() && pMbr->getShowDetails()); });
2345     }
2346     else
2347         nVisCount = nMbrCount;      // default for all
2348 
2349     return nVisCount;
2350 }
2351 
getByIndex(sal_Int32 nIndex) const2352 ScDPMember* ScDPMembers::getByIndex(sal_Int32 nIndex) const
2353 {
2354     //  result of GetColumnEntries must not change between ScDPMembers ctor
2355     //  and all calls to getByIndex
2356 
2357     if ( nIndex >= 0 && nIndex < nMbrCount )
2358     {
2359         if (maMembers.empty())
2360             maMembers.resize(nMbrCount);
2361 
2362         if (!maMembers[nIndex])
2363         {
2364             rtl::Reference<ScDPMember> pNew;
2365             sal_Int32 nSrcDim = pSource->GetSourceDim( nDim );
2366             if ( pSource->IsDataLayoutDimension(nSrcDim) )
2367             {
2368                 // empty name (never shown, not used for lookup)
2369                 pNew.set(new ScDPMember(pSource, nDim, nHier, nLev, 0));
2370             }
2371             else if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
2372             {
2373                 sal_Int32 nGroupBy = 0;
2374                 sal_Int32 nVal = 0;
2375                 OUString aName;
2376 
2377                 if ( nLev == SC_DAPI_LEVEL_YEAR )   // YEAR is in both hierarchies
2378                 {
2379                     //TODO: cache year range here!
2380 
2381                     double fFirstVal = pSource->GetData()->GetMemberByIndex( nSrcDim, 0 )->GetValue();
2382                     tools::Long nFirstYear = pSource->GetData()->GetDatePart(
2383                                         static_cast<tools::Long>(::rtl::math::approxFloor( fFirstVal )),
2384                                         nHier, nLev );
2385 
2386                     nVal = nFirstYear + nIndex;
2387                 }
2388                 else if ( nHier == SC_DAPI_HIERARCHY_WEEK && nLev == SC_DAPI_LEVEL_WEEKDAY )
2389                 {
2390                     nVal = nIndex;              // DayOfWeek is 0-based
2391                     aName = ScGlobal::GetCalendar()->getDisplayName(
2392                         css::i18n::CalendarDisplayIndex::DAY,
2393                         sal::static_int_cast<sal_Int16>(nVal), 0 );
2394                 }
2395                 else if ( nHier == SC_DAPI_HIERARCHY_QUARTER && nLev == SC_DAPI_LEVEL_MONTH )
2396                 {
2397                     nVal = nIndex;              // Month is 0-based
2398                     aName = ScGlobal::GetCalendar()->getDisplayName(
2399                         css::i18n::CalendarDisplayIndex::MONTH,
2400                         sal::static_int_cast<sal_Int16>(nVal), 0 );
2401                 }
2402                 else
2403                     nVal = nIndex + 1;          // Quarter, Day, Week are 1-based
2404 
2405                 switch (nLev)
2406                 {
2407                     case SC_DAPI_LEVEL_YEAR:
2408                         nGroupBy = sheet::DataPilotFieldGroupBy::YEARS;
2409                     break;
2410                     case SC_DAPI_LEVEL_QUARTER:
2411                     case SC_DAPI_LEVEL_WEEK:
2412                         nGroupBy = sheet::DataPilotFieldGroupBy::QUARTERS;
2413                     break;
2414                     case SC_DAPI_LEVEL_MONTH:
2415                     case SC_DAPI_LEVEL_WEEKDAY:
2416                         nGroupBy = sheet::DataPilotFieldGroupBy::MONTHS;
2417                     break;
2418                     case SC_DAPI_LEVEL_DAY:
2419                         nGroupBy = sheet::DataPilotFieldGroupBy::DAYS;
2420                     break;
2421                     default:
2422                         ;
2423                 }
2424                 if (aName.isEmpty())
2425                     aName = OUString::number(nVal);
2426 
2427                 ScDPItemData aData(nGroupBy, nVal);
2428                 SCROW nId = pSource->GetCache()->GetIdByItemData(nDim, aData);
2429                 pNew.set(new ScDPMember(pSource, nDim, nHier, nLev, nId));
2430             }
2431             else
2432             {
2433                 const std::vector<SCROW>& memberIndexs = pSource->GetData()->GetColumnEntries(nSrcDim);
2434                 pNew.set(new ScDPMember(pSource, nDim, nHier, nLev, memberIndexs[nIndex]));
2435             }
2436             maMembers[nIndex] = pNew;
2437         }
2438 
2439         return maMembers[nIndex].get();
2440     }
2441 
2442     return nullptr;    //TODO: exception?
2443 }
2444 
ScDPMember(ScDPSource * pSrc,sal_Int32 nD,sal_Int32 nH,sal_Int32 nL,SCROW nIndex)2445 ScDPMember::ScDPMember(
2446     ScDPSource* pSrc, sal_Int32 nD, sal_Int32 nH, sal_Int32 nL, SCROW nIndex) :
2447     pSource( pSrc ),
2448     nDim( nD ),
2449     nHier( nH ),
2450     nLev( nL ),
2451     mnDataId( nIndex ),
2452     nPosition( -1 ),
2453     bVisible( true ),
2454     bShowDet( true )
2455 {
2456     //TODO: hold pSource
2457 }
2458 
~ScDPMember()2459 ScDPMember::~ScDPMember()
2460 {
2461     //TODO: release pSource
2462 }
2463 
IsNamedItem(SCROW nIndex) const2464 bool ScDPMember::IsNamedItem(SCROW nIndex) const
2465 {
2466     sal_Int32 nSrcDim = pSource->GetSourceDim( nDim );
2467     if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) )
2468     {
2469         const ScDPItemData* pData = pSource->GetCache()->GetItemDataById(nDim, nIndex);
2470         if (pData->IsValue())
2471         {
2472             tools::Long nComp = pSource->GetData()->GetDatePart(
2473                 static_cast<tools::Long>(::rtl::math::approxFloor( pData->GetValue() )),
2474                 nHier, nLev );
2475             //  fValue is converted from integer, so simple comparison works
2476             const ScDPItemData* pData2 = GetItemData();
2477             return pData2 && nComp == pData2->GetValue();
2478         }
2479     }
2480 
2481     return  nIndex == mnDataId;
2482 }
2483 
Compare(const ScDPMember & rOther) const2484 sal_Int32 ScDPMember::Compare( const ScDPMember& rOther ) const
2485 {
2486     if ( nPosition >= 0 )
2487     {
2488         if ( rOther.nPosition >= 0 )
2489         {
2490             OSL_ENSURE( nPosition != rOther.nPosition, "same position for two members" );
2491             return ( nPosition < rOther.nPosition ) ? -1 : 1;
2492         }
2493         else
2494         {
2495             // only this has a position - members with specified positions come before those without
2496             return -1;
2497         }
2498     }
2499     else if ( rOther.nPosition >= 0 )
2500     {
2501         // only rOther has a position
2502         return 1;
2503     }
2504 
2505     // no positions set - compare names
2506     return pSource->GetData()->Compare( pSource->GetSourceDim(nDim),mnDataId,rOther.GetItemDataId());
2507 }
2508 
FillItemData() const2509 ScDPItemData ScDPMember::FillItemData() const
2510 {
2511     //TODO: handle date hierarchy...
2512 
2513     const ScDPItemData* pData = GetItemData();
2514     return (pData ? *pData : ScDPItemData());
2515 }
2516 
GetLayoutName() const2517 const std::optional<OUString> & ScDPMember::GetLayoutName() const
2518 {
2519     return mpLayoutName;
2520 }
2521 
GetNameStr(bool bLocaleIndependent) const2522 OUString ScDPMember::GetNameStr( bool bLocaleIndependent ) const
2523 {
2524     const ScDPItemData* pData = GetItemData();
2525     if (pData)
2526         return pSource->GetData()->GetFormattedString(nDim, *pData, bLocaleIndependent);
2527     return OUString();
2528 }
2529 
getName()2530 OUString SAL_CALL ScDPMember::getName()
2531 {
2532     return GetNameStr( false );
2533 }
2534 
setName(const OUString &)2535 void SAL_CALL ScDPMember::setName( const OUString& /* rNewName */ )
2536 {
2537     OSL_FAIL("not implemented");        //TODO: exception?
2538 }
2539 
2540 // XPropertySet
2541 
getPropertySetInfo()2542 uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPMember::getPropertySetInfo()
2543 {
2544     SolarMutexGuard aGuard;
2545 
2546     static const SfxItemPropertyMapEntry aDPMemberMap_Impl[] =
2547     {
2548         { u"" SC_UNO_DP_ISVISIBLE, 0,  cppu::UnoType<bool>::get(),              0, 0 },
2549         { u"" SC_UNO_DP_POSITION, 0,  cppu::UnoType<sal_Int32>::get(),        0, 0 },
2550         { u"" SC_UNO_DP_SHOWDETAILS, 0,  cppu::UnoType<bool>::get(),              0, 0 },
2551         { u"" SC_UNO_DP_LAYOUTNAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
2552         { u"", 0, css::uno::Type(), 0, 0 }
2553     };
2554     static uno::Reference<beans::XPropertySetInfo> aRef =
2555         new SfxItemPropertySetInfo( aDPMemberMap_Impl );
2556     return aRef;
2557 }
2558 
setPropertyValue(const OUString & aPropertyName,const uno::Any & aValue)2559 void SAL_CALL ScDPMember::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
2560 {
2561     if ( aPropertyName == SC_UNO_DP_ISVISIBLE )
2562         bVisible = lcl_GetBoolFromAny(aValue);
2563     else if ( aPropertyName == SC_UNO_DP_SHOWDETAILS )
2564         bShowDet = lcl_GetBoolFromAny(aValue);
2565     else if ( aPropertyName == SC_UNO_DP_POSITION )
2566         aValue >>= nPosition;
2567     else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
2568     {
2569         OUString aName;
2570         if (aValue >>= aName)
2571             mpLayoutName = aName;
2572     }
2573     else
2574     {
2575         OSL_FAIL("unknown property");
2576     }
2577 }
2578 
getPropertyValue(const OUString & aPropertyName)2579 uno::Any SAL_CALL ScDPMember::getPropertyValue( const OUString& aPropertyName )
2580 {
2581     uno::Any aRet;
2582     if ( aPropertyName == SC_UNO_DP_ISVISIBLE )
2583         aRet <<= bVisible;
2584     else if ( aPropertyName == SC_UNO_DP_SHOWDETAILS )
2585         aRet <<= bShowDet;
2586     else if ( aPropertyName == SC_UNO_DP_POSITION )
2587         aRet <<= nPosition;
2588     else if (aPropertyName == SC_UNO_DP_LAYOUTNAME)
2589         aRet <<= mpLayoutName ? *mpLayoutName : OUString();
2590     else
2591     {
2592         OSL_FAIL("unknown property");
2593     }
2594     return aRet;
2595 }
2596 
SC_IMPL_DUMMY_PROPERTY_LISTENER(ScDPMember) const2597 SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPMember )
2598 
2599 const ScDPCache* ScDPSource::GetCache()
2600 {
2601     OSL_ENSURE( GetData() , "empty ScDPTableData pointer");
2602     return ( GetData()!=nullptr ) ? &GetData()->GetCacheTable().getCache() : nullptr ;
2603 }
2604 
GetItemData() const2605 const ScDPItemData* ScDPMember::GetItemData() const
2606 {
2607     const ScDPItemData* pData = pSource->GetItemDataById(nDim, mnDataId);
2608     SAL_WARN_IF( !pData, "sc.core", "ScDPMember::GetItemData: what data? nDim " << nDim << ", mnDataId " << mnDataId);
2609     return pData;
2610 }
2611 
GetItemDataById(sal_Int32 nDim,sal_Int32 nId)2612 const ScDPItemData* ScDPSource::GetItemDataById(sal_Int32 nDim, sal_Int32 nId)
2613 {
2614     return GetData()->GetMemberById(nDim, nId);
2615 }
2616 
GetSrcItemDataByIndex(SCROW nIndex)2617 const ScDPItemData* ScDPMembers::GetSrcItemDataByIndex(SCROW nIndex)
2618 {
2619     const std::vector< SCROW >& memberIds = pSource->GetData()->GetColumnEntries( nDim );
2620     if ( nIndex >= static_cast<tools::Long>(memberIds.size()) || nIndex < 0 )
2621         return nullptr;
2622     SCROW nId =  memberIds[ nIndex ];
2623     return pSource->GetItemDataById( nDim, nId );
2624 }
2625 
2626 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2627