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