LibreOffice Module sc (master)  1
dptabres.cxx
Go to the documentation of this file.
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 <limits>
44 #include <memory>
45 #include <unordered_map>
46 
47 #include <com/sun/star/sheet/DataResultFlags.hpp>
48 #include <com/sun/star/sheet/MemberResultFlags.hpp>
49 #include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
50 #include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
51 #include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
52 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
53 #include <com/sun/star/sheet/GeneralFunction2.hpp>
54 
55 using namespace com::sun::star;
56 using ::std::vector;
57 using ::std::pair;
58 using ::com::sun::star::uno::Sequence;
59 
60 namespace {
61 
62 const char* aFuncStrIds[] = // matching enum ScSubTotalFunc
63 {
64  nullptr, // SUBTOTAL_FUNC_NONE
65  STR_FUN_TEXT_AVG, // SUBTOTAL_FUNC_AVE
66  STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT
67  STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT2
68  STR_FUN_TEXT_MAX, // SUBTOTAL_FUNC_MAX
69  STR_FUN_TEXT_MIN, // SUBTOTAL_FUNC_MIN
70  STR_FUN_TEXT_PRODUCT, // SUBTOTAL_FUNC_PROD
71  STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STD
72  STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STDP
73  STR_FUN_TEXT_SUM, // SUBTOTAL_FUNC_SUM
74  STR_FUN_TEXT_VAR, // SUBTOTAL_FUNC_VAR
75  STR_FUN_TEXT_VAR, // SUBTOTAL_FUNC_VARP
76  STR_FUN_TEXT_MEDIAN, // SUBTOTAL_FUNC_MED
77  nullptr // SUBTOTAL_FUNC_SELECTION_COUNT - not used for pivot table
78 };
79 
80 bool lcl_SearchMember( const std::vector<std::unique_ptr<ScDPResultMember>>& list, SCROW nOrder, SCROW& rIndex)
81 {
82  bool bFound = false;
83  SCROW nLo = 0;
84  SCROW nHi = list.size() - 1;
85  SCROW nIndex;
86  while (nLo <= nHi)
87  {
88  nIndex = (nLo + nHi) / 2;
89  if ( list[nIndex]->GetOrder() < nOrder )
90  nLo = nIndex + 1;
91  else
92  {
93  nHi = nIndex - 1;
94  if ( list[nIndex]->GetOrder() == nOrder )
95  {
96  bFound = true;
97  nLo = nIndex;
98  }
99  }
100  }
101  rIndex = nLo;
102  return bFound;
103 }
104 
105 class FilterStack
106 {
107  std::vector<ScDPResultFilter>& mrFilters;
108 public:
109  explicit FilterStack(std::vector<ScDPResultFilter>& rFilters) : mrFilters(rFilters) {}
110 
111  void pushDimName(const OUString& rName, bool bDataLayout)
112  {
113  mrFilters.emplace_back(rName, bDataLayout);
114  }
115 
116  void pushDimValue(const OUString& rValueName, const OUString& rValue)
117  {
118  ScDPResultFilter& rFilter = mrFilters.back();
119  rFilter.maValueName = rValueName;
120  rFilter.maValue = rValue;
121  rFilter.mbHasValue = true;
122  }
123 
124  ~FilterStack()
125  {
126  ScDPResultFilter& rFilter = mrFilters.back();
127  if (rFilter.mbHasValue)
128  rFilter.mbHasValue = false;
129  else
130  mrFilters.pop_back();
131  }
132 };
133 
134 // function objects for sorting of the column and row members:
135 
136 class ScDPRowMembersOrder
137 {
138  ScDPResultDimension& rDimension;
139  tools::Long nMeasure;
140  bool bAscending;
141 
142 public:
143  ScDPRowMembersOrder( ScDPResultDimension& rDim, tools::Long nM, bool bAsc ) :
144  rDimension(rDim),
145  nMeasure(nM),
146  bAscending(bAsc)
147  {}
148 
149  bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
150 };
151 
152 class ScDPColMembersOrder
153 {
154  ScDPDataDimension& rDimension;
155  tools::Long nMeasure;
156  bool bAscending;
157 
158 public:
159  ScDPColMembersOrder( ScDPDataDimension& rDim, tools::Long nM, bool bAsc ) :
160  rDimension(rDim),
161  nMeasure(nM),
162  bAscending(bAsc)
163  {}
164 
165  bool operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const;
166 };
167 
168 }
169 
170 static bool lcl_IsLess( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, tools::Long nMeasure, bool bAscending )
171 {
172  // members can be NULL if used for rows
173 
174  ScDPSubTotalState aEmptyState;
175  const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : nullptr;
176  const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : nullptr;
177 
178  bool bError1 = pAgg1 && pAgg1->HasError();
179  bool bError2 = pAgg2 && pAgg2->HasError();
180  if ( bError1 )
181  return false; // errors are always sorted at the end
182  else if ( bError2 )
183  return true; // errors are always sorted at the end
184  else
185  {
186  double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0
187  double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0;
188 
189  // compare values
190  // don't have to check approxEqual, as this is the only sort criterion
191 
192  return bAscending ? ( fVal1 < fVal2 ) : ( fVal1 > fVal2 );
193  }
194 }
195 
196 static bool lcl_IsEqual( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, tools::Long nMeasure )
197 {
198  // members can be NULL if used for rows
199 
200  ScDPSubTotalState aEmptyState;
201  const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : nullptr;
202  const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : nullptr;
203 
204  bool bError1 = pAgg1 && pAgg1->HasError();
205  bool bError2 = pAgg2 && pAgg2->HasError();
206  if ( bError1 )
207  {
208  if ( bError2 )
209  return true; // equal
210  else
211  return false;
212  }
213  else if ( bError2 )
214  return false;
215  else
216  {
217  double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0
218  double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0;
219 
220  // compare values
221  // this is used to find equal data at the end of the AutoShow range, so approxEqual must be used
222 
223  return rtl::math::approxEqual( fVal1, fVal2 );
224  }
225 }
226 
227 bool ScDPRowMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
228 {
229  const ScDPResultMember* pMember1 = rDimension.GetMember(nIndex1);
230  const ScDPResultMember* pMember2 = rDimension.GetMember(nIndex2);
231 
232 // make the hide item to the largest order.
233  if ( !pMember1->IsVisible() || !pMember2->IsVisible() )
234  return pMember1->IsVisible();
235  const ScDPDataMember* pDataMember1 = pMember1->GetDataRoot() ;
236  const ScDPDataMember* pDataMember2 = pMember2->GetDataRoot();
237  // GetDataRoot can be NULL if there was no data.
238  // IsVisible == false can happen after AutoShow.
239  return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending );
240 }
241 
242 bool ScDPColMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const
243 {
244  const ScDPDataMember* pDataMember1 = rDimension.GetMember(nIndex1);
245  const ScDPDataMember* pDataMember2 = rDimension.GetMember(nIndex2);
246  bool bHide1 = pDataMember1 && !pDataMember1->IsVisible();
247  bool bHide2 = pDataMember2 && !pDataMember2->IsVisible();
248  if ( bHide1 || bHide2 )
249  return !bHide1;
250  return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending );
251 }
252 
254  mnSrcIndex(nSrcIndex), mnNameIndex(nNameIndex) {}
255 
256 void ScDPInitState::AddMember( tools::Long nSourceIndex, SCROW nMember )
257 {
258  maMembers.emplace_back(nSourceIndex, nMember);
259 }
260 
262 {
263  OSL_ENSURE(!maMembers.empty(), "ScDPInitState::RemoveMember: Attempt to remove member while empty.");
264  if (!maMembers.empty())
265  maMembers.pop_back();
266 }
267 
268 namespace {
269 
270 #if DUMP_PIVOT_TABLE
271 void dumpRow(
272  const OUString& rType, const OUString& rName, const ScDPAggData* pAggData,
273  ScDocument* pDoc, ScAddress& rPos )
274 {
275  SCCOL nCol = rPos.Col();
276  SCROW nRow = rPos.Row();
277  SCTAB nTab = rPos.Tab();
278  pDoc->SetString( nCol++, nRow, nTab, rType );
279  pDoc->SetString( nCol++, nRow, nTab, rName );
280  while ( pAggData )
281  {
282  pDoc->SetValue( nCol++, nRow, nTab, pAggData->GetResult() );
283  pAggData = pAggData->GetExistingChild();
284  }
285  rPos.SetRow( nRow + 1 );
286 }
287 
288 void indent( ScDocument* pDoc, SCROW nStartRow, const ScAddress& rPos )
289 {
290  SCCOL nCol = rPos.Col();
291  SCTAB nTab = rPos.Tab();
292 
293  OUString aString;
294  for (SCROW nRow = nStartRow; nRow < rPos.Row(); nRow++)
295  {
296  aString = pDoc->GetString(nCol, nRow, nTab);
297  if (!aString.isEmpty())
298  {
299  aString = " " + aString;
300  pDoc->SetString( nCol, nRow, nTab, aString );
301  }
302  }
303 }
304 #endif
305 
306 }
307 
309  pColResRoot(pColRoot), pRowResRoot(pRowRoot)
310 {
311  // These arrays should never be empty as the terminating value must be present at all times.
312  maColVisible.push_back(-1);
313  maColSorted.push_back(-1);
314  maRowVisible.push_back(-1);
315  maRowSorted.push_back(-1);
316 }
317 
318 void ScDPRunningTotalState::AddColIndex( sal_Int32 nVisible, tools::Long nSorted )
319 {
320  maColVisible.back() = nVisible;
321  maColVisible.push_back(-1);
322 
323  maColSorted.back() = nSorted;
324  maColSorted.push_back(-1);
325 }
326 
327 void ScDPRunningTotalState::AddRowIndex( sal_Int32 nVisible, tools::Long nSorted )
328 {
329  maRowVisible.back() = nVisible;
330  maRowVisible.push_back(-1);
331 
332  maRowSorted.back() = nSorted;
333  maRowSorted.push_back(-1);
334 }
335 
337 {
338  OSL_ENSURE(!maColVisible.empty() && !maColSorted.empty(), "ScDPRunningTotalState::RemoveColIndex: array is already empty!");
339  if (maColVisible.size() >= 2)
340  {
341  maColVisible.pop_back();
342  maColVisible.back() = -1;
343  }
344 
345  if (maColSorted.size() >= 2)
346  {
347  maColSorted.pop_back();
348  maColSorted.back() = -1;
349  }
350 }
351 
353 {
354  OSL_ENSURE(!maRowVisible.empty() && !maRowSorted.empty(), "ScDPRunningTotalState::RemoveRowIndex: array is already empty!");
355  if (maRowVisible.size() >= 2)
356  {
357  maRowVisible.pop_back();
358  maRowVisible.back() = -1;
359  }
360 
361  if (maRowSorted.size() >= 2)
362  {
363  maRowSorted.pop_back();
364  maRowSorted.back() = -1;
365  }
366 }
367 
369  nBasePos( nBase ),
370  nDirection( nDir )
371 {
372 }
373 
374 void ScDPAggData::Update( const ScDPValue& rNext, ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState )
375 {
376  if (nCount<0) // error?
377  return; // nothing more...
378 
379  if (rNext.meType == ScDPValue::Empty)
380  return;
381 
382  if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE &&
383  rSubState.eColForce != rSubState.eRowForce )
384  return;
385  if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce;
386  if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce;
387 
388  if ( eFunc == SUBTOTAL_FUNC_NONE )
389  return;
390 
391  if ( eFunc != SUBTOTAL_FUNC_CNT2 ) // CNT2 counts everything, incl. strings and errors
392  {
393  if (rNext.meType == ScDPValue::Error)
394  {
395  nCount = -1; // -1 for error (not for CNT2)
396  return;
397  }
398  if (rNext.meType == ScDPValue::String)
399  return; // ignore
400  }
401 
402  ++nCount; // for all functions
403 
404  switch (eFunc)
405  {
406  case SUBTOTAL_FUNC_SUM:
407  case SUBTOTAL_FUNC_AVE:
408  if ( !SubTotal::SafePlus( fVal, rNext.mfValue ) )
409  nCount = -1; // -1 for error
410  break;
411  case SUBTOTAL_FUNC_PROD:
412  if ( nCount == 1 ) // copy first value (fVal is initialized to 0)
413  fVal = rNext.mfValue;
414  else if ( !SubTotal::SafeMult( fVal, rNext.mfValue ) )
415  nCount = -1; // -1 for error
416  break;
417  case SUBTOTAL_FUNC_CNT:
418  case SUBTOTAL_FUNC_CNT2:
419  // nothing more than incrementing nCount
420  break;
421  case SUBTOTAL_FUNC_MAX:
422  if ( nCount == 1 || rNext.mfValue > fVal )
423  fVal = rNext.mfValue;
424  break;
425  case SUBTOTAL_FUNC_MIN:
426  if ( nCount == 1 || rNext.mfValue < fVal )
427  fVal = rNext.mfValue;
428  break;
429  case SUBTOTAL_FUNC_STD:
430  case SUBTOTAL_FUNC_STDP:
431  case SUBTOTAL_FUNC_VAR:
432  case SUBTOTAL_FUNC_VARP:
433  maWelford.update( rNext.mfValue);
434  break;
435  case SUBTOTAL_FUNC_MED:
436  {
437  auto aIter = std::upper_bound(mSortedValues.begin(), mSortedValues.end(), rNext.mfValue);
438  if (aIter == mSortedValues.end())
439  mSortedValues.push_back(rNext.mfValue);
440  else
441  mSortedValues.insert(aIter, rNext.mfValue);
442  }
443  break;
444  default:
445  OSL_FAIL("invalid function");
446  }
447 }
448 
450 {
451  // calculate the original result
452  // (without reference value, used as the basis for reference value calculation)
453 
454  // called several times at the cross-section of several subtotals - don't calculate twice then
455  if ( IsCalculated() )
456  return;
457 
458  if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce;
459  if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce;
460 
461  if ( eFunc == SUBTOTAL_FUNC_NONE ) // this happens when there is no data dimension
462  {
463  nCount = SC_DPAGG_RESULT_EMPTY; // make sure there's a valid state for HasData etc.
464  return;
465  }
466 
467  // check the error conditions for the selected function
468 
469  bool bError = false;
470  switch (eFunc)
471  {
472  case SUBTOTAL_FUNC_SUM:
473  case SUBTOTAL_FUNC_PROD:
474  case SUBTOTAL_FUNC_CNT:
475  case SUBTOTAL_FUNC_CNT2:
476  bError = ( nCount < 0 ); // only real errors
477  break;
478 
479  case SUBTOTAL_FUNC_AVE:
480  case SUBTOTAL_FUNC_MED:
481  case SUBTOTAL_FUNC_MAX:
482  case SUBTOTAL_FUNC_MIN:
483  bError = ( nCount <= 0 ); // no data is an error
484  break;
485 
486  case SUBTOTAL_FUNC_STDP:
487  case SUBTOTAL_FUNC_VARP:
488  bError = ( nCount <= 0 ); // no data is an error
489  assert(bError || nCount == static_cast<sal_Int64>(maWelford.getCount()));
490  break;
491 
492  case SUBTOTAL_FUNC_STD:
493  case SUBTOTAL_FUNC_VAR:
494  bError = ( nCount < 2 ); // need at least 2 values
495  assert(bError || nCount == static_cast<sal_Int64>(maWelford.getCount()));
496  break;
497 
498  default:
499  OSL_FAIL("invalid function");
500  }
501 
502  // calculate the selected function
503 
504  double fResult = 0.0;
505  if ( !bError )
506  {
507  switch (eFunc)
508  {
509  case SUBTOTAL_FUNC_MAX:
510  case SUBTOTAL_FUNC_MIN:
511  case SUBTOTAL_FUNC_SUM:
512  case SUBTOTAL_FUNC_PROD:
513  // different error conditions are handled above
514  fResult = fVal;
515  break;
516 
517  case SUBTOTAL_FUNC_CNT:
518  case SUBTOTAL_FUNC_CNT2:
519  fResult = nCount;
520  break;
521 
522  case SUBTOTAL_FUNC_AVE:
523  if ( nCount > 0 )
524  fResult = fVal / static_cast<double>(nCount);
525  break;
526 
527  case SUBTOTAL_FUNC_STD:
528  if ( nCount >= 2 )
529  {
530  fResult = maWelford.getVarianceSample();
531  if (fResult < 0.0)
532  bError = true;
533  else
534  fResult = sqrt( fResult);
535  }
536  break;
537  case SUBTOTAL_FUNC_VAR:
538  if ( nCount >= 2 )
539  fResult = maWelford.getVarianceSample();
540  break;
541  case SUBTOTAL_FUNC_STDP:
542  if ( nCount > 0 )
543  {
544  fResult = maWelford.getVariancePopulation();
545  if (fResult < 0.0)
546  bError = true;
547  else
548  fResult = sqrt( fResult);
549  }
550  break;
551  case SUBTOTAL_FUNC_VARP:
552  if ( nCount > 0 )
553  fResult = maWelford.getVariancePopulation();
554  break;
555  case SUBTOTAL_FUNC_MED:
556  {
557  size_t nSize = mSortedValues.size();
558  if (nSize > 0)
559  {
560  assert(nSize == static_cast<size_t>(nCount));
561  if ((nSize % 2) == 1)
562  fResult = mSortedValues[nSize / 2];
563  else
564  fResult = (mSortedValues[nSize / 2 - 1] + mSortedValues[nSize / 2]) / 2.0;
565  }
566  }
567  break;
568  default:
569  OSL_FAIL("invalid function");
570  }
571  }
572 
573  bool bEmpty = ( nCount == 0 ); // no data
574 
575  // store the result
576  // Empty is checked first, so empty results are shown empty even for "average" etc.
577  // If these results should be treated as errors in reference value calculations,
578  // a separate state value (EMPTY_ERROR) is needed.
579  // Now, for compatibility, empty "average" results are counted as 0.
580 
581  if ( bEmpty )
583  else if ( bError )
585  else
587 
588  if ( bEmpty || bError )
589  fResult = 0.0; // default, in case the state is later modified
590 
591  fVal = fResult; // used directly from now on
592  fAux = 0.0; // used for running total or original result of reference value
593 }
594 
596 {
597  return ( nCount <= SC_DPAGG_RESULT_EMPTY );
598 }
599 
601 {
602  assert( IsCalculated() && "ScDPAggData not calculated" );
603 
604  return fVal; // use calculated value
605 }
606 
608 {
609  assert( IsCalculated() && "ScDPAggData not calculated" );
610 
611  return ( nCount == SC_DPAGG_RESULT_ERROR );
612 }
613 
615 {
616  assert( IsCalculated() && "ScDPAggData not calculated" );
617 
618  return ( nCount != SC_DPAGG_RESULT_EMPTY ); // values or error
619 }
620 
621 void ScDPAggData::SetResult( double fNew )
622 {
623  assert( IsCalculated() && "ScDPAggData not calculated" );
624 
625  fVal = fNew; // don't reset error flag
626 }
627 
629 {
630  assert( IsCalculated() && "ScDPAggData not calculated" );
631 
633 }
634 
635 void ScDPAggData::SetEmpty( bool bSet )
636 {
637  assert( IsCalculated() && "ScDPAggData not calculated" );
638 
639  if ( bSet )
641  else
643 }
644 
646 {
647  // after Calculate, fAux is used as auxiliary value for running totals and reference values
648  assert( IsCalculated() && "ScDPAggData not calculated" );
649 
650  return fAux;
651 }
652 
653 void ScDPAggData::SetAuxiliary( double fNew )
654 {
655  // after Calculate, fAux is used as auxiliary value for running totals and reference values
656  assert( IsCalculated() && "ScDPAggData not calculated" );
657 
658  fAux = fNew;
659 }
660 
662 {
663  if (!pChild)
664  pChild.reset( new ScDPAggData );
665  return pChild.get();
666 }
667 
669 {
671  fVal = 0.0;
672  fAux = 0.0;
674  pChild.reset();
675 }
676 
677 #if DUMP_PIVOT_TABLE
678 void ScDPAggData::Dump(int nIndent) const
679 {
680  std::string aIndent(nIndent*2, ' ');
681  std::cout << aIndent << "* ";
682  if (IsCalculated())
683  std::cout << GetResult();
684  else
685  std::cout << "not calculated";
686 
687  std::cout << " [val=" << fVal << "; aux=" << fAux << "; count=" << nCount << "]" << std::endl;
688 }
689 #endif
690 
692  bIsInColRoot( false )
693 {
694 }
695 
697 {
698 }
699 
701 {
702  OSL_ENSURE( nMeasure >= 0, "GetColTotal: no measure" );
703 
704  ScDPAggData* pAgg = pFirst;
705  tools::Long nSkip = nMeasure;
706 
707  // subtotal settings are ignored - column/row totals exist once per measure
708 
709  for ( tools::Long nPos=0; nPos<nSkip; nPos++ )
710  pAgg = pAgg->GetChild(); // column total is constructed empty - children need to be created
711 
712  if ( !pAgg->IsCalculated() )
713  {
714  // for first use, simulate an empty calculation
715  ScDPSubTotalState aEmptyState;
716  pAgg->Calculate( SUBTOTAL_FUNC_SUM, aEmptyState );
717  }
718 
719  return pAgg;
720 }
721 
723 {
724  return lcl_GetChildTotal( &aRowTotal, nMeasure );
725 }
726 
728 {
729  return lcl_GetChildTotal( &aGrandTotal, nMeasure );
730 }
731 
732 static ScSubTotalFunc lcl_GetForceFunc( const ScDPLevel* pLevel, tools::Long nFuncNo )
733 {
735  if ( pLevel )
736  {
737  //TODO: direct access via ScDPLevel
738 
739  uno::Sequence<sal_Int16> aSeq = pLevel->getSubTotals();
740  tools::Long nSequence = aSeq.getLength();
741  if ( nSequence && aSeq[0] != sheet::GeneralFunction2::AUTO )
742  {
743  // For manual subtotals, "automatic" is added as first function.
744  // ScDPResultMember::GetSubTotalCount adds to the count, here NONE has to be
745  // returned as the first function then.
746 
747  --nFuncNo; // keep NONE for first (check below), move the other entries
748  }
749 
750  if ( nFuncNo >= 0 && nFuncNo < nSequence )
751  {
752  ScGeneralFunction eUser = static_cast<ScGeneralFunction>(aSeq.getConstArray()[nFuncNo]);
753  if (eUser != ScGeneralFunction::AUTO)
754  eRet = ScDPUtil::toSubTotalFunc(eUser);
755  }
756  }
757  return eRet;
758 }
759 
761  mrSource(rSrc),
762  bLateInit( false ),
763  bDataAtCol( false ),
764  bDataAtRow( false )
765 {
766 }
767 
769 {
770 }
771 
773  std::vector<ScSubTotalFunc>& rFunctions, std::vector<sheet::DataPilotFieldReference>& rRefs,
774  std::vector<sheet::DataPilotFieldOrientation>& rRefOrient, std::vector<OUString>& rNames )
775 {
776  // We need to have at least one measure data at all times.
777 
778  maMeasureFuncs.swap(rFunctions);
779  if (maMeasureFuncs.empty())
781 
782  maMeasureRefs.swap(rRefs);
783  if (maMeasureRefs.empty())
784  maMeasureRefs.emplace_back(); // default ctor is ok.
785 
786  maMeasureRefOrients.swap(rRefOrient);
787  if (maMeasureRefOrients.empty())
788  maMeasureRefOrients.push_back(sheet::DataPilotFieldOrientation_HIDDEN);
789 
790  maMeasureNames.swap(rNames);
791  if (maMeasureNames.empty())
792  maMeasureNames.push_back(ScResId(STR_EMPTYDATA));
793 }
794 
795 void ScDPResultData::SetDataLayoutOrientation( sheet::DataPilotFieldOrientation nOrient )
796 {
797  bDataAtCol = ( nOrient == sheet::DataPilotFieldOrientation_COLUMN );
798  bDataAtRow = ( nOrient == sheet::DataPilotFieldOrientation_ROW );
799 }
800 
802 {
803  bLateInit = bSet;
804 }
805 
807 {
808  if (maMeasureFuncs.size() == 1)
809  return 0;
810 
812 }
813 
815 {
816  if (maMeasureFuncs.size() == 1)
817  return 0;
818 
820 }
821 
823 {
824  OSL_ENSURE(o3tl::make_unsigned(nMeasure) < maMeasureFuncs.size(), "bumm");
825  return maMeasureFuncs[nMeasure];
826 }
827 
828 const sheet::DataPilotFieldReference& ScDPResultData::GetMeasureRefVal(tools::Long nMeasure) const
829 {
830  OSL_ENSURE(o3tl::make_unsigned(nMeasure) < maMeasureRefs.size(), "bumm");
831  return maMeasureRefs[nMeasure];
832 }
833 
834 sheet::DataPilotFieldOrientation ScDPResultData::GetMeasureRefOrient(tools::Long nMeasure) const
835 {
836  OSL_ENSURE(o3tl::make_unsigned(nMeasure) < maMeasureRefOrients.size(), "bumm");
837  return maMeasureRefOrients[nMeasure];
838 }
839 
840 OUString ScDPResultData::GetMeasureString(tools::Long nMeasure, bool bForce, ScSubTotalFunc eForceFunc, bool& rbTotalResult) const
841 {
842  // with bForce==true, return function instead of "result" for single measure
843  // with eForceFunc != SUBTOTAL_FUNC_NONE, always use eForceFunc
844  rbTotalResult = false;
845  if ( nMeasure < 0 || (maMeasureFuncs.size() == 1 && !bForce && eForceFunc == SUBTOTAL_FUNC_NONE) )
846  {
847  // for user-specified subtotal function with all measures,
848  // display only function name
849  assert(unsigned(eForceFunc) < SAL_N_ELEMENTS(aFuncStrIds));
850  if ( eForceFunc != SUBTOTAL_FUNC_NONE )
851  return ScResId(aFuncStrIds[eForceFunc]);
852 
853  rbTotalResult = true;
854  return ScResId(STR_TABLE_ERGEBNIS);
855  }
856  else
857  {
858  OSL_ENSURE(o3tl::make_unsigned(nMeasure) < maMeasureFuncs.size(), "bumm");
859  const ScDPDimension* pDataDim = mrSource.GetDataDimension(nMeasure);
860  if (pDataDim)
861  {
862  const std::optional<OUString> & pLayoutName = pDataDim->GetLayoutName();
863  if (pLayoutName)
864  return *pLayoutName;
865  }
866 
867  ScSubTotalFunc eFunc = ( eForceFunc == SUBTOTAL_FUNC_NONE ) ?
868  GetMeasureFunction(nMeasure) : eForceFunc;
869 
870  return ScDPUtil::getDisplayedMeasureName(maMeasureNames[nMeasure], eFunc);
871  }
872 }
873 
875 {
876  if ( nMeasure < 0 )
877  {
878  OSL_FAIL("GetMeasureDimensionName: negative");
879  return "***";
880  }
881 
882  return mrSource.GetDataDimName(nMeasure);
883 }
884 
886 {
887  return mrSource.GetData()->IsBaseForGroup(nDim);
888 }
889 
891 {
892  return mrSource.GetData()->GetGroupBase(nGroupDim);
893 }
894 
896 {
897  return mrSource.GetData()->IsNumOrDateGroup(nDim);
898 }
899 
900 bool ScDPResultData::IsInGroup( SCROW nGroupDataId, tools::Long nGroupIndex,
901  const ScDPItemData& rBaseData, tools::Long nBaseIndex ) const
902 {
903  const ScDPItemData* pGroupData = mrSource.GetItemDataById(nGroupIndex , nGroupDataId);
904  if ( pGroupData )
905  return mrSource.GetData()->IsInGroup(*pGroupData, nGroupIndex, rBaseData, nBaseIndex);
906  else
907  return false;
908 }
909 
910 bool ScDPResultData::HasCommonElement( SCROW nFirstDataId, tools::Long nFirstIndex,
911  const ScDPItemData& rSecondData, tools::Long nSecondIndex ) const
912 {
913  const ScDPItemData* pFirstData = mrSource.GetItemDataById(nFirstIndex , nFirstDataId);
914  if ( pFirstData )
915  return mrSource.GetData()->HasCommonElement(*pFirstData, nFirstIndex, rSecondData, nSecondIndex);
916  else
917  return false;
918 }
919 
921 {
922  if (nDim < static_cast<tools::Long>(maDimMembers.size()) && maDimMembers[nDim])
923  return *maDimMembers[nDim];
924 
925  if (nDim >= static_cast<tools::Long>(maDimMembers.size()))
926  maDimMembers.resize(nDim+1);
927 
928  std::unique_ptr<ResultMembers> pResultMembers(new ResultMembers());
929  // global order is used to initialize aMembers, so it doesn't have to be looked at later
930  const ScMemberSortOrder& rGlobalOrder = pLevel->GetGlobalOrder();
931 
932  ScDPMembers* pMembers = pLevel->GetMembersObject();
933  tools::Long nMembCount = pMembers->getCount();
934  for (tools::Long i = 0; i < nMembCount; ++i)
935  {
936  tools::Long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i];
937  ScDPMember* pMember = pMembers->getByIndex(nSorted);
938  if (!pResultMembers->FindMember(pMember->GetItemDataId()))
939  {
940  ScDPParentDimData aNew(i, pDim, pLevel, pMember);
941  pResultMembers->InsertMember(aNew);
942  }
943  }
944 
945  maDimMembers[nDim] = std::move(pResultMembers);
946  return *maDimMembers[nDim];
947 }
948 
950  const ScDPResultData* pData, const ScDPParentDimData& rParentDimData ) :
951  pResultData( pData ),
952  aParentDimData( rParentDimData ),
953  bHasElements( false ),
954  bForceSubTotal( false ),
955  bHasHiddenDetails( false ),
956  bInitialized( false ),
957  bAutoHidden( false ),
958  nMemberStep( 1 )
959 {
960  // pParentLevel/pMemberDesc is 0 for root members
961 }
962 
964  const ScDPResultData* pData, bool bForceSub ) :
965  pResultData( pData ),
966  bHasElements( false ),
967  bForceSubTotal( bForceSub ),
968  bHasHiddenDetails( false ),
969  bInitialized( false ),
970  bAutoHidden( false ),
971  nMemberStep( 1 )
972 {
973 }
975 {
976 }
977 
979 {
980  const ScDPMember* pMemberDesc = GetDPMember();
981  if (pMemberDesc)
982  return pMemberDesc->GetNameStr( false );
983  else
984  return ScResId(STR_PIVOT_TOTAL); // root member
985 }
986 
987 OUString ScDPResultMember::GetDisplayName( bool bLocaleIndependent ) const
988 {
989  const ScDPMember* pDPMember = GetDPMember();
990  if (!pDPMember)
991  return OUString();
992 
993  ScDPItemData aItem(pDPMember->FillItemData());
995  {
997  return pResultData->GetSource().GetData()->GetFormattedString(nDim, aItem, bLocaleIndependent);
998  }
999 
1000  return aItem.GetString();
1001 }
1002 
1004 {
1005  const ScDPMember* pMemberDesc = GetDPMember();
1006  if (pMemberDesc)
1007  return pMemberDesc->FillItemData();
1008  return ScDPItemData(ScResId(STR_PIVOT_TOTAL)); // root member
1009 }
1010 
1012 {
1013  //TODO: store ScDPMember pointer instead of ScDPMember ???
1014  const ScDPMember* pMemberDesc = GetDPMember();
1015  if (pMemberDesc)
1016  return pMemberDesc->IsNamedItem(nIndex);
1017  return false;
1018 }
1019 
1020 bool ScDPResultMember::IsValidEntry( const vector< SCROW >& aMembers ) const
1021 {
1022  if ( !IsValid() )
1023  return false;
1024 
1025  const ScDPResultDimension* pChildDim = GetChildDimension();
1026  if (pChildDim)
1027  {
1028  if (aMembers.size() < 2)
1029  return false;
1030 
1031  vector<SCROW>::const_iterator itr = aMembers.begin();
1032  vector<SCROW> aChildMembers(++itr, aMembers.end());
1033  return pChildDim->IsValidEntry(aChildMembers);
1034  }
1035  else
1036  return true;
1037 }
1038 
1039 void ScDPResultMember::InitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev,
1040  size_t nPos, ScDPInitState& rInitState ,
1041  bool bInitChild )
1042 {
1043  // with LateInit, initialize only those members that have data
1044  if ( pResultData->IsLateInit() )
1045  return;
1046 
1047  bInitialized = true;
1048 
1049  if (nPos >= ppDim.size())
1050  return;
1051 
1052  // skip child dimension if details are not shown
1053  if ( GetDPMember() && !GetDPMember()->getShowDetails() )
1054  {
1055  // Show DataLayout dimension
1056  nMemberStep = 1;
1057  while ( nPos < ppDim.size() )
1058  {
1059  if ( ppDim[nPos]->getIsDataLayoutDimension() )
1060  {
1061  if ( !pChildDimension )
1063  pChildDimension->InitFrom( ppDim, ppLev, nPos, rInitState , false );
1064  return;
1065  }
1066  else
1067  { //find next dim
1068  nPos ++;
1069  nMemberStep ++;
1070  }
1071  }
1072  bHasHiddenDetails = true; // only if there is a next dimension
1073  return;
1074  }
1075 
1076  if ( bInitChild )
1077  {
1079  pChildDimension->InitFrom(ppDim, ppLev, nPos, rInitState);
1080  }
1081 }
1082 
1084  LateInitParams& rParams, const vector<SCROW>& pItemData, size_t nPos, ScDPInitState& rInitState)
1085 {
1086  // without LateInit, everything has already been initialized
1087  if ( !pResultData->IsLateInit() )
1088  return;
1089 
1090  bInitialized = true;
1091 
1092  if ( rParams.IsEnd( nPos ) /*nPos >= ppDim.size()*/)
1093  // No next dimension. Bail out.
1094  return;
1095 
1096  // skip child dimension if details are not shown
1097  if ( GetDPMember() && !GetDPMember()->getShowDetails() )
1098  {
1099  // Show DataLayout dimension
1100  nMemberStep = 1;
1101  while ( !rParams.IsEnd( nPos ) )
1102  {
1103  if ( rParams.GetDim( nPos )->getIsDataLayoutDimension() )
1104  {
1105  if ( !pChildDimension )
1107 
1108  // #i111462# reset InitChild flag only for this child dimension's LateInitFrom call,
1109  // not for following members of parent dimensions
1110  bool bWasInitChild = rParams.GetInitChild();
1111  rParams.SetInitChild( false );
1112  pChildDimension->LateInitFrom( rParams, pItemData, nPos, rInitState );
1113  rParams.SetInitChild( bWasInitChild );
1114  return;
1115  }
1116  else
1117  { //find next dim
1118  nPos ++;
1119  nMemberStep ++;
1120  }
1121  }
1122  bHasHiddenDetails = true; // only if there is a next dimension
1123  return;
1124  }
1125 
1126  // LateInitFrom is called several times...
1127  if ( rParams.GetInitChild() )
1128  {
1129  if ( !pChildDimension )
1131  pChildDimension->LateInitFrom( rParams, pItemData, nPos, rInitState );
1132  }
1133 }
1134 
1136 {
1137  bool bRet = false;
1138  if ( pChildDimension && /*pParentLevel*/GetParentLevel() &&
1139  /*pParentLevel*/GetParentLevel()->IsOutlineLayout() && /*pParentLevel*/GetParentLevel()->IsSubtotalsAtTop() )
1140  {
1141  tools::Long nUserSubStart;
1142  tools::Long nSubTotals = GetSubTotalCount( &nUserSubStart );
1143  nSubTotals -= nUserSubStart; // visible count
1144  if ( nSubTotals )
1145  {
1146  if ( nMeasure == SC_DPMEASURE_ALL )
1147  nSubTotals *= pResultData->GetMeasureCount(); // number of subtotals that will be inserted
1148 
1149  // only a single subtotal row will be shown in the outline title row
1150  if ( nSubTotals == 1 )
1151  bRet = true;
1152  }
1153  }
1154  return bRet;
1155 }
1156 
1158 {
1159  if ( !IsVisible() )
1160  return 0;
1161  const ScDPLevel* pParentLevel = GetParentLevel();
1162  tools::Long nExtraSpace = 0;
1163  if ( pParentLevel && pParentLevel->IsAddEmpty() )
1164  ++nExtraSpace;
1165 
1166  if ( pChildDimension )
1167  {
1168  // outline layout takes up an extra row for the title only if subtotals aren't shown in that row
1169  if ( pParentLevel && pParentLevel->IsOutlineLayout() && !IsSubTotalInTitle( nMeasure ) )
1170  ++nExtraSpace;
1171 
1172  tools::Long nSize = pChildDimension->GetSize(nMeasure);
1173  tools::Long nUserSubStart;
1174  tools::Long nUserSubCount = GetSubTotalCount( &nUserSubStart );
1175  nUserSubCount -= nUserSubStart; // for output size, use visible count
1176  if ( nUserSubCount )
1177  {
1178  if ( nMeasure == SC_DPMEASURE_ALL )
1179  nSize += pResultData->GetMeasureCount() * nUserSubCount;
1180  else
1181  nSize += nUserSubCount;
1182  }
1183  return nSize + nExtraSpace;
1184  }
1185  else
1186  {
1187  if ( nMeasure == SC_DPMEASURE_ALL )
1188  return pResultData->GetMeasureCount() + nExtraSpace;
1189  else
1190  return 1 + nExtraSpace;
1191  }
1192 }
1193 
1195 {
1196  if (!bInitialized)
1197  return false;
1198 
1199  if (!IsValid())
1200  return false;
1201 
1202  if (bHasElements)
1203  return true;
1204 
1205  // not initialized -> shouldn't be there at all
1206  // (allocated only to preserve ordering)
1207  const ScDPLevel* pParentLevel = GetParentLevel();
1208 
1209  return (pParentLevel && pParentLevel->getShowEmpty());
1210 }
1211 
1213 {
1214  // non-Valid members are left out of calculation
1215 
1216  // was member set no invisible at the DataPilotSource?
1217  const ScDPMember* pMemberDesc = GetDPMember();
1218  if ( pMemberDesc && !pMemberDesc->isVisible() )
1219  return false;
1220 
1221  if ( bAutoHidden )
1222  return false;
1223 
1224  return true;
1225 }
1226 
1228 {
1229  if ( pUserSubStart )
1230  *pUserSubStart = 0; // default
1231 
1232  const ScDPLevel* pParentLevel = GetParentLevel();
1233 
1234  if ( bForceSubTotal ) // set if needed for root members
1235  return 1; // grand total is always "automatic"
1236  else if ( pParentLevel )
1237  {
1238  //TODO: direct access via ScDPLevel
1239 
1240  uno::Sequence<sal_Int16> aSeq = pParentLevel->getSubTotals();
1241  tools::Long nSequence = aSeq.getLength();
1242  if ( nSequence && aSeq[0] != sheet::GeneralFunction2::AUTO )
1243  {
1244  // For manual subtotals, always add "automatic" as first function
1245  // (used for calculation, but not for display, needed for sorting, see lcl_GetForceFunc)
1246 
1247  ++nSequence;
1248  if ( pUserSubStart )
1249  *pUserSubStart = 1; // visible subtotals start at 1
1250  }
1251  return nSequence;
1252  }
1253  else
1254  return 0;
1255 }
1256 
1257 void ScDPResultMember::ProcessData( const vector< SCROW >& aChildMembers, const ScDPResultDimension* pDataDim,
1258  const vector< SCROW >& aDataMembers, const vector<ScDPValue>& aValues )
1259 {
1260  SetHasElements();
1261 
1262  if (pChildDimension)
1263  pChildDimension->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues );
1264 
1265  if ( !pDataRoot )
1266  {
1267  pDataRoot.reset( new ScDPDataMember( pResultData, nullptr ) );
1268  if ( pDataDim )
1269  pDataRoot->InitFrom( pDataDim ); // recursive
1270  }
1271 
1272  ScDPSubTotalState aSubState; // initial state
1273 
1274  tools::Long nUserSubCount = GetSubTotalCount();
1275 
1276  // Calculate at least automatic if no subtotals are selected,
1277  // show only own values if there's no child dimension (innermost).
1278  if ( !nUserSubCount || !pChildDimension )
1279  nUserSubCount = 1;
1280 
1281  const ScDPLevel* pParentLevel = GetParentLevel();
1282 
1283  for (tools::Long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic"
1284  {
1285  // #i68338# if nUserSubCount is 1 (automatic only), don't set nRowSubTotalFunc
1286  if ( pChildDimension && nUserSubCount > 1 )
1287  {
1288  aSubState.nRowSubTotalFunc = nUserPos;
1289  aSubState.eRowForce = lcl_GetForceFunc( pParentLevel, nUserPos );
1290  }
1291 
1292  pDataRoot->ProcessData( aDataMembers, aValues, aSubState );
1293  }
1294 }
1295 
1300 static OUString lcl_parseSubtotalName(const OUString& rSubStr, std::u16string_view rCaption)
1301 {
1302  OUStringBuffer aNewStr;
1303  sal_Int32 n = rSubStr.getLength();
1304  bool bEscaped = false;
1305  for (sal_Int32 i = 0; i < n; ++i)
1306  {
1307  sal_Unicode c = rSubStr[i];
1308  if (!bEscaped && c == '\\')
1309  {
1310  bEscaped = true;
1311  continue;
1312  }
1313 
1314  if (!bEscaped && c == '?')
1315  aNewStr.append(rCaption);
1316  else
1317  aNewStr.append(c);
1318  bEscaped = false;
1319  }
1320  return aNewStr.makeStringAndClear();
1321 }
1322 
1324  uno::Sequence<sheet::MemberResult>* pSequences, tools::Long& rPos, tools::Long nMeasure, bool bRoot,
1325  const OUString* pMemberName, const OUString* pMemberCaption )
1326 {
1327  // IsVisible() test is in ScDPResultDimension::FillMemberResults
1328  // (not on data layout dimension)
1329 
1330  if (!pSequences->hasElements())
1331  // empty sequence. Bail out.
1332  return;
1333 
1334  tools::Long nSize = GetSize(nMeasure);
1335  sheet::MemberResult* pArray = pSequences->getArray();
1336  OSL_ENSURE( rPos+nSize <= pSequences->getLength(), "bumm" );
1337 
1338  bool bIsNumeric = false;
1339  double fValue = std::numeric_limits<double>::quiet_NaN();
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());
1349  {
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 
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  fValue = std::numeric_limits<double>::quiet_NaN(); /* 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 
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;
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 
1649 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;
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 
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 
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 
1729 {
1730  if (pDataRoot)
1731  pDataRoot->ResetResults();
1732 
1733  if (pChildDimension)
1734  pChildDimension->ResetResults();
1735 }
1736 
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;
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
1792 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 
1806 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 
1827 {
1828  return lcl_GetChildTotal( const_cast<ScDPAggData*>(&aColTotal), nMeasure );
1829 }
1830 
1832 {
1833  if (pChildDimension)
1834  pChildDimension->FillVisibilityData(rData);
1835 }
1836 
1838  pResultData( pData ),
1839  pResultMember( pRes )
1840 {
1841  // pResultMember is 0 for root members
1842 }
1843 
1845 {
1846 }
1847 
1848 OUString ScDPDataMember::GetName() const
1849 {
1850  if (pResultMember)
1851  return pResultMember->GetName();
1852  else
1853  return EMPTY_OUSTRING;
1854 }
1855 
1857 {
1858  if (pResultMember)
1859  return pResultMember->IsVisible();
1860  else
1861  return false;
1862 }
1863 
1865 {
1866  if (pResultMember)
1867  return pResultMember->IsNamedItem(nRow);
1868  else
1869  return false;
1870 }
1871 
1873 {
1874  if (pResultMember)
1875  return pResultMember->HasHiddenDetails();
1876  else
1877  return false;
1878 }
1879 
1881 {
1882  if ( !pChildDimension )
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 
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 
1902  if ( rSubState.nColSubTotalFunc >= 0 ) nRet = rSubState.nColSubTotalFunc;
1903  if ( rSubState.nRowSubTotalFunc >= 0 ) nRet = rSubState.nRowSubTotalFunc;
1904  return nRet;
1905 }
1906 
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 
1931 void ScDPDataMember::ProcessData( const vector< SCROW >& aChildMembers, const vector<ScDPValue>& aValues,
1932  const ScDPSubTotalState& rSubState )
1933 {
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
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 
1966 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 
1981 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 
1990 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 
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 
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 
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 
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 
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 
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 
2269 {
2270  aAggregate.Reset();
2271 
2272  ScDPDataDimension* pDataChild = GetChildDimension();
2273  if ( pDataChild )
2274  pDataChild->ResetResults();
2275 }
2276 
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
2652 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 
2665 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 
2695  bool IsIncluded( const ScDPMember& rMember ) { return bIncludeAll || TestIncluded( rMember ); }
2696  bool TestIncluded( const ScDPMember& rMember );
2697 };
2698 
2699 }
2700 
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 
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 
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 
2764 {
2765 }
2766 
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 
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 
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)
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 
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 
2995 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 
3009 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 
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 
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 
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 
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 
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 
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 
3212 {
3213  return aMemberOrder.empty() ? nUnsorted : aMemberOrder[nUnsorted];
3214 }
3215 
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 
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 
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
3460 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 
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 
3477 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 
3490 {
3491  return maMemberArray.size();
3492 }
3493 
3495 {
3496  return maMemberArray[n].get();
3497 }
3499 {
3500  return maMemberArray[n].get();
3501 }
3502 
3504 {
3505  if ( !maMemberArray.empty() )
3506  return maMemberArray[0]->GetChildDimension();
3507  else
3508  return nullptr;
3509 }
3510 
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 
3529  pResultData( pData ),
3530  pResultDimension( nullptr ),
3531  bIsDataLayout( false )
3532 {
3533 }
3534 
3536 {
3537 }
3538 
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 
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 
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 
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 
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 
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 
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 
3786 {
3787  if (!pResultDimension)
3788  return nUnsorted;
3789 
3790  const ScMemberSortOrder& rMemberOrder = pResultDimension->GetMemberOrder();
3791  return rMemberOrder.empty() ? nUnsorted : rMemberOrder[nUnsorted];
3792 }
3793 
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
3836 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 
3854 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 
3865 {
3866  return maMembers.size();
3867 }
3868 
3870 {
3871  return maMembers[n].get();
3872 }
3873 
3875 {
3876  return maMembers[n].get();
3877 }
3878 
3880  ScDPSource* pSource) :
3881  mpSource(pSource)
3882 {
3883 }
3884 
3886 {
3887 }
3888 
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 
3907 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  {
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 
3948 {
3949  if (r.IsValue())
3950  return static_cast<size_t>(::rtl::math::approxFloor(r.GetValue()));
3951  else
3952  return r.GetString().hashCode();
3953 }
3955 {
3956  const ScDPMember* pMemberDesc = GetDPMember();
3957  if (pMemberDesc)
3958  return pMemberDesc->GetItemDataId();
3959  return -1;
3960 }
3961 
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 
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 
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 
4027  mnOrder(-1), mpParentDim(nullptr), mpParentLevel(nullptr), mpMemberDesc(nullptr) {}
4028 
4030  SCROW nIndex, const ScDPDimension* pDim, const ScDPLevel* pLev, const ScDPMember* pMember) :
4031  mnOrder(nIndex), mpParentDim(pDim), mpParentLevel(pLev), mpMemberDesc(pMember) {}
4032 
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 }
4043 {
4044  if ( !rNew.mpMemberDesc->getShowDetails() )
4045  mbHasHideDetailsMember = true;
4046  maMemberHash.emplace( rNew.mpMemberDesc->GetItemDataId(), rNew );
4047 }
4048 
4050  mbHasHideDetailsMember( false )
4051 {
4052 }
4054 {
4055 }
4056 
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 
4068 {
4069 }
4070 
4071 bool LateInitParams::IsEnd( size_t nPos ) const
4072 {
4073  return nPos >= mppDim.size();
4074 }
4075 
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 
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: */
OUString maValueName
Definition: dpresfilter.hxx:32
void SetAuxiliary(double fNew)
Definition: dptabres.cxx:653
ScDPRelativePos(tools::Long nBase, tools::Long nDir)
Definition: dptabres.cxx:368
bool IsOutlineLayout() const
Definition: dptabsrc.hxx:521
void DoAutoShow(ScDPResultMember *pRefMember)
Definition: dptabres.cxx:1713
std::unique_ptr< ScDPDataMember > pDataRoot
Definition: dptabres.hxx:334
MemberArray maMemberArray
Definition: dptabres.hxx:494
function is determined automatically.
void SetInitAllChildren(bool b)
Definition: dptabres.hxx:258
ScDPTableData * GetData()
Definition: dptabsrc.hxx:143
const tools::Long SC_SUBTOTALPOS_AUTO
Definition: dptabres.cxx:1887
virtual bool HasCommonElement(const ScDPItemData &rFirstData, sal_Int32 nFirstIndex, const ScDPItemData &rSecondData, sal_Int32 nSecondIndex) const
Definition: dptabdat.cxx:134
sal_Int32 nIndex
const sal_Int64 SC_DPAGG_RESULT_EMPTY
Definition: dptabres.hxx:149
bool bHasHiddenDetails
Definition: dptabres.hxx:337
std::unique_ptr< ScDPAggData > pChild
Definition: dptabres.hxx:160
bool IsCalculated() const
Definition: dptabres.cxx:595
bool IsAutoTopItems() const
Definition: dptabres.hxx:592
ScSubTotalFunc eRowForce
Definition: dptabres.hxx:83
void UpdateDataRow(const ScDPResultMember *pRefMember, tools::Long nMeasure, bool bIsSubTotalRow, const ScDPSubTotalState &rSubState)
Definition: dptabres.cxx:2169
OUString GetName() const
Definition: dptabres.cxx:978
bool HasCommonElement(SCROW nFirstDataId, tools::Long nFirstIndex, const ScDPItemData &rSecondData, tools::Long nSecondIndex) const
Definition: dptabres.cxx:910
std::unique_ptr< ScDPResultDimension > pChildDimension
Definition: dptabres.hxx:333
#define EMPTY_OUSTRING
Definition: global.hxx:213
const std::optional< OUString > & GetGrandTotalName() const
Definition: dptabsrc.cxx:119
ScDPDimension * GetDataDimension(sal_Int32 nIndex)
Definition: dptabsrc.cxx:146
virtual sal_Int32 GetGroupBase(sal_Int32 nGroupDim) const
Definition: dptabdat.cxx:117
ScDPDimension * GetDim(size_t nPos) const
Definition: dptabres.hxx:260
void SetEmpty(bool bSet)
Definition: dptabres.cxx:635
ScDPHierarchy * getByIndex(tools::Long nIndex) const
Definition: dptabsrc.cxx:1672
double getVarianceSample() const
Definition: subtotal.hxx:44
SCROW Row() const
Definition: address.hxx:261
const css::sheet::DataPilotFieldAutoShowInfo & GetAutoShow() const
Definition: dptabsrc.hxx:511
Flags
void Update(const ScDPValue &rNext, ScSubTotalFunc eFunc, const ScDPSubTotalState &rSubState)
Definition: dptabres.cxx:374
static ScSubTotalFunc lcl_GetForceFunc(const ScDPLevel *pLevel, tools::Long nFuncNo)
Definition: dptabres.cxx:732
const ScDPParentDimData * FindMember(SCROW nIndex) const
Definition: dptabres.cxx:4033
void InitFrom(const ::std::vector< ScDPDimension * > &ppDim, const ::std::vector< ScDPLevel * > &ppLev, size_t nPos, ScDPInitState &rInitState, bool bInitChild=true)
Definition: dptabres.cxx:1039
std::vector< css::sheet::DataPilotFieldOrientation > maMeasureRefOrients
Definition: dptabres.hxx:279
void SetInitChild(bool b)
Definition: dptabres.hxx:257
ScDPResultTree maFilterSet
tools::Long GetMemberCount() const
Definition: dptabres.cxx:3864
void InitFrom(const ScDPResultDimension *pDim)
Definition: dptabres.cxx:1880
sal_Int32 GetDimension() const
Definition: dptabsrc.hxx:287
void CheckShowEmpty(bool bShow=false)
Definition: dptabres.cxx:4076
The term 'measure' here roughly equals "data dimension" ?
Definition: dptabres.hxx:272
void SortMembers(ScDPResultMember *pRefMember)
Definition: dptabres.cxx:1698
ScDPAggData aAggregate
Definition: dptabres.hxx:433
OUString GetDataDimName(sal_Int32 nIndex)
Definition: dptabsrc.cxx:155
void CheckShowEmpty(bool bShow=false)
Definition: dptabres.cxx:4088
ScDPParentDimData aParentDimData
Definition: dptabres.hxx:332
static ScDPDataMember * GetColReferenceMember(const ScDPRelativePos *pMemberPos, const OUString *pName, sal_Int32 nRefDimPos, const ScDPRunningTotalState &rRunning)
Definition: dptabres.cxx:3338
long Long
std::vector< css::sheet::DataPilotFieldReference > maMeasureRefs
Definition: dptabres.hxx:278
tools::Long nRowSubTotalFunc
Definition: dptabres.hxx:85
sal_Int64 n
void SetLateInit(bool bSet)
Definition: dptabres.cxx:801
const ScDPResultData * pResultData
Definition: dptabres.hxx:331
sal_Int32 getCount() const
Definition: dptabsrc.hxx:581
OUString aDimensionName
Definition: dptabres.hxx:496
std::vector< ScDPResultFilter > maFilters
const css::sheet::DataPilotFieldReference & GetMeasureRefVal(tools::Long nMeasure) const
Definition: dptabres.cxx:828
Member names that are being processed for InitFrom/LateInitFrom (needed for initialization of grouped...
Definition: dptabres.hxx:55
IndexArray maRowSorted
Definition: dptabres.hxx:131
const ScDPResultDimension * pResultDimension
Definition: dptabres.hxx:605
ScDPLevel * GetLevel(size_t nPos) const
Definition: dptabres.hxx:261
ResultMembers & GetDimResultMembers(tools::Long nDim, const ScDPDimension *pDim, ScDPLevel *pLevel) const
Definition: dptabres.cxx:920
const ScDPMember * GetDPMember() const
Ref.
Definition: dptabres.hxx:417
bool IsNamedItem(SCROW nIndex) const
Definition: dptabres.cxx:1011
const ScDPDimension * mpParentDim
Ref.
Definition: dptabres.hxx:222
void UpdateDataResults(const ScDPResultMember *pRefMember, tools::Long nMeasure) const
Definition: dptabres.cxx:1649
void DumpState(const ScDPResultMember *pRefMember, ScDocument *pDoc, ScAddress &rPos) const
ScDPParentDimData()
Ref.
Definition: dptabres.cxx:4026
ScDPMembers * GetMembersObject()
Definition: dptabsrc.cxx:1969
void ProcessData(const ::std::vector< SCROW > &aChildMembers, const ScDPResultDimension *pDataDim, const ::std::vector< SCROW > &aDataMembers, const ::std::vector< ScDPValue > &aValues)
Definition: dptabres.cxx:1257
Select subtotal information, passed down the dimensions.
Definition: dptabres.hxx:80
SC_DLLPUBLIC void SetValue(SCCOL nCol, SCROW nRow, SCTAB nTab, const double &rVal)
Definition: document.cxx:3489
void SetAutoHidden()
Definition: dptabres.hxx:378
void FillDataResults(const ScDPResultMember *pRefMember, ScDPResultFilterContext &rFilterCxt, css::uno::Sequence< css::uno::Sequence< css::sheet::DataResult > > &rSequence, tools::Long nMeasure) const
Definition: dptabres.cxx:3060
bool IsValid() const
Definition: dptabres.cxx:1212
void UpdateRunningTotals(const ScDPResultDimension *pRefDim, tools::Long nMeasure, bool bIsSubTotalRow, const ScDPSubTotalState &rSubState, ScDPRunningTotalState &rRunning, ScDPRowTotals &rTotals, const ScDPResultMember &rRowParent) const
Definition: dptabres.cxx:3794
tools::Long GetSize(tools::Long nMeasure) const
Definition: dptabres.cxx:2975
void Dump(int nIndent) const
OUString GetMeasureString(tools::Long nMeasure, bool bForce, ScSubTotalFunc eForceFunc, bool &rbTotalResult) const
Definition: dptabres.cxx:840
bool IsAddEmpty() const
Definition: dptabsrc.hxx:535
bool IsVisible() const
Definition: dptabres.cxx:1856
void DoAutoShow(ScDPResultMember *pRefMember)
Definition: dptabres.cxx:3140
ScMemberSortOrder aMemberOrder
Definition: dptabres.hxx:498
std::vector< Member > maMembers
Definition: dptabres.hxx:72
tools::Long GetRowStartMeasure() const
Definition: dptabres.cxx:814
exports com.sun.star. sheet
std::vector< sal_Int32 > IndexArray
Definition: dptabres.hxx:107
tools::Long GetCountForMeasure(tools::Long nMeas) const
Definition: dptabres.hxx:313
const ::std::vector< ScDPDimension * > & mppDim
Definition: dptabres.hxx:246
std::unique_ptr< ScDPDataDimension > pChildDimension
Ref?
Definition: dptabres.hxx:432
void add(const std::vector< ScDPResultFilter > &rFilter, double fVal)
Add a single value filter path.
Definition: dpresfilter.cxx:89
ScDPDataDimension(const ScDPResultData *pData)
or ptr to IntDimension?
Definition: dptabres.cxx:3528
bool IsVisible() const
Definition: dptabres.cxx:1194
void Calculate(ScSubTotalFunc eFunc, const ScDPSubTotalState &rSubState)
Definition: dptabres.cxx:449
ScDPResultMember * GetRowResRoot() const
Definition: dptabres.hxx:112
ScDPAggData * GetColTotal(tools::Long nMeasure) const
Definition: dptabres.cxx:1826
sal_uInt16 sal_Unicode
double getVariancePopulation() const
Definition: subtotal.hxx:45
ScDPResultMember(const ScDPResultData *pData, const ScDPParentDimData &rParentDimData)
Definition: dptabres.cxx:949
const ScDPResultData * pResultData
Definition: dptabres.hxx:604
void SetError()
Definition: dptabres.cxx:628
bool mbHasHideDetailsMember
Definition: dptabres.hxx:233
void FillMemberResults(css::uno::Sequence< css::sheet::MemberResult > *pSequences, tools::Long nStart, tools::Long nMeasure)
Test.
Definition: dptabres.cxx:3033
const std::optional< OUString > & GetSubtotalName() const
Definition: dptabsrc.cxx:1300
bool IsSortAscending() const
Definition: dptabres.hxx:588
void AddRowIndex(sal_Int32 nVisible, tools::Long nSorted)
Definition: dptabres.cxx:327
void FillVisibilityData(ScDPResultVisibilityData &rData) const
Definition: dptabres.cxx:3511
ScDPResultMember * GetColResRoot() const
Definition: dptabres.hxx:111
bool IsNamedItem(SCROW nRow) const
Definition: dptabres.cxx:1864
tools::Long nColSubTotalFunc
Definition: dptabres.hxx:84
tools::Long nAutoCount
Definition: dptabres.hxx:506
void SortMembers(ScDPResultDimension *pRefDim)
Definition: dptabres.cxx:3668
void SortMembers(ScDPResultMember *pRefMember)
Definition: dptabres.cxx:3111
ScDPDimensions * GetDimensionsObject()
Definition: dptabsrc.cxx:292
const ScDPSource & GetSource() const
Definition: dptabres.hxx:325
ScDPAggData aRowTotal
Definition: dptabres.hxx:195
static tools::Long lcl_GetSubTotalPos(const ScDPSubTotalState &rSubState)
Definition: dptabres.cxx:1890
multi-item (group) filter.
int nCount
void SetDataLayoutOrientation(css::sheet::DataPilotFieldOrientation nOrient)
Definition: dptabres.cxx:795
SC_DLLPUBLIC OUString GetString(SCCOL nCol, SCROW nRow, SCTAB nTab, const ScInterpreterContext *pContext=nullptr) const
Definition: document.cxx:3517
SCROW GetDataId() const
Definition: dptabres.cxx:3954
static bool SafeMult(double &fVal1, double fVal2)
Definition: subtotal.cxx:40
sal_Int32 GetSortMeasure() const
Definition: dptabsrc.hxx:518
void InitWithMembers(LateInitParams &rParams, const ::std::vector< SCROW > &pItemData, size_t nPos, ScDPInitState &rInitState)
Definition: dptabres.cxx:3987
sal_Int32 GetAutoMeasure() const
Definition: dptabsrc.hxx:519
ScDPResultMember * FindMember(SCROW iData) const
Definition: dptabres.cxx:2767
virtual OUString SAL_CALL getName() override
Definition: dptabsrc.cxx:1310
const ScDPLevel * GetParentLevel() const
Ref.
Definition: dptabres.hxx:416
SCTAB Tab() const
Definition: address.hxx:270
void SetRow(SCROW nRowP)
Definition: address.hxx:274
OUString GetName() const
Definition: dptabres.cxx:1848
void RemoveMember()
Definition: dptabres.cxx:261
void SetInColRoot(bool bSet)
Definition: dptabres.hxx:207
static bool lcl_IsLess(const ScDPDataMember *pDataMember1, const ScDPDataMember *pDataMember2, tools::Long nMeasure, bool bAscending)
Definition: dptabres.cxx:170
std::shared_ptr< FilterBase > mpFilter
bool getShowEmpty() const
Definition: dptabsrc.hxx:507
tools::Long GetSortedIndex(tools::Long nUnsorted) const
Definition: dptabres.cxx:3785
bool IsInColRoot() const
Definition: dptabres.hxx:206
ScDPResultMember * AddMember(const ScDPParentDimData &aData)
Definition: dptabres.cxx:3962
bool IsInGroup(SCROW nGroupDataId, tools::Long nGroupIndex, const ScDPItemData &rBaseData, tools::Long nBaseIndex) const
Definition: dptabres.cxx:900
const ::std::vector< sal_Int32 > & GetGlobalOrder() const
Definition: dptabsrc.hxx:516
bool HasHiddenDetails() const
Definition: dptabres.cxx:1872
ScDPResultMember * InsertMember(const ScDPParentDimData *pMemberData)
Definition: dptabres.cxx:3972
ScDPResultVisibilityData(ScDPSource *pSource)
Definition: dptabres.cxx:3879
void UpdateRunningTotals(const ScDPResultMember *pRefMember, tools::Long nMeasure, ScDPRunningTotalState &rRunning, ScDPRowTotals &rTotals) const
Definition: dptabres.cxx:1737
void FillDataResults(const ScDPResultMember *pRefMember, ScDPResultFilterContext &rFilterCxt, css::uno::Sequence< css::uno::Sequence< css::sheet::DataResult > > &rSequence, tools::Long nMeasure) const
Definition: dptabres.cxx:1537
indexes when calculating running totals
Definition: dptabres.hxx:104
constexpr OUStringLiteral aData
#define SAL_N_ELEMENTS(arr)
virtual bool IsInGroup(const ScDPItemData &rGroupData, sal_Int32 nGroupIndex, const ScDPItemData &rBaseData, sal_Int32 nBaseIndex) const
Definition: dptabdat.cxx:127
DocumentType eType
virtual bool IsBaseForGroup(sal_Int32 nDim) const
Definition: dptabdat.cxx:112
const OUString & GetName() const
Definition: dptabres.hxx:585
void Dump(int nIndent) const
bool HasData() const
Definition: dptabres.cxx:614
void FillVisibilityData(ScDPResultVisibilityData &rData) const
Definition: dptabres.cxx:1831
double GetResult() const
Definition: dptabres.cxx:600
double GetAggregate(tools::Long nMeasure, const ScDPSubTotalState &rSubState) const
Definition: dptabres.cxx:1990
tools::Long GetSortedIndex(tools::Long nUnsorted) const
Definition: dptabres.cxx:3211
const ScDPDataMember * GetMember(tools::Long n) const
Definition: dptabres.cxx:3869
SC_DLLPUBLIC bool SetString(SCCOL nCol, SCROW nRow, SCTAB nTab, const OUString &rString, const ScSetStringParam *pParam=nullptr)
Definition: document.cxx:3394
Implements the Welford Online one-pass algorithm.
Definition: subtotal.hxx:38
void addVisibleMember(const OUString &rDimName, const ScDPItemData &rMemberItem)
Definition: dptabres.cxx:3889
ScDPResultData(ScDPSource &rSrc)
Definition: dptabres.cxx:760
sal_uInt16 char * pName
Definition: callform.cxx:57
bool IsHasHideDetailsMembers() const
Definition: dptabres.hxx:237
Type GetType() const
Definition: dpitemdata.hxx:67
double fVal
Definition: dptabres.hxx:157
When assigning a string value, you can also assign an interned string whose life-cycle is managed by ...
Definition: dpitemdata.hxx:28
ScDPResultDimension(const ScDPResultData *pData)
Definition: dptabres.cxx:2749
WelfordRunner maWelford
Definition: dptabres.hxx:156
DimMemberType maDimensions
Definition: dptabres.hxx:665
int i
void UpdateDataRow(const ScDPResultDimension *pRefDim, tools::Long nMeasure, bool bIsSubTotalRow, const ScDPSubTotalState &rSubState) const
Definition: dptabres.cxx:3641
ScDPDimension * getByIndex(tools::Long nIndex) const
Definition: dptabsrc.cxx:1249
tools::Long GetAutoCount() const
Definition: dptabres.hxx:594
void FillMemberResults(css::uno::Sequence< css::sheet::MemberResult > *pSequences, tools::Long &rPos, tools::Long nMeasure, bool bRoot, const OUString *pMemberName, const OUString *pMemberCaption)
Definition: dptabres.cxx:1323
std::vector< ScSubTotalFunc > maMeasureFuncs
keep things like measure lists here
Definition: dptabres.hxx:277
virtual OUString getDimensionName(sal_Int32 nColumn)=0
void ProcessData(const ::std::vector< SCROW > &aChildMembers, const ::std::vector< ScDPValue > &aValues, const ScDPSubTotalState &rSubState)
Definition: dptabres.cxx:1931
static ScDPAggData * lcl_GetChildTotal(ScDPAggData *pFirst, tools::Long nMeasure)
Definition: dptabres.cxx:700
tools::Long GetGroupBase(tools::Long nGroupDim) const
Definition: dptabres.cxx:890
void AddMember(tools::Long nSourceIndex, SCROW nMember)
Definition: dptabres.cxx:256
sal_Int16 SCCOL
Definition: types.hxx:21
std::vector< std::unique_ptr< ScDPDataMember > > maMembers
Definition: dptabres.hxx:606
static bool lcl_IsEqual(const ScDPDataMember *pDataMember1, const ScDPDataMember *pDataMember2, tools::Long nMeasure)
Definition: dptabres.cxx:196
const ScDPItemData * GetItemDataById(sal_Int32 nDim, sal_Int32 nId)
Definition: dptabsrc.cxx:2612
void DoAutoShow(ScDPResultDimension *pRefDim)
Definition: dptabres.cxx:3704
bool bSortByData
or ptr to IntDimension?
Definition: dptabres.hxx:500
std::unordered_set< ScDPItemData, MemberHash > VisibleMemberType
Definition: dptabres.hxx:663
const css::sheet::DataPilotFieldSortInfo & GetSortInfo() const
Definition: dptabsrc.hxx:510
css::sheet::DataPilotFieldOrientation GetMeasureRefOrient(tools::Long nMeasure) const
Definition: dptabres.cxx:834
void ResetResults()
Definition: dptabres.cxx:2268
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
void DumpState(const ScDPResultMember *pRefMember, ScDocument *pDoc, ScAddress &rPos) const
SCROW GetItemDataId() const
Definition: dptabsrc.hxx:621
ScDPDataMember(const ScDPResultData *pData, const ScDPResultMember *pRes)
Definition: dptabres.cxx:1837
LateInitParams(const ::std::vector< ScDPDimension * > &ppDim, const ::std::vector< ScDPLevel * > &ppLev, bool bRow)
Definition: dptabres.cxx:4057
const ScMemberSortOrder & GetMemberOrder() const
Definition: dptabres.hxx:581
tools::Long GetSize(tools::Long nMeasure) const
Definition: dptabres.cxx:1157
void DoAutoShow(ScDPResultMember *pRefMember)
Definition: dptabres.cxx:2255
tools::Long GetMemberCount() const
Definition: dptabres.cxx:3489
double GetAuxiliary() const
Definition: dptabres.cxx:645
double mfValue
Definition: dpglobal.hxx:48
bool HasHiddenDetails() const
Definition: dptabres.hxx:369
void FillDataRow(const ScDPResultDimension *pRefDim, ScDPResultFilterContext &rFilterCxt, css::uno::Sequence< css::sheet::DataResult > &rSequence, tools::Long nMeasure, bool bIsSubTotalRow, const ScDPSubTotalState &rSubState) const
Definition: dptabres.cxx:3596
ScDPSource & mrSource
Definition: dptabres.hxx:274
ScDPItemData FillItemData() const
Definition: dptabsrc.cxx:2509
css::uno::Sequence< sal_Int16 > getSubTotals() const
Definition: dptabsrc.cxx:2054
tools::Long nBasePos
Definition: dptabres.hxx:136
size_t operator()(const ScDPItemData &r) const
Definition: dptabres.cxx:3947
const ScDPDimension * GetParentDim() const
Definition: dptabres.hxx:415
bool IsEnd(size_t nPos) const
Definition: dptabres.cxx:4071
bool IsSortByData() const
Definition: dptabres.hxx:587
const ScDPResultMember * pResultMember
Definition: dptabres.hxx:430
std::unordered_map< SCROW, ScDPParentDimData > maMemberHash
Definition: dptabres.hxx:232
bool getShowDetails() const
Definition: dptabsrc.hxx:655
ScDPAggData aGrandTotal
Definition: dptabres.hxx:196
void addMatchItem(const ScDPItemData &rItem)
void fillFieldFilters(::std::vector< ScDPFilteredCache::Criterion > &rFilters) const
Definition: dptabres.cxx:3907
SCCOL Col() const
Definition: address.hxx:266
bool IsRoot() const
Ref.
Definition: dptabres.hxx:419
ScDPAggData * GetAggData(tools::Long nMeasure, const ScDPSubTotalState &rSubState)
Definition: dptabres.cxx:1999
const ScDPResultData * pResultData
Definition: dptabres.hxx:493
const SCROW mnOrder
Definition: dptabres.hxx:221
void UpdateRunningTotals(const ScDPResultMember *pRefMember, tools::Long nMeasure, ScDPRunningTotalState &rRunning, ScDPRowTotals &rTotals) const
Definition: dptabres.cxx:3216
std::vector< std::unique_ptr< ResultMembers > > maDimMembers
add "displayed values" settings
Definition: dptabres.hxx:287
std::vector< double > mSortedValues
Definition: dptabres.hxx:161
void Dump(int nIndent) const
const IndexArray & GetRowVisible() const
Definition: dptabres.hxx:116
const ScDPMember * mpMemberDesc
Ref.
Definition: dptabres.hxx:224
static bool SafePlus(double &fVal1, double fVal2)
Definition: subtotal.cxx:24
void AddColIndex(sal_Int32 nVisible, tools::Long nSorted)
Definition: dptabres.cxx:318
void SortMembers(ScDPResultMember *pRefMember)
Definition: dptabres.cxx:2242
OUString GetMeasureDimensionName(tools::Long nMeasure) const
Definition: dptabres.cxx:874
const ScDPResultMember * GetMember(tools::Long n) const
Definition: dptabres.cxx:3494
void SetHasHideDetailsMembers(bool b)
Definition: dptabres.hxx:238
Base class that abstracts different data source types of a datapilot table.
Definition: dptabdat.hxx:56
sal_Int32 SCROW
Definition: types.hxx:17
IndexArray maColVisible
Definition: dptabres.hxx:128
#define SC_DPMEASURE_ALL
Definition: dptabres.hxx:216
single filtering criterion.
void FillDataRow(const ScDPResultMember *pRefMember, ScDPResultFilterContext &rFilterCxt, css::uno::Sequence< css::sheet::DataResult > &rSequence, tools::Long nMeasure, bool bIsSubTotalRow, const ScDPSubTotalState &rSubState) const
Definition: dptabres.cxx:2039
const IndexArray & GetColVisible() const
Definition: dptabres.hxx:114
bool HasError() const
Definition: dptabres.cxx:607
bool IsNamedItem(SCROW nIndex) const
Definition: dptabsrc.cxx:2464
OUString ScResId(std::string_view aId)
Definition: scdll.cxx:89
void ProcessData(const ::std::vector< SCROW > &aMembers, const ScDPResultDimension *pDataDim, const ::std::vector< SCROW > &aDataMembers, const ::std::vector< ScDPValue > &aValues) const
Definition: dptabres.cxx:3009
const IndexArray & GetRowSorted() const
Definition: dptabres.hxx:117
void SetResult(double fNew)
Definition: dptabres.cxx:621
bool IsValidEntry(const ::std::vector< SCROW > &aMembers) const
Definition: dptabres.cxx:1020
void DumpState(const ScDPResultMember *pRefMember, ScDocument *pDoc, ScAddress &rPos) const
void InitFrom(const ScDPResultDimension *pDim)
Definition: dptabres.cxx:3539
double fAux
Definition: dptabres.hxx:158
void InitFrom(const ::std::vector< ScDPDimension * > &ppDim, const ::std::vector< ScDPLevel * > &ppLev, size_t nPos, ScDPInitState &rInitState, bool bInitChild=true)
Definition: dptabres.cxx:2790
void SetMeasureData(std::vector< ScSubTotalFunc > &rFunctions, std::vector< css::sheet::DataPilotFieldReference > &rRefs, std::vector< css::sheet::DataPilotFieldOrientation > &rRefOrient, std::vector< OUString > &rNames)
Definition: dptabres.cxx:772
const ScDPResultDimension * GetChildDimension() const
this will be removed!
Definition: dptabres.hxx:410
#define SC_DPMEASURE_ANY
Definition: dptabres.hxx:217
ScGeneralFunction
the css::sheet::GeneralFunction enum is extended by constants in GeneralFunction2, which causes some type-safety issues.
sal_uInt64 getCount() const
Definition: subtotal.hxx:43
#define SAL_INFO(area, stream)
OUString aName
ScDPResultDimension * GetFirstChildDimension() const
Definition: dptabres.cxx:3503
tools::Long GetSortMeasure() const
Definition: dptabres.hxx:589
This class collects visible members of each dimension and uses that information to create filtering c...
Definition: dptabres.hxx:649
ScSubTotalFunc eColForce
Definition: dptabres.hxx:82
IndexArray maRowVisible
Definition: dptabres.hxx:130
tools::Long nSortMeasure
or ptr to IntDimension?
Definition: dptabres.hxx:497
void ProcessData(const ::std::vector< SCROW > &aDataMembers, const ::std::vector< ScDPValue > &aValues, const ScDPSubTotalState &rSubState)
Definition: dptabres.cxx:3569
sal_uInt16 nMemberStep
Definition: dptabres.hxx:342
tools::Long GetMeasureCount() const
Definition: dptabres.hxx:301
tools::Long nDirection
Definition: dptabres.hxx:137
bool IsDataLayout() const
Definition: dptabres.hxx:584
void * p
bool IsRow() const
Definition: dptabres.hxx:265
const IndexArray & GetColSorted() const
Definition: dptabres.hxx:115
void Dump(int nIndent) const
void update(double fVal)
Definition: subtotal.cxx:196
double GetValue() const
Definition: dpitemdata.cxx:347
Sequence< sal_Int8 > aSeq
double getLength(const B2DPolygon &rCandidate)
ScDPAggData * GetRowTotal(tools::Long nMeasure)
Definition: dptabres.cxx:722
tools::Long GetDim() const
Definition: dptabsrc.hxx:625
ScDPRunningTotalState(ScDPResultMember *pColRoot, ScDPResultMember *pRowRoot)
array of sal_Int32 terminated by -1.
Definition: dptabres.cxx:308
OUString GetNameStr(bool bLocaleIndependent) const
Definition: dptabsrc.cxx:2522
bool HasData(tools::Long nMeasure, const ScDPSubTotalState &rSubState) const
Definition: dptabres.cxx:1966
OUString GetFormattedString(sal_Int32 nDim, const ScDPItemData &rItem, bool bLocaleIndependent) const
Definition: dptabdat.cxx:51
bool IsValue() const
Definition: dpitemdata.cxx:322
void DumpState(const ScDPResultDimension *pRefDim, ScDocument *pDoc, ScAddress &rPos) const
bool GetInitAllChild() const
Definition: dptabres.hxx:264
bool IsValidEntry(const ::std::vector< SCROW > &aMembers) const
Definition: dptabres.cxx:2995
Type meType
Definition: dpglobal.hxx:49
ScDPDataMember * GetRowReferenceMember(const ScDPRelativePos *pMemberPos, const OUString *pName, const sal_Int32 *pRowIndexes, const sal_Int32 *pColIndexes) const
Definition: dptabres.cxx:3248
ScDPAggData * GetChild()
Definition: dptabres.cxx:661
const tools::Long SC_SUBTOTALPOS_SKIP
Definition: dptabres.cxx:1888
bool getIsDataLayoutDimension() const
Definition: dptabsrc.cxx:1329
void SetHasElements()
Definition: dptabres.hxx:377
IndexArray maColSorted
Definition: dptabres.hxx:129
static SC_DLLPUBLIC OUString getDisplayedMeasureName(const OUString &rName, ScSubTotalFunc eFunc)
Definition: dputil.cxx:384
const ScDPAggData * GetExistingChild() const
Definition: dptabres.hxx:183
#define SAL_WARN(area, stream)
tools::Long GetColStartMeasure() const
Definition: dptabres.cxx:806
OUString GetString() const
Definition: dpitemdata.cxx:327
void UpdateValues(const ::std::vector< ScDPValue > &aValues, const ScDPSubTotalState &rSubState)
Definition: dptabres.cxx:1907
void Reset()
Definition: dptabres.cxx:668
void InsertMember(ScDPParentDimData const &rNew)
Definition: dptabres.cxx:4042
sal_Int64 nCount
Definition: dptabres.hxx:159
const ScDPDataDimension * GetChildDimension() const
this will be removed!
Definition: dptabres.hxx:481
ScSubTotalFunc
Definition: global.hxx:844
ScDPHierarchies * GetHierarchiesObject()
Definition: dptabsrc.cxx:1286
const char *const aFieldNames[]
void Dump(int nIndent) const
void UpdateRunningTotals(const ScDPResultMember *pRefMember, tools::Long nMeasure, bool bIsSubTotalRow, const ScDPSubTotalState &rSubState, ScDPRunningTotalState &rRunning, ScDPRowTotals &rTotals, const ScDPResultMember &rRowParent)
Definition: dptabres.cxx:2277
std::vector< OUString > maMeasureNames
Definition: dptabres.hxx:280
OUString GetDisplayName(bool bLocaleIndependent) const
Definition: dptabres.cxx:987
bool IsAutoShow() const
Definition: dptabres.hxx:591
bool IsLateInit() const
Definition: dptabres.hxx:308
const std::optional< OUString > & GetLayoutName() const
Definition: dptabsrc.cxx:2517
const ScDPResultData * pResultData
Definition: dptabres.hxx:429
bool IsSubTotalInTitle(tools::Long nMeasure) const
Definition: dptabres.cxx:1135
static OUString lcl_parseSubtotalName(const OUString &rSubStr, std::u16string_view rCaption)
Parse subtotal string and replace all occurrences of '?' with the caption string. ...
Definition: dptabres.cxx:1300
ScDPItemData FillItemData() const
Definition: dptabres.cxx:1003
void LateInitFrom(LateInitParams &rParams, const ::std::vector< SCROW > &pItemData, size_t nPos, ScDPInitState &rInitState)
Definition: dptabres.cxx:2858
bool GetInitChild() const
Definition: dptabres.hxx:263
tools::Long GetSubTotalCount(tools::Long *pUserSubStart=nullptr) const
Definition: dptabres.cxx:1227
bool IsBaseForGroup(tools::Long nDim) const
Definition: dptabres.cxx:885
ScDPAggData aColTotal
Definition: dptabres.hxx:340
Member(tools::Long nSrcIndex, SCROW nNameIndex)
Definition: dptabres.cxx:253
bool IsNumOrDateGroup(tools::Long nDim) const
Definition: dptabres.cxx:895
bool HasError(tools::Long nMeasure, const ScDPSubTotalState &rSubState) const
Definition: dptabres.cxx:1981
virtual bool IsNumOrDateGroup(sal_Int32 nDim) const
Definition: dptabdat.cxx:122
tools::Long nAutoMeasure
Definition: dptabres.hxx:505
const sal_Int64 SC_DPAGG_RESULT_VALID
Definition: dptabres.hxx:150
void LateInitFrom(LateInitParams &rParams, const ::std::vector< SCROW > &pItemData, size_t nPos, ScDPInitState &rInitState)
Definition: dptabres.cxx:1083
const sal_Int64 SC_DPAGG_EMPTY
separate header file?
Definition: dptabres.hxx:147
sal_uInt16 nPos
static SC_DLLPUBLIC ScSubTotalFunc toSubTotalFunc(ScGeneralFunction eGenFunc)
Definition: dputil.cxx:395
sal_Int16 SCTAB
Definition: types.hxx:22
tools::Long GetAutoMeasure() const
Definition: dptabres.hxx:593
const ScDPAggData * GetConstAggData(tools::Long nMeasure, const ScDPSubTotalState &rSubState) const
Definition: dptabres.cxx:2017
ScDPMember * getByIndex(sal_Int32 nIndex) const
Definition: dptabsrc.cxx:2352
const sal_Int64 SC_DPAGG_RESULT_ERROR
Definition: dptabres.hxx:151
MemberHash maMemberHash
Definition: dptabres.hxx:495
ScSubTotalFunc GetMeasureFunction(tools::Long nMeasure) const
Definition: dptabres.cxx:822
bool getRepeatItemLabels() const
Definition: dptabsrc.hxx:508
::std::vector< sal_Int32 > ScMemberSortOrder
Definition: dptabres.hxx:75
void UpdateDataResults(const ScDPResultMember *pRefMember, tools::Long nMeasure) const
Definition: dptabres.cxx:3089
virtual sal_Int32 GetColumnCount()=0
use (new) typed collection instead of ScStrCollection or separate Str and ValueCollection ...
bool isVisible() const
Definition: dptabsrc.hxx:654
const std::optional< OUString > & GetLayoutName() const
Definition: dptabsrc.cxx:1295
ScDPDataMember * GetDataRoot() const
Definition: dptabres.hxx:413
ScDPAggData * GetGrandTotal(tools::Long nMeasure)
Definition: dptabres.cxx:727