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