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 <dptabres.hxx>
21 
22 #include <dptabdat.hxx>
23 #include <dptabsrc.hxx>
24 #include <global.hxx>
25 #include <subtotal.hxx>
26 #include <globstr.hrc>
27 #include <scresid.hxx>
28 #include <dpitemdata.hxx>
29 #include <generalfunction.hxx>
30 
31 #include <document.hxx>
32 #include <dpresfilter.hxx>
33 #include <dputil.hxx>
34 
35 #include <o3tl/safeint.hxx>
36 #include <osl/diagnose.h>
37 #include <rtl/math.hxx>
38 #include <sal/log.hxx>
39 
40 #include <math.h>
41 #include <float.h>
42 #include <algorithm>
43 #include <memory>
44 #include <unordered_map>
45 
46 #include <com/sun/star/sheet/DataResultFlags.hpp>
47 #include <com/sun/star/sheet/MemberResultFlags.hpp>
48 #include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
49 #include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
50 #include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
51 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
52 #include <com/sun/star/sheet/GeneralFunction2.hpp>
53 
54 using namespace com::sun::star;
55 using ::std::vector;
56 using ::std::pair;
57 using ::com::sun::star::uno::Sequence;
58 
59 namespace {
60 
61 const char* aFuncStrIds[] =     // matching enum ScSubTotalFunc
62 {
63     nullptr,                        // SUBTOTAL_FUNC_NONE
64     STR_FUN_TEXT_AVG,               // SUBTOTAL_FUNC_AVE
65     STR_FUN_TEXT_COUNT,             // SUBTOTAL_FUNC_CNT
66     STR_FUN_TEXT_COUNT,             // SUBTOTAL_FUNC_CNT2
67     STR_FUN_TEXT_MAX,               // SUBTOTAL_FUNC_MAX
68     STR_FUN_TEXT_MIN,               // SUBTOTAL_FUNC_MIN
69     STR_FUN_TEXT_PRODUCT,           // SUBTOTAL_FUNC_PROD
70     STR_FUN_TEXT_STDDEV,            // SUBTOTAL_FUNC_STD
71     STR_FUN_TEXT_STDDEV,            // SUBTOTAL_FUNC_STDP
72     STR_FUN_TEXT_SUM,               // SUBTOTAL_FUNC_SUM
73     STR_FUN_TEXT_VAR,               // SUBTOTAL_FUNC_VAR
74     STR_FUN_TEXT_VAR,               // SUBTOTAL_FUNC_VARP
75     STR_FUN_TEXT_MEDIAN,            // SUBTOTAL_FUNC_MED
76     nullptr                         // SUBTOTAL_FUNC_SELECTION_COUNT - not used for pivot table
77 };
78 
lcl_SearchMember(const std::vector<std::unique_ptr<ScDPResultMember>> & list,SCROW nOrder,SCROW & rIndex)79 bool lcl_SearchMember( const std::vector<std::unique_ptr<ScDPResultMember>>& list, SCROW nOrder, SCROW& rIndex)
80 {
81     bool bFound = false;
82     SCROW  nLo = 0;
83     SCROW nHi = list.size() - 1;
84     SCROW nIndex;
85     while (nLo <= nHi)
86     {
87         nIndex = (nLo + nHi) / 2;
88         if ( list[nIndex]->GetOrder() < nOrder )
89             nLo = nIndex + 1;
90         else
91         {
92             nHi = nIndex - 1;
93             if ( list[nIndex]->GetOrder() == nOrder )
94             {
95                 bFound = true;
96                 nLo = nIndex;
97             }
98         }
99     }
100     rIndex = nLo;
101     return bFound;
102 }
103 
104 class FilterStack
105 {
106     std::vector<ScDPResultFilter>& mrFilters;
107 public:
FilterStack(std::vector<ScDPResultFilter> & rFilters)108     explicit FilterStack(std::vector<ScDPResultFilter>& rFilters) : mrFilters(rFilters) {}
109 
pushDimName(const OUString & rName,bool bDataLayout)110     void pushDimName(const OUString& rName, bool bDataLayout)
111     {
112         mrFilters.emplace_back(rName, bDataLayout);
113     }
114 
pushDimValue(const OUString & rValueName,const OUString & rValue)115     void pushDimValue(const OUString& rValueName, const OUString& rValue)
116     {
117         ScDPResultFilter& rFilter = mrFilters.back();
118         rFilter.maValueName = rValueName;
119         rFilter.maValue = rValue;
120         rFilter.mbHasValue = true;
121     }
122 
~FilterStack()123     ~FilterStack()
124     {
125         ScDPResultFilter& rFilter = mrFilters.back();
126         if (rFilter.mbHasValue)
127             rFilter.mbHasValue = false;
128         else
129             mrFilters.pop_back();
130     }
131 };
132 
133 // function objects for sorting of the column and row members:
134 
135 class ScDPRowMembersOrder
136 {
137     ScDPResultDimension& rDimension;
138     tools::Long                 nMeasure;
139     bool                 bAscending;
140 
141 public:
ScDPRowMembersOrder(ScDPResultDimension & rDim,tools::Long nM,bool bAsc)142             ScDPRowMembersOrder( ScDPResultDimension& rDim, tools::Long nM, bool bAsc ) :
143                 rDimension(rDim),
144                 nMeasure(nM),
145                 bAscending(bAsc)
146             {}
147 
148     bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
149 };
150 
151 class ScDPColMembersOrder
152 {
153     ScDPDataDimension& rDimension;
154     tools::Long               nMeasure;
155     bool               bAscending;
156 
157 public:
ScDPColMembersOrder(ScDPDataDimension & rDim,tools::Long nM,bool bAsc)158             ScDPColMembersOrder( ScDPDataDimension& rDim, tools::Long nM, bool bAsc ) :
159                 rDimension(rDim),
160                 nMeasure(nM),
161                 bAscending(bAsc)
162             {}
163 
164     bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
165 };
166 
167 }
168 
lcl_IsLess(const ScDPDataMember * pDataMember1,const ScDPDataMember * pDataMember2,tools::Long nMeasure,bool bAscending)169 static bool lcl_IsLess( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, tools::Long nMeasure, bool bAscending )
170 {
171     // members can be NULL if used for rows
172 
173     ScDPSubTotalState aEmptyState;
174     const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : nullptr;
175     const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : nullptr;
176 
177     bool bError1 = pAgg1 && pAgg1->HasError();
178     bool bError2 = pAgg2 && pAgg2->HasError();
179     if ( bError1 )
180         return false;       // errors are always sorted at the end
181     else if ( bError2 )
182         return true;            // errors are always sorted at the end
183     else
184     {
185         double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0;    // no data is sorted as 0
186         double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0;
187 
188         // compare values
189         // don't have to check approxEqual, as this is the only sort criterion
190 
191         return bAscending ? ( fVal1 < fVal2 ) : ( fVal1 > fVal2 );
192     }
193 }
194 
lcl_IsEqual(const ScDPDataMember * pDataMember1,const ScDPDataMember * pDataMember2,tools::Long nMeasure)195 static bool lcl_IsEqual( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, tools::Long nMeasure )
196 {
197     // members can be NULL if used for rows
198 
199     ScDPSubTotalState aEmptyState;
200     const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : nullptr;
201     const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : nullptr;
202 
203     bool bError1 = pAgg1 && pAgg1->HasError();
204     bool bError2 = pAgg2 && pAgg2->HasError();
205     if ( bError1 )
206     {
207         if ( bError2 )
208             return true;        // equal
209         else
210             return false;
211     }
212     else if ( bError2 )
213         return false;
214     else
215     {
216         double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0;    // no data is sorted as 0
217         double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0;
218 
219         // compare values
220         // this is used to find equal data at the end of the AutoShow range, so approxEqual must be used
221 
222         return rtl::math::approxEqual( fVal1, fVal2 );
223     }
224 }
225 
operator ()(sal_Int32 nIndex1,sal_Int32 nIndex2) const226 bool ScDPRowMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
227 {
228     const ScDPResultMember* pMember1 = rDimension.GetMember(nIndex1);
229     const ScDPResultMember* pMember2 = rDimension.GetMember(nIndex2);
230 
231 // make the hide item to the largest order.
232     if ( !pMember1->IsVisible() || !pMember2->IsVisible() )
233         return pMember1->IsVisible();
234     const ScDPDataMember* pDataMember1 =  pMember1->GetDataRoot() ;
235     const ScDPDataMember* pDataMember2 =  pMember2->GetDataRoot();
236     //  GetDataRoot can be NULL if there was no data.
237     //  IsVisible == false can happen after AutoShow.
238     return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending );
239 }
240 
operator ()(sal_Int32 nIndex1,sal_Int32 nIndex2) const241 bool ScDPColMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
242 {
243     const ScDPDataMember* pDataMember1 = rDimension.GetMember(nIndex1);
244     const ScDPDataMember* pDataMember2 = rDimension.GetMember(nIndex2);
245     bool bHide1 = pDataMember1 && !pDataMember1->IsVisible();
246     bool bHide2 =  pDataMember2 && !pDataMember2->IsVisible();
247     if ( bHide1 || bHide2 )
248         return !bHide1;
249     return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending );
250 }
251 
Member(tools::Long nSrcIndex,SCROW nNameIndex)252 ScDPInitState::Member::Member(tools::Long nSrcIndex, SCROW nNameIndex) :
253     mnSrcIndex(nSrcIndex), mnNameIndex(nNameIndex) {}
254 
AddMember(tools::Long nSourceIndex,SCROW nMember)255 void ScDPInitState::AddMember( tools::Long nSourceIndex, SCROW nMember )
256 {
257     maMembers.emplace_back(nSourceIndex, nMember);
258 }
259 
RemoveMember()260 void ScDPInitState::RemoveMember()
261 {
262     OSL_ENSURE(!maMembers.empty(), "ScDPInitState::RemoveMember: Attempt to remove member while empty.");
263     if (!maMembers.empty())
264         maMembers.pop_back();
265 }
266 
267 namespace {
268 
269 #if DUMP_PIVOT_TABLE
dumpRow(const OUString & rType,const OUString & rName,const ScDPAggData * pAggData,ScDocument * pDoc,ScAddress & rPos)270 void dumpRow(
271     const OUString& rType, const OUString& rName, const ScDPAggData* pAggData,
272     ScDocument* pDoc, ScAddress& rPos )
273 {
274     SCCOL nCol = rPos.Col();
275     SCROW nRow = rPos.Row();
276     SCTAB nTab = rPos.Tab();
277     pDoc->SetString( nCol++, nRow, nTab, rType );
278     pDoc->SetString( nCol++, nRow, nTab, rName );
279     while ( pAggData )
280     {
281         pDoc->SetValue( nCol++, nRow, nTab, pAggData->GetResult() );
282         pAggData = pAggData->GetExistingChild();
283     }
284     rPos.SetRow( nRow + 1 );
285 }
286 
indent(ScDocument * pDoc,SCROW nStartRow,const ScAddress & rPos)287 void indent( ScDocument* pDoc, SCROW nStartRow, const ScAddress& rPos )
288 {
289     SCCOL nCol = rPos.Col();
290     SCTAB nTab = rPos.Tab();
291 
292     OUString aString;
293     for (SCROW nRow = nStartRow; nRow < rPos.Row(); nRow++)
294     {
295         aString = pDoc->GetString(nCol, nRow, nTab);
296         if (!aString.isEmpty())
297         {
298             aString = " " + aString;
299             pDoc->SetString( nCol, nRow, nTab, aString );
300         }
301     }
302 }
303 #endif
304 
305 }
306 
ScDPRunningTotalState(ScDPResultMember * pColRoot,ScDPResultMember * pRowRoot)307 ScDPRunningTotalState::ScDPRunningTotalState( ScDPResultMember* pColRoot, ScDPResultMember* pRowRoot ) :
308     pColResRoot(pColRoot), pRowResRoot(pRowRoot)
309 {
310     // These arrays should never be empty as the terminating value must be present at all times.
311     maColVisible.push_back(-1);
312     maColSorted.push_back(-1);
313     maRowVisible.push_back(-1);
314     maRowSorted.push_back(-1);
315 }
316 
AddColIndex(sal_Int32 nVisible,tools::Long nSorted)317 void ScDPRunningTotalState::AddColIndex( sal_Int32 nVisible, tools::Long nSorted )
318 {
319     maColVisible.back() = nVisible;
320     maColVisible.push_back(-1);
321 
322     maColSorted.back() = nSorted;
323     maColSorted.push_back(-1);
324 }
325 
AddRowIndex(sal_Int32 nVisible,tools::Long nSorted)326 void ScDPRunningTotalState::AddRowIndex( sal_Int32 nVisible, tools::Long nSorted )
327 {
328     maRowVisible.back() = nVisible;
329     maRowVisible.push_back(-1);
330 
331     maRowSorted.back() = nSorted;
332     maRowSorted.push_back(-1);
333 }
334 
RemoveColIndex()335 void ScDPRunningTotalState::RemoveColIndex()
336 {
337     OSL_ENSURE(!maColVisible.empty() && !maColSorted.empty(), "ScDPRunningTotalState::RemoveColIndex: array is already empty!");
338     if (maColVisible.size() >= 2)
339     {
340         maColVisible.pop_back();
341         maColVisible.back() = -1;
342     }
343 
344     if (maColSorted.size() >= 2)
345     {
346         maColSorted.pop_back();
347         maColSorted.back() = -1;
348     }
349 }
350 
RemoveRowIndex()351 void ScDPRunningTotalState::RemoveRowIndex()
352 {
353     OSL_ENSURE(!maRowVisible.empty() && !maRowSorted.empty(), "ScDPRunningTotalState::RemoveRowIndex: array is already empty!");
354     if (maRowVisible.size() >= 2)
355     {
356         maRowVisible.pop_back();
357         maRowVisible.back() = -1;
358     }
359 
360     if (maRowSorted.size() >= 2)
361     {
362         maRowSorted.pop_back();
363         maRowSorted.back() = -1;
364     }
365 }
366 
ScDPRelativePos(tools::Long nBase,tools::Long nDir)367 ScDPRelativePos::ScDPRelativePos( tools::Long nBase, tools::Long nDir ) :
368     nBasePos( nBase ),
369     nDirection( nDir )
370 {
371 }
372 
Update(const ScDPValue & rNext,ScSubTotalFunc eFunc,const ScDPSubTotalState & rSubState)373 void ScDPAggData::Update( const ScDPValue& rNext, ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState )
374 {
375     if (nCount<0)       // error?
376         return;         // nothing more...
377 
378     if (rNext.meType == ScDPValue::Empty)
379         return;
380 
381     if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE &&
382                                                         rSubState.eColForce != rSubState.eRowForce )
383         return;
384     if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce;
385     if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce;
386 
387     if ( eFunc == SUBTOTAL_FUNC_NONE )
388         return;
389 
390     if ( eFunc != SUBTOTAL_FUNC_CNT2 )          // CNT2 counts everything, incl. strings and errors
391     {
392         if (rNext.meType == ScDPValue::Error)
393         {
394             nCount = -1;        // -1 for error (not for CNT2)
395             return;
396         }
397         if (rNext.meType == ScDPValue::String)
398             return;             // ignore
399     }
400 
401     ++nCount;           // for all functions
402 
403     switch (eFunc)
404     {
405         case SUBTOTAL_FUNC_SUM:
406         case SUBTOTAL_FUNC_AVE:
407             if ( !SubTotal::SafePlus( fVal, rNext.mfValue ) )
408                 nCount = -1;                            // -1 for error
409             break;
410         case SUBTOTAL_FUNC_PROD:
411             if ( nCount == 1 )          // copy first value (fVal is initialized to 0)
412                 fVal = rNext.mfValue;
413             else if ( !SubTotal::SafeMult( fVal, rNext.mfValue ) )
414                 nCount = -1;                            // -1 for error
415             break;
416         case SUBTOTAL_FUNC_CNT:
417         case SUBTOTAL_FUNC_CNT2:
418             //  nothing more than incrementing nCount
419             break;
420         case SUBTOTAL_FUNC_MAX:
421             if ( nCount == 1 || rNext.mfValue > fVal )
422                 fVal = rNext.mfValue;
423             break;
424         case SUBTOTAL_FUNC_MIN:
425             if ( nCount == 1 || rNext.mfValue < fVal )
426                 fVal = rNext.mfValue;
427             break;
428         case SUBTOTAL_FUNC_STD:
429         case SUBTOTAL_FUNC_STDP:
430         case SUBTOTAL_FUNC_VAR:
431         case SUBTOTAL_FUNC_VARP:
432             maWelford.update( rNext.mfValue);
433             break;
434         case SUBTOTAL_FUNC_MED:
435             {
436                 auto aIter = std::upper_bound(mSortedValues.begin(), mSortedValues.end(), rNext.mfValue);
437                 if (aIter == mSortedValues.end())
438                     mSortedValues.push_back(rNext.mfValue);
439                 else
440                     mSortedValues.insert(aIter, rNext.mfValue);
441             }
442             break;
443         default:
444             OSL_FAIL("invalid function");
445     }
446 }
447 
Calculate(ScSubTotalFunc eFunc,const ScDPSubTotalState & rSubState)448 void ScDPAggData::Calculate( ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState )
449 {
450     //  calculate the original result
451     //  (without reference value, used as the basis for reference value calculation)
452 
453     //  called several times at the cross-section of several subtotals - don't calculate twice then
454     if ( IsCalculated() )
455         return;
456 
457     if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce;
458     if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce;
459 
460     if ( eFunc == SUBTOTAL_FUNC_NONE )      // this happens when there is no data dimension
461     {
462         nCount = SC_DPAGG_RESULT_EMPTY;     // make sure there's a valid state for HasData etc.
463         return;
464     }
465 
466     //  check the error conditions for the selected function
467 
468     bool bError = false;
469     switch (eFunc)
470     {
471         case SUBTOTAL_FUNC_SUM:
472         case SUBTOTAL_FUNC_PROD:
473         case SUBTOTAL_FUNC_CNT:
474         case SUBTOTAL_FUNC_CNT2:
475             bError = ( nCount < 0 );        // only real errors
476             break;
477 
478         case SUBTOTAL_FUNC_AVE:
479         case SUBTOTAL_FUNC_MED:
480         case SUBTOTAL_FUNC_MAX:
481         case SUBTOTAL_FUNC_MIN:
482             bError = ( nCount <= 0 );       // no data is an error
483             break;
484 
485         case SUBTOTAL_FUNC_STDP:
486         case SUBTOTAL_FUNC_VARP:
487             bError = ( nCount <= 0 );       // no data is an error
488             assert(bError || nCount == static_cast<sal_Int64>(maWelford.getCount()));
489             break;
490 
491         case SUBTOTAL_FUNC_STD:
492         case SUBTOTAL_FUNC_VAR:
493             bError = ( nCount < 2 );        // need at least 2 values
494             assert(bError || nCount == static_cast<sal_Int64>(maWelford.getCount()));
495             break;
496 
497         default:
498             OSL_FAIL("invalid function");
499     }
500 
501     //  calculate the selected function
502 
503     double fResult = 0.0;
504     if ( !bError )
505     {
506         switch (eFunc)
507         {
508             case SUBTOTAL_FUNC_MAX:
509             case SUBTOTAL_FUNC_MIN:
510             case SUBTOTAL_FUNC_SUM:
511             case SUBTOTAL_FUNC_PROD:
512                 //  different error conditions are handled above
513                 fResult = fVal;
514                 break;
515 
516             case SUBTOTAL_FUNC_CNT:
517             case SUBTOTAL_FUNC_CNT2:
518                 fResult = nCount;
519                 break;
520 
521             case SUBTOTAL_FUNC_AVE:
522                 if ( nCount > 0 )
523                     fResult = fVal / static_cast<double>(nCount);
524                 break;
525 
526             case SUBTOTAL_FUNC_STD:
527                 if ( nCount >= 2 )
528                 {
529                     fResult = maWelford.getVarianceSample();
530                     if (fResult < 0.0)
531                         bError = true;
532                     else
533                         fResult = sqrt( fResult);
534                 }
535                 break;
536             case SUBTOTAL_FUNC_VAR:
537                 if ( nCount >= 2 )
538                     fResult = maWelford.getVarianceSample();
539                 break;
540             case SUBTOTAL_FUNC_STDP:
541                 if ( nCount > 0 )
542                 {
543                     fResult = maWelford.getVariancePopulation();
544                     if (fResult < 0.0)
545                         bError = true;
546                     else
547                         fResult = sqrt( fResult);
548                 }
549                 break;
550             case SUBTOTAL_FUNC_VARP:
551                 if ( nCount > 0 )
552                     fResult = maWelford.getVariancePopulation();
553                 break;
554             case SUBTOTAL_FUNC_MED:
555                 {
556                     size_t nSize = mSortedValues.size();
557                     if (nSize > 0)
558                     {
559                         assert(nSize == static_cast<size_t>(nCount));
560                         if ((nSize % 2) == 1)
561                             fResult = mSortedValues[nSize / 2];
562                         else
563                             fResult = (mSortedValues[nSize / 2 - 1] + mSortedValues[nSize / 2]) / 2.0;
564                     }
565                 }
566                 break;
567             default:
568                 OSL_FAIL("invalid function");
569         }
570     }
571 
572     bool bEmpty = ( nCount == 0 );          // no data
573 
574     //  store the result
575     //  Empty is checked first, so empty results are shown empty even for "average" etc.
576     //  If these results should be treated as errors in reference value calculations,
577     //  a separate state value (EMPTY_ERROR) is needed.
578     //  Now, for compatibility, empty "average" results are counted as 0.
579 
580     if ( bEmpty )
581         nCount = SC_DPAGG_RESULT_EMPTY;
582     else if ( bError )
583         nCount = SC_DPAGG_RESULT_ERROR;
584     else
585         nCount = SC_DPAGG_RESULT_VALID;
586 
587     if ( bEmpty || bError )
588         fResult = 0.0;      // default, in case the state is later modified
589 
590     fVal = fResult;         // used directly from now on
591     fAux = 0.0;             // used for running total or original result of reference value
592 }
593 
IsCalculated() const594 bool ScDPAggData::IsCalculated() const
595 {
596     return ( nCount <= SC_DPAGG_RESULT_EMPTY );
597 }
598 
GetResult() const599 double ScDPAggData::GetResult() const
600 {
601     assert( IsCalculated() && "ScDPAggData not calculated" );
602 
603     return fVal;        // use calculated value
604 }
605 
HasError() const606 bool ScDPAggData::HasError() const
607 {
608     assert( IsCalculated() && "ScDPAggData not calculated" );
609 
610     return ( nCount == SC_DPAGG_RESULT_ERROR );
611 }
612 
HasData() const613 bool ScDPAggData::HasData() const
614 {
615     assert( IsCalculated() && "ScDPAggData not calculated" );
616 
617     return ( nCount != SC_DPAGG_RESULT_EMPTY );     // values or error
618 }
619 
SetResult(double fNew)620 void ScDPAggData::SetResult( double fNew )
621 {
622     assert( IsCalculated() && "ScDPAggData not calculated" );
623 
624     fVal = fNew;        // don't reset error flag
625 }
626 
SetError()627 void ScDPAggData::SetError()
628 {
629     assert( IsCalculated() && "ScDPAggData not calculated" );
630 
631     nCount = SC_DPAGG_RESULT_ERROR;
632 }
633 
SetEmpty(bool bSet)634 void ScDPAggData::SetEmpty( bool bSet )
635 {
636     assert( IsCalculated() && "ScDPAggData not calculated" );
637 
638     if ( bSet )
639         nCount = SC_DPAGG_RESULT_EMPTY;
640     else
641         nCount = SC_DPAGG_RESULT_VALID;
642 }
643 
GetAuxiliary() const644 double ScDPAggData::GetAuxiliary() const
645 {
646     // after Calculate, fAux is used as auxiliary value for running totals and reference values
647     assert( IsCalculated() && "ScDPAggData not calculated" );
648 
649     return fAux;
650 }
651 
SetAuxiliary(double fNew)652 void ScDPAggData::SetAuxiliary( double fNew )
653 {
654     // after Calculate, fAux is used as auxiliary value for running totals and reference values
655     assert( IsCalculated() && "ScDPAggData not calculated" );
656 
657     fAux = fNew;
658 }
659 
GetChild()660 ScDPAggData* ScDPAggData::GetChild()
661 {
662     if (!pChild)
663         pChild.reset( new ScDPAggData );
664     return pChild.get();
665 }
666 
Reset()667 void ScDPAggData::Reset()
668 {
669     maWelford = WelfordRunner();
670     fVal = 0.0;
671     fAux = 0.0;
672     nCount = SC_DPAGG_EMPTY;
673     pChild.reset();
674 }
675 
676 #if DUMP_PIVOT_TABLE
Dump(int nIndent) const677 void ScDPAggData::Dump(int nIndent) const
678 {
679     std::string aIndent(nIndent*2, ' ');
680     std::cout << aIndent << "* ";
681     if (IsCalculated())
682         std::cout << GetResult();
683     else
684         std::cout << "not calculated";
685 
686     std::cout << "  [val=" << fVal << "; aux=" << fAux << "; count=" << nCount << "]" << std::endl;
687 }
688 #endif
689 
ScDPRowTotals()690 ScDPRowTotals::ScDPRowTotals() :
691     bIsInColRoot( false )
692 {
693 }
694 
~ScDPRowTotals()695 ScDPRowTotals::~ScDPRowTotals()
696 {
697 }
698 
lcl_GetChildTotal(ScDPAggData * pFirst,tools::Long nMeasure)699 static ScDPAggData* lcl_GetChildTotal( ScDPAggData* pFirst, tools::Long nMeasure )
700 {
701     OSL_ENSURE( nMeasure >= 0, "GetColTotal: no measure" );
702 
703     ScDPAggData* pAgg = pFirst;
704     tools::Long nSkip = nMeasure;
705 
706     // subtotal settings are ignored - column/row totals exist once per measure
707 
708     for ( tools::Long nPos=0; nPos<nSkip; nPos++ )
709         pAgg = pAgg->GetChild();    // column total is constructed empty - children need to be created
710 
711     if ( !pAgg->IsCalculated() )
712     {
713         // for first use, simulate an empty calculation
714         ScDPSubTotalState aEmptyState;
715         pAgg->Calculate( SUBTOTAL_FUNC_SUM, aEmptyState );
716     }
717 
718     return pAgg;
719 }
720 
GetRowTotal(tools::Long nMeasure)721 ScDPAggData* ScDPRowTotals::GetRowTotal( tools::Long nMeasure )
722 {
723     return lcl_GetChildTotal( &aRowTotal, nMeasure );
724 }
725 
GetGrandTotal(tools::Long nMeasure)726 ScDPAggData* ScDPRowTotals::GetGrandTotal( tools::Long nMeasure )
727 {
728     return lcl_GetChildTotal( &aGrandTotal, nMeasure );
729 }
730 
lcl_GetForceFunc(const ScDPLevel * pLevel,tools::Long nFuncNo)731 static ScSubTotalFunc lcl_GetForceFunc( const ScDPLevel* pLevel, tools::Long nFuncNo )
732 {
733     ScSubTotalFunc eRet = SUBTOTAL_FUNC_NONE;
734     if ( pLevel )
735     {
736         //TODO: direct access via ScDPLevel
737 
738         uno::Sequence<sal_Int16> aSeq = pLevel->getSubTotals();
739         tools::Long nSequence = aSeq.getLength();
740         if ( nSequence && aSeq[0] != sheet::GeneralFunction2::AUTO )
741         {
742             // For manual subtotals, "automatic" is added as first function.
743             // ScDPResultMember::GetSubTotalCount adds to the count, here NONE has to be
744             // returned as the first function then.
745 
746             --nFuncNo;      // keep NONE for first (check below), move the other entries
747         }
748 
749         if ( nFuncNo >= 0 && nFuncNo < nSequence )
750         {
751             ScGeneralFunction eUser = static_cast<ScGeneralFunction>(aSeq.getConstArray()[nFuncNo]);
752             if (eUser != ScGeneralFunction::AUTO)
753                 eRet = ScDPUtil::toSubTotalFunc(eUser);
754         }
755     }
756     return eRet;
757 }
758 
ScDPResultData(ScDPSource & rSrc)759 ScDPResultData::ScDPResultData( ScDPSource& rSrc ) :
760     mrSource(rSrc),
761     bLateInit( false ),
762     bDataAtCol( false ),
763     bDataAtRow( false )
764 {
765 }
766 
~ScDPResultData()767 ScDPResultData::~ScDPResultData()
768 {
769 }
770 
SetMeasureData(std::vector<ScSubTotalFunc> & rFunctions,std::vector<sheet::DataPilotFieldReference> & rRefs,std::vector<sheet::DataPilotFieldOrientation> & rRefOrient,std::vector<OUString> & rNames)771 void ScDPResultData::SetMeasureData(
772     std::vector<ScSubTotalFunc>& rFunctions, std::vector<sheet::DataPilotFieldReference>& rRefs,
773     std::vector<sheet::DataPilotFieldOrientation>& rRefOrient, std::vector<OUString>& rNames )
774 {
775     // We need to have at least one measure data at all times.
776 
777     maMeasureFuncs.swap(rFunctions);
778     if (maMeasureFuncs.empty())
779         maMeasureFuncs.push_back(SUBTOTAL_FUNC_NONE);
780 
781     maMeasureRefs.swap(rRefs);
782     if (maMeasureRefs.empty())
783         maMeasureRefs.emplace_back(); // default ctor is ok.
784 
785     maMeasureRefOrients.swap(rRefOrient);
786     if (maMeasureRefOrients.empty())
787         maMeasureRefOrients.push_back(sheet::DataPilotFieldOrientation_HIDDEN);
788 
789     maMeasureNames.swap(rNames);
790     if (maMeasureNames.empty())
791         maMeasureNames.push_back(ScResId(STR_EMPTYDATA));
792 }
793 
SetDataLayoutOrientation(sheet::DataPilotFieldOrientation nOrient)794 void ScDPResultData::SetDataLayoutOrientation( sheet::DataPilotFieldOrientation nOrient )
795 {
796     bDataAtCol = ( nOrient == sheet::DataPilotFieldOrientation_COLUMN );
797     bDataAtRow = ( nOrient == sheet::DataPilotFieldOrientation_ROW );
798 }
799 
SetLateInit(bool bSet)800 void ScDPResultData::SetLateInit( bool bSet )
801 {
802     bLateInit = bSet;
803 }
804 
GetColStartMeasure() const805 tools::Long ScDPResultData::GetColStartMeasure() const
806 {
807     if (maMeasureFuncs.size() == 1)
808         return 0;
809 
810     return bDataAtCol ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY;
811 }
812 
GetRowStartMeasure() const813 tools::Long ScDPResultData::GetRowStartMeasure() const
814 {
815     if (maMeasureFuncs.size() == 1)
816         return 0;
817 
818     return bDataAtRow ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY;
819 }
820 
GetMeasureFunction(tools::Long nMeasure) const821 ScSubTotalFunc ScDPResultData::GetMeasureFunction(tools::Long nMeasure) const
822 {
823     OSL_ENSURE(o3tl::make_unsigned(nMeasure) < maMeasureFuncs.size(), "bumm");
824     return maMeasureFuncs[nMeasure];
825 }
826 
GetMeasureRefVal(tools::Long nMeasure) const827 const sheet::DataPilotFieldReference& ScDPResultData::GetMeasureRefVal(tools::Long nMeasure) const
828 {
829     OSL_ENSURE(o3tl::make_unsigned(nMeasure) < maMeasureRefs.size(), "bumm");
830     return maMeasureRefs[nMeasure];
831 }
832 
GetMeasureRefOrient(tools::Long nMeasure) const833 sheet::DataPilotFieldOrientation ScDPResultData::GetMeasureRefOrient(tools::Long nMeasure) const
834 {
835     OSL_ENSURE(o3tl::make_unsigned(nMeasure) < maMeasureRefOrients.size(), "bumm");
836     return maMeasureRefOrients[nMeasure];
837 }
838 
GetMeasureString(tools::Long nMeasure,bool bForce,ScSubTotalFunc eForceFunc,bool & rbTotalResult) const839 OUString ScDPResultData::GetMeasureString(tools::Long nMeasure, bool bForce, ScSubTotalFunc eForceFunc, bool& rbTotalResult) const
840 {
841     //  with bForce==true, return function instead of "result" for single measure
842     //  with eForceFunc != SUBTOTAL_FUNC_NONE, always use eForceFunc
843     rbTotalResult = false;
844     if ( nMeasure < 0 || (maMeasureFuncs.size() == 1 && !bForce && eForceFunc == SUBTOTAL_FUNC_NONE) )
845     {
846         //  for user-specified subtotal function with all measures,
847         //  display only function name
848         assert(unsigned(eForceFunc) < SAL_N_ELEMENTS(aFuncStrIds));
849         if ( eForceFunc != SUBTOTAL_FUNC_NONE )
850             return ScResId(aFuncStrIds[eForceFunc]);
851 
852         rbTotalResult = true;
853         return ScResId(STR_TABLE_ERGEBNIS);
854     }
855     else
856     {
857         OSL_ENSURE(o3tl::make_unsigned(nMeasure) < maMeasureFuncs.size(), "bumm");
858         const ScDPDimension* pDataDim = mrSource.GetDataDimension(nMeasure);
859         if (pDataDim)
860         {
861             const std::optional<OUString> & pLayoutName = pDataDim->GetLayoutName();
862             if (pLayoutName)
863                 return *pLayoutName;
864         }
865 
866         ScSubTotalFunc eFunc = ( eForceFunc == SUBTOTAL_FUNC_NONE ) ?
867                                     GetMeasureFunction(nMeasure) : eForceFunc;
868 
869         return ScDPUtil::getDisplayedMeasureName(maMeasureNames[nMeasure], eFunc);
870     }
871 }
872 
GetMeasureDimensionName(tools::Long nMeasure) const873 OUString ScDPResultData::GetMeasureDimensionName(tools::Long nMeasure) const
874 {
875     if ( nMeasure < 0 )
876     {
877         OSL_FAIL("GetMeasureDimensionName: negative");
878         return "***";
879     }
880 
881     return mrSource.GetDataDimName(nMeasure);
882 }
883 
IsBaseForGroup(tools::Long nDim) const884 bool ScDPResultData::IsBaseForGroup( tools::Long nDim ) const
885 {
886     return mrSource.GetData()->IsBaseForGroup(nDim);
887 }
888 
GetGroupBase(tools::Long nGroupDim) const889 tools::Long ScDPResultData::GetGroupBase( tools::Long nGroupDim ) const
890 {
891     return mrSource.GetData()->GetGroupBase(nGroupDim);
892 }
893 
IsNumOrDateGroup(tools::Long nDim) const894 bool ScDPResultData::IsNumOrDateGroup( tools::Long nDim ) const
895 {
896     return mrSource.GetData()->IsNumOrDateGroup(nDim);
897 }
898 
IsInGroup(SCROW nGroupDataId,tools::Long nGroupIndex,const ScDPItemData & rBaseData,tools::Long nBaseIndex) const899 bool ScDPResultData::IsInGroup( SCROW nGroupDataId, tools::Long nGroupIndex,
900                                 const ScDPItemData& rBaseData, tools::Long nBaseIndex ) const
901 {
902     const ScDPItemData* pGroupData = mrSource.GetItemDataById(nGroupIndex , nGroupDataId);
903     if ( pGroupData )
904         return mrSource.GetData()->IsInGroup(*pGroupData, nGroupIndex, rBaseData, nBaseIndex);
905     else
906         return false;
907 }
908 
HasCommonElement(SCROW nFirstDataId,tools::Long nFirstIndex,const ScDPItemData & rSecondData,tools::Long nSecondIndex) const909 bool ScDPResultData::HasCommonElement( SCROW nFirstDataId, tools::Long nFirstIndex,
910                                        const ScDPItemData& rSecondData, tools::Long nSecondIndex ) const
911 {
912     const ScDPItemData* pFirstData = mrSource.GetItemDataById(nFirstIndex , nFirstDataId);
913     if ( pFirstData )
914         return mrSource.GetData()->HasCommonElement(*pFirstData, nFirstIndex, rSecondData, nSecondIndex);
915     else
916         return false;
917 }
918 
GetDimResultMembers(tools::Long nDim,const ScDPDimension * pDim,ScDPLevel * pLevel) const919 ResultMembers& ScDPResultData::GetDimResultMembers(tools::Long nDim, const ScDPDimension* pDim, ScDPLevel* pLevel) const
920 {
921     if (nDim < static_cast<tools::Long>(maDimMembers.size()) && maDimMembers[nDim])
922         return *maDimMembers[nDim];
923 
924     if (nDim >= static_cast<tools::Long>(maDimMembers.size()))
925         maDimMembers.resize(nDim+1);
926 
927     std::unique_ptr<ResultMembers> pResultMembers(new ResultMembers());
928     // global order is used to initialize aMembers, so it doesn't have to be looked at later
929     const ScMemberSortOrder& rGlobalOrder = pLevel->GetGlobalOrder();
930 
931     ScDPMembers* pMembers = pLevel->GetMembersObject();
932     tools::Long nMembCount = pMembers->getCount();
933     for (tools::Long i = 0; i < nMembCount; ++i)
934     {
935         tools::Long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
936         ScDPMember* pMember = pMembers->getByIndex(nSorted);
937         if (!pResultMembers->FindMember(pMember->GetItemDataId()))
938         {
939             ScDPParentDimData aNew(i, pDim, pLevel, pMember);
940             pResultMembers->InsertMember(aNew);
941         }
942     }
943 
944     maDimMembers[nDim] = std::move(pResultMembers);
945     return *maDimMembers[nDim];
946 }
947 
ScDPResultMember(const ScDPResultData * pData,const ScDPParentDimData & rParentDimData)948 ScDPResultMember::ScDPResultMember(
949     const ScDPResultData* pData, const ScDPParentDimData& rParentDimData ) :
950     pResultData( pData ),
951        aParentDimData( rParentDimData ),
952     bHasElements( false ),
953     bForceSubTotal( false ),
954     bHasHiddenDetails( false ),
955     bInitialized( false ),
956     bAutoHidden( false ),
957     nMemberStep( 1 )
958 {
959     // pParentLevel/pMemberDesc is 0 for root members
960 }
961 
ScDPResultMember(const ScDPResultData * pData,bool bForceSub)962 ScDPResultMember::ScDPResultMember(
963     const ScDPResultData* pData, bool bForceSub ) :
964     pResultData( pData ),
965     bHasElements( false ),
966     bForceSubTotal( bForceSub ),
967     bHasHiddenDetails( false ),
968     bInitialized( false ),
969     bAutoHidden( false ),
970     nMemberStep( 1 )
971 {
972 }
~ScDPResultMember()973 ScDPResultMember::~ScDPResultMember()
974 {
975 }
976 
GetName() const977 OUString ScDPResultMember::GetName() const
978 {
979     const ScDPMember* pMemberDesc = GetDPMember();
980     if (pMemberDesc)
981         return pMemberDesc->GetNameStr( false );
982     else
983         return ScResId(STR_PIVOT_TOTAL);         // root member
984 }
985 
GetDisplayName(bool bLocaleIndependent) const986 OUString ScDPResultMember::GetDisplayName( bool bLocaleIndependent ) const
987 {
988     const ScDPMember* pDPMember = GetDPMember();
989     if (!pDPMember)
990         return OUString();
991 
992     ScDPItemData aItem(pDPMember->FillItemData());
993     if (aParentDimData.mpParentDim)
994     {
995         tools::Long nDim = aParentDimData.mpParentDim->GetDimension();
996         return pResultData->GetSource().GetData()->GetFormattedString(nDim, aItem, bLocaleIndependent);
997     }
998 
999     return aItem.GetString();
1000 }
1001 
FillItemData() const1002 ScDPItemData ScDPResultMember::FillItemData() const
1003 {
1004     const ScDPMember* pMemberDesc = GetDPMember();
1005     if (pMemberDesc)
1006         return pMemberDesc->FillItemData();
1007     return ScDPItemData(ScResId(STR_PIVOT_TOTAL));     // root member
1008 }
1009 
IsNamedItem(SCROW nIndex) const1010 bool ScDPResultMember::IsNamedItem( SCROW nIndex ) const
1011 {
1012     //TODO: store ScDPMember pointer instead of ScDPMember ???
1013     const ScDPMember* pMemberDesc = GetDPMember();
1014     if (pMemberDesc)
1015         return pMemberDesc->IsNamedItem(nIndex);
1016     return false;
1017 }
1018 
IsValidEntry(const vector<SCROW> & aMembers) const1019 bool ScDPResultMember::IsValidEntry( const vector< SCROW >& aMembers ) const
1020 {
1021     if ( !IsValid() )
1022         return false;
1023 
1024     const ScDPResultDimension* pChildDim = GetChildDimension();
1025     if (pChildDim)
1026     {
1027         if (aMembers.size() < 2)
1028             return false;
1029 
1030         vector<SCROW>::const_iterator itr = aMembers.begin();
1031         vector<SCROW> aChildMembers(++itr, aMembers.end());
1032         return pChildDim->IsValidEntry(aChildMembers);
1033     }
1034     else
1035         return true;
1036 }
1037 
InitFrom(const vector<ScDPDimension * > & ppDim,const vector<ScDPLevel * > & ppLev,size_t nPos,ScDPInitState & rInitState,bool bInitChild)1038 void ScDPResultMember::InitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev,
1039                                  size_t nPos, ScDPInitState& rInitState ,
1040                                  bool bInitChild )
1041 {
1042     //  with LateInit, initialize only those members that have data
1043     if ( pResultData->IsLateInit() )
1044         return;
1045 
1046     bInitialized = true;
1047 
1048     if (nPos >= ppDim.size())
1049         return;
1050 
1051     //  skip child dimension if details are not shown
1052     if ( GetDPMember() && !GetDPMember()->getShowDetails() )
1053     {
1054         // Show DataLayout dimension
1055         nMemberStep = 1;
1056         while ( nPos < ppDim.size() )
1057         {
1058             if (  ppDim[nPos]->getIsDataLayoutDimension() )
1059             {
1060                 if ( !pChildDimension )
1061                     pChildDimension.reset( new ScDPResultDimension( pResultData ) );
1062                 pChildDimension->InitFrom( ppDim, ppLev, nPos, rInitState , false );
1063                 return;
1064             }
1065             else
1066             { //find next dim
1067                 nPos ++;
1068                 nMemberStep ++;
1069             }
1070         }
1071         bHasHiddenDetails = true;   // only if there is a next dimension
1072         return;
1073     }
1074 
1075     if ( bInitChild )
1076     {
1077         pChildDimension.reset( new ScDPResultDimension( pResultData ) );
1078         pChildDimension->InitFrom(ppDim, ppLev, nPos, rInitState);
1079     }
1080 }
1081 
LateInitFrom(LateInitParams & rParams,const vector<SCROW> & pItemData,size_t nPos,ScDPInitState & rInitState)1082 void ScDPResultMember::LateInitFrom(
1083     LateInitParams& rParams, const vector<SCROW>& pItemData, size_t nPos, ScDPInitState& rInitState)
1084 {
1085     //  without LateInit, everything has already been initialized
1086     if ( !pResultData->IsLateInit() )
1087         return;
1088 
1089     bInitialized = true;
1090 
1091     if ( rParams.IsEnd( nPos )  /*nPos >= ppDim.size()*/)
1092         // No next dimension.  Bail out.
1093         return;
1094 
1095     //  skip child dimension if details are not shown
1096     if ( GetDPMember() && !GetDPMember()->getShowDetails() )
1097     {
1098         // Show DataLayout dimension
1099         nMemberStep = 1;
1100         while ( !rParams.IsEnd( nPos ) )
1101         {
1102             if (  rParams.GetDim( nPos )->getIsDataLayoutDimension() )
1103             {
1104                 if ( !pChildDimension )
1105                     pChildDimension.reset( new ScDPResultDimension( pResultData ) );
1106 
1107                 // #i111462# reset InitChild flag only for this child dimension's LateInitFrom call,
1108                 // not for following members of parent dimensions
1109                 bool bWasInitChild = rParams.GetInitChild();
1110                 rParams.SetInitChild( false );
1111                 pChildDimension->LateInitFrom( rParams, pItemData, nPos, rInitState );
1112                 rParams.SetInitChild( bWasInitChild );
1113                 return;
1114             }
1115             else
1116             { //find next dim
1117                 nPos ++;
1118                 nMemberStep ++;
1119             }
1120         }
1121         bHasHiddenDetails = true;   // only if there is a next dimension
1122         return;
1123     }
1124 
1125     //  LateInitFrom is called several times...
1126     if ( rParams.GetInitChild() )
1127     {
1128         if ( !pChildDimension )
1129             pChildDimension.reset( new ScDPResultDimension( pResultData ) );
1130         pChildDimension->LateInitFrom( rParams, pItemData, nPos, rInitState );
1131     }
1132 }
1133 
IsSubTotalInTitle(tools::Long nMeasure) const1134 bool ScDPResultMember::IsSubTotalInTitle(tools::Long nMeasure) const
1135 {
1136     bool bRet = false;
1137     if ( pChildDimension && /*pParentLevel*/GetParentLevel() &&
1138          /*pParentLevel*/GetParentLevel()->IsOutlineLayout() && /*pParentLevel*/GetParentLevel()->IsSubtotalsAtTop() )
1139     {
1140         tools::Long nUserSubStart;
1141         tools::Long nSubTotals = GetSubTotalCount( &nUserSubStart );
1142         nSubTotals -= nUserSubStart;            // visible count
1143         if ( nSubTotals )
1144         {
1145             if ( nMeasure == SC_DPMEASURE_ALL )
1146                 nSubTotals *= pResultData->GetMeasureCount();   // number of subtotals that will be inserted
1147 
1148             // only a single subtotal row will be shown in the outline title row
1149             if ( nSubTotals == 1 )
1150                 bRet = true;
1151         }
1152     }
1153     return bRet;
1154 }
1155 
GetSize(tools::Long nMeasure) const1156 tools::Long ScDPResultMember::GetSize(tools::Long nMeasure) const
1157 {
1158     if ( !IsVisible() )
1159         return 0;
1160     const ScDPLevel*       pParentLevel = GetParentLevel();
1161     tools::Long nExtraSpace = 0;
1162     if ( pParentLevel && pParentLevel->IsAddEmpty() )
1163         ++nExtraSpace;
1164 
1165     if ( pChildDimension )
1166     {
1167         //  outline layout takes up an extra row for the title only if subtotals aren't shown in that row
1168         if ( pParentLevel && pParentLevel->IsOutlineLayout() && !IsSubTotalInTitle( nMeasure ) )
1169             ++nExtraSpace;
1170 
1171         tools::Long nSize = pChildDimension->GetSize(nMeasure);
1172         tools::Long nUserSubStart;
1173         tools::Long nUserSubCount = GetSubTotalCount( &nUserSubStart );
1174         nUserSubCount -= nUserSubStart;     // for output size, use visible count
1175         if ( nUserSubCount )
1176         {
1177             if ( nMeasure == SC_DPMEASURE_ALL )
1178                 nSize += pResultData->GetMeasureCount() * nUserSubCount;
1179             else
1180                 nSize += nUserSubCount;
1181         }
1182         return nSize + nExtraSpace;
1183     }
1184     else
1185     {
1186         if ( nMeasure == SC_DPMEASURE_ALL )
1187             return pResultData->GetMeasureCount() + nExtraSpace;
1188         else
1189             return 1 + nExtraSpace;
1190     }
1191 }
1192 
IsVisible() const1193 bool ScDPResultMember::IsVisible() const
1194 {
1195     if (!bInitialized)
1196         return false;
1197 
1198     if (!IsValid())
1199         return false;
1200 
1201     if (bHasElements)
1202         return true;
1203 
1204     //  not initialized -> shouldn't be there at all
1205     //  (allocated only to preserve ordering)
1206     const ScDPLevel* pParentLevel = GetParentLevel();
1207 
1208     return (pParentLevel && pParentLevel->getShowEmpty());
1209 }
1210 
IsValid() const1211 bool ScDPResultMember::IsValid() const
1212 {
1213     //  non-Valid members are left out of calculation
1214 
1215     //  was member set no invisible at the DataPilotSource?
1216     const ScDPMember* pMemberDesc = GetDPMember();
1217     if ( pMemberDesc && !pMemberDesc->isVisible() )
1218         return false;
1219 
1220     if ( bAutoHidden )
1221         return false;
1222 
1223     return true;
1224 }
1225 
GetSubTotalCount(tools::Long * pUserSubStart) const1226 tools::Long ScDPResultMember::GetSubTotalCount( tools::Long* pUserSubStart ) const
1227 {
1228     if ( pUserSubStart )
1229         *pUserSubStart = 0;     // default
1230 
1231     const ScDPLevel* pParentLevel = GetParentLevel();
1232 
1233     if ( bForceSubTotal )       // set if needed for root members
1234         return 1;               // grand total is always "automatic"
1235     else if ( pParentLevel )
1236     {
1237         //TODO: direct access via ScDPLevel
1238 
1239         uno::Sequence<sal_Int16> aSeq = pParentLevel->getSubTotals();
1240         tools::Long nSequence = aSeq.getLength();
1241         if ( nSequence && aSeq[0] != sheet::GeneralFunction2::AUTO )
1242         {
1243             // For manual subtotals, always add "automatic" as first function
1244             // (used for calculation, but not for display, needed for sorting, see lcl_GetForceFunc)
1245 
1246             ++nSequence;
1247             if ( pUserSubStart )
1248                 *pUserSubStart = 1;     // visible subtotals start at 1
1249         }
1250         return nSequence;
1251     }
1252     else
1253         return 0;
1254 }
1255 
ProcessData(const vector<SCROW> & aChildMembers,const ScDPResultDimension * pDataDim,const vector<SCROW> & aDataMembers,const vector<ScDPValue> & aValues)1256 void ScDPResultMember::ProcessData( const vector< SCROW >& aChildMembers, const ScDPResultDimension* pDataDim,
1257                                     const vector< SCROW >& aDataMembers, const vector<ScDPValue>& aValues )
1258 {
1259     SetHasElements();
1260 
1261     if (pChildDimension)
1262         pChildDimension->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues );
1263 
1264     if ( !pDataRoot )
1265     {
1266         pDataRoot.reset( new ScDPDataMember( pResultData, nullptr ) );
1267         if ( pDataDim )
1268             pDataRoot->InitFrom( pDataDim );            // recursive
1269     }
1270 
1271     ScDPSubTotalState aSubState;        // initial state
1272 
1273     tools::Long nUserSubCount = GetSubTotalCount();
1274 
1275     // Calculate at least automatic if no subtotals are selected,
1276     // show only own values if there's no child dimension (innermost).
1277     if ( !nUserSubCount || !pChildDimension )
1278         nUserSubCount = 1;
1279 
1280     const ScDPLevel*    pParentLevel = GetParentLevel();
1281 
1282     for (tools::Long nUserPos=0; nUserPos<nUserSubCount; nUserPos++)   // including hidden "automatic"
1283     {
1284         // #i68338# if nUserSubCount is 1 (automatic only), don't set nRowSubTotalFunc
1285         if ( pChildDimension && nUserSubCount > 1 )
1286         {
1287             aSubState.nRowSubTotalFunc = nUserPos;
1288             aSubState.eRowForce = lcl_GetForceFunc( pParentLevel, nUserPos );
1289         }
1290 
1291         pDataRoot->ProcessData( aDataMembers, aValues, aSubState );
1292     }
1293 }
1294 
1295 /**
1296  * Parse subtotal string and replace all occurrences of '?' with the caption
1297  * string.  Do ensure that escaped characters are not translated.
1298  */
lcl_parseSubtotalName(const OUString & rSubStr,std::u16string_view rCaption)1299 static OUString lcl_parseSubtotalName(const OUString& rSubStr, std::u16string_view rCaption)
1300 {
1301     OUStringBuffer aNewStr;
1302     sal_Int32 n = rSubStr.getLength();
1303     bool bEscaped = false;
1304     for (sal_Int32 i = 0; i < n; ++i)
1305     {
1306         sal_Unicode c = rSubStr[i];
1307         if (!bEscaped && c == '\\')
1308         {
1309             bEscaped = true;
1310             continue;
1311         }
1312 
1313         if (!bEscaped && c == '?')
1314             aNewStr.append(rCaption);
1315         else
1316             aNewStr.append(c);
1317         bEscaped = false;
1318     }
1319     return aNewStr.makeStringAndClear();
1320 }
1321 
FillMemberResults(uno::Sequence<sheet::MemberResult> * pSequences,tools::Long & rPos,tools::Long nMeasure,bool bRoot,const OUString * pMemberName,const OUString * pMemberCaption)1322 void ScDPResultMember::FillMemberResults(
1323     uno::Sequence<sheet::MemberResult>* pSequences, tools::Long& rPos, tools::Long nMeasure, bool bRoot,
1324     const OUString* pMemberName, const OUString* pMemberCaption )
1325 {
1326     //  IsVisible() test is in ScDPResultDimension::FillMemberResults
1327     //  (not on data layout dimension)
1328 
1329     if (!pSequences->hasElements())
1330         // empty sequence.  Bail out.
1331         return;
1332 
1333     tools::Long nSize = GetSize(nMeasure);
1334     sheet::MemberResult* pArray = pSequences->getArray();
1335     OSL_ENSURE( rPos+nSize <= pSequences->getLength(), "bumm" );
1336 
1337     bool bIsNumeric = false;
1338     double fValue;
1339     rtl::math::setNan(&fValue);
1340     OUString aName;
1341     if ( pMemberName )          // if pMemberName != NULL, use instead of real member name
1342     {
1343         aName = *pMemberName;
1344     }
1345     else
1346     {
1347         ScDPItemData aItemData(FillItemData());
1348         if (aParentDimData.mpParentDim)
1349         {
1350             tools::Long nDim = aParentDimData.mpParentDim->GetDimension();
1351             aName = pResultData->GetSource().GetData()->GetFormattedString(nDim, aItemData, false);
1352         }
1353         else
1354         {
1355             tools::Long nDim = -1;
1356             const ScDPMember* pMem = GetDPMember();
1357             if (pMem)
1358                 nDim = pMem->GetDim();
1359             aName = pResultData->GetSource().GetData()->GetFormattedString(nDim, aItemData, false);
1360         }
1361 
1362         ScDPItemData::Type eType = aItemData.GetType();
1363         bIsNumeric = eType == ScDPItemData::Value || eType == ScDPItemData::GroupValue;
1364         // IsValue() is not identical to bIsNumeric, i.e.
1365         // ScDPItemData::GroupValue is excluded and not stored in the double,
1366         // so even if the item is numeric the Value may be NaN.
1367         if (aItemData.IsValue())
1368             fValue = aItemData.GetValue();
1369     }
1370 
1371     const ScDPDimension*        pParentDim = GetParentDim();
1372     if ( bIsNumeric && pParentDim && pResultData->IsNumOrDateGroup( pParentDim->GetDimension() ) )
1373     {
1374         // Numeric group dimensions use numeric entries for proper sorting,
1375         // but the group titles must be output as text.
1376         bIsNumeric = false;
1377     }
1378 
1379     OUString aCaption = aName;
1380     const ScDPMember* pMemberDesc = GetDPMember();
1381     if (pMemberDesc)
1382     {
1383         const std::optional<OUString> & pLayoutName = pMemberDesc->GetLayoutName();
1384         if (pLayoutName)
1385         {
1386             aCaption = *pLayoutName;
1387             bIsNumeric = false; // layout name is always non-numeric.
1388         }
1389     }
1390 
1391     if ( pMemberCaption )                   // use pMemberCaption if != NULL
1392         aCaption = *pMemberCaption;
1393     if (aCaption.isEmpty())
1394         aCaption = ScResId(STR_EMPTYDATA);
1395 
1396     if (bIsNumeric)
1397         pArray[rPos].Flags |= sheet::MemberResultFlags::NUMERIC;
1398     else
1399         pArray[rPos].Flags &= ~sheet::MemberResultFlags::NUMERIC;
1400 
1401     const ScDPLevel*    pParentLevel = GetParentLevel();
1402     if ( nSize && !bRoot )                  // root is overwritten by first dimension
1403     {
1404         pArray[rPos].Name    = aName;
1405         pArray[rPos].Caption = aCaption;
1406         pArray[rPos].Flags  |= sheet::MemberResultFlags::HASMEMBER;
1407         pArray[rPos].Value   = fValue;
1408 
1409         //  set "continue" flag (removed for subtotals later)
1410         for (tools::Long i=1; i<nSize; i++)
1411             pArray[rPos+i].Flags |= sheet::MemberResultFlags::CONTINUE;
1412         if ( pParentLevel && pParentLevel->getRepeatItemLabels() )
1413         {
1414             tools::Long nSizeNonEmpty = nSize;
1415             if ( pParentLevel->IsAddEmpty() )
1416                 --nSizeNonEmpty;
1417             for (tools::Long i=1; i<nSizeNonEmpty; i++)
1418             {
1419                 pArray[rPos+i].Name = aName;
1420                 pArray[rPos+i].Caption = aCaption;
1421                 pArray[rPos+i].Flags  |= sheet::MemberResultFlags::HASMEMBER;
1422                 pArray[rPos+i].Value   = fValue;
1423             }
1424         }
1425     }
1426 
1427     tools::Long nExtraSpace = 0;
1428     if ( pParentLevel && pParentLevel->IsAddEmpty() )
1429         ++nExtraSpace;
1430 
1431     bool bTitleLine = false;
1432     if ( pParentLevel && pParentLevel->IsOutlineLayout() )
1433         bTitleLine = true;
1434 
1435     // if the subtotals are shown at the top (title row) in outline layout,
1436     // no extra row for the subtotals is needed
1437     bool bSubTotalInTitle = IsSubTotalInTitle( nMeasure );
1438 
1439     bool bHasChild = ( pChildDimension != nullptr );
1440     if (bHasChild)
1441     {
1442         if ( bTitleLine )           // in tabular layout the title is on a separate row
1443             ++rPos;                 // -> fill child dimension one row below
1444 
1445         if (bRoot)      // same sequence for root member
1446             pChildDimension->FillMemberResults( pSequences, rPos, nMeasure );
1447         else
1448             pChildDimension->FillMemberResults( pSequences + nMemberStep/*1*/, rPos, nMeasure );
1449 
1450         if ( bTitleLine )           // title row is included in GetSize, so the following
1451             --rPos;                 // positions are calculated with the normal values
1452     }
1453 
1454     rPos += nSize;
1455 
1456     tools::Long nUserSubStart;
1457     tools::Long nUserSubCount = GetSubTotalCount(&nUserSubStart);
1458     if ( !nUserSubCount || !pChildDimension || bSubTotalInTitle )
1459         return;
1460 
1461     tools::Long nMemberMeasure = nMeasure;
1462     tools::Long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1463 
1464     rPos -= nSubSize * (nUserSubCount - nUserSubStart);     // GetSize includes space for SubTotal
1465     rPos -= nExtraSpace;                                    // GetSize includes the empty line
1466 
1467     for (tools::Long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
1468     {
1469         for ( tools::Long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1470         {
1471             if ( nMeasure == SC_DPMEASURE_ALL )
1472                 nMemberMeasure = nSubCount;
1473 
1474             ScSubTotalFunc eForce = SUBTOTAL_FUNC_NONE;
1475             if (bHasChild)
1476                 eForce = lcl_GetForceFunc( pParentLevel, nUserPos );
1477 
1478             bool bTotalResult = false;
1479             OUString aSubStr = aCaption + " " + pResultData->GetMeasureString(nMemberMeasure, false, eForce, bTotalResult);
1480 
1481             if (bTotalResult)
1482             {
1483                 if (pMemberDesc)
1484                 {
1485                     // single data field layout.
1486                     const std::optional<OUString> & pSubtotalName = pParentDim->GetSubtotalName();
1487                     if (pSubtotalName)
1488                         aSubStr = lcl_parseSubtotalName(*pSubtotalName, aCaption);
1489                     pArray[rPos].Flags &= ~sheet::MemberResultFlags::GRANDTOTAL;
1490                 }
1491                 else
1492                 {
1493                     // root member - subtotal (grand total?) for multi-data field layout.
1494                     const std::optional<OUString> & pGrandTotalName = pResultData->GetSource().GetGrandTotalName();
1495                     if (pGrandTotalName)
1496                         aSubStr = *pGrandTotalName;
1497                     pArray[rPos].Flags |= sheet::MemberResultFlags::GRANDTOTAL;
1498                 }
1499             }
1500 
1501             rtl::math::setNan(&fValue); /* TODO: any numeric value to obtain? */
1502             pArray[rPos].Name    = aName;
1503             pArray[rPos].Caption = aSubStr;
1504             pArray[rPos].Flags = ( pArray[rPos].Flags |
1505                                 ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL) ) &
1506                                 ~sheet::MemberResultFlags::CONTINUE;
1507             pArray[rPos].Value   = fValue;
1508 
1509             if ( nMeasure == SC_DPMEASURE_ALL )
1510             {
1511                 //  data layout dimension is (direct/indirect) child of this.
1512                 //  data layout dimension must have name for all entries.
1513 
1514                 uno::Sequence<sheet::MemberResult>* pLayoutSeq = pSequences;
1515                 if (!bRoot)
1516                     ++pLayoutSeq;
1517                 ScDPResultDimension* pLayoutDim = pChildDimension.get();
1518                 while ( pLayoutDim && !pLayoutDim->IsDataLayout() )
1519                 {
1520                     pLayoutDim = pLayoutDim->GetFirstChildDimension();
1521                     ++pLayoutSeq;
1522                 }
1523                 if ( pLayoutDim )
1524                 {
1525                     sheet::MemberResult* pLayoutArray = pLayoutSeq->getArray();
1526                     pLayoutArray[rPos].Name = pResultData->GetMeasureDimensionName(nMemberMeasure);
1527                 }
1528             }
1529 
1530             rPos += 1;
1531         }
1532     }
1533 
1534     rPos += nExtraSpace;                                    // add again (subtracted above)
1535 }
1536 
FillDataResults(const ScDPResultMember * pRefMember,ScDPResultFilterContext & rFilterCxt,uno::Sequence<uno::Sequence<sheet::DataResult>> & rSequence,tools::Long nMeasure) const1537 void ScDPResultMember::FillDataResults(
1538     const ScDPResultMember* pRefMember,
1539     ScDPResultFilterContext& rFilterCxt, uno::Sequence<uno::Sequence<sheet::DataResult> >& rSequence,
1540     tools::Long nMeasure) const
1541 {
1542     std::unique_ptr<FilterStack> pFilterStack;
1543     const ScDPMember* pDPMember = GetDPMember();
1544     if (pDPMember)
1545     {
1546         // Root result has no corresponding DP member. Only take the non-root results.
1547         pFilterStack.reset(new FilterStack(rFilterCxt.maFilters));
1548         pFilterStack->pushDimValue( GetDisplayName( false), GetDisplayName( true));
1549     }
1550 
1551     //  IsVisible() test is in ScDPResultDimension::FillDataResults
1552     //  (not on data layout dimension)
1553     const ScDPLevel*     pParentLevel = GetParentLevel();
1554     sal_Int32 nStartRow = rFilterCxt.mnRow;
1555 
1556     tools::Long nExtraSpace = 0;
1557     if ( pParentLevel && pParentLevel->IsAddEmpty() )
1558         ++nExtraSpace;
1559 
1560     bool bTitleLine = false;
1561     if ( pParentLevel && pParentLevel->IsOutlineLayout() )
1562         bTitleLine = true;
1563 
1564     bool bSubTotalInTitle = IsSubTotalInTitle( nMeasure );
1565 
1566     bool bHasChild = ( pChildDimension != nullptr );
1567     if (bHasChild)
1568     {
1569         if ( bTitleLine )           // in tabular layout the title is on a separate row
1570             ++rFilterCxt.mnRow;                 // -> fill child dimension one row below
1571 
1572         sal_Int32 nOldRow = rFilterCxt.mnRow;
1573         pChildDimension->FillDataResults(pRefMember, rFilterCxt, rSequence, nMeasure);
1574         rFilterCxt.mnRow = nOldRow; // Revert to the original row before the call.
1575 
1576         rFilterCxt.mnRow += GetSize( nMeasure );
1577 
1578         if ( bTitleLine )           // title row is included in GetSize, so the following
1579             --rFilterCxt.mnRow;                 // positions are calculated with the normal values
1580     }
1581 
1582     tools::Long nUserSubStart;
1583     tools::Long nUserSubCount = GetSubTotalCount(&nUserSubStart);
1584     if ( !nUserSubCount && bHasChild )
1585         return;
1586 
1587     // Calculate at least automatic if no subtotals are selected,
1588     // show only own values if there's no child dimension (innermost).
1589     if ( !nUserSubCount || !bHasChild )
1590     {
1591         nUserSubCount = 1;
1592         nUserSubStart = 0;
1593     }
1594 
1595     tools::Long nMemberMeasure = nMeasure;
1596     tools::Long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1597     if (bHasChild)
1598     {
1599         rFilterCxt.mnRow -= nSubSize * ( nUserSubCount - nUserSubStart );   // GetSize includes space for SubTotal
1600         rFilterCxt.mnRow -= nExtraSpace;                                    // GetSize includes the empty line
1601     }
1602 
1603     tools::Long nMoveSubTotal = 0;
1604     if ( bSubTotalInTitle )
1605     {
1606         nMoveSubTotal = rFilterCxt.mnRow - nStartRow;   // force to first (title) row
1607         rFilterCxt.mnRow = nStartRow;
1608     }
1609 
1610     if ( pDataRoot )
1611     {
1612         ScDPSubTotalState aSubState;        // initial state
1613 
1614         for (tools::Long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
1615         {
1616             if ( bHasChild && nUserSubCount > 1 )
1617             {
1618                 aSubState.nRowSubTotalFunc = nUserPos;
1619                 aSubState.eRowForce = lcl_GetForceFunc( /*pParentLevel*/GetParentLevel() , nUserPos );
1620             }
1621 
1622             for ( tools::Long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1623             {
1624                 if ( nMeasure == SC_DPMEASURE_ALL )
1625                     nMemberMeasure = nSubCount;
1626                 else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL )
1627                     nMemberMeasure = SC_DPMEASURE_ALL;
1628 
1629                 OSL_ENSURE( rFilterCxt.mnRow < rSequence.getLength(), "bumm" );
1630                 rFilterCxt.mnCol = 0;
1631                 if (pRefMember->IsVisible())
1632                 {
1633                     uno::Sequence<sheet::DataResult>& rSubSeq = rSequence.getArray()[rFilterCxt.mnRow];
1634                     pDataRoot->FillDataRow(pRefMember, rFilterCxt, rSubSeq, nMemberMeasure, bHasChild, aSubState);
1635                 }
1636                 rFilterCxt.mnRow += 1;
1637             }
1638         }
1639     }
1640     else
1641         rFilterCxt.mnRow += nSubSize * ( nUserSubCount - nUserSubStart );   // empty rows occur when ShowEmpty is true
1642 
1643     // add extra space again if subtracted from GetSize above,
1644     // add to own size if no children
1645     rFilterCxt.mnRow += nExtraSpace;
1646     rFilterCxt.mnRow += nMoveSubTotal;
1647 }
1648 
UpdateDataResults(const ScDPResultMember * pRefMember,tools::Long nMeasure) const1649 void ScDPResultMember::UpdateDataResults( const ScDPResultMember* pRefMember, tools::Long nMeasure ) const
1650 {
1651     //  IsVisible() test is in ScDPResultDimension::FillDataResults
1652     //  (not on data layout dimension)
1653 
1654     bool bHasChild = ( pChildDimension != nullptr );
1655 
1656     tools::Long nUserSubCount = GetSubTotalCount();
1657 
1658     // process subtotals even if not shown
1659 
1660     // Calculate at least automatic if no subtotals are selected,
1661     // show only own values if there's no child dimension (innermost).
1662     if (!nUserSubCount || !bHasChild)
1663         nUserSubCount = 1;
1664 
1665     tools::Long nMemberMeasure = nMeasure;
1666     tools::Long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1667 
1668     if (pDataRoot)
1669     {
1670         ScDPSubTotalState aSubState;        // initial state
1671 
1672         for (tools::Long nUserPos = 0; nUserPos < nUserSubCount; ++nUserPos)   // including hidden "automatic"
1673         {
1674             if (bHasChild && nUserSubCount > 1)
1675             {
1676                 aSubState.nRowSubTotalFunc = nUserPos;
1677                 aSubState.eRowForce = lcl_GetForceFunc(GetParentLevel(), nUserPos);
1678             }
1679 
1680             for (tools::Long nSubCount = 0; nSubCount < nSubSize; ++nSubCount)
1681             {
1682                 if (nMeasure == SC_DPMEASURE_ALL)
1683                     nMemberMeasure = nSubCount;
1684                 else if (pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL)
1685                     nMemberMeasure = SC_DPMEASURE_ALL;
1686 
1687                 pDataRoot->UpdateDataRow(pRefMember, nMemberMeasure, bHasChild, aSubState);
1688             }
1689         }
1690     }
1691 
1692     if (bHasChild)  // child dimension must be processed last, so the column total is known
1693     {
1694         pChildDimension->UpdateDataResults( pRefMember, nMeasure );
1695     }
1696 }
1697 
SortMembers(ScDPResultMember * pRefMember)1698 void ScDPResultMember::SortMembers( ScDPResultMember* pRefMember )
1699 {
1700     bool bHasChild = ( pChildDimension != nullptr );
1701     if (bHasChild)
1702         pChildDimension->SortMembers( pRefMember );     // sorting is done at the dimension
1703 
1704     if ( IsRoot() && pDataRoot )
1705     {
1706         // use the row root member to sort columns
1707         // sub total count is always 1
1708 
1709         pDataRoot->SortMembers( pRefMember );
1710     }
1711 }
1712 
DoAutoShow(ScDPResultMember * pRefMember)1713 void ScDPResultMember::DoAutoShow( ScDPResultMember* pRefMember )
1714 {
1715     bool bHasChild = ( pChildDimension != nullptr );
1716     if (bHasChild)
1717         pChildDimension->DoAutoShow( pRefMember );     // sorting is done at the dimension
1718 
1719     if ( IsRoot()&& pDataRoot )
1720     {
1721         // use the row root member to sort columns
1722         // sub total count is always 1
1723 
1724         pDataRoot->DoAutoShow( pRefMember );
1725     }
1726 }
1727 
ResetResults()1728 void ScDPResultMember::ResetResults()
1729 {
1730     if (pDataRoot)
1731         pDataRoot->ResetResults();
1732 
1733     if (pChildDimension)
1734         pChildDimension->ResetResults();
1735 }
1736 
UpdateRunningTotals(const ScDPResultMember * pRefMember,tools::Long nMeasure,ScDPRunningTotalState & rRunning,ScDPRowTotals & rTotals) const1737 void ScDPResultMember::UpdateRunningTotals( const ScDPResultMember* pRefMember, tools::Long nMeasure,
1738                                             ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const
1739 {
1740     //  IsVisible() test is in ScDPResultDimension::FillDataResults
1741     //  (not on data layout dimension)
1742 
1743     rTotals.SetInColRoot( IsRoot() );
1744 
1745     bool bHasChild = ( pChildDimension != nullptr );
1746 
1747     tools::Long nUserSubCount = GetSubTotalCount();
1748     //if ( nUserSubCount || !bHasChild )
1749     {
1750         // Calculate at least automatic if no subtotals are selected,
1751         // show only own values if there's no child dimension (innermost).
1752         if ( !nUserSubCount || !bHasChild )
1753             nUserSubCount = 1;
1754 
1755         tools::Long nMemberMeasure = nMeasure;
1756         tools::Long nSubSize = pResultData->GetCountForMeasure(nMeasure);
1757 
1758         if ( pDataRoot )
1759         {
1760             ScDPSubTotalState aSubState;        // initial state
1761 
1762             for (tools::Long nUserPos=0; nUserPos<nUserSubCount; nUserPos++)   // including hidden "automatic"
1763             {
1764                 if ( bHasChild && nUserSubCount > 1 )
1765                 {
1766                     aSubState.nRowSubTotalFunc = nUserPos;
1767                     aSubState.eRowForce = lcl_GetForceFunc(GetParentLevel(), nUserPos);
1768                 }
1769 
1770                 for ( tools::Long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
1771                 {
1772                     if ( nMeasure == SC_DPMEASURE_ALL )
1773                         nMemberMeasure = nSubCount;
1774                     else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL )
1775                         nMemberMeasure = SC_DPMEASURE_ALL;
1776 
1777                     if (pRefMember->IsVisible())
1778                         pDataRoot->UpdateRunningTotals(
1779                             pRefMember, nMemberMeasure, bHasChild, aSubState, rRunning, rTotals, *this);
1780                 }
1781             }
1782         }
1783     }
1784 
1785     if (bHasChild)  // child dimension must be processed last, so the column total is known
1786     {
1787         pChildDimension->UpdateRunningTotals( pRefMember, nMeasure, rRunning, rTotals );
1788     }
1789 }
1790 
1791 #if DUMP_PIVOT_TABLE
DumpState(const ScDPResultMember * pRefMember,ScDocument * pDoc,ScAddress & rPos) const1792 void ScDPResultMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
1793 {
1794     dumpRow("ScDPResultMember", GetName(), nullptr, pDoc, rPos);
1795     SCROW nStartRow = rPos.Row();
1796 
1797     if (pDataRoot)
1798         pDataRoot->DumpState( pRefMember, pDoc, rPos );
1799 
1800     if (pChildDimension)
1801         pChildDimension->DumpState( pRefMember, pDoc, rPos );
1802 
1803     indent(pDoc, nStartRow, rPos);
1804 }
1805 
Dump(int nIndent) const1806 void ScDPResultMember::Dump(int nIndent) const
1807 {
1808     std::string aIndent(nIndent*2, ' ');
1809     std::cout << aIndent << "-- result member '" << GetName() << "'" << std::endl;
1810 
1811     std::cout << aIndent << " column totals" << std::endl;
1812     for (const ScDPAggData* p = &aColTotal; p; p = p->GetExistingChild())
1813         p->Dump(nIndent+1);
1814 
1815     if (pChildDimension)
1816         pChildDimension->Dump(nIndent+1);
1817 
1818     if (pDataRoot)
1819     {
1820         std::cout << aIndent << " data root" << std::endl;
1821         pDataRoot->Dump(nIndent+1);
1822     }
1823 }
1824 #endif
1825 
GetColTotal(tools::Long nMeasure) const1826 ScDPAggData* ScDPResultMember::GetColTotal( tools::Long nMeasure ) const
1827 {
1828     return lcl_GetChildTotal( const_cast<ScDPAggData*>(&aColTotal), nMeasure );
1829 }
1830 
FillVisibilityData(ScDPResultVisibilityData & rData) const1831 void ScDPResultMember::FillVisibilityData(ScDPResultVisibilityData& rData) const
1832 {
1833     if (pChildDimension)
1834         pChildDimension->FillVisibilityData(rData);
1835 }
1836 
ScDPDataMember(const ScDPResultData * pData,const ScDPResultMember * pRes)1837 ScDPDataMember::ScDPDataMember( const ScDPResultData* pData, const ScDPResultMember* pRes ) :
1838     pResultData( pData ),
1839     pResultMember( pRes )
1840 {
1841     // pResultMember is 0 for root members
1842 }
1843 
~ScDPDataMember()1844 ScDPDataMember::~ScDPDataMember()
1845 {
1846 }
1847 
GetName() const1848 OUString ScDPDataMember::GetName() const
1849 {
1850     if (pResultMember)
1851         return pResultMember->GetName();
1852     else
1853         return EMPTY_OUSTRING;
1854 }
1855 
IsVisible() const1856 bool ScDPDataMember::IsVisible() const
1857 {
1858     if (pResultMember)
1859         return pResultMember->IsVisible();
1860     else
1861         return false;
1862 }
1863 
IsNamedItem(SCROW nRow) const1864 bool ScDPDataMember::IsNamedItem( SCROW nRow ) const
1865 {
1866     if (pResultMember)
1867         return pResultMember->IsNamedItem(nRow);
1868     else
1869         return false;
1870 }
1871 
HasHiddenDetails() const1872 bool ScDPDataMember::HasHiddenDetails() const
1873 {
1874     if (pResultMember)
1875         return pResultMember->HasHiddenDetails();
1876     else
1877         return false;
1878 }
1879 
InitFrom(const ScDPResultDimension * pDim)1880 void ScDPDataMember::InitFrom( const ScDPResultDimension* pDim )
1881 {
1882     if ( !pChildDimension )
1883         pChildDimension.reset( new ScDPDataDimension(pResultData) );
1884     pChildDimension->InitFrom(pDim);
1885 }
1886 
1887 const tools::Long SC_SUBTOTALPOS_AUTO = -1;    // default
1888 const tools::Long SC_SUBTOTALPOS_SKIP = -2;    // don't use
1889 
lcl_GetSubTotalPos(const ScDPSubTotalState & rSubState)1890 static tools::Long lcl_GetSubTotalPos( const ScDPSubTotalState& rSubState )
1891 {
1892     if ( rSubState.nColSubTotalFunc >= 0 && rSubState.nRowSubTotalFunc >= 0 &&
1893          rSubState.nColSubTotalFunc != rSubState.nRowSubTotalFunc )
1894     {
1895         // #i68338# don't return the same index for different combinations (leading to repeated updates),
1896         // return a "don't use" value instead
1897 
1898         return SC_SUBTOTALPOS_SKIP;
1899     }
1900 
1901     tools::Long nRet = SC_SUBTOTALPOS_AUTO;
1902     if ( rSubState.nColSubTotalFunc >= 0 ) nRet = rSubState.nColSubTotalFunc;
1903     if ( rSubState.nRowSubTotalFunc >= 0 ) nRet = rSubState.nRowSubTotalFunc;
1904     return nRet;
1905 }
1906 
UpdateValues(const vector<ScDPValue> & aValues,const ScDPSubTotalState & rSubState)1907 void ScDPDataMember::UpdateValues( const vector<ScDPValue>& aValues, const ScDPSubTotalState& rSubState )
1908 {
1909     //TODO: find out how many and which subtotals are used
1910 
1911     ScDPAggData* pAgg = &aAggregate;
1912 
1913     tools::Long nSubPos = lcl_GetSubTotalPos(rSubState);
1914     if (nSubPos == SC_SUBTOTALPOS_SKIP)
1915         return;
1916     if (nSubPos > 0)
1917     {
1918         tools::Long nSkip = nSubPos * pResultData->GetMeasureCount();
1919         for (tools::Long i=0; i<nSkip; i++)
1920             pAgg = pAgg->GetChild();        // created if not there
1921     }
1922 
1923     size_t nCount = aValues.size();
1924     for (size_t nPos = 0; nPos < nCount; ++nPos)
1925     {
1926         pAgg->Update(aValues[nPos], pResultData->GetMeasureFunction(nPos), rSubState);
1927         pAgg = pAgg->GetChild();
1928     }
1929 }
1930 
ProcessData(const vector<SCROW> & aChildMembers,const vector<ScDPValue> & aValues,const ScDPSubTotalState & rSubState)1931 void ScDPDataMember::ProcessData( const vector< SCROW >& aChildMembers, const vector<ScDPValue>& aValues,
1932                                     const ScDPSubTotalState& rSubState )
1933 {
1934     if ( pResultData->IsLateInit() && !pChildDimension && pResultMember && pResultMember->GetChildDimension() )
1935     {
1936         //  if this DataMember doesn't have a child dimension because the ResultMember's
1937         //  child dimension wasn't there yet during this DataMembers's creation,
1938         //  create the child dimension now
1939         InitFrom( pResultMember->GetChildDimension() );
1940     }
1941 
1942     tools::Long nUserSubCount = pResultMember ? pResultMember->GetSubTotalCount() : 0;
1943 
1944     // Calculate at least automatic if no subtotals are selected,
1945     // show only own values if there's no child dimension (innermost).
1946     if ( !nUserSubCount || !pChildDimension )
1947         nUserSubCount = 1;
1948 
1949     ScDPSubTotalState aLocalSubState = rSubState;        // keep row state, modify column
1950     for (tools::Long nUserPos=0; nUserPos<nUserSubCount; nUserPos++)   // including hidden "automatic"
1951     {
1952         if ( pChildDimension && nUserSubCount > 1 )
1953         {
1954             const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : nullptr;
1955             aLocalSubState.nColSubTotalFunc = nUserPos;
1956             aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
1957         }
1958 
1959         UpdateValues( aValues, aLocalSubState );
1960     }
1961 
1962     if (pChildDimension)
1963         pChildDimension->ProcessData( aChildMembers, aValues, rSubState );      // with unmodified subtotal state
1964 }
1965 
HasData(tools::Long nMeasure,const ScDPSubTotalState & rSubState) const1966 bool ScDPDataMember::HasData( tools::Long nMeasure, const ScDPSubTotalState& rSubState ) const
1967 {
1968     if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE &&
1969                                                         rSubState.eColForce != rSubState.eRowForce )
1970         return false;
1971 
1972     //  HasData can be different between measures!
1973 
1974     const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
1975     if (!pAgg)
1976         return false;           //TODO: error?
1977 
1978     return pAgg->HasData();
1979 }
1980 
HasError(tools::Long nMeasure,const ScDPSubTotalState & rSubState) const1981 bool ScDPDataMember::HasError( tools::Long nMeasure, const ScDPSubTotalState& rSubState ) const
1982 {
1983     const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
1984     if (!pAgg)
1985         return true;
1986 
1987     return pAgg->HasError();
1988 }
1989 
GetAggregate(tools::Long nMeasure,const ScDPSubTotalState & rSubState) const1990 double ScDPDataMember::GetAggregate( tools::Long nMeasure, const ScDPSubTotalState& rSubState ) const
1991 {
1992     const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState );
1993     if (!pAgg)
1994         return DBL_MAX;         //TODO: error?
1995 
1996     return pAgg->GetResult();
1997 }
1998 
GetAggData(tools::Long nMeasure,const ScDPSubTotalState & rSubState)1999 ScDPAggData* ScDPDataMember::GetAggData( tools::Long nMeasure, const ScDPSubTotalState& rSubState )
2000 {
2001     OSL_ENSURE( nMeasure >= 0, "GetAggData: no measure" );
2002 
2003     ScDPAggData* pAgg = &aAggregate;
2004     tools::Long nSkip = nMeasure;
2005     tools::Long nSubPos = lcl_GetSubTotalPos(rSubState);
2006     if (nSubPos == SC_SUBTOTALPOS_SKIP)
2007         return nullptr;
2008     if (nSubPos > 0)
2009         nSkip += nSubPos * pResultData->GetMeasureCount();
2010 
2011     for ( tools::Long nPos=0; nPos<nSkip; nPos++ )
2012         pAgg = pAgg->GetChild();        //TODO: need to create children here?
2013 
2014     return pAgg;
2015 }
2016 
GetConstAggData(tools::Long nMeasure,const ScDPSubTotalState & rSubState) const2017 const ScDPAggData* ScDPDataMember::GetConstAggData( tools::Long nMeasure, const ScDPSubTotalState& rSubState ) const
2018 {
2019     OSL_ENSURE( nMeasure >= 0, "GetConstAggData: no measure" );
2020 
2021     const ScDPAggData* pAgg = &aAggregate;
2022     tools::Long nSkip = nMeasure;
2023     tools::Long nSubPos = lcl_GetSubTotalPos(rSubState);
2024     if (nSubPos == SC_SUBTOTALPOS_SKIP)
2025         return nullptr;
2026     if (nSubPos > 0)
2027         nSkip += nSubPos * pResultData->GetMeasureCount();
2028 
2029     for ( tools::Long nPos=0; nPos<nSkip; nPos++ )
2030     {
2031         pAgg = pAgg->GetExistingChild();
2032         if (!pAgg)
2033             return nullptr;
2034     }
2035 
2036     return pAgg;
2037 }
2038 
FillDataRow(const ScDPResultMember * pRefMember,ScDPResultFilterContext & rFilterCxt,uno::Sequence<sheet::DataResult> & rSequence,tools::Long nMeasure,bool bIsSubTotalRow,const ScDPSubTotalState & rSubState) const2039 void ScDPDataMember::FillDataRow(
2040     const ScDPResultMember* pRefMember, ScDPResultFilterContext& rFilterCxt,
2041     uno::Sequence<sheet::DataResult>& rSequence, tools::Long nMeasure, bool bIsSubTotalRow,
2042     const ScDPSubTotalState& rSubState) const
2043 {
2044     std::unique_ptr<FilterStack> pFilterStack;
2045     if (pResultMember)
2046     {
2047         // Topmost data member (pResultMember=NULL) doesn't need to be handled
2048         // since its immediate parent result member is linked to the same
2049         // dimension member.
2050         pFilterStack.reset(new FilterStack(rFilterCxt.maFilters));
2051         pFilterStack->pushDimValue( pResultMember->GetDisplayName( false), pResultMember->GetDisplayName( true));
2052     }
2053 
2054     OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2055 
2056     tools::Long nStartCol = rFilterCxt.mnCol;
2057 
2058     const ScDPDataDimension* pDataChild = GetChildDimension();
2059     const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2060 
2061     const ScDPLevel* pRefParentLevel = pRefMember->GetParentLevel();
2062 
2063     tools::Long nExtraSpace = 0;
2064     if ( pRefParentLevel && pRefParentLevel->IsAddEmpty() )
2065         ++nExtraSpace;
2066 
2067     bool bTitleLine = false;
2068     if ( pRefParentLevel && pRefParentLevel->IsOutlineLayout() )
2069         bTitleLine = true;
2070 
2071     bool bSubTotalInTitle = pRefMember->IsSubTotalInTitle( nMeasure );
2072 
2073     //  leave space for children even if the DataMember hasn't been initialized
2074     //  (pDataChild is null then, this happens when no values for it are in this row)
2075     bool bHasChild = ( pRefChild != nullptr );
2076 
2077     if ( bHasChild )
2078     {
2079         if ( bTitleLine )           // in tabular layout the title is on a separate column
2080             ++rFilterCxt.mnCol;                 // -> fill child dimension one column below
2081 
2082         if ( pDataChild )
2083         {
2084             tools::Long nOldCol = rFilterCxt.mnCol;
2085             pDataChild->FillDataRow(pRefChild, rFilterCxt, rSequence, nMeasure, bIsSubTotalRow, rSubState);
2086             rFilterCxt.mnCol = nOldCol; // Revert to the old column value before the call.
2087         }
2088         rFilterCxt.mnCol += static_cast<sal_uInt16>(pRefMember->GetSize( nMeasure ));
2089 
2090         if ( bTitleLine )           // title column is included in GetSize, so the following
2091             --rFilterCxt.mnCol;                 // positions are calculated with the normal values
2092     }
2093 
2094     tools::Long nUserSubStart;
2095     tools::Long nUserSubCount = pRefMember->GetSubTotalCount(&nUserSubStart);
2096     if ( !nUserSubCount && bHasChild )
2097         return;
2098 
2099     // Calculate at least automatic if no subtotals are selected,
2100     // show only own values if there's no child dimension (innermost).
2101     if ( !nUserSubCount || !bHasChild )
2102     {
2103         nUserSubCount = 1;
2104         nUserSubStart = 0;
2105     }
2106 
2107     ScDPSubTotalState aLocalSubState(rSubState);        // keep row state, modify column
2108 
2109     tools::Long nMemberMeasure = nMeasure;
2110     tools::Long nSubSize = pResultData->GetCountForMeasure(nMeasure);
2111     if (bHasChild)
2112     {
2113         rFilterCxt.mnCol -= nSubSize * ( nUserSubCount - nUserSubStart );   // GetSize includes space for SubTotal
2114         rFilterCxt.mnCol -= nExtraSpace;                                    // GetSize includes the empty line
2115     }
2116 
2117     tools::Long nMoveSubTotal = 0;
2118     if ( bSubTotalInTitle )
2119     {
2120         nMoveSubTotal = rFilterCxt.mnCol - nStartCol;   // force to first (title) column
2121         rFilterCxt.mnCol = nStartCol;
2122     }
2123 
2124     for (tools::Long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++)
2125     {
2126         if ( pChildDimension && nUserSubCount > 1 )
2127         {
2128             const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : nullptr;
2129             aLocalSubState.nColSubTotalFunc = nUserPos;
2130             aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
2131         }
2132 
2133         for ( tools::Long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
2134         {
2135             if ( nMeasure == SC_DPMEASURE_ALL )
2136                 nMemberMeasure = nSubCount;
2137 
2138             OSL_ENSURE( rFilterCxt.mnCol < rSequence.getLength(), "bumm" );
2139             sheet::DataResult& rRes = rSequence.getArray()[rFilterCxt.mnCol];
2140 
2141             if ( HasData( nMemberMeasure, aLocalSubState ) )
2142             {
2143                 if ( HasError( nMemberMeasure, aLocalSubState ) )
2144                 {
2145                     rRes.Value = 0;
2146                     rRes.Flags |= sheet::DataResultFlags::ERROR;
2147                 }
2148                 else
2149                 {
2150                     rRes.Value = GetAggregate( nMemberMeasure, aLocalSubState );
2151                     rRes.Flags |= sheet::DataResultFlags::HASDATA;
2152                 }
2153             }
2154 
2155             if ( bHasChild || bIsSubTotalRow )
2156                 rRes.Flags |= sheet::DataResultFlags::SUBTOTAL;
2157 
2158             rFilterCxt.maFilterSet.add(rFilterCxt.maFilters, rRes.Value);
2159             rFilterCxt.mnCol += 1;
2160         }
2161     }
2162 
2163     // add extra space again if subtracted from GetSize above,
2164     // add to own size if no children
2165     rFilterCxt.mnCol += nExtraSpace;
2166     rFilterCxt.mnCol += nMoveSubTotal;
2167 }
2168 
UpdateDataRow(const ScDPResultMember * pRefMember,tools::Long nMeasure,bool bIsSubTotalRow,const ScDPSubTotalState & rSubState)2169 void ScDPDataMember::UpdateDataRow(
2170     const ScDPResultMember* pRefMember, tools::Long nMeasure, bool bIsSubTotalRow,
2171     const ScDPSubTotalState& rSubState )
2172 {
2173     OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2174 
2175     // Calculate must be called even if not visible (for use as reference value)
2176     const ScDPDataDimension* pDataChild = GetChildDimension();
2177     const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2178 
2179     //  leave space for children even if the DataMember hasn't been initialized
2180     //  (pDataChild is null then, this happens when no values for it are in this row)
2181     bool bHasChild = ( pRefChild != nullptr );
2182 
2183     // process subtotals even if not shown
2184     tools::Long nUserSubCount = pRefMember->GetSubTotalCount();
2185 
2186     // Calculate at least automatic if no subtotals are selected,
2187     // show only own values if there's no child dimension (innermost).
2188     if ( !nUserSubCount || !bHasChild )
2189         nUserSubCount = 1;
2190 
2191     ScDPSubTotalState aLocalSubState(rSubState);        // keep row state, modify column
2192 
2193     tools::Long nMemberMeasure = nMeasure;
2194     tools::Long nSubSize = pResultData->GetCountForMeasure(nMeasure);
2195 
2196     for (tools::Long nUserPos=0; nUserPos<nUserSubCount; nUserPos++)   // including hidden "automatic"
2197     {
2198         if ( pChildDimension && nUserSubCount > 1 )
2199         {
2200             const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : nullptr;
2201             aLocalSubState.nColSubTotalFunc = nUserPos;
2202             aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
2203         }
2204 
2205         for ( tools::Long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
2206         {
2207             if ( nMeasure == SC_DPMEASURE_ALL )
2208                 nMemberMeasure = nSubCount;
2209 
2210             // update data...
2211             ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState );
2212             if (pAggData)
2213             {
2214                 //TODO: aLocalSubState?
2215                 ScSubTotalFunc eFunc = pResultData->GetMeasureFunction( nMemberMeasure );
2216                 sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure );
2217                 sal_Int32 eRefType = aReferenceValue.ReferenceType;
2218 
2219                 // calculate the result first - for all members, regardless of reference value
2220                 pAggData->Calculate( eFunc, aLocalSubState );
2221 
2222                 if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
2223                      eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
2224                      eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE )
2225                 {
2226                     // copy the result into auxiliary value, so differences can be
2227                     // calculated in any order
2228                     pAggData->SetAuxiliary( pAggData->GetResult() );
2229                 }
2230                 // column/row percentage/index is now in UpdateRunningTotals, so it doesn't disturb sorting
2231             }
2232         }
2233     }
2234 
2235     if ( bHasChild )    // child dimension must be processed last, so the row total is known
2236     {
2237         if ( pDataChild )
2238             pDataChild->UpdateDataRow( pRefChild, nMeasure, bIsSubTotalRow, rSubState );
2239     }
2240 }
2241 
SortMembers(ScDPResultMember * pRefMember)2242 void ScDPDataMember::SortMembers( ScDPResultMember* pRefMember )
2243 {
2244     OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2245 
2246     if ( pRefMember->IsVisible() )  //TODO: here or in ScDPDataDimension ???
2247     {
2248         ScDPDataDimension* pDataChild = GetChildDimension();
2249         ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2250         if ( pRefChild && pDataChild )
2251             pDataChild->SortMembers( pRefChild );       // sorting is done at the dimension
2252     }
2253 }
2254 
DoAutoShow(ScDPResultMember * pRefMember)2255 void ScDPDataMember::DoAutoShow( ScDPResultMember* pRefMember )
2256 {
2257     OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2258 
2259     if ( pRefMember->IsVisible() )  //TODO: here or in ScDPDataDimension ???
2260     {
2261         ScDPDataDimension* pDataChild = GetChildDimension();
2262         ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2263         if ( pRefChild && pDataChild )
2264             pDataChild->DoAutoShow( pRefChild );       // sorting is done at the dimension
2265     }
2266 }
2267 
ResetResults()2268 void ScDPDataMember::ResetResults()
2269 {
2270     aAggregate.Reset();
2271 
2272     ScDPDataDimension* pDataChild = GetChildDimension();
2273     if ( pDataChild )
2274         pDataChild->ResetResults();
2275 }
2276 
UpdateRunningTotals(const ScDPResultMember * pRefMember,tools::Long nMeasure,bool bIsSubTotalRow,const ScDPSubTotalState & rSubState,ScDPRunningTotalState & rRunning,ScDPRowTotals & rTotals,const ScDPResultMember & rRowParent)2277 void ScDPDataMember::UpdateRunningTotals(
2278     const ScDPResultMember* pRefMember, tools::Long nMeasure, bool bIsSubTotalRow,
2279     const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning,
2280     ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent )
2281 {
2282     OSL_ENSURE( pRefMember == pResultMember || !pResultMember, "bla" );
2283 
2284     const ScDPDataDimension* pDataChild = GetChildDimension();
2285     const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2286 
2287     bool bIsRoot = ( pResultMember == nullptr || pResultMember->GetParentLevel() == nullptr );
2288 
2289     //  leave space for children even if the DataMember hasn't been initialized
2290     //  (pDataChild is null then, this happens when no values for it are in this row)
2291     bool bHasChild = ( pRefChild != nullptr );
2292 
2293     tools::Long nUserSubCount = pRefMember->GetSubTotalCount();
2294     {
2295         // Calculate at least automatic if no subtotals are selected,
2296         // show only own values if there's no child dimension (innermost).
2297         if ( !nUserSubCount || !bHasChild )
2298             nUserSubCount = 1;
2299 
2300         ScDPSubTotalState aLocalSubState(rSubState);        // keep row state, modify column
2301 
2302         tools::Long nMemberMeasure = nMeasure;
2303         tools::Long nSubSize = pResultData->GetCountForMeasure(nMeasure);
2304 
2305         for (tools::Long nUserPos=0; nUserPos<nUserSubCount; nUserPos++)   // including hidden "automatic"
2306         {
2307             if ( pChildDimension && nUserSubCount > 1 )
2308             {
2309                 const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : nullptr;
2310                 aLocalSubState.nColSubTotalFunc = nUserPos;
2311                 aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos );
2312             }
2313 
2314             for ( tools::Long nSubCount=0; nSubCount<nSubSize; nSubCount++ )
2315             {
2316                 if ( nMeasure == SC_DPMEASURE_ALL )
2317                     nMemberMeasure = nSubCount;
2318 
2319                 // update data...
2320                 ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState );
2321                 if (pAggData)
2322                 {
2323                     //TODO: aLocalSubState?
2324                     sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure );
2325                     sal_Int32 eRefType = aReferenceValue.ReferenceType;
2326 
2327                     if ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL ||
2328                          eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE ||
2329                          eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ||
2330                          eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE )
2331                     {
2332                         bool bRunningTotal = ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL );
2333                         bool bRelative =
2334                             ( aReferenceValue.ReferenceItemType != sheet::DataPilotFieldReferenceItemType::NAMED && !bRunningTotal );
2335                         tools::Long nRelativeDir = bRelative ?
2336                             ( ( aReferenceValue.ReferenceItemType == sheet::DataPilotFieldReferenceItemType::PREVIOUS ) ? -1 : 1 ) : 0;
2337 
2338                         const ScDPRunningTotalState::IndexArray& rColVisible = rRunning.GetColVisible();
2339                         const ScDPRunningTotalState::IndexArray& rColSorted = rRunning.GetColSorted();
2340                         const ScDPRunningTotalState::IndexArray& rRowVisible = rRunning.GetRowVisible();
2341                         const ScDPRunningTotalState::IndexArray& rRowSorted = rRunning.GetRowSorted();
2342 
2343                         OUString aRefFieldName = aReferenceValue.ReferenceField;
2344 
2345                         //TODO: aLocalSubState?
2346                         sheet::DataPilotFieldOrientation nRefOrient = pResultData->GetMeasureRefOrient( nMemberMeasure );
2347                         bool bRefDimInCol = ( nRefOrient == sheet::DataPilotFieldOrientation_COLUMN );
2348                         bool bRefDimInRow = ( nRefOrient == sheet::DataPilotFieldOrientation_ROW );
2349 
2350                         ScDPResultDimension* pSelectDim = nullptr;
2351                         sal_Int32 nRowPos = 0;
2352                         sal_Int32 nColPos = 0;
2353 
2354                         //  find the reference field in column or row dimensions
2355 
2356                         if ( bRefDimInRow )     //  look in row dimensions
2357                         {
2358                             pSelectDim = rRunning.GetRowResRoot()->GetChildDimension();
2359                             while ( pSelectDim && pSelectDim->GetName() != aRefFieldName )
2360                             {
2361                                 tools::Long nIndex = rRowSorted[nRowPos];
2362                                 if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() )
2363                                     pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension();
2364                                 else
2365                                     pSelectDim = nullptr;
2366                                 ++nRowPos;
2367                             }
2368                             // child dimension of innermost member?
2369                             if ( pSelectDim && rRowSorted[nRowPos] < 0 )
2370                                 pSelectDim = nullptr;
2371                         }
2372 
2373                         if ( bRefDimInCol )     //  look in column dimensions
2374                         {
2375                             pSelectDim = rRunning.GetColResRoot()->GetChildDimension();
2376                             while ( pSelectDim && pSelectDim->GetName() != aRefFieldName )
2377                             {
2378                                 tools::Long nIndex = rColSorted[nColPos];
2379                                 if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() )
2380                                     pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension();
2381                                 else
2382                                     pSelectDim = nullptr;
2383                                 ++nColPos;
2384                             }
2385                             // child dimension of innermost member?
2386                             if ( pSelectDim && rColSorted[nColPos] < 0 )
2387                                 pSelectDim = nullptr;
2388                         }
2389 
2390                         bool bNoDetailsInRef = false;
2391                         if ( pSelectDim && bRunningTotal )
2392                         {
2393                             //  Running totals:
2394                             //  If details are hidden for this member in the reference dimension,
2395                             //  don't show or sum up the value. Otherwise, for following members,
2396                             //  the running totals of details and subtotals wouldn't match.
2397 
2398                             tools::Long nMyIndex = bRefDimInCol ? rColSorted[nColPos] : rRowSorted[nRowPos];
2399                             if ( nMyIndex >= 0 && nMyIndex < pSelectDim->GetMemberCount() )
2400                             {
2401                                 const ScDPResultMember* pMyRefMember = pSelectDim->GetMember(nMyIndex);
2402                                 if ( pMyRefMember && pMyRefMember->HasHiddenDetails() )
2403                                 {
2404                                     pSelectDim = nullptr;          // don't calculate
2405                                     bNoDetailsInRef = true;     // show error, not empty
2406                                 }
2407                             }
2408                         }
2409 
2410                         if ( bRelative )
2411                         {
2412                             //  Difference/Percentage from previous/next:
2413                             //  If details are hidden for this member in the innermost column/row
2414                             //  dimension (the orientation of the reference dimension), show an
2415                             //  error value.
2416                             //  - If the no-details dimension is the reference dimension, its
2417                             //    members will be skipped when finding the previous/next member,
2418                             //    so there must be no results for its members.
2419                             //  - If the no-details dimension is outside of the reference dimension,
2420                             //    no calculation in the reference dimension is possible.
2421                             //  - Otherwise, the error isn't strictly necessary, but shown for
2422                             //    consistency.
2423 
2424                             bool bInnerNoDetails = bRefDimInCol ? HasHiddenDetails() :
2425                                                  ( !bRefDimInRow || rRowParent.HasHiddenDetails() );
2426                             if ( bInnerNoDetails )
2427                             {
2428                                 pSelectDim = nullptr;
2429                                 bNoDetailsInRef = true;         // show error, not empty
2430                             }
2431                         }
2432 
2433                         if ( !bRefDimInCol && !bRefDimInRow )   // invalid dimension specified
2434                             bNoDetailsInRef = true;             // pSelectDim is then already NULL
2435 
2436                         //  get the member for the reference item and do the calculation
2437 
2438                         if ( bRunningTotal )
2439                         {
2440                             // running total in (dimension) -> find first existing member
2441 
2442                             if ( pSelectDim )
2443                             {
2444                                 ScDPDataMember* pSelectMember;
2445                                 if ( bRefDimInCol )
2446                                     pSelectMember = ScDPResultDimension::GetColReferenceMember( nullptr, nullptr,
2447                                                                     nColPos, rRunning );
2448                                 else
2449                                 {
2450                                     const sal_Int32* pRowSorted = rRowSorted.data();
2451                                     const sal_Int32* pColSorted = rColSorted.data();
2452                                     pRowSorted += nRowPos + 1; // including the reference dimension
2453                                     pSelectMember = pSelectDim->GetRowReferenceMember(
2454                                         nullptr, nullptr, pRowSorted, pColSorted);
2455                                 }
2456 
2457                                 if ( pSelectMember )
2458                                 {
2459                                     // The running total is kept as the auxiliary value in
2460                                     // the first available member for the reference dimension.
2461                                     // Members are visited in final order, so each one's result
2462                                     // can be used and then modified.
2463 
2464                                     ScDPAggData* pSelectData = pSelectMember->
2465                                                     GetAggData( nMemberMeasure, aLocalSubState );
2466                                     if ( pSelectData )
2467                                     {
2468                                         double fTotal = pSelectData->GetAuxiliary();
2469                                         fTotal += pAggData->GetResult();
2470                                         pSelectData->SetAuxiliary( fTotal );
2471                                         pAggData->SetResult( fTotal );
2472                                         pAggData->SetEmpty(false);              // always display
2473                                     }
2474                                 }
2475                                 else
2476                                     pAggData->SetError();
2477                             }
2478                             else if (bNoDetailsInRef)
2479                                 pAggData->SetError();
2480                             else
2481                                 pAggData->SetEmpty(true);                       // empty (dim set to 0 above)
2482                         }
2483                         else
2484                         {
2485                             // difference/percentage -> find specified member
2486 
2487                             if ( pSelectDim )
2488                             {
2489                                 OUString aRefItemName = aReferenceValue.ReferenceItemName;
2490                                 ScDPRelativePos aRefItemPos( 0, nRelativeDir );     // nBasePos is modified later
2491 
2492                                 const OUString* pRefName = nullptr;
2493                                 const ScDPRelativePos* pRefPos = nullptr;
2494                                 if ( bRelative )
2495                                     pRefPos = &aRefItemPos;
2496                                 else
2497                                     pRefName = &aRefItemName;
2498 
2499                                 ScDPDataMember* pSelectMember;
2500                                 if ( bRefDimInCol )
2501                                 {
2502                                     aRefItemPos.nBasePos = rColVisible[nColPos];    // without sort order applied
2503                                     pSelectMember = ScDPResultDimension::GetColReferenceMember( pRefPos, pRefName,
2504                                                                     nColPos, rRunning );
2505                                 }
2506                                 else
2507                                 {
2508                                     aRefItemPos.nBasePos = rRowVisible[nRowPos];    // without sort order applied
2509                                     const sal_Int32* pRowSorted = rRowSorted.data();
2510                                     const sal_Int32* pColSorted = rColSorted.data();
2511                                     pRowSorted += nRowPos + 1; // including the reference dimension
2512                                     pSelectMember = pSelectDim->GetRowReferenceMember(
2513                                         pRefPos, pRefName, pRowSorted, pColSorted);
2514                                 }
2515 
2516                                 // difference or perc.difference is empty for the reference item itself
2517                                 if ( pSelectMember == this &&
2518                                      eRefType != sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE )
2519                                 {
2520                                     pAggData->SetEmpty(true);
2521                                 }
2522                                 else if ( pSelectMember )
2523                                 {
2524                                     const ScDPAggData* pOtherAggData = pSelectMember->
2525                                                         GetConstAggData( nMemberMeasure, aLocalSubState );
2526                                     OSL_ENSURE( pOtherAggData, "no agg data" );
2527                                     if ( pOtherAggData )
2528                                     {
2529                                         // Reference member may be visited before or after this one,
2530                                         // so the auxiliary value is used for the original result.
2531 
2532                                         double fOtherResult = pOtherAggData->GetAuxiliary();
2533                                         double fThisResult = pAggData->GetResult();
2534                                         bool bError = false;
2535                                         switch ( eRefType )
2536                                         {
2537                                             case sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE:
2538                                                 fThisResult = fThisResult - fOtherResult;
2539                                                 break;
2540                                             case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE:
2541                                                 if ( fOtherResult == 0.0 )
2542                                                     bError = true;
2543                                                 else
2544                                                     fThisResult = fThisResult / fOtherResult;
2545                                                 break;
2546                                             case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE:
2547                                                 if ( fOtherResult == 0.0 )
2548                                                     bError = true;
2549                                                 else
2550                                                     fThisResult = ( fThisResult - fOtherResult ) / fOtherResult;
2551                                                 break;
2552                                             default:
2553                                                 OSL_FAIL("invalid calculation type");
2554                                         }
2555                                         if ( bError )
2556                                         {
2557                                             pAggData->SetError();
2558                                         }
2559                                         else
2560                                         {
2561                                             pAggData->SetResult(fThisResult);
2562                                             pAggData->SetEmpty(false);              // always display
2563                                         }
2564                                         //TODO: errors in data?
2565                                     }
2566                                 }
2567                                 else if (bRelative && !bNoDetailsInRef)
2568                                     pAggData->SetEmpty(true);                   // empty
2569                                 else
2570                                     pAggData->SetError();                       // error
2571                             }
2572                             else if (bNoDetailsInRef)
2573                                 pAggData->SetError();                           // error
2574                             else
2575                                 pAggData->SetEmpty(true);                       // empty
2576                         }
2577                     }
2578                     else if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE ||
2579                               eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE ||
2580                               eRefType == sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE ||
2581                               eRefType == sheet::DataPilotFieldReferenceType::INDEX )
2582                     {
2583 
2584                         //  set total values when they are encountered (always before their use)
2585 
2586                         ScDPAggData* pColTotalData = pRefMember->GetColTotal( nMemberMeasure );
2587                         ScDPAggData* pRowTotalData = rTotals.GetRowTotal( nMemberMeasure );
2588                         ScDPAggData* pGrandTotalData = rTotals.GetGrandTotal( nMemberMeasure );
2589 
2590                         double fTotalValue = pAggData->HasError() ? 0 : pAggData->GetResult();
2591 
2592                         if ( bIsRoot && rTotals.IsInColRoot() && pGrandTotalData )
2593                             pGrandTotalData->SetAuxiliary( fTotalValue );
2594 
2595                         if ( bIsRoot && pRowTotalData )
2596                             pRowTotalData->SetAuxiliary( fTotalValue );
2597 
2598                         if ( rTotals.IsInColRoot() && pColTotalData )
2599                             pColTotalData->SetAuxiliary( fTotalValue );
2600 
2601                         //  find relation to total values
2602 
2603                         switch ( eRefType )
2604                         {
2605                             case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE:
2606                             case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE:
2607                             case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE:
2608                                 {
2609                                     double nTotal;
2610                                     if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE )
2611                                         nTotal = pRowTotalData ? pRowTotalData->GetAuxiliary() : 0.0;
2612                                     else if ( eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE )
2613                                         nTotal = pColTotalData ? pColTotalData->GetAuxiliary() : 0.0;
2614                                     else
2615                                         nTotal = pGrandTotalData ? pGrandTotalData->GetAuxiliary() : 0.0;
2616 
2617                                     if ( nTotal == 0.0 )
2618                                         pAggData->SetError();
2619                                     else
2620                                         pAggData->SetResult( pAggData->GetResult() / nTotal );
2621                                 }
2622                                 break;
2623                             case sheet::DataPilotFieldReferenceType::INDEX:
2624                                 {
2625                                     double nColTotal = pColTotalData ? pColTotalData->GetAuxiliary() : 0.0;
2626                                     double nRowTotal = pRowTotalData ? pRowTotalData->GetAuxiliary() : 0.0;
2627                                     double nGrandTotal = pGrandTotalData ? pGrandTotalData->GetAuxiliary() : 0.0;
2628                                     if ( nRowTotal == 0.0 || nColTotal == 0.0 )
2629                                         pAggData->SetError();
2630                                     else
2631                                         pAggData->SetResult(
2632                                             ( pAggData->GetResult() * nGrandTotal ) /
2633                                             ( nRowTotal * nColTotal ) );
2634                                 }
2635                                 break;
2636                         }
2637                     }
2638                 }
2639             }
2640         }
2641     }
2642 
2643     if ( bHasChild )    // child dimension must be processed last, so the row total is known
2644     {
2645         if ( pDataChild )
2646             pDataChild->UpdateRunningTotals( pRefChild, nMeasure,
2647                                             bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent );
2648     }
2649 }
2650 
2651 #if DUMP_PIVOT_TABLE
DumpState(const ScDPResultMember * pRefMember,ScDocument * pDoc,ScAddress & rPos) const2652 void ScDPDataMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
2653 {
2654     dumpRow("ScDPDataMember", GetName(), &aAggregate, pDoc, rPos);
2655     SCROW nStartRow = rPos.Row();
2656 
2657     const ScDPDataDimension* pDataChild = GetChildDimension();
2658     const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension();
2659     if ( pDataChild && pRefChild )
2660         pDataChild->DumpState( pRefChild, pDoc, rPos );
2661 
2662     indent(pDoc, nStartRow, rPos);
2663 }
2664 
Dump(int nIndent) const2665 void ScDPDataMember::Dump(int nIndent) const
2666 {
2667     std::string aIndent(nIndent*2, ' ');
2668     std::cout << aIndent << "-- data member '"
2669         << (pResultMember ? pResultMember->GetName() : OUString()) << "'" << std::endl;
2670     for (const ScDPAggData* pAgg = &aAggregate; pAgg; pAgg = pAgg->GetExistingChild())
2671         pAgg->Dump(nIndent+1);
2672 
2673     if (pChildDimension)
2674         pChildDimension->Dump(nIndent+1);
2675 }
2676 #endif
2677 
2678 //  Helper class to select the members to include in
2679 //  ScDPResultDimension::InitFrom or LateInitFrom if groups are used
2680 
2681 namespace {
2682 
2683 class ScDPGroupCompare
2684 {
2685 private:
2686     const ScDPResultData* pResultData;
2687     const ScDPInitState& rInitState;
2688     tools::Long                 nDimSource;
2689     bool                 bIncludeAll;
2690     bool                 bIsBase;
2691     tools::Long                 nGroupBase;
2692 public:
2693             ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, tools::Long nDimension );
2694 
IsIncluded(const ScDPMember & rMember)2695     bool    IsIncluded( const ScDPMember& rMember )     { return bIncludeAll || TestIncluded( rMember ); }
2696     bool    TestIncluded( const ScDPMember& rMember );
2697 };
2698 
2699 }
2700 
ScDPGroupCompare(const ScDPResultData * pData,const ScDPInitState & rState,tools::Long nDimension)2701 ScDPGroupCompare::ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, tools::Long nDimension ) :
2702     pResultData( pData ),
2703     rInitState( rState ),
2704     nDimSource( nDimension )
2705 {
2706     bIsBase = pResultData->IsBaseForGroup( nDimSource );
2707     nGroupBase = pResultData->GetGroupBase( nDimSource );      //TODO: get together in one call?
2708 
2709     // if bIncludeAll is set, TestIncluded doesn't need to be called
2710     bIncludeAll = !( bIsBase || nGroupBase >= 0 );
2711 }
2712 
TestIncluded(const ScDPMember & rMember)2713 bool ScDPGroupCompare::TestIncluded( const ScDPMember& rMember )
2714 {
2715     bool bInclude = true;
2716     if ( bIsBase )
2717     {
2718         // need to check all previous groups
2719         //TODO: get array of groups (or indexes) before loop?
2720         ScDPItemData aMemberData(rMember.FillItemData());
2721 
2722         const std::vector<ScDPInitState::Member>& rMemStates = rInitState.GetMembers();
2723         bInclude = std::all_of(rMemStates.begin(), rMemStates.end(),
2724             [this, &aMemberData](const ScDPInitState::Member& rMem) {
2725                 return (pResultData->GetGroupBase(rMem.mnSrcIndex) != nDimSource)
2726                     || pResultData->IsInGroup(rMem.mnNameIndex, rMem.mnSrcIndex, aMemberData, nDimSource);
2727             });
2728     }
2729     else if ( nGroupBase >= 0 )
2730     {
2731         // base isn't used in preceding fields
2732         // -> look for other groups using the same base
2733 
2734         //TODO: get array of groups (or indexes) before loop?
2735         ScDPItemData aMemberData(rMember.FillItemData());
2736         const std::vector<ScDPInitState::Member>& rMemStates = rInitState.GetMembers();
2737         bInclude = std::all_of(rMemStates.begin(), rMemStates.end(),
2738             [this, &aMemberData](const ScDPInitState::Member& rMem) {
2739                 // coverity[copy_paste_error : FALSE] - same base (hierarchy between
2740                 // the two groups is irrelevant)
2741                 return (pResultData->GetGroupBase(rMem.mnSrcIndex) != nGroupBase)
2742                     || pResultData->HasCommonElement(rMem.mnNameIndex, rMem.mnSrcIndex, aMemberData, nDimSource);
2743             });
2744     }
2745 
2746     return bInclude;
2747 }
2748 
ScDPResultDimension(const ScDPResultData * pData)2749 ScDPResultDimension::ScDPResultDimension( const ScDPResultData* pData ) :
2750     pResultData( pData ),
2751     nSortMeasure( 0 ),
2752     bIsDataLayout( false ),
2753     bSortByData( false ),
2754     bSortAscending( false ),
2755     bAutoShow( false ),
2756     bAutoTopItems( false ),
2757     bInitialized( false ),
2758     nAutoMeasure( 0 ),
2759     nAutoCount( 0 )
2760 {
2761 }
2762 
~ScDPResultDimension()2763 ScDPResultDimension::~ScDPResultDimension()
2764 {
2765 }
2766 
FindMember(SCROW iData) const2767 ScDPResultMember *ScDPResultDimension::FindMember(  SCROW  iData ) const
2768 {
2769     if( bIsDataLayout )
2770         return maMemberArray[0].get();
2771 
2772     MemberHash::const_iterator aRes = maMemberHash.find( iData );
2773     if( aRes != maMemberHash.end()) {
2774         if ( aRes->second->IsNamedItem( iData ) )
2775             return aRes->second;
2776         OSL_FAIL("problem!  hash result is not the same as IsNamedItem");
2777     }
2778 
2779     unsigned int i;
2780     unsigned int nCount = maMemberArray.size();
2781     for( i = 0; i < nCount ; i++ )
2782     {
2783         ScDPResultMember* pResultMember = maMemberArray[i].get();
2784         if ( pResultMember->IsNamedItem( iData ) )
2785             return pResultMember;
2786     }
2787     return nullptr;
2788 }
2789 
InitFrom(const vector<ScDPDimension * > & ppDim,const vector<ScDPLevel * > & ppLev,size_t nPos,ScDPInitState & rInitState,bool bInitChild)2790 void ScDPResultDimension::InitFrom(
2791     const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev,
2792     size_t nPos, ScDPInitState& rInitState,  bool bInitChild )
2793 {
2794     if (nPos >= ppDim.size() || nPos >= ppLev.size())
2795     {
2796         bInitialized = true;
2797         return;
2798     }
2799 
2800     ScDPDimension* pThisDim = ppDim[nPos];
2801     ScDPLevel* pThisLevel = ppLev[nPos];
2802 
2803     if (!pThisDim || !pThisLevel)
2804     {
2805         bInitialized = true;
2806         return;
2807     }
2808 
2809     bIsDataLayout = pThisDim->getIsDataLayoutDimension();   // member
2810     aDimensionName = pThisDim->getName();                   // member
2811 
2812     // Check the autoshow setting.  If it's enabled, store the settings.
2813     const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow();
2814     if ( rAutoInfo.IsEnabled )
2815     {
2816         bAutoShow     = true;
2817         bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP );
2818         nAutoMeasure  = pThisLevel->GetAutoMeasure();
2819         nAutoCount    = rAutoInfo.ItemCount;
2820     }
2821 
2822     // Check the sort info, and store the settings if appropriate.
2823     const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo();
2824     if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA )
2825     {
2826         bSortByData = true;
2827         bSortAscending = rSortInfo.IsAscending;
2828         nSortMeasure = pThisLevel->GetSortMeasure();
2829     }
2830 
2831     // global order is used to initialize aMembers, so it doesn't have to be looked at later
2832     const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder();
2833 
2834     tools::Long nDimSource = pThisDim->GetDimension();     //TODO: check GetSourceDim?
2835     ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
2836 
2837     // Now, go through all members and initialize them.
2838     ScDPMembers* pMembers = pThisLevel->GetMembersObject();
2839     tools::Long nMembCount = pMembers->getCount();
2840     for ( tools::Long i=0; i<nMembCount; i++ )
2841     {
2842         tools::Long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
2843 
2844         ScDPMember* pMember = pMembers->getByIndex(nSorted);
2845         if ( aCompare.IsIncluded( *pMember ) )
2846         {
2847             ScDPParentDimData aData( i, pThisDim, pThisLevel, pMember);
2848             ScDPResultMember* pNew = AddMember( aData );
2849 
2850             rInitState.AddMember(nDimSource, pNew->GetDataId());
2851             pNew->InitFrom( ppDim, ppLev, nPos+1, rInitState, bInitChild  );
2852             rInitState.RemoveMember();
2853         }
2854     }
2855     bInitialized = true;
2856 }
2857 
LateInitFrom(LateInitParams & rParams,const vector<SCROW> & pItemData,size_t nPos,ScDPInitState & rInitState)2858 void ScDPResultDimension::LateInitFrom(
2859     LateInitParams& rParams, const vector<SCROW>& pItemData, size_t nPos, ScDPInitState& rInitState)
2860 {
2861     if ( rParams.IsEnd( nPos ) )
2862         return;
2863     if (nPos >= pItemData.size())
2864     {
2865         SAL_WARN("sc.core", "pos " << nPos << ", but vector size is " << pItemData.size());
2866         return;
2867     }
2868     SCROW rThisData = pItemData[nPos];
2869     ScDPDimension* pThisDim = rParams.GetDim( nPos );
2870     ScDPLevel* pThisLevel = rParams.GetLevel( nPos );
2871 
2872     if (!pThisDim || !pThisLevel)
2873         return;
2874 
2875     tools::Long nDimSource = pThisDim->GetDimension();     //TODO: check GetSourceDim?
2876 
2877     bool bShowEmpty = pThisLevel->getShowEmpty();
2878 
2879     if ( !bInitialized )
2880     { // init some values
2881         //  create all members at the first call (preserve order)
2882         bIsDataLayout = pThisDim->getIsDataLayoutDimension();
2883         aDimensionName = pThisDim->getName();
2884 
2885         const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow();
2886         if ( rAutoInfo.IsEnabled )
2887         {
2888             bAutoShow     = true;
2889             bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP );
2890             nAutoMeasure  = pThisLevel->GetAutoMeasure();
2891             nAutoCount    = rAutoInfo.ItemCount;
2892         }
2893 
2894         const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo();
2895         if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA )
2896         {
2897             bSortByData = true;
2898             bSortAscending = rSortInfo.IsAscending;
2899             nSortMeasure = pThisLevel->GetSortMeasure();
2900         }
2901     }
2902 
2903     bool bLateInitAllMembers=  bIsDataLayout || rParams.GetInitAllChild() || bShowEmpty;
2904 
2905     if ( !bLateInitAllMembers )
2906     {
2907         ResultMembers& rMembers = pResultData->GetDimResultMembers(nDimSource, pThisDim, pThisLevel);
2908         bLateInitAllMembers = rMembers.IsHasHideDetailsMembers();
2909 
2910         SAL_INFO("sc.core", aDimensionName << (rMembers.IsHasHideDetailsMembers() ? " HasHideDetailsMembers" : ""));
2911 
2912         rMembers.SetHasHideDetailsMembers( false );
2913     }
2914 
2915     bool bNewAllMembers = (!rParams.IsRow()) ||  nPos == 0 || bLateInitAllMembers;
2916 
2917     if (bNewAllMembers )
2918     {
2919       // global order is used to initialize aMembers, so it doesn't have to be looked at later
2920         if ( !bInitialized )
2921         { //init all members
2922             const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder();
2923 
2924             ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
2925             ScDPMembers* pMembers = pThisLevel->GetMembersObject();
2926             tools::Long nMembCount = pMembers->getCount();
2927             for ( tools::Long i=0; i<nMembCount; i++ )
2928             {
2929                 tools::Long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
2930 
2931                 ScDPMember* pMember = pMembers->getByIndex(nSorted);
2932                 if ( aCompare.IsIncluded( *pMember ) )
2933                 { // add all members
2934                     ScDPParentDimData aData( i, pThisDim, pThisLevel, pMember );
2935                     AddMember( aData );
2936                 }
2937             }
2938             bInitialized = true;    // don't call again, even if no members were included
2939         }
2940         //  initialize only specific member (or all if "show empty" flag is set)
2941         if ( bLateInitAllMembers  )
2942         {
2943             tools::Long nCount = maMemberArray.size();
2944             for (tools::Long i=0; i<nCount; i++)
2945             {
2946                 ScDPResultMember* pResultMember = maMemberArray[i].get();
2947 
2948                 // check show empty
2949                 bool bAllChildren = false;
2950                 if( bShowEmpty )
2951                 {
2952                     bAllChildren = !pResultMember->IsNamedItem( rThisData );
2953                 }
2954                 rParams.SetInitAllChildren( bAllChildren );
2955                 rInitState.AddMember( nDimSource,  pResultMember->GetDataId() );
2956                 pResultMember->LateInitFrom( rParams, pItemData, nPos+1, rInitState );
2957                 rInitState.RemoveMember();
2958             }
2959         }
2960         else
2961         {
2962             ScDPResultMember* pResultMember = FindMember( rThisData );
2963             if( nullptr != pResultMember )
2964             {
2965                 rInitState.AddMember( nDimSource,  pResultMember->GetDataId() );
2966                 pResultMember->LateInitFrom( rParams, pItemData, nPos+1, rInitState );
2967                 rInitState.RemoveMember();
2968             }
2969         }
2970     }
2971     else
2972         InitWithMembers( rParams, pItemData, nPos, rInitState );
2973 }
2974 
GetSize(tools::Long nMeasure) const2975 tools::Long ScDPResultDimension::GetSize(tools::Long nMeasure) const
2976 {
2977     tools::Long nTotal = 0;
2978     tools::Long nMemberCount = maMemberArray.size();
2979     if (bIsDataLayout)
2980     {
2981         OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
2982                     "DataLayout dimension twice?");
2983         //  repeat first member...
2984         nTotal = nMemberCount * maMemberArray[0]->GetSize(0);   // all measures have equal size
2985     }
2986     else
2987     {
2988         //  add all members
2989         for (tools::Long nMem=0; nMem<nMemberCount; nMem++)
2990             nTotal += maMemberArray[nMem]->GetSize(nMeasure);
2991     }
2992     return nTotal;
2993 }
2994 
IsValidEntry(const vector<SCROW> & aMembers) const2995 bool ScDPResultDimension::IsValidEntry( const vector< SCROW >& aMembers ) const
2996 {
2997     if (aMembers.empty())
2998         return false;
2999 
3000     const ScDPResultMember* pMember = FindMember( aMembers[0] );
3001     if ( nullptr != pMember )
3002         return pMember->IsValidEntry( aMembers );
3003 #if OSL_DEBUG_LEVEL > 0
3004     SAL_INFO("sc.core", "IsValidEntry: Member not found, DimNam = "  << GetName());
3005 #endif
3006     return false;
3007 }
3008 
ProcessData(const vector<SCROW> & aMembers,const ScDPResultDimension * pDataDim,const vector<SCROW> & aDataMembers,const vector<ScDPValue> & aValues) const3009 void ScDPResultDimension::ProcessData( const vector< SCROW >& aMembers,
3010                                        const ScDPResultDimension* pDataDim,
3011                                        const vector< SCROW >& aDataMembers,
3012                                        const vector<ScDPValue>& aValues ) const
3013 {
3014     if (aMembers.empty())
3015         return;
3016 
3017     ScDPResultMember* pMember = FindMember( aMembers[0] );
3018     if ( nullptr != pMember )
3019     {
3020         vector<SCROW> aChildMembers;
3021         if (aMembers.size() > 1)
3022         {
3023             vector<SCROW>::const_iterator itr = aMembers.begin();
3024             aChildMembers.insert(aChildMembers.begin(), ++itr, aMembers.end());
3025         }
3026         pMember->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues );
3027         return;
3028     }
3029 
3030     OSL_FAIL("ProcessData: Member not found");
3031 }
3032 
FillMemberResults(uno::Sequence<sheet::MemberResult> * pSequences,tools::Long nStart,tools::Long nMeasure)3033 void ScDPResultDimension::FillMemberResults( uno::Sequence<sheet::MemberResult>* pSequences,
3034                                                 tools::Long nStart, tools::Long nMeasure )
3035 {
3036     tools::Long nPos = nStart;
3037     tools::Long nCount = maMemberArray.size();
3038 
3039     for (tools::Long i=0; i<nCount; i++)
3040     {
3041         tools::Long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
3042 
3043         ScDPResultMember* pMember = maMemberArray[nSorted].get();
3044         //  in data layout dimension, use first member with different measures/names
3045         if ( bIsDataLayout )
3046         {
3047             bool bTotalResult = false;
3048             OUString aMbrName = pResultData->GetMeasureDimensionName( nSorted );
3049             OUString aMbrCapt = pResultData->GetMeasureString( nSorted, false, SUBTOTAL_FUNC_NONE, bTotalResult );
3050             maMemberArray[0]->FillMemberResults( pSequences, nPos, nSorted, false, &aMbrName, &aMbrCapt );
3051         }
3052         else if ( pMember->IsVisible() )
3053         {
3054             pMember->FillMemberResults( pSequences, nPos, nMeasure, false, nullptr, nullptr );
3055         }
3056         // nPos is modified
3057     }
3058 }
3059 
FillDataResults(const ScDPResultMember * pRefMember,ScDPResultFilterContext & rFilterCxt,uno::Sequence<uno::Sequence<sheet::DataResult>> & rSequence,tools::Long nMeasure) const3060 void ScDPResultDimension::FillDataResults(
3061     const ScDPResultMember* pRefMember, ScDPResultFilterContext& rFilterCxt,
3062     uno::Sequence< uno::Sequence<sheet::DataResult> >& rSequence, tools::Long nMeasure) const
3063 {
3064     FilterStack aFilterStack(rFilterCxt.maFilters);
3065     aFilterStack.pushDimName(GetName(), bIsDataLayout);
3066 
3067     tools::Long nMemberMeasure = nMeasure;
3068     tools::Long nCount = maMemberArray.size();
3069     for (tools::Long i=0; i<nCount; i++)
3070     {
3071         tools::Long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
3072 
3073         const ScDPResultMember* pMember;
3074         if (bIsDataLayout)
3075         {
3076             OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3077                         "DataLayout dimension twice?");
3078             pMember = maMemberArray[0].get();
3079             nMemberMeasure = nSorted;
3080         }
3081         else
3082             pMember = maMemberArray[nSorted].get();
3083 
3084         if ( pMember->IsVisible() )
3085             pMember->FillDataResults(pRefMember, rFilterCxt, rSequence, nMemberMeasure);
3086     }
3087 }
3088 
UpdateDataResults(const ScDPResultMember * pRefMember,tools::Long nMeasure) const3089 void ScDPResultDimension::UpdateDataResults( const ScDPResultMember* pRefMember, tools::Long nMeasure ) const
3090 {
3091     tools::Long nMemberMeasure = nMeasure;
3092     tools::Long nCount = maMemberArray.size();
3093     for (tools::Long i=0; i<nCount; i++)
3094     {
3095         const ScDPResultMember* pMember;
3096         if (bIsDataLayout)
3097         {
3098             OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3099                         "DataLayout dimension twice?");
3100             pMember = maMemberArray[0].get();
3101             nMemberMeasure = i;
3102         }
3103         else
3104             pMember = maMemberArray[i].get();
3105 
3106         if ( pMember->IsVisible() )
3107             pMember->UpdateDataResults( pRefMember, nMemberMeasure );
3108     }
3109 }
3110 
SortMembers(ScDPResultMember * pRefMember)3111 void ScDPResultDimension::SortMembers( ScDPResultMember* pRefMember )
3112 {
3113     tools::Long nCount = maMemberArray.size();
3114 
3115     if ( bSortByData )
3116     {
3117         // sort members
3118 
3119         OSL_ENSURE( aMemberOrder.empty(), "sort twice?" );
3120         aMemberOrder.resize( nCount );
3121         for (tools::Long nPos=0; nPos<nCount; nPos++)
3122             aMemberOrder[nPos] = nPos;
3123 
3124         ScDPRowMembersOrder aComp( *this, nSortMeasure, bSortAscending );
3125         ::std::sort( aMemberOrder.begin(), aMemberOrder.end(), aComp );
3126     }
3127 
3128     // handle children
3129 
3130     // for data layout, call only once - sorting measure is always taken from settings
3131     tools::Long nLoopCount = bIsDataLayout ? 1 : nCount;
3132     for (tools::Long i=0; i<nLoopCount; i++)
3133     {
3134         ScDPResultMember* pMember = maMemberArray[i].get();
3135         if ( pMember->IsVisible() )
3136             pMember->SortMembers( pRefMember );
3137     }
3138 }
3139 
DoAutoShow(ScDPResultMember * pRefMember)3140 void ScDPResultDimension::DoAutoShow( ScDPResultMember* pRefMember )
3141 {
3142     tools::Long nCount = maMemberArray.size();
3143 
3144     // handle children first, before changing the visible state
3145 
3146     // for data layout, call only once - sorting measure is always taken from settings
3147     tools::Long nLoopCount = bIsDataLayout ? 1 : nCount;
3148     for (tools::Long i=0; i<nLoopCount; i++)
3149     {
3150         ScDPResultMember* pMember = maMemberArray[i].get();
3151         if ( pMember->IsVisible() )
3152             pMember->DoAutoShow( pRefMember );
3153     }
3154 
3155     if ( !(bAutoShow && nAutoCount > 0 && nAutoCount < nCount) )
3156         return;
3157 
3158     // establish temporary order, hide remaining members
3159 
3160     ScMemberSortOrder aAutoOrder;
3161     aAutoOrder.resize( nCount );
3162     tools::Long nPos;
3163     for (nPos=0; nPos<nCount; nPos++)
3164         aAutoOrder[nPos] = nPos;
3165 
3166     ScDPRowMembersOrder aComp( *this, nAutoMeasure, !bAutoTopItems );
3167     ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp );
3168 
3169     // look for equal values to the last included one
3170 
3171     tools::Long nIncluded = nAutoCount;
3172     const ScDPResultMember* pMember1 = maMemberArray[aAutoOrder[nIncluded - 1]].get();
3173     const ScDPDataMember* pDataMember1 = pMember1->IsVisible() ? pMember1->GetDataRoot() : nullptr;
3174     bool bContinue = true;
3175     while ( bContinue )
3176     {
3177         bContinue = false;
3178         if ( nIncluded < nCount )
3179         {
3180             const ScDPResultMember* pMember2 = maMemberArray[aAutoOrder[nIncluded]].get();
3181             const ScDPDataMember* pDataMember2 = pMember2->IsVisible() ? pMember2->GetDataRoot() : nullptr;
3182 
3183             if ( lcl_IsEqual( pDataMember1, pDataMember2, nAutoMeasure ) )
3184             {
3185                 ++nIncluded;                // include more members if values are equal
3186                 bContinue = true;
3187             }
3188         }
3189     }
3190 
3191     // hide the remaining members
3192 
3193     for (nPos = nIncluded; nPos < nCount; nPos++)
3194     {
3195         ScDPResultMember* pMember = maMemberArray[aAutoOrder[nPos]].get();
3196         pMember->SetAutoHidden();
3197     }
3198 }
3199 
ResetResults()3200 void ScDPResultDimension::ResetResults()
3201 {
3202     tools::Long nCount = maMemberArray.size();
3203     for (tools::Long i=0; i<nCount; i++)
3204     {
3205         // sort order doesn't matter
3206         ScDPResultMember* pMember = maMemberArray[bIsDataLayout ? 0 : i].get();
3207         pMember->ResetResults();
3208     }
3209 }
3210 
GetSortedIndex(tools::Long nUnsorted) const3211 tools::Long ScDPResultDimension::GetSortedIndex( tools::Long nUnsorted ) const
3212 {
3213     return aMemberOrder.empty() ? nUnsorted : aMemberOrder[nUnsorted];
3214 }
3215 
UpdateRunningTotals(const ScDPResultMember * pRefMember,tools::Long nMeasure,ScDPRunningTotalState & rRunning,ScDPRowTotals & rTotals) const3216 void ScDPResultDimension::UpdateRunningTotals( const ScDPResultMember* pRefMember, tools::Long nMeasure,
3217                                                 ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const
3218 {
3219     const ScDPResultMember* pMember;
3220     tools::Long nMemberMeasure = nMeasure;
3221     tools::Long nCount = maMemberArray.size();
3222     for (tools::Long i=0; i<nCount; i++)
3223     {
3224         tools::Long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i];
3225 
3226         if (bIsDataLayout)
3227         {
3228             OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3229                         "DataLayout dimension twice?");
3230             pMember = maMemberArray[0].get();
3231             nMemberMeasure = nSorted;
3232         }
3233         else
3234             pMember = maMemberArray[nSorted].get();
3235 
3236         if ( pMember->IsVisible() )
3237         {
3238             if ( bIsDataLayout )
3239                 rRunning.AddRowIndex( 0, 0 );
3240             else
3241                 rRunning.AddRowIndex( i, nSorted );
3242             pMember->UpdateRunningTotals( pRefMember, nMemberMeasure, rRunning, rTotals );
3243             rRunning.RemoveRowIndex();
3244         }
3245     }
3246 }
3247 
GetRowReferenceMember(const ScDPRelativePos * pRelativePos,const OUString * pName,const sal_Int32 * pRowIndexes,const sal_Int32 * pColIndexes) const3248 ScDPDataMember* ScDPResultDimension::GetRowReferenceMember(
3249     const ScDPRelativePos* pRelativePos, const OUString* pName,
3250     const sal_Int32* pRowIndexes, const sal_Int32* pColIndexes ) const
3251 {
3252     // get named, previous/next, or first member of this dimension (first existing if pRelativePos and pName are NULL)
3253 
3254     OSL_ENSURE( pRelativePos == nullptr || pName == nullptr, "can't use position and name" );
3255 
3256     ScDPDataMember* pColMember = nullptr;
3257 
3258     bool bFirstExisting = ( pRelativePos == nullptr && pName == nullptr );
3259     tools::Long nMemberCount = maMemberArray.size();
3260     tools::Long nMemberIndex = 0;      // unsorted
3261     tools::Long nDirection = 1;        // forward if no relative position is used
3262     if ( pRelativePos )
3263     {
3264         nDirection = pRelativePos->nDirection;
3265         nMemberIndex = pRelativePos->nBasePos + nDirection;     // bounds are handled below
3266 
3267         OSL_ENSURE( nDirection == 1 || nDirection == -1, "Direction must be 1 or -1" );
3268     }
3269     else if ( pName )
3270     {
3271         // search for named member
3272 
3273         const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)].get();
3274 
3275         //TODO: use ScDPItemData, as in ScDPDimension::IsValidPage?
3276         while ( pRowMember && pRowMember->GetName() != *pName )
3277         {
3278             ++nMemberIndex;
3279             if ( nMemberIndex < nMemberCount )
3280                 pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)].get();
3281             else
3282                 pRowMember = nullptr;
3283         }
3284     }
3285 
3286     bool bContinue = true;
3287     while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nMemberCount )
3288     {
3289         const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)].get();
3290 
3291         // get child members by given indexes
3292 
3293         const sal_Int32* pNextRowIndex = pRowIndexes;
3294         while ( *pNextRowIndex >= 0 && pRowMember )
3295         {
3296             const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension();
3297             if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() )
3298                 pRowMember = pRowChild->GetMember( *pNextRowIndex );
3299             else
3300                 pRowMember = nullptr;
3301             ++pNextRowIndex;
3302         }
3303 
3304         if ( pRowMember && pRelativePos )
3305         {
3306             //  Skip the member if it has hidden details
3307             //  (because when looking for the details, it is skipped, too).
3308             //  Also skip if the member is invisible because it has no data,
3309             //  for consistent ordering.
3310             if ( pRowMember->HasHiddenDetails() || !pRowMember->IsVisible() )
3311                 pRowMember = nullptr;
3312         }
3313 
3314         if ( pRowMember )
3315         {
3316             pColMember = pRowMember->GetDataRoot();
3317 
3318             const sal_Int32* pNextColIndex = pColIndexes;
3319             while ( *pNextColIndex >= 0 && pColMember )
3320             {
3321                 ScDPDataDimension* pColChild = pColMember->GetChildDimension();
3322                 if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
3323                     pColMember = pColChild->GetMember( *pNextColIndex );
3324                 else
3325                     pColMember = nullptr;
3326                 ++pNextColIndex;
3327             }
3328         }
3329 
3330         // continue searching only if looking for first existing or relative position
3331         bContinue = ( pColMember == nullptr && ( bFirstExisting || pRelativePos ) );
3332         nMemberIndex += nDirection;
3333     }
3334 
3335     return pColMember;
3336 }
3337 
GetColReferenceMember(const ScDPRelativePos * pRelativePos,const OUString * pName,sal_Int32 nRefDimPos,const ScDPRunningTotalState & rRunning)3338 ScDPDataMember* ScDPResultDimension::GetColReferenceMember(
3339     const ScDPRelativePos* pRelativePos, const OUString* pName,
3340     sal_Int32 nRefDimPos, const ScDPRunningTotalState& rRunning )
3341 {
3342     OSL_ENSURE( pRelativePos == nullptr || pName == nullptr, "can't use position and name" );
3343 
3344     const sal_Int32* pColIndexes = rRunning.GetColSorted().data();
3345     const sal_Int32* pRowIndexes = rRunning.GetRowSorted().data();
3346 
3347     // get own row member using all indexes
3348 
3349     const ScDPResultMember* pRowMember = rRunning.GetRowResRoot();
3350     ScDPDataMember* pColMember = nullptr;
3351 
3352     const sal_Int32* pNextRowIndex = pRowIndexes;
3353     while ( *pNextRowIndex >= 0 && pRowMember )
3354     {
3355         const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension();
3356         if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() )
3357             pRowMember = pRowChild->GetMember( *pNextRowIndex );
3358         else
3359             pRowMember = nullptr;
3360         ++pNextRowIndex;
3361     }
3362 
3363     // get column (data) members before the reference field
3364     //TODO: pass rRowParent from ScDPDataMember::UpdateRunningTotals instead
3365 
3366     if ( pRowMember )
3367     {
3368         pColMember = pRowMember->GetDataRoot();
3369 
3370         const sal_Int32* pNextColIndex = pColIndexes;
3371         sal_Int32 nColSkipped = 0;
3372         while ( *pNextColIndex >= 0 && pColMember && nColSkipped < nRefDimPos )
3373         {
3374             ScDPDataDimension* pColChild = pColMember->GetChildDimension();
3375             if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
3376                 pColMember = pColChild->GetMember( *pNextColIndex );
3377             else
3378                 pColMember = nullptr;
3379             ++pNextColIndex;
3380             ++nColSkipped;
3381         }
3382     }
3383 
3384     // get column member for the reference field
3385 
3386     if ( pColMember )
3387     {
3388         ScDPDataDimension* pReferenceDim = pColMember->GetChildDimension();
3389         if ( pReferenceDim )
3390         {
3391             tools::Long nReferenceCount = pReferenceDim->GetMemberCount();
3392 
3393             bool bFirstExisting = ( pRelativePos == nullptr && pName == nullptr );
3394             tools::Long nMemberIndex = 0;      // unsorted
3395             tools::Long nDirection = 1;        // forward if no relative position is used
3396             pColMember = nullptr;          // don't use parent dimension's member if none found
3397             if ( pRelativePos )
3398             {
3399                 nDirection = pRelativePos->nDirection;
3400                 nMemberIndex = pRelativePos->nBasePos + nDirection;     // bounds are handled below
3401             }
3402             else if ( pName )
3403             {
3404                 // search for named member
3405 
3406                 pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
3407 
3408                 //TODO: use ScDPItemData, as in ScDPDimension::IsValidPage?
3409                 while ( pColMember && pColMember->GetName() != *pName )
3410                 {
3411                     ++nMemberIndex;
3412                     if ( nMemberIndex < nReferenceCount )
3413                         pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
3414                     else
3415                         pColMember = nullptr;
3416                 }
3417             }
3418 
3419             bool bContinue = true;
3420             while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nReferenceCount )
3421             {
3422                 pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) );
3423 
3424                 // get column members below the reference field
3425 
3426                 const sal_Int32* pNextColIndex = pColIndexes + nRefDimPos + 1;
3427                 while ( *pNextColIndex >= 0 && pColMember )
3428                 {
3429                     ScDPDataDimension* pColChild = pColMember->GetChildDimension();
3430                     if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() )
3431                         pColMember = pColChild->GetMember( *pNextColIndex );
3432                     else
3433                         pColMember = nullptr;
3434                     ++pNextColIndex;
3435                 }
3436 
3437                 if ( pColMember && pRelativePos )
3438                 {
3439                     //  Skip the member if it has hidden details
3440                     //  (because when looking for the details, it is skipped, too).
3441                     //  Also skip if the member is invisible because it has no data,
3442                     //  for consistent ordering.
3443                     if ( pColMember->HasHiddenDetails() || !pColMember->IsVisible() )
3444                         pColMember = nullptr;
3445                 }
3446 
3447                 // continue searching only if looking for first existing or relative position
3448                 bContinue = ( pColMember == nullptr && ( bFirstExisting || pRelativePos ) );
3449                 nMemberIndex += nDirection;
3450             }
3451         }
3452         else
3453             pColMember = nullptr;
3454     }
3455 
3456     return pColMember;
3457 }
3458 
3459 #if DUMP_PIVOT_TABLE
DumpState(const ScDPResultMember * pRefMember,ScDocument * pDoc,ScAddress & rPos) const3460 void ScDPResultDimension::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const
3461 {
3462     OUString aDimName = bIsDataLayout ? OUString("(data layout)") : GetName();
3463     dumpRow("ScDPResultDimension", aDimName, nullptr, pDoc, rPos);
3464 
3465     SCROW nStartRow = rPos.Row();
3466 
3467     tools::Long nCount = bIsDataLayout ? 1 : maMemberArray.size();
3468     for (tools::Long i=0; i<nCount; i++)
3469     {
3470         const ScDPResultMember* pMember = maMemberArray[i].get();
3471         pMember->DumpState( pRefMember, pDoc, rPos );
3472     }
3473 
3474     indent(pDoc, nStartRow, rPos);
3475 }
3476 
Dump(int nIndent) const3477 void ScDPResultDimension::Dump(int nIndent) const
3478 {
3479     std::string aIndent(nIndent*2, ' ');
3480     std::cout << aIndent << "-- dimension '" << GetName() << "'" << std::endl;
3481     for (const auto& rxMember : maMemberArray)
3482     {
3483         const ScDPResultMember* p = rxMember.get();
3484         p->Dump(nIndent+1);
3485     }
3486 }
3487 #endif
3488 
GetMemberCount() const3489 tools::Long ScDPResultDimension::GetMemberCount() const
3490 {
3491     return maMemberArray.size();
3492 }
3493 
GetMember(tools::Long n) const3494 const ScDPResultMember* ScDPResultDimension::GetMember(tools::Long n) const
3495 {
3496     return maMemberArray[n].get();
3497 }
GetMember(tools::Long n)3498 ScDPResultMember* ScDPResultDimension::GetMember(tools::Long n)
3499 {
3500     return maMemberArray[n].get();
3501 }
3502 
GetFirstChildDimension() const3503 ScDPResultDimension* ScDPResultDimension::GetFirstChildDimension() const
3504 {
3505     if ( !maMemberArray.empty() )
3506         return maMemberArray[0]->GetChildDimension();
3507     else
3508         return nullptr;
3509 }
3510 
FillVisibilityData(ScDPResultVisibilityData & rData) const3511 void ScDPResultDimension::FillVisibilityData(ScDPResultVisibilityData& rData) const
3512 {
3513     if (IsDataLayout())
3514         return;
3515 
3516     for (const auto& rxMember : maMemberArray)
3517     {
3518         ScDPResultMember* pMember = rxMember.get();
3519         if (pMember->IsValid())
3520         {
3521             ScDPItemData aItem(pMember->FillItemData());
3522             rData.addVisibleMember(GetName(), aItem);
3523             pMember->FillVisibilityData(rData);
3524         }
3525     }
3526 }
3527 
ScDPDataDimension(const ScDPResultData * pData)3528 ScDPDataDimension::ScDPDataDimension( const ScDPResultData* pData ) :
3529     pResultData( pData ),
3530     pResultDimension( nullptr ),
3531     bIsDataLayout( false )
3532 {
3533 }
3534 
~ScDPDataDimension()3535 ScDPDataDimension::~ScDPDataDimension()
3536 {
3537 }
3538 
InitFrom(const ScDPResultDimension * pDim)3539 void ScDPDataDimension::InitFrom( const ScDPResultDimension* pDim )
3540 {
3541     if (!pDim)
3542         return;
3543 
3544     pResultDimension = pDim;
3545     bIsDataLayout = pDim->IsDataLayout();
3546 
3547     // Go through all result members under the given result dimension, and
3548     // create a new data member instance for each result member.
3549     tools::Long nCount = pDim->GetMemberCount();
3550     for (tools::Long i=0; i<nCount; i++)
3551     {
3552         const ScDPResultMember* pResMem = pDim->GetMember(i);
3553 
3554         ScDPDataMember* pNew = new ScDPDataMember( pResultData, pResMem );
3555         maMembers.emplace_back( pNew);
3556 
3557         if ( !pResultData->IsLateInit() )
3558         {
3559             //  with LateInit, pResMem hasn't necessarily been initialized yet,
3560             //  so InitFrom for the new result member is called from its ProcessData method
3561 
3562             const ScDPResultDimension* pChildDim = pResMem->GetChildDimension();
3563             if ( pChildDim )
3564                 pNew->InitFrom( pChildDim );
3565         }
3566     }
3567 }
3568 
ProcessData(const vector<SCROW> & aDataMembers,const vector<ScDPValue> & aValues,const ScDPSubTotalState & rSubState)3569 void ScDPDataDimension::ProcessData( const vector< SCROW >& aDataMembers, const vector<ScDPValue>& aValues,
3570                                      const ScDPSubTotalState& rSubState )
3571 {
3572     // the ScDPItemData array must contain enough entries for all dimensions - this isn't checked
3573 
3574     tools::Long nCount = maMembers.size();
3575     for (tools::Long i=0; i<nCount; i++)
3576     {
3577         ScDPDataMember* pMember = maMembers[static_cast<sal_uInt16>(i)].get();
3578 
3579         // always first member for data layout dim
3580         if ( bIsDataLayout || ( !aDataMembers.empty() && pMember->IsNamedItem(aDataMembers[0]) ) )
3581         {
3582             vector<SCROW> aChildDataMembers;
3583             if (aDataMembers.size() > 1)
3584             {
3585                 vector<SCROW>::const_iterator itr = aDataMembers.begin();
3586                 aChildDataMembers.insert(aChildDataMembers.begin(), ++itr, aDataMembers.end());
3587             }
3588             pMember->ProcessData( aChildDataMembers, aValues, rSubState );
3589             return;
3590         }
3591     }
3592 
3593     OSL_FAIL("ProcessData: Member not found");
3594 }
3595 
FillDataRow(const ScDPResultDimension * pRefDim,ScDPResultFilterContext & rFilterCxt,uno::Sequence<sheet::DataResult> & rSequence,tools::Long nMeasure,bool bIsSubTotalRow,const ScDPSubTotalState & rSubState) const3596 void ScDPDataDimension::FillDataRow(
3597     const ScDPResultDimension* pRefDim, ScDPResultFilterContext& rFilterCxt,
3598     uno::Sequence<sheet::DataResult>& rSequence, tools::Long nMeasure, bool bIsSubTotalRow,
3599     const ScDPSubTotalState& rSubState) const
3600 {
3601     OUString aDimName;
3602     bool bDataLayout = false;
3603     if (pResultDimension)
3604     {
3605         aDimName = pResultDimension->GetName();
3606         bDataLayout = pResultDimension->IsDataLayout();
3607     }
3608 
3609     FilterStack aFilterStack(rFilterCxt.maFilters);
3610     aFilterStack.pushDimName(aDimName, bDataLayout);
3611 
3612     OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3613     OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3614 
3615     const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
3616 
3617     tools::Long nMemberMeasure = nMeasure;
3618     tools::Long nCount = maMembers.size();
3619     for (tools::Long i=0; i<nCount; i++)
3620     {
3621         tools::Long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i];
3622 
3623         tools::Long nMemberPos = nSorted;
3624         if (bIsDataLayout)
3625         {
3626             OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3627                         "DataLayout dimension twice?");
3628             nMemberPos = 0;
3629             nMemberMeasure = nSorted;
3630         }
3631 
3632         const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
3633         if ( pRefMember->IsVisible() )  //TODO: here or in ScDPDataMember::FillDataRow ???
3634         {
3635             const ScDPDataMember* pDataMember = maMembers[static_cast<sal_uInt16>(nMemberPos)].get();
3636             pDataMember->FillDataRow(pRefMember, rFilterCxt, rSequence, nMemberMeasure, bIsSubTotalRow, rSubState);
3637         }
3638     }
3639 }
3640 
UpdateDataRow(const ScDPResultDimension * pRefDim,tools::Long nMeasure,bool bIsSubTotalRow,const ScDPSubTotalState & rSubState) const3641 void ScDPDataDimension::UpdateDataRow( const ScDPResultDimension* pRefDim,
3642                                     tools::Long nMeasure, bool bIsSubTotalRow,
3643                                     const ScDPSubTotalState& rSubState ) const
3644 {
3645     OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3646     OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3647 
3648     tools::Long nMemberMeasure = nMeasure;
3649     tools::Long nCount = maMembers.size();
3650     for (tools::Long i=0; i<nCount; i++)
3651     {
3652         tools::Long nMemberPos = i;
3653         if (bIsDataLayout)
3654         {
3655             OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3656                         "DataLayout dimension twice?");
3657             nMemberPos = 0;
3658             nMemberMeasure = i;
3659         }
3660 
3661         // Calculate must be called even if the member is not visible (for use as reference value)
3662         const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
3663         ScDPDataMember* pDataMember = maMembers[static_cast<sal_uInt16>(nMemberPos)].get();
3664         pDataMember->UpdateDataRow( pRefMember, nMemberMeasure, bIsSubTotalRow, rSubState );
3665     }
3666 }
3667 
SortMembers(ScDPResultDimension * pRefDim)3668 void ScDPDataDimension::SortMembers( ScDPResultDimension* pRefDim )
3669 {
3670     tools::Long nCount = maMembers.size();
3671 
3672     if ( pRefDim->IsSortByData() )
3673     {
3674         // sort members
3675 
3676         ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
3677         OSL_ENSURE( rMemberOrder.empty(), "sort twice?" );
3678         rMemberOrder.resize( nCount );
3679         for (tools::Long nPos=0; nPos<nCount; nPos++)
3680             rMemberOrder[nPos] = nPos;
3681 
3682         ScDPColMembersOrder aComp( *this, pRefDim->GetSortMeasure(), pRefDim->IsSortAscending() );
3683         ::std::sort( rMemberOrder.begin(), rMemberOrder.end(), aComp );
3684     }
3685 
3686     // handle children
3687 
3688     OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3689     OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3690 
3691     // for data layout, call only once - sorting measure is always taken from settings
3692     tools::Long nLoopCount = bIsDataLayout ? 1 : nCount;
3693     for (tools::Long i=0; i<nLoopCount; i++)
3694     {
3695         ScDPResultMember* pRefMember = pRefDim->GetMember(i);
3696         if ( pRefMember->IsVisible() )  //TODO: here or in ScDPDataMember ???
3697         {
3698             ScDPDataMember* pDataMember = maMembers[static_cast<sal_uInt16>(i)].get();
3699             pDataMember->SortMembers( pRefMember );
3700         }
3701     }
3702 }
3703 
DoAutoShow(ScDPResultDimension * pRefDim)3704 void ScDPDataDimension::DoAutoShow( ScDPResultDimension* pRefDim )
3705 {
3706     tools::Long nCount = maMembers.size();
3707 
3708     // handle children first, before changing the visible state
3709 
3710     OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3711     OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3712 
3713     // for data layout, call only once - sorting measure is always taken from settings
3714     tools::Long nLoopCount = bIsDataLayout ? 1 : nCount;
3715     for (tools::Long i=0; i<nLoopCount; i++)
3716     {
3717         ScDPResultMember* pRefMember = pRefDim->GetMember(i);
3718         if ( pRefMember->IsVisible() )  //TODO: here or in ScDPDataMember ???
3719         {
3720             ScDPDataMember* pDataMember = maMembers[i].get();
3721             pDataMember->DoAutoShow( pRefMember );
3722         }
3723     }
3724 
3725     if ( !(pRefDim->IsAutoShow() && pRefDim->GetAutoCount() > 0 && pRefDim->GetAutoCount() < nCount) )
3726         return;
3727 
3728     // establish temporary order, hide remaining members
3729 
3730     ScMemberSortOrder aAutoOrder;
3731     aAutoOrder.resize( nCount );
3732     tools::Long nPos;
3733     for (nPos=0; nPos<nCount; nPos++)
3734         aAutoOrder[nPos] = nPos;
3735 
3736     ScDPColMembersOrder aComp( *this, pRefDim->GetAutoMeasure(), !pRefDim->IsAutoTopItems() );
3737     ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp );
3738 
3739     // look for equal values to the last included one
3740 
3741     tools::Long nIncluded = pRefDim->GetAutoCount();
3742     ScDPDataMember* pDataMember1 = maMembers[aAutoOrder[nIncluded - 1]].get();
3743     if ( !pDataMember1->IsVisible() )
3744         pDataMember1 = nullptr;
3745     bool bContinue = true;
3746     while ( bContinue )
3747     {
3748         bContinue = false;
3749         if ( nIncluded < nCount )
3750         {
3751             ScDPDataMember* pDataMember2 = maMembers[aAutoOrder[nIncluded]].get();
3752             if ( !pDataMember2->IsVisible() )
3753                 pDataMember2 = nullptr;
3754 
3755             if ( lcl_IsEqual( pDataMember1, pDataMember2, pRefDim->GetAutoMeasure() ) )
3756             {
3757                 ++nIncluded;                // include more members if values are equal
3758                 bContinue = true;
3759             }
3760         }
3761     }
3762 
3763     // hide the remaining members
3764 
3765     for (nPos = nIncluded; nPos < nCount; nPos++)
3766     {
3767         ScDPResultMember* pMember = pRefDim->GetMember(aAutoOrder[nPos]);
3768         pMember->SetAutoHidden();
3769     }
3770 }
3771 
ResetResults()3772 void ScDPDataDimension::ResetResults()
3773 {
3774     tools::Long nCount = maMembers.size();
3775     for (tools::Long i=0; i<nCount; i++)
3776     {
3777         //  sort order doesn't matter
3778 
3779         tools::Long nMemberPos = bIsDataLayout ? 0 : i;
3780         ScDPDataMember* pDataMember = maMembers[nMemberPos].get();
3781         pDataMember->ResetResults();
3782     }
3783 }
3784 
GetSortedIndex(tools::Long nUnsorted) const3785 tools::Long ScDPDataDimension::GetSortedIndex( tools::Long nUnsorted ) const
3786 {
3787     if (!pResultDimension)
3788        return nUnsorted;
3789 
3790     const ScMemberSortOrder& rMemberOrder = pResultDimension->GetMemberOrder();
3791     return rMemberOrder.empty() ? nUnsorted : rMemberOrder[nUnsorted];
3792 }
3793 
UpdateRunningTotals(const ScDPResultDimension * pRefDim,tools::Long nMeasure,bool bIsSubTotalRow,const ScDPSubTotalState & rSubState,ScDPRunningTotalState & rRunning,ScDPRowTotals & rTotals,const ScDPResultMember & rRowParent) const3794 void ScDPDataDimension::UpdateRunningTotals( const ScDPResultDimension* pRefDim,
3795                                     tools::Long nMeasure, bool bIsSubTotalRow,
3796                                     const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning,
3797                                     ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent ) const
3798 {
3799     OSL_ENSURE( pRefDim && static_cast<size_t>(pRefDim->GetMemberCount()) == maMembers.size(), "dimensions don't match" );
3800     OSL_ENSURE( pRefDim == pResultDimension, "wrong dim" );
3801 
3802     tools::Long nMemberMeasure = nMeasure;
3803     tools::Long nCount = maMembers.size();
3804     for (tools::Long i=0; i<nCount; i++)
3805     {
3806         const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder();
3807         tools::Long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i];
3808 
3809         tools::Long nMemberPos = nSorted;
3810         if (bIsDataLayout)
3811         {
3812             OSL_ENSURE(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1,
3813                         "DataLayout dimension twice?");
3814             nMemberPos = 0;
3815             nMemberMeasure = nSorted;
3816         }
3817 
3818         const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos);
3819         if ( pRefMember->IsVisible() )
3820         {
3821             if ( bIsDataLayout )
3822                 rRunning.AddColIndex( 0, 0 );
3823             else
3824                 rRunning.AddColIndex( i, nSorted );
3825 
3826             ScDPDataMember* pDataMember = maMembers[nMemberPos].get();
3827             pDataMember->UpdateRunningTotals(
3828                 pRefMember, nMemberMeasure, bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent);
3829 
3830             rRunning.RemoveColIndex();
3831         }
3832     }
3833 }
3834 
3835 #if DUMP_PIVOT_TABLE
DumpState(const ScDPResultDimension * pRefDim,ScDocument * pDoc,ScAddress & rPos) const3836 void ScDPDataDimension::DumpState( const ScDPResultDimension* pRefDim, ScDocument* pDoc, ScAddress& rPos ) const
3837 {
3838     OUString aDimName = bIsDataLayout ? OUString("(data layout)") : OUString("(unknown)");
3839     dumpRow("ScDPDataDimension", aDimName, nullptr, pDoc, rPos);
3840 
3841     SCROW nStartRow = rPos.Row();
3842 
3843     tools::Long nCount = bIsDataLayout ? 1 : maMembers.size();
3844     for (tools::Long i=0; i<nCount; i++)
3845     {
3846         const ScDPResultMember* pRefMember = pRefDim->GetMember(i);
3847         const ScDPDataMember* pDataMember = maMembers[i].get();
3848         pDataMember->DumpState( pRefMember, pDoc, rPos );
3849     }
3850 
3851     indent(pDoc, nStartRow, rPos);
3852 }
3853 
Dump(int nIndent) const3854 void ScDPDataDimension::Dump(int nIndent) const
3855 {
3856     std::string aIndent(nIndent*2, ' ');
3857     std::cout << aIndent << "-- data dimension '"
3858         << (pResultDimension ? pResultDimension->GetName() : OUString()) << "'" << std::endl;
3859     for (auto& rxMember : maMembers)
3860         rxMember->Dump(nIndent+1);
3861 }
3862 #endif
3863 
GetMemberCount() const3864 tools::Long ScDPDataDimension::GetMemberCount() const
3865 {
3866     return maMembers.size();
3867 }
3868 
GetMember(tools::Long n) const3869 const ScDPDataMember* ScDPDataDimension::GetMember(tools::Long n) const
3870 {
3871     return maMembers[n].get();
3872 }
3873 
GetMember(tools::Long n)3874 ScDPDataMember* ScDPDataDimension::GetMember(tools::Long n)
3875 {
3876     return maMembers[n].get();
3877 }
3878 
ScDPResultVisibilityData(ScDPSource * pSource)3879 ScDPResultVisibilityData::ScDPResultVisibilityData(
3880  ScDPSource* pSource) :
3881     mpSource(pSource)
3882 {
3883 }
3884 
~ScDPResultVisibilityData()3885 ScDPResultVisibilityData::~ScDPResultVisibilityData()
3886 {
3887 }
3888 
addVisibleMember(const OUString & rDimName,const ScDPItemData & rMemberItem)3889 void ScDPResultVisibilityData::addVisibleMember(const OUString& rDimName, const ScDPItemData& rMemberItem)
3890 {
3891     DimMemberType::iterator itr = maDimensions.find(rDimName);
3892     if (itr == maDimensions.end())
3893     {
3894         pair<DimMemberType::iterator, bool> r = maDimensions.emplace(
3895             rDimName, VisibleMemberType());
3896 
3897         if (!r.second)
3898             // insertion failed.
3899             return;
3900 
3901         itr = r.first;
3902     }
3903     VisibleMemberType& rMem = itr->second;
3904     rMem.insert(rMemberItem);
3905 }
3906 
fillFieldFilters(vector<ScDPFilteredCache::Criterion> & rFilters) const3907 void ScDPResultVisibilityData::fillFieldFilters(vector<ScDPFilteredCache::Criterion>& rFilters) const
3908 {
3909     typedef std::unordered_map<OUString, tools::Long> FieldNameMapType;
3910     FieldNameMapType aFieldNames;
3911     ScDPTableData* pData = mpSource->GetData();
3912     sal_Int32 nColumnCount = pData->GetColumnCount();
3913     for (sal_Int32 i = 0; i < nColumnCount; ++i)
3914     {
3915         aFieldNames.emplace(pData->getDimensionName(i), i);
3916     }
3917 
3918     const ScDPDimensions* pDims = mpSource->GetDimensionsObject();
3919     for (const auto& [rDimName, rMem] : maDimensions)
3920     {
3921         ScDPFilteredCache::Criterion aCri;
3922         FieldNameMapType::const_iterator itrField = aFieldNames.find(rDimName);
3923         if (itrField == aFieldNames.end())
3924             // This should never happen!
3925             continue;
3926 
3927         tools::Long nDimIndex = itrField->second;
3928         aCri.mnFieldIndex = static_cast<sal_Int32>(nDimIndex);
3929         aCri.mpFilter = std::make_shared<ScDPFilteredCache::GroupFilter>();
3930 
3931         ScDPFilteredCache::GroupFilter* pGrpFilter =
3932             static_cast<ScDPFilteredCache::GroupFilter*>(aCri.mpFilter.get());
3933 
3934         for (const ScDPItemData& rMemItem : rMem)
3935         {
3936             pGrpFilter->addMatchItem(rMemItem);
3937         }
3938 
3939         ScDPDimension* pDim = pDims->getByIndex(nDimIndex);
3940         ScDPMembers* pMembers = pDim->GetHierarchiesObject()->getByIndex(0)->
3941             GetLevelsObject()->getByIndex(0)->GetMembersObject();
3942         if (pGrpFilter->getMatchItemCount() < o3tl::make_unsigned(pMembers->getCount()))
3943             rFilters.push_back(aCri);
3944     }
3945 }
3946 
operator ()(const ScDPItemData & r) const3947 size_t ScDPResultVisibilityData::MemberHash::operator() (const ScDPItemData& r) const
3948 {
3949     if (r.IsValue())
3950         return static_cast<size_t>(::rtl::math::approxFloor(r.GetValue()));
3951     else
3952         return r.GetString().hashCode();
3953 }
GetDataId() const3954 SCROW ScDPResultMember::GetDataId( ) const
3955 {
3956     const ScDPMember*   pMemberDesc = GetDPMember();
3957     if (pMemberDesc)
3958         return  pMemberDesc->GetItemDataId();
3959     return -1;
3960 }
3961 
AddMember(const ScDPParentDimData & aData)3962 ScDPResultMember* ScDPResultDimension::AddMember(const ScDPParentDimData &aData )
3963 {
3964     ScDPResultMember* pMember = new ScDPResultMember( pResultData, aData );
3965     SCROW   nDataIndex = pMember->GetDataId();
3966     maMemberArray.emplace_back( pMember );
3967 
3968     maMemberHash.emplace( nDataIndex, pMember );
3969     return pMember;
3970 }
3971 
InsertMember(const ScDPParentDimData * pMemberData)3972 ScDPResultMember* ScDPResultDimension::InsertMember(const ScDPParentDimData *pMemberData)
3973 {
3974     SCROW  nInsert = 0;
3975     if ( !lcl_SearchMember( maMemberArray, pMemberData->mnOrder , nInsert ) )
3976     {
3977         ScDPResultMember* pNew = new ScDPResultMember( pResultData, *pMemberData );
3978         maMemberArray.emplace( maMemberArray.begin()+nInsert, pNew );
3979 
3980         SCROW   nDataIndex = pMemberData->mpMemberDesc->GetItemDataId();
3981         maMemberHash.emplace( nDataIndex, pNew );
3982         return pNew;
3983     }
3984     return maMemberArray[ nInsert ].get();
3985 }
3986 
InitWithMembers(LateInitParams & rParams,const std::vector<SCROW> & pItemData,size_t nPos,ScDPInitState & rInitState)3987 void ScDPResultDimension::InitWithMembers(
3988     LateInitParams& rParams, const std::vector<SCROW>& pItemData, size_t nPos,
3989     ScDPInitState& rInitState)
3990 {
3991     if ( rParams.IsEnd( nPos ) )
3992         return;
3993     ScDPDimension* pThisDim        = rParams.GetDim( nPos );
3994     ScDPLevel*        pThisLevel      = rParams.GetLevel( nPos );
3995     SCROW             nDataID         = pItemData[nPos];
3996 
3997     if (!(pThisDim && pThisLevel))
3998         return;
3999 
4000     tools::Long nDimSource = pThisDim->GetDimension();     //TODO: check GetSourceDim?
4001 
4002     //  create all members at the first call (preserve order)
4003     ResultMembers& rMembers = pResultData->GetDimResultMembers(nDimSource, pThisDim, pThisLevel);
4004     ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource );
4005     //  initialize only specific member (or all if "show empty" flag is set)
4006     ScDPResultMember* pResultMember = nullptr;
4007     if ( bInitialized  )
4008         pResultMember = FindMember( nDataID );
4009     else
4010         bInitialized = true;
4011 
4012     if ( pResultMember == nullptr )
4013     { //only insert found item
4014         const ScDPParentDimData* pMemberData = rMembers.FindMember( nDataID );
4015         if ( pMemberData && aCompare.IsIncluded( *( pMemberData->mpMemberDesc ) ) )
4016             pResultMember = InsertMember( pMemberData );
4017     }
4018     if ( pResultMember )
4019     {
4020         rInitState.AddMember( nDimSource, pResultMember->GetDataId()  );
4021         pResultMember->LateInitFrom(rParams, pItemData, nPos+1, rInitState);
4022         rInitState.RemoveMember();
4023     }
4024 }
4025 
ScDPParentDimData()4026 ScDPParentDimData::ScDPParentDimData() :
4027     mnOrder(-1), mpParentDim(nullptr), mpParentLevel(nullptr), mpMemberDesc(nullptr) {}
4028 
ScDPParentDimData(SCROW nIndex,const ScDPDimension * pDim,const ScDPLevel * pLev,const ScDPMember * pMember)4029 ScDPParentDimData::ScDPParentDimData(
4030     SCROW nIndex, const ScDPDimension* pDim, const ScDPLevel* pLev, const ScDPMember* pMember) :
4031     mnOrder(nIndex), mpParentDim(pDim), mpParentLevel(pLev), mpMemberDesc(pMember) {}
4032 
FindMember(SCROW nIndex) const4033 const ScDPParentDimData* ResultMembers::FindMember( SCROW nIndex ) const
4034 {
4035     auto aRes = maMemberHash.find( nIndex );
4036     if( aRes != maMemberHash.end()) {
4037         if ( aRes->second.mpMemberDesc && aRes->second.mpMemberDesc->GetItemDataId()==nIndex )
4038             return &aRes->second;
4039     }
4040     return nullptr;
4041 }
InsertMember(const ScDPParentDimData & rNew)4042 void  ResultMembers::InsertMember(  const ScDPParentDimData& rNew )
4043 {
4044     if ( !rNew.mpMemberDesc->getShowDetails() )
4045         mbHasHideDetailsMember = true;
4046     maMemberHash.emplace( rNew.mpMemberDesc->GetItemDataId(), rNew );
4047 }
4048 
ResultMembers()4049 ResultMembers::ResultMembers():
4050     mbHasHideDetailsMember( false )
4051 {
4052 }
~ResultMembers()4053 ResultMembers::~ResultMembers()
4054 {
4055 }
4056 
LateInitParams(const vector<ScDPDimension * > & ppDim,const vector<ScDPLevel * > & ppLev,bool bRow)4057 LateInitParams::LateInitParams(
4058     const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev, bool bRow ) :
4059     mppDim( ppDim ),
4060     mppLev( ppLev ),
4061     mbRow( bRow ),
4062     mbInitChild( true ),
4063     mbAllChildren( false )
4064 {
4065 }
4066 
~LateInitParams()4067 LateInitParams::~LateInitParams()
4068 {
4069 }
4070 
IsEnd(size_t nPos) const4071 bool LateInitParams::IsEnd( size_t nPos ) const
4072 {
4073     return nPos >= mppDim.size();
4074 }
4075 
CheckShowEmpty(bool bShow)4076 void ScDPResultDimension::CheckShowEmpty( bool bShow )
4077 {
4078     tools::Long nCount = maMemberArray.size();
4079 
4080     for (tools::Long i=0; i<nCount; i++)
4081     {
4082         ScDPResultMember* pMember = maMemberArray.at(i).get();
4083         pMember->CheckShowEmpty(bShow);
4084     }
4085 
4086 }
4087 
CheckShowEmpty(bool bShow)4088 void ScDPResultMember::CheckShowEmpty( bool bShow )
4089 {
4090     if (bHasElements)
4091     {
4092         ScDPResultDimension* pChildDim = GetChildDimension();
4093         if (pChildDim)
4094             pChildDim->CheckShowEmpty();
4095     }
4096     else if (IsValid() && bInitialized)
4097     {
4098         bShow = bShow || (GetParentLevel() && GetParentLevel()->getShowEmpty());
4099         if (bShow)
4100         {
4101             SetHasElements();
4102             ScDPResultDimension* pChildDim = GetChildDimension();
4103             if (pChildDim)
4104                 pChildDim->CheckShowEmpty(true);
4105         }
4106     }
4107 }
4108 
4109 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
4110