1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <dptabres.hxx>
22 #include <dptabdat.hxx>
23 #include <dptabsrc.hxx>
25 #include <subtotal.hxx>
26 #include <globstr.hrc>
27 #include <scresid.hxx>
28 #include <dpitemdata.hxx>
29 #include <generalfunction.hxx>
31 #include <document.hxx>
32 #include <dpresfilter.hxx>
35 #include <o3tl/safeint.hxx>
36 #include <osl/diagnose.h>
37 #include <rtl/math.hxx>
38 #include <sal/log.hxx>
45 #include <unordered_map>
47 #include <com/sun/star/sheet/DataResultFlags.hpp>
48 #include <com/sun/star/sheet/MemberResultFlags.hpp>
49 #include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
50 #include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
51 #include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
52 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
53 #include <com/sun/star/sheet/GeneralFunction2.hpp>
55 using namespace com::sun::star
;
58 using ::com::sun::star::uno::Sequence
;
62 const TranslateId aFuncStrIds
[] = // matching enum ScSubTotalFunc
64 {}, // SUBTOTAL_FUNC_NONE
65 STR_FUN_TEXT_AVG
, // SUBTOTAL_FUNC_AVE
66 STR_FUN_TEXT_COUNT
, // SUBTOTAL_FUNC_CNT
67 STR_FUN_TEXT_COUNT
, // SUBTOTAL_FUNC_CNT2
68 STR_FUN_TEXT_MAX
, // SUBTOTAL_FUNC_MAX
69 STR_FUN_TEXT_MIN
, // SUBTOTAL_FUNC_MIN
70 STR_FUN_TEXT_PRODUCT
, // SUBTOTAL_FUNC_PROD
71 STR_FUN_TEXT_STDDEV
, // SUBTOTAL_FUNC_STD
72 STR_FUN_TEXT_STDDEV
, // SUBTOTAL_FUNC_STDP
73 STR_FUN_TEXT_SUM
, // SUBTOTAL_FUNC_SUM
74 STR_FUN_TEXT_VAR
, // SUBTOTAL_FUNC_VAR
75 STR_FUN_TEXT_VAR
, // SUBTOTAL_FUNC_VARP
76 STR_FUN_TEXT_MEDIAN
, // SUBTOTAL_FUNC_MED
77 {} // SUBTOTAL_FUNC_SELECTION_COUNT - not used for pivot table
80 bool lcl_SearchMember( const std::vector
<std::unique_ptr
<ScDPResultMember
>>& list
, SCROW nOrder
, SCROW
& rIndex
)
84 SCROW nHi
= list
.size() - 1;
88 nIndex
= (nLo
+ nHi
) / 2;
89 if ( list
[nIndex
]->GetOrder() < nOrder
)
94 if ( list
[nIndex
]->GetOrder() == nOrder
)
107 std::vector
<ScDPResultFilter
>& mrFilters
;
109 explicit FilterStack(std::vector
<ScDPResultFilter
>& rFilters
) : mrFilters(rFilters
) {}
111 void pushDimName(const OUString
& rName
, bool bDataLayout
)
113 mrFilters
.emplace_back(rName
, bDataLayout
);
116 void pushDimValue(const OUString
& rValueName
, const OUString
& rValue
)
118 ScDPResultFilter
& rFilter
= mrFilters
.back();
119 rFilter
.maValueName
= rValueName
;
120 rFilter
.maValue
= rValue
;
121 rFilter
.mbHasValue
= true;
126 ScDPResultFilter
& rFilter
= mrFilters
.back();
127 if (rFilter
.mbHasValue
)
128 rFilter
.mbHasValue
= false;
130 mrFilters
.pop_back();
134 // function objects for sorting of the column and row members:
136 class ScDPRowMembersOrder
138 ScDPResultDimension
& rDimension
;
139 tools::Long nMeasure
;
143 ScDPRowMembersOrder( ScDPResultDimension
& rDim
, tools::Long nM
, bool bAsc
) :
149 bool operator()( sal_Int32 nIndex1
, sal_Int32 nIndex2
) const;
152 class ScDPColMembersOrder
154 ScDPDataDimension
& rDimension
;
155 tools::Long nMeasure
;
159 ScDPColMembersOrder( ScDPDataDimension
& rDim
, tools::Long nM
, bool bAsc
) :
165 bool operator()( sal_Int32 nIndex1
, sal_Int32 nIndex2
) const;
170 static bool lcl_IsLess( const ScDPDataMember
* pDataMember1
, const ScDPDataMember
* pDataMember2
, tools::Long nMeasure
, bool bAscending
)
172 // members can be NULL if used for rows
174 ScDPSubTotalState aEmptyState
;
175 const ScDPAggData
* pAgg1
= pDataMember1
? pDataMember1
->GetConstAggData( nMeasure
, aEmptyState
) : nullptr;
176 const ScDPAggData
* pAgg2
= pDataMember2
? pDataMember2
->GetConstAggData( nMeasure
, aEmptyState
) : nullptr;
178 bool bError1
= pAgg1
&& pAgg1
->HasError();
179 bool bError2
= pAgg2
&& pAgg2
->HasError();
181 return false; // errors are always sorted at the end
183 return true; // errors are always sorted at the end
186 double fVal1
= ( pAgg1
&& pAgg1
->HasData() ) ? pAgg1
->GetResult() : 0.0; // no data is sorted as 0
187 double fVal2
= ( pAgg2
&& pAgg2
->HasData() ) ? pAgg2
->GetResult() : 0.0;
190 // don't have to check approxEqual, as this is the only sort criterion
192 return bAscending
? ( fVal1
< fVal2
) : ( fVal1
> fVal2
);
196 static bool lcl_IsEqual( const ScDPDataMember
* pDataMember1
, const ScDPDataMember
* pDataMember2
, tools::Long nMeasure
)
198 // members can be NULL if used for rows
200 ScDPSubTotalState aEmptyState
;
201 const ScDPAggData
* pAgg1
= pDataMember1
? pDataMember1
->GetConstAggData( nMeasure
, aEmptyState
) : nullptr;
202 const ScDPAggData
* pAgg2
= pDataMember2
? pDataMember2
->GetConstAggData( nMeasure
, aEmptyState
) : nullptr;
204 bool bError1
= pAgg1
&& pAgg1
->HasError();
205 bool bError2
= pAgg2
&& pAgg2
->HasError();
209 return true; // equal
217 double fVal1
= ( pAgg1
&& pAgg1
->HasData() ) ? pAgg1
->GetResult() : 0.0; // no data is sorted as 0
218 double fVal2
= ( pAgg2
&& pAgg2
->HasData() ) ? pAgg2
->GetResult() : 0.0;
221 // this is used to find equal data at the end of the AutoShow range, so approxEqual must be used
223 return rtl::math::approxEqual( fVal1
, fVal2
);
227 bool ScDPRowMembersOrder::operator()( sal_Int32 nIndex1
, sal_Int32 nIndex2
) const
229 const ScDPResultMember
* pMember1
= rDimension
.GetMember(nIndex1
);
230 const ScDPResultMember
* pMember2
= rDimension
.GetMember(nIndex2
);
232 // make the hide item to the largest order.
233 if ( !pMember1
->IsVisible() || !pMember2
->IsVisible() )
234 return pMember1
->IsVisible();
235 const ScDPDataMember
* pDataMember1
= pMember1
->GetDataRoot() ;
236 const ScDPDataMember
* pDataMember2
= pMember2
->GetDataRoot();
237 // GetDataRoot can be NULL if there was no data.
238 // IsVisible == false can happen after AutoShow.
239 return lcl_IsLess( pDataMember1
, pDataMember2
, nMeasure
, bAscending
);
242 bool ScDPColMembersOrder::operator()( sal_Int32 nIndex1
, sal_Int32 nIndex2
) const
244 const ScDPDataMember
* pDataMember1
= rDimension
.GetMember(nIndex1
);
245 const ScDPDataMember
* pDataMember2
= rDimension
.GetMember(nIndex2
);
246 bool bHide1
= pDataMember1
&& !pDataMember1
->IsVisible();
247 bool bHide2
= pDataMember2
&& !pDataMember2
->IsVisible();
248 if ( bHide1
|| bHide2
)
250 return lcl_IsLess( pDataMember1
, pDataMember2
, nMeasure
, bAscending
);
253 ScDPInitState::Member::Member(tools::Long nSrcIndex
, SCROW nNameIndex
) :
254 mnSrcIndex(nSrcIndex
), mnNameIndex(nNameIndex
) {}
256 void ScDPInitState::AddMember( tools::Long nSourceIndex
, SCROW nMember
)
258 maMembers
.emplace_back(nSourceIndex
, nMember
);
261 void ScDPInitState::RemoveMember()
263 OSL_ENSURE(!maMembers
.empty(), "ScDPInitState::RemoveMember: Attempt to remove member while empty.");
264 if (!maMembers
.empty())
265 maMembers
.pop_back();
272 const OUString
& rType
, const OUString
& rName
, const ScDPAggData
* pAggData
,
273 ScDocument
* pDoc
, ScAddress
& rPos
)
275 SCCOL nCol
= rPos
.Col();
276 SCROW nRow
= rPos
.Row();
277 SCTAB nTab
= rPos
.Tab();
278 pDoc
->SetString( nCol
++, nRow
, nTab
, rType
);
279 pDoc
->SetString( nCol
++, nRow
, nTab
, rName
);
282 pDoc
->SetValue( nCol
++, nRow
, nTab
, pAggData
->GetResult() );
283 pAggData
= pAggData
->GetExistingChild();
285 rPos
.SetRow( nRow
+ 1 );
288 void indent( ScDocument
* pDoc
, SCROW nStartRow
, const ScAddress
& rPos
)
290 SCCOL nCol
= rPos
.Col();
291 SCTAB nTab
= rPos
.Tab();
294 for (SCROW nRow
= nStartRow
; nRow
< rPos
.Row(); nRow
++)
296 aString
= pDoc
->GetString(nCol
, nRow
, nTab
);
297 if (!aString
.isEmpty())
299 aString
= " " + aString
;
300 pDoc
->SetString( nCol
, nRow
, nTab
, aString
);
308 ScDPRunningTotalState::ScDPRunningTotalState( ScDPResultMember
* pColRoot
, ScDPResultMember
* pRowRoot
) :
309 pColResRoot(pColRoot
), pRowResRoot(pRowRoot
)
311 // These arrays should never be empty as the terminating value must be present at all times.
312 maColVisible
.push_back(-1);
313 maColSorted
.push_back(-1);
314 maRowVisible
.push_back(-1);
315 maRowSorted
.push_back(-1);
318 void ScDPRunningTotalState::AddColIndex( sal_Int32 nVisible
, tools::Long nSorted
)
320 maColVisible
.back() = nVisible
;
321 maColVisible
.push_back(-1);
323 maColSorted
.back() = nSorted
;
324 maColSorted
.push_back(-1);
327 void ScDPRunningTotalState::AddRowIndex( sal_Int32 nVisible
, tools::Long nSorted
)
329 maRowVisible
.back() = nVisible
;
330 maRowVisible
.push_back(-1);
332 maRowSorted
.back() = nSorted
;
333 maRowSorted
.push_back(-1);
336 void ScDPRunningTotalState::RemoveColIndex()
338 OSL_ENSURE(!maColVisible
.empty() && !maColSorted
.empty(), "ScDPRunningTotalState::RemoveColIndex: array is already empty!");
339 if (maColVisible
.size() >= 2)
341 maColVisible
.pop_back();
342 maColVisible
.back() = -1;
345 if (maColSorted
.size() >= 2)
347 maColSorted
.pop_back();
348 maColSorted
.back() = -1;
352 void ScDPRunningTotalState::RemoveRowIndex()
354 OSL_ENSURE(!maRowVisible
.empty() && !maRowSorted
.empty(), "ScDPRunningTotalState::RemoveRowIndex: array is already empty!");
355 if (maRowVisible
.size() >= 2)
357 maRowVisible
.pop_back();
358 maRowVisible
.back() = -1;
361 if (maRowSorted
.size() >= 2)
363 maRowSorted
.pop_back();
364 maRowSorted
.back() = -1;
368 ScDPRelativePos::ScDPRelativePos( tools::Long nBase
, tools::Long nDir
) :
374 void ScDPAggData::Update( const ScDPValue
& rNext
, ScSubTotalFunc eFunc
, const ScDPSubTotalState
& rSubState
)
376 if (nCount
<0) // error?
377 return; // nothing more...
379 if (rNext
.meType
== ScDPValue::Empty
)
382 if ( rSubState
.eColForce
!= SUBTOTAL_FUNC_NONE
&& rSubState
.eRowForce
!= SUBTOTAL_FUNC_NONE
&&
383 rSubState
.eColForce
!= rSubState
.eRowForce
)
385 if ( rSubState
.eColForce
!= SUBTOTAL_FUNC_NONE
) eFunc
= rSubState
.eColForce
;
386 if ( rSubState
.eRowForce
!= SUBTOTAL_FUNC_NONE
) eFunc
= rSubState
.eRowForce
;
388 if ( eFunc
== SUBTOTAL_FUNC_NONE
)
391 if ( eFunc
!= SUBTOTAL_FUNC_CNT2
) // CNT2 counts everything, incl. strings and errors
393 if (rNext
.meType
== ScDPValue::Error
)
395 nCount
= -1; // -1 for error (not for CNT2)
398 if (rNext
.meType
== ScDPValue::String
)
402 ++nCount
; // for all functions
406 case SUBTOTAL_FUNC_SUM
:
407 case SUBTOTAL_FUNC_AVE
:
408 if ( !SubTotal::SafePlus( fVal
, rNext
.mfValue
) )
409 nCount
= -1; // -1 for error
411 case SUBTOTAL_FUNC_PROD
:
412 if ( nCount
== 1 ) // copy first value (fVal is initialized to 0)
413 fVal
= rNext
.mfValue
;
414 else if ( !SubTotal::SafeMult( fVal
, rNext
.mfValue
) )
415 nCount
= -1; // -1 for error
417 case SUBTOTAL_FUNC_CNT
:
418 case SUBTOTAL_FUNC_CNT2
:
419 // nothing more than incrementing nCount
421 case SUBTOTAL_FUNC_MAX
:
422 if ( nCount
== 1 || rNext
.mfValue
> fVal
)
423 fVal
= rNext
.mfValue
;
425 case SUBTOTAL_FUNC_MIN
:
426 if ( nCount
== 1 || rNext
.mfValue
< fVal
)
427 fVal
= rNext
.mfValue
;
429 case SUBTOTAL_FUNC_STD
:
430 case SUBTOTAL_FUNC_STDP
:
431 case SUBTOTAL_FUNC_VAR
:
432 case SUBTOTAL_FUNC_VARP
:
433 maWelford
.update( rNext
.mfValue
);
435 case SUBTOTAL_FUNC_MED
:
437 auto aIter
= std::upper_bound(mSortedValues
.begin(), mSortedValues
.end(), rNext
.mfValue
);
438 if (aIter
== mSortedValues
.end())
439 mSortedValues
.push_back(rNext
.mfValue
);
441 mSortedValues
.insert(aIter
, rNext
.mfValue
);
445 OSL_FAIL("invalid function");
449 void ScDPAggData::Calculate( ScSubTotalFunc eFunc
, const ScDPSubTotalState
& rSubState
)
451 // calculate the original result
452 // (without reference value, used as the basis for reference value calculation)
454 // called several times at the cross-section of several subtotals - don't calculate twice then
455 if ( IsCalculated() )
458 if ( rSubState
.eColForce
!= SUBTOTAL_FUNC_NONE
) eFunc
= rSubState
.eColForce
;
459 if ( rSubState
.eRowForce
!= SUBTOTAL_FUNC_NONE
) eFunc
= rSubState
.eRowForce
;
461 if ( eFunc
== SUBTOTAL_FUNC_NONE
) // this happens when there is no data dimension
463 nCount
= SC_DPAGG_RESULT_EMPTY
; // make sure there's a valid state for HasData etc.
467 // check the error conditions for the selected function
472 case SUBTOTAL_FUNC_SUM
:
473 case SUBTOTAL_FUNC_PROD
:
474 case SUBTOTAL_FUNC_CNT
:
475 case SUBTOTAL_FUNC_CNT2
:
476 bError
= ( nCount
< 0 ); // only real errors
479 case SUBTOTAL_FUNC_AVE
:
480 case SUBTOTAL_FUNC_MED
:
481 case SUBTOTAL_FUNC_MAX
:
482 case SUBTOTAL_FUNC_MIN
:
483 bError
= ( nCount
<= 0 ); // no data is an error
486 case SUBTOTAL_FUNC_STDP
:
487 case SUBTOTAL_FUNC_VARP
:
488 bError
= ( nCount
<= 0 ); // no data is an error
489 assert(bError
|| nCount
== static_cast<sal_Int64
>(maWelford
.getCount()));
492 case SUBTOTAL_FUNC_STD
:
493 case SUBTOTAL_FUNC_VAR
:
494 bError
= ( nCount
< 2 ); // need at least 2 values
495 assert(bError
|| nCount
== static_cast<sal_Int64
>(maWelford
.getCount()));
499 OSL_FAIL("invalid function");
502 // calculate the selected function
504 double fResult
= 0.0;
509 case SUBTOTAL_FUNC_MAX
:
510 case SUBTOTAL_FUNC_MIN
:
511 case SUBTOTAL_FUNC_SUM
:
512 case SUBTOTAL_FUNC_PROD
:
513 // different error conditions are handled above
517 case SUBTOTAL_FUNC_CNT
:
518 case SUBTOTAL_FUNC_CNT2
:
522 case SUBTOTAL_FUNC_AVE
:
524 fResult
= fVal
/ static_cast<double>(nCount
);
527 case SUBTOTAL_FUNC_STD
:
530 fResult
= maWelford
.getVarianceSample();
534 fResult
= sqrt( fResult
);
537 case SUBTOTAL_FUNC_VAR
:
539 fResult
= maWelford
.getVarianceSample();
541 case SUBTOTAL_FUNC_STDP
:
544 fResult
= maWelford
.getVariancePopulation();
548 fResult
= sqrt( fResult
);
551 case SUBTOTAL_FUNC_VARP
:
553 fResult
= maWelford
.getVariancePopulation();
555 case SUBTOTAL_FUNC_MED
:
557 size_t nSize
= mSortedValues
.size();
560 assert(nSize
== static_cast<size_t>(nCount
));
561 if ((nSize
% 2) == 1)
562 fResult
= mSortedValues
[nSize
/ 2];
564 fResult
= (mSortedValues
[nSize
/ 2 - 1] + mSortedValues
[nSize
/ 2]) / 2.0;
569 OSL_FAIL("invalid function");
573 bool bEmpty
= ( nCount
== 0 ); // no data
576 // Empty is checked first, so empty results are shown empty even for "average" etc.
577 // If these results should be treated as errors in reference value calculations,
578 // a separate state value (EMPTY_ERROR) is needed.
579 // Now, for compatibility, empty "average" results are counted as 0.
582 nCount
= SC_DPAGG_RESULT_EMPTY
;
584 nCount
= SC_DPAGG_RESULT_ERROR
;
586 nCount
= SC_DPAGG_RESULT_VALID
;
588 if ( bEmpty
|| bError
)
589 fResult
= 0.0; // default, in case the state is later modified
591 fVal
= fResult
; // used directly from now on
592 fAux
= 0.0; // used for running total or original result of reference value
595 bool ScDPAggData::IsCalculated() const
597 return ( nCount
<= SC_DPAGG_RESULT_EMPTY
);
600 double ScDPAggData::GetResult() const
602 assert( IsCalculated() && "ScDPAggData not calculated" );
604 return fVal
; // use calculated value
607 bool ScDPAggData::HasError() const
609 assert( IsCalculated() && "ScDPAggData not calculated" );
611 return ( nCount
== SC_DPAGG_RESULT_ERROR
);
614 bool ScDPAggData::HasData() const
616 assert( IsCalculated() && "ScDPAggData not calculated" );
618 return ( nCount
!= SC_DPAGG_RESULT_EMPTY
); // values or error
621 void ScDPAggData::SetResult( double fNew
)
623 assert( IsCalculated() && "ScDPAggData not calculated" );
625 fVal
= fNew
; // don't reset error flag
628 void ScDPAggData::SetError()
630 assert( IsCalculated() && "ScDPAggData not calculated" );
632 nCount
= SC_DPAGG_RESULT_ERROR
;
635 void ScDPAggData::SetEmpty( bool bSet
)
637 assert( IsCalculated() && "ScDPAggData not calculated" );
640 nCount
= SC_DPAGG_RESULT_EMPTY
;
642 nCount
= SC_DPAGG_RESULT_VALID
;
645 double ScDPAggData::GetAuxiliary() const
647 // after Calculate, fAux is used as auxiliary value for running totals and reference values
648 assert( IsCalculated() && "ScDPAggData not calculated" );
653 void ScDPAggData::SetAuxiliary( double fNew
)
655 // after Calculate, fAux is used as auxiliary value for running totals and reference values
656 assert( IsCalculated() && "ScDPAggData not calculated" );
661 ScDPAggData
* ScDPAggData::GetChild()
664 pChild
.reset( new ScDPAggData
);
668 void ScDPAggData::Reset()
670 maWelford
= WelfordRunner();
673 nCount
= SC_DPAGG_EMPTY
;
678 void ScDPAggData::Dump(int nIndent
) const
680 std::string
aIndent(nIndent
*2, ' ');
681 std::cout
<< aIndent
<< "* ";
683 std::cout
<< GetResult();
685 std::cout
<< "not calculated";
687 std::cout
<< " [val=" << fVal
<< "; aux=" << fAux
<< "; count=" << nCount
<< "]" << std::endl
;
691 ScDPRowTotals::ScDPRowTotals() :
692 bIsInColRoot( false )
696 ScDPRowTotals::~ScDPRowTotals()
700 static ScDPAggData
* lcl_GetChildTotal( ScDPAggData
* pFirst
, tools::Long nMeasure
)
702 OSL_ENSURE( nMeasure
>= 0, "GetColTotal: no measure" );
704 ScDPAggData
* pAgg
= pFirst
;
705 tools::Long nSkip
= nMeasure
;
707 // subtotal settings are ignored - column/row totals exist once per measure
709 for ( tools::Long nPos
=0; nPos
<nSkip
; nPos
++ )
710 pAgg
= pAgg
->GetChild(); // column total is constructed empty - children need to be created
712 if ( !pAgg
->IsCalculated() )
714 // for first use, simulate an empty calculation
715 ScDPSubTotalState aEmptyState
;
716 pAgg
->Calculate( SUBTOTAL_FUNC_SUM
, aEmptyState
);
722 ScDPAggData
* ScDPRowTotals::GetRowTotal( tools::Long nMeasure
)
724 return lcl_GetChildTotal( &aRowTotal
, nMeasure
);
727 ScDPAggData
* ScDPRowTotals::GetGrandTotal( tools::Long nMeasure
)
729 return lcl_GetChildTotal( &aGrandTotal
, nMeasure
);
732 static ScSubTotalFunc
lcl_GetForceFunc( const ScDPLevel
* pLevel
, tools::Long nFuncNo
)
734 ScSubTotalFunc eRet
= SUBTOTAL_FUNC_NONE
;
737 //TODO: direct access via ScDPLevel
739 uno::Sequence
<sal_Int16
> aSeq
= pLevel
->getSubTotals();
740 tools::Long nSequence
= aSeq
.getLength();
741 if ( nSequence
&& aSeq
[0] != sheet::GeneralFunction2::AUTO
)
743 // For manual subtotals, "automatic" is added as first function.
744 // ScDPResultMember::GetSubTotalCount adds to the count, here NONE has to be
745 // returned as the first function then.
747 --nFuncNo
; // keep NONE for first (check below), move the other entries
750 if ( nFuncNo
>= 0 && nFuncNo
< nSequence
)
752 ScGeneralFunction eUser
= static_cast<ScGeneralFunction
>(aSeq
.getConstArray()[nFuncNo
]);
753 if (eUser
!= ScGeneralFunction::AUTO
)
754 eRet
= ScDPUtil::toSubTotalFunc(eUser
);
760 ScDPResultData::ScDPResultData( ScDPSource
& rSrc
) :
768 ScDPResultData::~ScDPResultData()
772 void ScDPResultData::SetMeasureData(
773 std::vector
<ScSubTotalFunc
>& rFunctions
, std::vector
<sheet::DataPilotFieldReference
>& rRefs
,
774 std::vector
<sheet::DataPilotFieldOrientation
>& rRefOrient
, std::vector
<OUString
>& rNames
)
776 // We need to have at least one measure data at all times.
778 maMeasureFuncs
.swap(rFunctions
);
779 if (maMeasureFuncs
.empty())
780 maMeasureFuncs
.push_back(SUBTOTAL_FUNC_NONE
);
782 maMeasureRefs
.swap(rRefs
);
783 if (maMeasureRefs
.empty())
784 maMeasureRefs
.emplace_back(); // default ctor is ok.
786 maMeasureRefOrients
.swap(rRefOrient
);
787 if (maMeasureRefOrients
.empty())
788 maMeasureRefOrients
.push_back(sheet::DataPilotFieldOrientation_HIDDEN
);
790 maMeasureNames
.swap(rNames
);
791 if (maMeasureNames
.empty())
792 maMeasureNames
.push_back(ScResId(STR_EMPTYDATA
));
795 void ScDPResultData::SetDataLayoutOrientation( sheet::DataPilotFieldOrientation nOrient
)
797 bDataAtCol
= ( nOrient
== sheet::DataPilotFieldOrientation_COLUMN
);
798 bDataAtRow
= ( nOrient
== sheet::DataPilotFieldOrientation_ROW
);
801 void ScDPResultData::SetLateInit( bool bSet
)
806 tools::Long
ScDPResultData::GetColStartMeasure() const
808 if (maMeasureFuncs
.size() == 1)
811 return bDataAtCol
? SC_DPMEASURE_ALL
: SC_DPMEASURE_ANY
;
814 tools::Long
ScDPResultData::GetRowStartMeasure() const
816 if (maMeasureFuncs
.size() == 1)
819 return bDataAtRow
? SC_DPMEASURE_ALL
: SC_DPMEASURE_ANY
;
822 ScSubTotalFunc
ScDPResultData::GetMeasureFunction(tools::Long nMeasure
) const
824 OSL_ENSURE(o3tl::make_unsigned(nMeasure
) < maMeasureFuncs
.size(), "bumm");
825 return maMeasureFuncs
[nMeasure
];
828 const sheet::DataPilotFieldReference
& ScDPResultData::GetMeasureRefVal(tools::Long nMeasure
) const
830 OSL_ENSURE(o3tl::make_unsigned(nMeasure
) < maMeasureRefs
.size(), "bumm");
831 return maMeasureRefs
[nMeasure
];
834 sheet::DataPilotFieldOrientation
ScDPResultData::GetMeasureRefOrient(tools::Long nMeasure
) const
836 OSL_ENSURE(o3tl::make_unsigned(nMeasure
) < maMeasureRefOrients
.size(), "bumm");
837 return maMeasureRefOrients
[nMeasure
];
840 OUString
ScDPResultData::GetMeasureString(tools::Long nMeasure
, bool bForce
, ScSubTotalFunc eForceFunc
, bool& rbTotalResult
) const
842 // with bForce==true, return function instead of "result" for single measure
843 // with eForceFunc != SUBTOTAL_FUNC_NONE, always use eForceFunc
844 rbTotalResult
= false;
845 if ( nMeasure
< 0 || (maMeasureFuncs
.size() == 1 && !bForce
&& eForceFunc
== SUBTOTAL_FUNC_NONE
) )
847 // for user-specified subtotal function with all measures,
848 // display only function name
849 assert(unsigned(eForceFunc
) < SAL_N_ELEMENTS(aFuncStrIds
));
850 if ( eForceFunc
!= SUBTOTAL_FUNC_NONE
)
851 return ScResId(aFuncStrIds
[eForceFunc
]);
853 rbTotalResult
= true;
854 return ScResId(STR_TABLE_ERGEBNIS
);
858 OSL_ENSURE(o3tl::make_unsigned(nMeasure
) < maMeasureFuncs
.size(), "bumm");
859 const ScDPDimension
* pDataDim
= mrSource
.GetDataDimension(nMeasure
);
862 const std::optional
<OUString
> & pLayoutName
= pDataDim
->GetLayoutName();
867 ScSubTotalFunc eFunc
= ( eForceFunc
== SUBTOTAL_FUNC_NONE
) ?
868 GetMeasureFunction(nMeasure
) : eForceFunc
;
870 return ScDPUtil::getDisplayedMeasureName(maMeasureNames
[nMeasure
], eFunc
);
874 OUString
ScDPResultData::GetMeasureDimensionName(tools::Long nMeasure
) const
878 OSL_FAIL("GetMeasureDimensionName: negative");
882 return mrSource
.GetDataDimName(nMeasure
);
885 bool ScDPResultData::IsBaseForGroup( tools::Long nDim
) const
887 return mrSource
.GetData()->IsBaseForGroup(nDim
);
890 tools::Long
ScDPResultData::GetGroupBase( tools::Long nGroupDim
) const
892 return mrSource
.GetData()->GetGroupBase(nGroupDim
);
895 bool ScDPResultData::IsNumOrDateGroup( tools::Long nDim
) const
897 return mrSource
.GetData()->IsNumOrDateGroup(nDim
);
900 bool ScDPResultData::IsInGroup( SCROW nGroupDataId
, tools::Long nGroupIndex
,
901 const ScDPItemData
& rBaseData
, tools::Long nBaseIndex
) const
903 const ScDPItemData
* pGroupData
= mrSource
.GetItemDataById(nGroupIndex
, nGroupDataId
);
905 return mrSource
.GetData()->IsInGroup(*pGroupData
, nGroupIndex
, rBaseData
, nBaseIndex
);
910 bool ScDPResultData::HasCommonElement( SCROW nFirstDataId
, tools::Long nFirstIndex
,
911 const ScDPItemData
& rSecondData
, tools::Long nSecondIndex
) const
913 const ScDPItemData
* pFirstData
= mrSource
.GetItemDataById(nFirstIndex
, nFirstDataId
);
915 return mrSource
.GetData()->HasCommonElement(*pFirstData
, nFirstIndex
, rSecondData
, nSecondIndex
);
920 ResultMembers
& ScDPResultData::GetDimResultMembers(tools::Long nDim
, const ScDPDimension
* pDim
, ScDPLevel
* pLevel
) const
922 if (nDim
< static_cast<tools::Long
>(maDimMembers
.size()) && maDimMembers
[nDim
])
923 return *maDimMembers
[nDim
];
925 if (nDim
>= static_cast<tools::Long
>(maDimMembers
.size()))
926 maDimMembers
.resize(nDim
+1);
928 std::unique_ptr
<ResultMembers
> pResultMembers(new ResultMembers());
929 // global order is used to initialize aMembers, so it doesn't have to be looked at later
930 const ScMemberSortOrder
& rGlobalOrder
= pLevel
->GetGlobalOrder();
932 ScDPMembers
* pMembers
= pLevel
->GetMembersObject();
933 tools::Long nMembCount
= pMembers
->getCount();
934 for (tools::Long i
= 0; i
< nMembCount
; ++i
)
936 tools::Long nSorted
= rGlobalOrder
.empty() ? i
: rGlobalOrder
[i
];
937 ScDPMember
* pMember
= pMembers
->getByIndex(nSorted
);
938 if (!pResultMembers
->FindMember(pMember
->GetItemDataId()))
940 ScDPParentDimData
aNew(i
, pDim
, pLevel
, pMember
);
941 pResultMembers
->InsertMember(aNew
);
945 maDimMembers
[nDim
] = std::move(pResultMembers
);
946 return *maDimMembers
[nDim
];
949 ScDPResultMember::ScDPResultMember(
950 const ScDPResultData
* pData
, const ScDPParentDimData
& rParentDimData
) :
951 pResultData( pData
),
952 aParentDimData( rParentDimData
),
953 bHasElements( false ),
954 bForceSubTotal( false ),
955 bHasHiddenDetails( false ),
956 bInitialized( false ),
957 bAutoHidden( false ),
960 // pParentLevel/pMemberDesc is 0 for root members
963 ScDPResultMember::ScDPResultMember(
964 const ScDPResultData
* pData
, bool bForceSub
) :
965 pResultData( pData
),
966 bHasElements( false ),
967 bForceSubTotal( bForceSub
),
968 bHasHiddenDetails( false ),
969 bInitialized( false ),
970 bAutoHidden( false ),
974 ScDPResultMember::~ScDPResultMember()
978 OUString
ScDPResultMember::GetName() const
980 const ScDPMember
* pMemberDesc
= GetDPMember();
982 return pMemberDesc
->GetNameStr( false );
984 return ScResId(STR_PIVOT_TOTAL
); // root member
987 OUString
ScDPResultMember::GetDisplayName( bool bLocaleIndependent
) const
989 const ScDPMember
* pDPMember
= GetDPMember();
993 ScDPItemData
aItem(pDPMember
->FillItemData());
994 if (aParentDimData
.mpParentDim
)
996 tools::Long nDim
= aParentDimData
.mpParentDim
->GetDimension();
997 return pResultData
->GetSource().GetData()->GetFormattedString(nDim
, aItem
, bLocaleIndependent
);
1000 return aItem
.GetString();
1003 ScDPItemData
ScDPResultMember::FillItemData() const
1005 const ScDPMember
* pMemberDesc
= GetDPMember();
1007 return pMemberDesc
->FillItemData();
1008 return ScDPItemData(ScResId(STR_PIVOT_TOTAL
)); // root member
1011 bool ScDPResultMember::IsNamedItem( SCROW nIndex
) const
1013 //TODO: store ScDPMember pointer instead of ScDPMember ???
1014 const ScDPMember
* pMemberDesc
= GetDPMember();
1016 return pMemberDesc
->IsNamedItem(nIndex
);
1020 bool ScDPResultMember::IsValidEntry( const vector
< SCROW
>& aMembers
) const
1025 const ScDPResultDimension
* pChildDim
= GetChildDimension();
1028 if (aMembers
.size() < 2)
1031 vector
<SCROW
>::const_iterator itr
= aMembers
.begin();
1032 vector
<SCROW
> aChildMembers(++itr
, aMembers
.end());
1033 return pChildDim
->IsValidEntry(aChildMembers
);
1039 void ScDPResultMember::InitFrom( const vector
<ScDPDimension
*>& ppDim
, const vector
<ScDPLevel
*>& ppLev
,
1040 size_t nPos
, ScDPInitState
& rInitState
,
1043 // with LateInit, initialize only those members that have data
1044 if ( pResultData
->IsLateInit() )
1047 bInitialized
= true;
1049 if (nPos
>= ppDim
.size())
1052 // skip child dimension if details are not shown
1053 if ( GetDPMember() && !GetDPMember()->getShowDetails() )
1055 // Show DataLayout dimension
1057 while ( nPos
< ppDim
.size() )
1059 if ( ppDim
[nPos
]->getIsDataLayoutDimension() )
1061 if ( !pChildDimension
)
1062 pChildDimension
.reset( new ScDPResultDimension( pResultData
) );
1063 pChildDimension
->InitFrom( ppDim
, ppLev
, nPos
, rInitState
, false );
1072 bHasHiddenDetails
= true; // only if there is a next dimension
1078 pChildDimension
.reset( new ScDPResultDimension( pResultData
) );
1079 pChildDimension
->InitFrom(ppDim
, ppLev
, nPos
, rInitState
);
1083 void ScDPResultMember::LateInitFrom(
1084 LateInitParams
& rParams
, const vector
<SCROW
>& pItemData
, size_t nPos
, ScDPInitState
& rInitState
)
1086 // without LateInit, everything has already been initialized
1087 if ( !pResultData
->IsLateInit() )
1090 bInitialized
= true;
1092 if ( rParams
.IsEnd( nPos
) /*nPos >= ppDim.size()*/)
1093 // No next dimension. Bail out.
1096 // skip child dimension if details are not shown
1097 if ( GetDPMember() && !GetDPMember()->getShowDetails() )
1099 // Show DataLayout dimension
1101 while ( !rParams
.IsEnd( nPos
) )
1103 if ( rParams
.GetDim( nPos
)->getIsDataLayoutDimension() )
1105 if ( !pChildDimension
)
1106 pChildDimension
.reset( new ScDPResultDimension( pResultData
) );
1108 // #i111462# reset InitChild flag only for this child dimension's LateInitFrom call,
1109 // not for following members of parent dimensions
1110 bool bWasInitChild
= rParams
.GetInitChild();
1111 rParams
.SetInitChild( false );
1112 pChildDimension
->LateInitFrom( rParams
, pItemData
, nPos
, rInitState
);
1113 rParams
.SetInitChild( bWasInitChild
);
1122 bHasHiddenDetails
= true; // only if there is a next dimension
1126 // LateInitFrom is called several times...
1127 if ( rParams
.GetInitChild() )
1129 if ( !pChildDimension
)
1130 pChildDimension
.reset( new ScDPResultDimension( pResultData
) );
1131 pChildDimension
->LateInitFrom( rParams
, pItemData
, nPos
, rInitState
);
1135 bool ScDPResultMember::IsSubTotalInTitle(tools::Long nMeasure
) const
1138 if ( pChildDimension
&& /*pParentLevel*/GetParentLevel() &&
1139 /*pParentLevel*/GetParentLevel()->IsOutlineLayout() && /*pParentLevel*/GetParentLevel()->IsSubtotalsAtTop() )
1141 tools::Long nUserSubStart
;
1142 tools::Long nSubTotals
= GetSubTotalCount( &nUserSubStart
);
1143 nSubTotals
-= nUserSubStart
; // visible count
1146 if ( nMeasure
== SC_DPMEASURE_ALL
)
1147 nSubTotals
*= pResultData
->GetMeasureCount(); // number of subtotals that will be inserted
1149 // only a single subtotal row will be shown in the outline title row
1150 if ( nSubTotals
== 1 )
1157 tools::Long
ScDPResultMember::GetSize(tools::Long nMeasure
) const
1161 const ScDPLevel
* pParentLevel
= GetParentLevel();
1162 tools::Long nExtraSpace
= 0;
1163 if ( pParentLevel
&& pParentLevel
->IsAddEmpty() )
1166 if ( pChildDimension
)
1168 // outline layout takes up an extra row for the title only if subtotals aren't shown in that row
1169 if ( pParentLevel
&& pParentLevel
->IsOutlineLayout() && !IsSubTotalInTitle( nMeasure
) )
1172 tools::Long nSize
= pChildDimension
->GetSize(nMeasure
);
1173 tools::Long nUserSubStart
;
1174 tools::Long nUserSubCount
= GetSubTotalCount( &nUserSubStart
);
1175 nUserSubCount
-= nUserSubStart
; // for output size, use visible count
1176 if ( nUserSubCount
)
1178 if ( nMeasure
== SC_DPMEASURE_ALL
)
1179 nSize
+= pResultData
->GetMeasureCount() * nUserSubCount
;
1181 nSize
+= nUserSubCount
;
1183 return nSize
+ nExtraSpace
;
1187 if ( nMeasure
== SC_DPMEASURE_ALL
)
1188 return pResultData
->GetMeasureCount() + nExtraSpace
;
1190 return 1 + nExtraSpace
;
1194 bool ScDPResultMember::IsVisible() const
1205 // not initialized -> shouldn't be there at all
1206 // (allocated only to preserve ordering)
1207 const ScDPLevel
* pParentLevel
= GetParentLevel();
1209 return (pParentLevel
&& pParentLevel
->getShowEmpty());
1212 bool ScDPResultMember::IsValid() const
1214 // non-Valid members are left out of calculation
1216 // was member set no invisible at the DataPilotSource?
1217 const ScDPMember
* pMemberDesc
= GetDPMember();
1218 if ( pMemberDesc
&& !pMemberDesc
->isVisible() )
1227 tools::Long
ScDPResultMember::GetSubTotalCount( tools::Long
* pUserSubStart
) const
1229 if ( pUserSubStart
)
1230 *pUserSubStart
= 0; // default
1232 const ScDPLevel
* pParentLevel
= GetParentLevel();
1234 if ( bForceSubTotal
) // set if needed for root members
1235 return 1; // grand total is always "automatic"
1236 else if ( pParentLevel
)
1238 //TODO: direct access via ScDPLevel
1240 uno::Sequence
<sal_Int16
> aSeq
= pParentLevel
->getSubTotals();
1241 tools::Long nSequence
= aSeq
.getLength();
1242 if ( nSequence
&& aSeq
[0] != sheet::GeneralFunction2::AUTO
)
1244 // For manual subtotals, always add "automatic" as first function
1245 // (used for calculation, but not for display, needed for sorting, see lcl_GetForceFunc)
1248 if ( pUserSubStart
)
1249 *pUserSubStart
= 1; // visible subtotals start at 1
1257 void ScDPResultMember::ProcessData( const vector
< SCROW
>& aChildMembers
, const ScDPResultDimension
* pDataDim
,
1258 const vector
< SCROW
>& aDataMembers
, const vector
<ScDPValue
>& aValues
)
1262 if (pChildDimension
)
1263 pChildDimension
->ProcessData( aChildMembers
, pDataDim
, aDataMembers
, aValues
);
1267 pDataRoot
.reset( new ScDPDataMember( pResultData
, nullptr ) );
1269 pDataRoot
->InitFrom( pDataDim
); // recursive
1272 ScDPSubTotalState aSubState
; // initial state
1274 tools::Long nUserSubCount
= GetSubTotalCount();
1276 // Calculate at least automatic if no subtotals are selected,
1277 // show only own values if there's no child dimension (innermost).
1278 if ( !nUserSubCount
|| !pChildDimension
)
1281 const ScDPLevel
* pParentLevel
= GetParentLevel();
1283 for (tools::Long nUserPos
=0; nUserPos
<nUserSubCount
; nUserPos
++) // including hidden "automatic"
1285 // #i68338# if nUserSubCount is 1 (automatic only), don't set nRowSubTotalFunc
1286 if ( pChildDimension
&& nUserSubCount
> 1 )
1288 aSubState
.nRowSubTotalFunc
= nUserPos
;
1289 aSubState
.eRowForce
= lcl_GetForceFunc( pParentLevel
, nUserPos
);
1292 pDataRoot
->ProcessData( aDataMembers
, aValues
, aSubState
);
1297 * Parse subtotal string and replace all occurrences of '?' with the caption
1298 * string. Do ensure that escaped characters are not translated.
1300 static OUString
lcl_parseSubtotalName(std::u16string_view rSubStr
, std::u16string_view rCaption
)
1302 OUStringBuffer aNewStr
;
1303 sal_Int32 n
= rSubStr
.size();
1304 bool bEscaped
= false;
1305 for (sal_Int32 i
= 0; i
< n
; ++i
)
1307 sal_Unicode c
= rSubStr
[i
];
1308 if (!bEscaped
&& c
== '\\')
1314 if (!bEscaped
&& c
== '?')
1315 aNewStr
.append(rCaption
);
1320 return aNewStr
.makeStringAndClear();
1323 void ScDPResultMember::FillMemberResults(
1324 uno::Sequence
<sheet::MemberResult
>* pSequences
, tools::Long
& rPos
, tools::Long nMeasure
, bool bRoot
,
1325 const OUString
* pMemberName
, const OUString
* pMemberCaption
)
1327 // IsVisible() test is in ScDPResultDimension::FillMemberResults
1328 // (not on data layout dimension)
1330 if (!pSequences
->hasElements())
1331 // empty sequence. Bail out.
1334 tools::Long nSize
= GetSize(nMeasure
);
1335 sheet::MemberResult
* pArray
= pSequences
->getArray();
1336 OSL_ENSURE( rPos
+nSize
<= pSequences
->getLength(), "bumm" );
1338 bool bIsNumeric
= false;
1339 double fValue
= std::numeric_limits
<double>::quiet_NaN();
1341 if ( pMemberName
) // if pMemberName != NULL, use instead of real member name
1343 aName
= *pMemberName
;
1347 ScDPItemData
aItemData(FillItemData());
1348 if (aParentDimData
.mpParentDim
)
1350 tools::Long nDim
= aParentDimData
.mpParentDim
->GetDimension();
1351 aName
= pResultData
->GetSource().GetData()->GetFormattedString(nDim
, aItemData
, false);
1355 tools::Long nDim
= -1;
1356 const ScDPMember
* pMem
= GetDPMember();
1358 nDim
= pMem
->GetDim();
1359 aName
= pResultData
->GetSource().GetData()->GetFormattedString(nDim
, aItemData
, false);
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();
1371 const ScDPDimension
* pParentDim
= GetParentDim();
1372 if ( bIsNumeric
&& pParentDim
&& pResultData
->IsNumOrDateGroup( pParentDim
->GetDimension() ) )
1374 // Numeric group dimensions use numeric entries for proper sorting,
1375 // but the group titles must be output as text.
1379 OUString aCaption
= aName
;
1380 const ScDPMember
* pMemberDesc
= GetDPMember();
1383 const std::optional
<OUString
> & pLayoutName
= pMemberDesc
->GetLayoutName();
1386 aCaption
= *pLayoutName
;
1387 bIsNumeric
= false; // layout name is always non-numeric.
1391 if ( pMemberCaption
) // use pMemberCaption if != NULL
1392 aCaption
= *pMemberCaption
;
1393 if (aCaption
.isEmpty())
1394 aCaption
= ScResId(STR_EMPTYDATA
);
1397 pArray
[rPos
].Flags
|= sheet::MemberResultFlags::NUMERIC
;
1399 pArray
[rPos
].Flags
&= ~sheet::MemberResultFlags::NUMERIC
;
1401 const ScDPLevel
* pParentLevel
= GetParentLevel();
1402 if ( nSize
&& !bRoot
) // root is overwritten by first dimension
1404 pArray
[rPos
].Name
= aName
;
1405 pArray
[rPos
].Caption
= aCaption
;
1406 pArray
[rPos
].Flags
|= sheet::MemberResultFlags::HASMEMBER
;
1407 pArray
[rPos
].Value
= fValue
;
1409 // set "continue" flag (removed for subtotals later)
1410 for (tools::Long i
=1; i
<nSize
; i
++)
1412 pArray
[rPos
+i
].Flags
|= sheet::MemberResultFlags::CONTINUE
;
1413 // tdf#113002 - add numeric flag to recurring data fields
1415 pArray
[rPos
+ i
].Flags
|= sheet::MemberResultFlags::NUMERIC
;
1418 if ( pParentLevel
&& pParentLevel
->getRepeatItemLabels() )
1420 tools::Long nSizeNonEmpty
= nSize
;
1421 if ( pParentLevel
->IsAddEmpty() )
1423 for (tools::Long i
=1; i
<nSizeNonEmpty
; i
++)
1425 pArray
[rPos
+i
].Name
= aName
;
1426 pArray
[rPos
+i
].Caption
= aCaption
;
1427 pArray
[rPos
+i
].Flags
|= sheet::MemberResultFlags::HASMEMBER
;
1428 pArray
[rPos
+i
].Value
= fValue
;
1433 tools::Long nExtraSpace
= 0;
1434 if ( pParentLevel
&& pParentLevel
->IsAddEmpty() )
1437 bool bTitleLine
= false;
1438 if ( pParentLevel
&& pParentLevel
->IsOutlineLayout() )
1441 // if the subtotals are shown at the top (title row) in outline layout,
1442 // no extra row for the subtotals is needed
1443 bool bSubTotalInTitle
= IsSubTotalInTitle( nMeasure
);
1445 bool bHasChild
= ( pChildDimension
!= nullptr );
1448 if ( bTitleLine
) // in tabular layout the title is on a separate row
1449 ++rPos
; // -> fill child dimension one row below
1451 if (bRoot
) // same sequence for root member
1452 pChildDimension
->FillMemberResults( pSequences
, rPos
, nMeasure
);
1454 pChildDimension
->FillMemberResults( pSequences
+ nMemberStep
/*1*/, rPos
, nMeasure
);
1456 if ( bTitleLine
) // title row is included in GetSize, so the following
1457 --rPos
; // positions are calculated with the normal values
1462 tools::Long nUserSubStart
;
1463 tools::Long nUserSubCount
= GetSubTotalCount(&nUserSubStart
);
1464 if ( !nUserSubCount
|| !pChildDimension
|| bSubTotalInTitle
)
1467 tools::Long nMemberMeasure
= nMeasure
;
1468 tools::Long nSubSize
= pResultData
->GetCountForMeasure(nMeasure
);
1470 rPos
-= nSubSize
* (nUserSubCount
- nUserSubStart
); // GetSize includes space for SubTotal
1471 rPos
-= nExtraSpace
; // GetSize includes the empty line
1473 for (tools::Long nUserPos
=nUserSubStart
; nUserPos
<nUserSubCount
; nUserPos
++)
1475 for ( tools::Long nSubCount
=0; nSubCount
<nSubSize
; nSubCount
++ )
1477 if ( nMeasure
== SC_DPMEASURE_ALL
)
1478 nMemberMeasure
= nSubCount
;
1480 ScSubTotalFunc eForce
= SUBTOTAL_FUNC_NONE
;
1482 eForce
= lcl_GetForceFunc( pParentLevel
, nUserPos
);
1484 bool bTotalResult
= false;
1485 OUString aSubStr
= aCaption
+ " " + pResultData
->GetMeasureString(nMemberMeasure
, false, eForce
, bTotalResult
);
1491 // single data field layout.
1492 const std::optional
<OUString
> & pSubtotalName
= pParentDim
->GetSubtotalName();
1494 aSubStr
= lcl_parseSubtotalName(*pSubtotalName
, aCaption
);
1495 pArray
[rPos
].Flags
&= ~sheet::MemberResultFlags::GRANDTOTAL
;
1499 // root member - subtotal (grand total?) for multi-data field layout.
1500 const std::optional
<OUString
> & pGrandTotalName
= pResultData
->GetSource().GetGrandTotalName();
1501 if (pGrandTotalName
)
1502 aSubStr
= *pGrandTotalName
;
1503 pArray
[rPos
].Flags
|= sheet::MemberResultFlags::GRANDTOTAL
;
1507 fValue
= std::numeric_limits
<double>::quiet_NaN(); /* TODO: any numeric value to obtain? */
1508 pArray
[rPos
].Name
= aName
;
1509 pArray
[rPos
].Caption
= aSubStr
;
1510 pArray
[rPos
].Flags
= ( pArray
[rPos
].Flags
|
1511 ( sheet::MemberResultFlags::HASMEMBER
| sheet::MemberResultFlags::SUBTOTAL
) ) &
1512 ~sheet::MemberResultFlags::CONTINUE
;
1513 pArray
[rPos
].Value
= fValue
;
1515 if ( nMeasure
== SC_DPMEASURE_ALL
)
1517 // data layout dimension is (direct/indirect) child of this.
1518 // data layout dimension must have name for all entries.
1520 uno::Sequence
<sheet::MemberResult
>* pLayoutSeq
= pSequences
;
1523 ScDPResultDimension
* pLayoutDim
= pChildDimension
.get();
1524 while ( pLayoutDim
&& !pLayoutDim
->IsDataLayout() )
1526 pLayoutDim
= pLayoutDim
->GetFirstChildDimension();
1531 sheet::MemberResult
* pLayoutArray
= pLayoutSeq
->getArray();
1532 pLayoutArray
[rPos
].Name
= pResultData
->GetMeasureDimensionName(nMemberMeasure
);
1540 rPos
+= nExtraSpace
; // add again (subtracted above)
1543 void ScDPResultMember::FillDataResults(
1544 const ScDPResultMember
* pRefMember
,
1545 ScDPResultFilterContext
& rFilterCxt
, uno::Sequence
<uno::Sequence
<sheet::DataResult
> >& rSequence
,
1546 tools::Long nMeasure
) const
1548 std::unique_ptr
<FilterStack
> pFilterStack
;
1549 const ScDPMember
* pDPMember
= GetDPMember();
1552 // Root result has no corresponding DP member. Only take the non-root results.
1553 pFilterStack
.reset(new FilterStack(rFilterCxt
.maFilters
));
1554 pFilterStack
->pushDimValue( GetDisplayName( false), GetDisplayName( true));
1557 // IsVisible() test is in ScDPResultDimension::FillDataResults
1558 // (not on data layout dimension)
1559 const ScDPLevel
* pParentLevel
= GetParentLevel();
1560 sal_Int32 nStartRow
= rFilterCxt
.mnRow
;
1562 tools::Long nExtraSpace
= 0;
1563 if ( pParentLevel
&& pParentLevel
->IsAddEmpty() )
1566 bool bTitleLine
= false;
1567 if ( pParentLevel
&& pParentLevel
->IsOutlineLayout() )
1570 bool bSubTotalInTitle
= IsSubTotalInTitle( nMeasure
);
1572 bool bHasChild
= ( pChildDimension
!= nullptr );
1575 if ( bTitleLine
) // in tabular layout the title is on a separate row
1576 ++rFilterCxt
.mnRow
; // -> fill child dimension one row below
1578 sal_Int32 nOldRow
= rFilterCxt
.mnRow
;
1579 pChildDimension
->FillDataResults(pRefMember
, rFilterCxt
, rSequence
, nMeasure
);
1580 rFilterCxt
.mnRow
= nOldRow
; // Revert to the original row before the call.
1582 rFilterCxt
.mnRow
+= GetSize( nMeasure
);
1584 if ( bTitleLine
) // title row is included in GetSize, so the following
1585 --rFilterCxt
.mnRow
; // positions are calculated with the normal values
1588 tools::Long nUserSubStart
;
1589 tools::Long nUserSubCount
= GetSubTotalCount(&nUserSubStart
);
1590 if ( !nUserSubCount
&& bHasChild
)
1593 // Calculate at least automatic if no subtotals are selected,
1594 // show only own values if there's no child dimension (innermost).
1595 if ( !nUserSubCount
|| !bHasChild
)
1601 tools::Long nMemberMeasure
= nMeasure
;
1602 tools::Long nSubSize
= pResultData
->GetCountForMeasure(nMeasure
);
1605 rFilterCxt
.mnRow
-= nSubSize
* ( nUserSubCount
- nUserSubStart
); // GetSize includes space for SubTotal
1606 rFilterCxt
.mnRow
-= nExtraSpace
; // GetSize includes the empty line
1609 tools::Long nMoveSubTotal
= 0;
1610 if ( bSubTotalInTitle
)
1612 nMoveSubTotal
= rFilterCxt
.mnRow
- nStartRow
; // force to first (title) row
1613 rFilterCxt
.mnRow
= nStartRow
;
1618 ScDPSubTotalState aSubState
; // initial state
1620 for (tools::Long nUserPos
=nUserSubStart
; nUserPos
<nUserSubCount
; nUserPos
++)
1622 if ( bHasChild
&& nUserSubCount
> 1 )
1624 aSubState
.nRowSubTotalFunc
= nUserPos
;
1625 aSubState
.eRowForce
= lcl_GetForceFunc( /*pParentLevel*/GetParentLevel() , nUserPos
);
1628 for ( tools::Long nSubCount
=0; nSubCount
<nSubSize
; nSubCount
++ )
1630 if ( nMeasure
== SC_DPMEASURE_ALL
)
1631 nMemberMeasure
= nSubCount
;
1632 else if ( pResultData
->GetColStartMeasure() == SC_DPMEASURE_ALL
)
1633 nMemberMeasure
= SC_DPMEASURE_ALL
;
1635 OSL_ENSURE( rFilterCxt
.mnRow
< rSequence
.getLength(), "bumm" );
1636 rFilterCxt
.mnCol
= 0;
1637 if (pRefMember
->IsVisible())
1639 uno::Sequence
<sheet::DataResult
>& rSubSeq
= rSequence
.getArray()[rFilterCxt
.mnRow
];
1640 pDataRoot
->FillDataRow(pRefMember
, rFilterCxt
, rSubSeq
, nMemberMeasure
, bHasChild
, aSubState
);
1642 rFilterCxt
.mnRow
+= 1;
1647 rFilterCxt
.mnRow
+= nSubSize
* ( nUserSubCount
- nUserSubStart
); // empty rows occur when ShowEmpty is true
1649 // add extra space again if subtracted from GetSize above,
1650 // add to own size if no children
1651 rFilterCxt
.mnRow
+= nExtraSpace
;
1652 rFilterCxt
.mnRow
+= nMoveSubTotal
;
1655 void ScDPResultMember::UpdateDataResults( const ScDPResultMember
* pRefMember
, tools::Long nMeasure
) const
1657 // IsVisible() test is in ScDPResultDimension::FillDataResults
1658 // (not on data layout dimension)
1660 bool bHasChild
= ( pChildDimension
!= nullptr );
1662 tools::Long nUserSubCount
= GetSubTotalCount();
1664 // process subtotals even if not shown
1666 // Calculate at least automatic if no subtotals are selected,
1667 // show only own values if there's no child dimension (innermost).
1668 if (!nUserSubCount
|| !bHasChild
)
1671 tools::Long nMemberMeasure
= nMeasure
;
1672 tools::Long nSubSize
= pResultData
->GetCountForMeasure(nMeasure
);
1676 ScDPSubTotalState aSubState
; // initial state
1678 for (tools::Long nUserPos
= 0; nUserPos
< nUserSubCount
; ++nUserPos
) // including hidden "automatic"
1680 if (bHasChild
&& nUserSubCount
> 1)
1682 aSubState
.nRowSubTotalFunc
= nUserPos
;
1683 aSubState
.eRowForce
= lcl_GetForceFunc(GetParentLevel(), nUserPos
);
1686 for (tools::Long nSubCount
= 0; nSubCount
< nSubSize
; ++nSubCount
)
1688 if (nMeasure
== SC_DPMEASURE_ALL
)
1689 nMemberMeasure
= nSubCount
;
1690 else if (pResultData
->GetColStartMeasure() == SC_DPMEASURE_ALL
)
1691 nMemberMeasure
= SC_DPMEASURE_ALL
;
1693 pDataRoot
->UpdateDataRow(pRefMember
, nMemberMeasure
, bHasChild
, aSubState
);
1698 if (bHasChild
) // child dimension must be processed last, so the column total is known
1700 pChildDimension
->UpdateDataResults( pRefMember
, nMeasure
);
1704 void ScDPResultMember::SortMembers( ScDPResultMember
* pRefMember
)
1706 bool bHasChild
= ( pChildDimension
!= nullptr );
1708 pChildDimension
->SortMembers( pRefMember
); // sorting is done at the dimension
1710 if ( IsRoot() && pDataRoot
)
1712 // use the row root member to sort columns
1713 // sub total count is always 1
1715 pDataRoot
->SortMembers( pRefMember
);
1719 void ScDPResultMember::DoAutoShow( ScDPResultMember
* pRefMember
)
1721 bool bHasChild
= ( pChildDimension
!= nullptr );
1723 pChildDimension
->DoAutoShow( pRefMember
); // sorting is done at the dimension
1725 if ( IsRoot()&& pDataRoot
)
1727 // use the row root member to sort columns
1728 // sub total count is always 1
1730 pDataRoot
->DoAutoShow( pRefMember
);
1734 void ScDPResultMember::ResetResults()
1737 pDataRoot
->ResetResults();
1739 if (pChildDimension
)
1740 pChildDimension
->ResetResults();
1743 void ScDPResultMember::UpdateRunningTotals( const ScDPResultMember
* pRefMember
, tools::Long nMeasure
,
1744 ScDPRunningTotalState
& rRunning
, ScDPRowTotals
& rTotals
) const
1746 // IsVisible() test is in ScDPResultDimension::FillDataResults
1747 // (not on data layout dimension)
1749 rTotals
.SetInColRoot( IsRoot() );
1751 bool bHasChild
= ( pChildDimension
!= nullptr );
1753 tools::Long nUserSubCount
= GetSubTotalCount();
1754 //if ( nUserSubCount || !bHasChild )
1756 // Calculate at least automatic if no subtotals are selected,
1757 // show only own values if there's no child dimension (innermost).
1758 if ( !nUserSubCount
|| !bHasChild
)
1761 tools::Long nMemberMeasure
= nMeasure
;
1762 tools::Long nSubSize
= pResultData
->GetCountForMeasure(nMeasure
);
1766 ScDPSubTotalState aSubState
; // initial state
1768 for (tools::Long nUserPos
=0; nUserPos
<nUserSubCount
; nUserPos
++) // including hidden "automatic"
1770 if ( bHasChild
&& nUserSubCount
> 1 )
1772 aSubState
.nRowSubTotalFunc
= nUserPos
;
1773 aSubState
.eRowForce
= lcl_GetForceFunc(GetParentLevel(), nUserPos
);
1776 for ( tools::Long nSubCount
=0; nSubCount
<nSubSize
; nSubCount
++ )
1778 if ( nMeasure
== SC_DPMEASURE_ALL
)
1779 nMemberMeasure
= nSubCount
;
1780 else if ( pResultData
->GetColStartMeasure() == SC_DPMEASURE_ALL
)
1781 nMemberMeasure
= SC_DPMEASURE_ALL
;
1783 if (pRefMember
->IsVisible())
1784 pDataRoot
->UpdateRunningTotals(
1785 pRefMember
, nMemberMeasure
, bHasChild
, aSubState
, rRunning
, rTotals
, *this);
1791 if (bHasChild
) // child dimension must be processed last, so the column total is known
1793 pChildDimension
->UpdateRunningTotals( pRefMember
, nMeasure
, rRunning
, rTotals
);
1797 #if DUMP_PIVOT_TABLE
1798 void ScDPResultMember::DumpState( const ScDPResultMember
* pRefMember
, ScDocument
* pDoc
, ScAddress
& rPos
) const
1800 dumpRow(u
"ScDPResultMember"_ustr
, GetName(), nullptr, pDoc
, rPos
);
1801 SCROW nStartRow
= rPos
.Row();
1804 pDataRoot
->DumpState( pRefMember
, pDoc
, rPos
);
1806 if (pChildDimension
)
1807 pChildDimension
->DumpState( pRefMember
, pDoc
, rPos
);
1809 indent(pDoc
, nStartRow
, rPos
);
1812 void ScDPResultMember::Dump(int nIndent
) const
1814 std::string
aIndent(nIndent
*2, ' ');
1815 std::cout
<< aIndent
<< "-- result member '" << GetName() << "'" << std::endl
;
1817 std::cout
<< aIndent
<< " column totals" << std::endl
;
1818 for (const ScDPAggData
* p
= &aColTotal
; p
; p
= p
->GetExistingChild())
1821 if (pChildDimension
)
1822 pChildDimension
->Dump(nIndent
+1);
1826 std::cout
<< aIndent
<< " data root" << std::endl
;
1827 pDataRoot
->Dump(nIndent
+1);
1832 ScDPAggData
* ScDPResultMember::GetColTotal( tools::Long nMeasure
) const
1834 return lcl_GetChildTotal( const_cast<ScDPAggData
*>(&aColTotal
), nMeasure
);
1837 void ScDPResultMember::FillVisibilityData(ScDPResultVisibilityData
& rData
) const
1839 if (pChildDimension
)
1840 pChildDimension
->FillVisibilityData(rData
);
1843 ScDPDataMember::ScDPDataMember( const ScDPResultData
* pData
, const ScDPResultMember
* pRes
) :
1844 pResultData( pData
),
1845 pResultMember( pRes
)
1847 // pResultMember is 0 for root members
1850 ScDPDataMember::~ScDPDataMember()
1854 OUString
ScDPDataMember::GetName() const
1857 return pResultMember
->GetName();
1862 bool ScDPDataMember::IsVisible() const
1865 return pResultMember
->IsVisible();
1870 bool ScDPDataMember::IsNamedItem( SCROW nRow
) const
1873 return pResultMember
->IsNamedItem(nRow
);
1878 bool ScDPDataMember::HasHiddenDetails() const
1881 return pResultMember
->HasHiddenDetails();
1886 void ScDPDataMember::InitFrom( const ScDPResultDimension
* pDim
)
1888 if ( !pChildDimension
)
1889 pChildDimension
.reset( new ScDPDataDimension(pResultData
) );
1890 pChildDimension
->InitFrom(pDim
);
1893 const tools::Long SC_SUBTOTALPOS_AUTO
= -1; // default
1894 const tools::Long SC_SUBTOTALPOS_SKIP
= -2; // don't use
1896 static tools::Long
lcl_GetSubTotalPos( const ScDPSubTotalState
& rSubState
)
1898 if ( rSubState
.nColSubTotalFunc
>= 0 && rSubState
.nRowSubTotalFunc
>= 0 &&
1899 rSubState
.nColSubTotalFunc
!= rSubState
.nRowSubTotalFunc
)
1901 // #i68338# don't return the same index for different combinations (leading to repeated updates),
1902 // return a "don't use" value instead
1904 return SC_SUBTOTALPOS_SKIP
;
1907 tools::Long nRet
= SC_SUBTOTALPOS_AUTO
;
1908 if ( rSubState
.nColSubTotalFunc
>= 0 ) nRet
= rSubState
.nColSubTotalFunc
;
1909 if ( rSubState
.nRowSubTotalFunc
>= 0 ) nRet
= rSubState
.nRowSubTotalFunc
;
1913 void ScDPDataMember::UpdateValues( const vector
<ScDPValue
>& aValues
, const ScDPSubTotalState
& rSubState
)
1915 //TODO: find out how many and which subtotals are used
1917 ScDPAggData
* pAgg
= &aAggregate
;
1919 tools::Long nSubPos
= lcl_GetSubTotalPos(rSubState
);
1920 if (nSubPos
== SC_SUBTOTALPOS_SKIP
)
1924 tools::Long nSkip
= nSubPos
* pResultData
->GetMeasureCount();
1925 for (tools::Long i
=0; i
<nSkip
; i
++)
1926 pAgg
= pAgg
->GetChild(); // created if not there
1929 size_t nCount
= aValues
.size();
1930 for (size_t nPos
= 0; nPos
< nCount
; ++nPos
)
1932 pAgg
->Update(aValues
[nPos
], pResultData
->GetMeasureFunction(nPos
), rSubState
);
1933 pAgg
= pAgg
->GetChild();
1937 void ScDPDataMember::ProcessData( const vector
< SCROW
>& aChildMembers
, const vector
<ScDPValue
>& aValues
,
1938 const ScDPSubTotalState
& rSubState
)
1940 if ( pResultData
->IsLateInit() && !pChildDimension
&& pResultMember
&& pResultMember
->GetChildDimension() )
1942 // if this DataMember doesn't have a child dimension because the ResultMember's
1943 // child dimension wasn't there yet during this DataMembers's creation,
1944 // create the child dimension now
1945 InitFrom( pResultMember
->GetChildDimension() );
1948 tools::Long nUserSubCount
= pResultMember
? pResultMember
->GetSubTotalCount() : 0;
1950 // Calculate at least automatic if no subtotals are selected,
1951 // show only own values if there's no child dimension (innermost).
1952 if ( !nUserSubCount
|| !pChildDimension
)
1955 ScDPSubTotalState aLocalSubState
= rSubState
; // keep row state, modify column
1956 for (tools::Long nUserPos
=0; nUserPos
<nUserSubCount
; nUserPos
++) // including hidden "automatic"
1958 if ( pChildDimension
&& nUserSubCount
> 1 )
1960 const ScDPLevel
* pForceLevel
= pResultMember
? pResultMember
->GetParentLevel() : nullptr;
1961 aLocalSubState
.nColSubTotalFunc
= nUserPos
;
1962 aLocalSubState
.eColForce
= lcl_GetForceFunc( pForceLevel
, nUserPos
);
1965 UpdateValues( aValues
, aLocalSubState
);
1968 if (pChildDimension
)
1969 pChildDimension
->ProcessData( aChildMembers
, aValues
, rSubState
); // with unmodified subtotal state
1972 bool ScDPDataMember::HasData( tools::Long nMeasure
, const ScDPSubTotalState
& rSubState
) const
1974 if ( rSubState
.eColForce
!= SUBTOTAL_FUNC_NONE
&& rSubState
.eRowForce
!= SUBTOTAL_FUNC_NONE
&&
1975 rSubState
.eColForce
!= rSubState
.eRowForce
)
1978 // HasData can be different between measures!
1980 const ScDPAggData
* pAgg
= GetConstAggData( nMeasure
, rSubState
);
1982 return false; //TODO: error?
1984 return pAgg
->HasData();
1987 bool ScDPDataMember::HasError( tools::Long nMeasure
, const ScDPSubTotalState
& rSubState
) const
1989 const ScDPAggData
* pAgg
= GetConstAggData( nMeasure
, rSubState
);
1993 return pAgg
->HasError();
1996 double ScDPDataMember::GetAggregate( tools::Long nMeasure
, const ScDPSubTotalState
& rSubState
) const
1998 const ScDPAggData
* pAgg
= GetConstAggData( nMeasure
, rSubState
);
2000 return DBL_MAX
; //TODO: error?
2002 return pAgg
->GetResult();
2005 ScDPAggData
* ScDPDataMember::GetAggData( tools::Long nMeasure
, const ScDPSubTotalState
& rSubState
)
2007 OSL_ENSURE( nMeasure
>= 0, "GetAggData: no measure" );
2009 ScDPAggData
* pAgg
= &aAggregate
;
2010 tools::Long nSkip
= nMeasure
;
2011 tools::Long nSubPos
= lcl_GetSubTotalPos(rSubState
);
2012 if (nSubPos
== SC_SUBTOTALPOS_SKIP
)
2015 nSkip
+= nSubPos
* pResultData
->GetMeasureCount();
2017 for ( tools::Long nPos
=0; nPos
<nSkip
; nPos
++ )
2018 pAgg
= pAgg
->GetChild(); //TODO: need to create children here?
2023 const ScDPAggData
* ScDPDataMember::GetConstAggData( tools::Long nMeasure
, const ScDPSubTotalState
& rSubState
) const
2025 OSL_ENSURE( nMeasure
>= 0, "GetConstAggData: no measure" );
2027 const ScDPAggData
* pAgg
= &aAggregate
;
2028 tools::Long nSkip
= nMeasure
;
2029 tools::Long nSubPos
= lcl_GetSubTotalPos(rSubState
);
2030 if (nSubPos
== SC_SUBTOTALPOS_SKIP
)
2033 nSkip
+= nSubPos
* pResultData
->GetMeasureCount();
2035 for ( tools::Long nPos
=0; nPos
<nSkip
; nPos
++ )
2037 pAgg
= pAgg
->GetExistingChild();
2045 void ScDPDataMember::FillDataRow(
2046 const ScDPResultMember
* pRefMember
, ScDPResultFilterContext
& rFilterCxt
,
2047 uno::Sequence
<sheet::DataResult
>& rSequence
, tools::Long nMeasure
, bool bIsSubTotalRow
,
2048 const ScDPSubTotalState
& rSubState
) const
2050 std::unique_ptr
<FilterStack
> pFilterStack
;
2053 // Topmost data member (pResultMember=NULL) doesn't need to be handled
2054 // since its immediate parent result member is linked to the same
2055 // dimension member.
2056 pFilterStack
.reset(new FilterStack(rFilterCxt
.maFilters
));
2057 pFilterStack
->pushDimValue( pResultMember
->GetDisplayName( false), pResultMember
->GetDisplayName( true));
2060 OSL_ENSURE( pRefMember
== pResultMember
|| !pResultMember
, "bla" );
2062 tools::Long nStartCol
= rFilterCxt
.mnCol
;
2064 const ScDPDataDimension
* pDataChild
= GetChildDimension();
2065 const ScDPResultDimension
* pRefChild
= pRefMember
->GetChildDimension();
2067 const ScDPLevel
* pRefParentLevel
= pRefMember
->GetParentLevel();
2069 tools::Long nExtraSpace
= 0;
2070 if ( pRefParentLevel
&& pRefParentLevel
->IsAddEmpty() )
2073 bool bTitleLine
= false;
2074 if ( pRefParentLevel
&& pRefParentLevel
->IsOutlineLayout() )
2077 bool bSubTotalInTitle
= pRefMember
->IsSubTotalInTitle( nMeasure
);
2079 // leave space for children even if the DataMember hasn't been initialized
2080 // (pDataChild is null then, this happens when no values for it are in this row)
2081 bool bHasChild
= ( pRefChild
!= nullptr );
2085 if ( bTitleLine
) // in tabular layout the title is on a separate column
2086 ++rFilterCxt
.mnCol
; // -> fill child dimension one column below
2090 tools::Long nOldCol
= rFilterCxt
.mnCol
;
2091 pDataChild
->FillDataRow(pRefChild
, rFilterCxt
, rSequence
, nMeasure
, bIsSubTotalRow
, rSubState
);
2092 rFilterCxt
.mnCol
= nOldCol
; // Revert to the old column value before the call.
2094 rFilterCxt
.mnCol
+= static_cast<sal_uInt16
>(pRefMember
->GetSize( nMeasure
));
2096 if ( bTitleLine
) // title column is included in GetSize, so the following
2097 --rFilterCxt
.mnCol
; // positions are calculated with the normal values
2100 tools::Long nUserSubStart
;
2101 tools::Long nUserSubCount
= pRefMember
->GetSubTotalCount(&nUserSubStart
);
2102 if ( !nUserSubCount
&& bHasChild
)
2105 // Calculate at least automatic if no subtotals are selected,
2106 // show only own values if there's no child dimension (innermost).
2107 if ( !nUserSubCount
|| !bHasChild
)
2113 ScDPSubTotalState
aLocalSubState(rSubState
); // keep row state, modify column
2115 tools::Long nMemberMeasure
= nMeasure
;
2116 tools::Long nSubSize
= pResultData
->GetCountForMeasure(nMeasure
);
2119 rFilterCxt
.mnCol
-= nSubSize
* ( nUserSubCount
- nUserSubStart
); // GetSize includes space for SubTotal
2120 rFilterCxt
.mnCol
-= nExtraSpace
; // GetSize includes the empty line
2123 tools::Long nMoveSubTotal
= 0;
2124 if ( bSubTotalInTitle
)
2126 nMoveSubTotal
= rFilterCxt
.mnCol
- nStartCol
; // force to first (title) column
2127 rFilterCxt
.mnCol
= nStartCol
;
2130 for (tools::Long nUserPos
=nUserSubStart
; nUserPos
<nUserSubCount
; nUserPos
++)
2132 if ( pChildDimension
&& nUserSubCount
> 1 )
2134 const ScDPLevel
* pForceLevel
= pResultMember
? pResultMember
->GetParentLevel() : nullptr;
2135 aLocalSubState
.nColSubTotalFunc
= nUserPos
;
2136 aLocalSubState
.eColForce
= lcl_GetForceFunc( pForceLevel
, nUserPos
);
2139 for ( tools::Long nSubCount
=0; nSubCount
<nSubSize
; nSubCount
++ )
2141 if ( nMeasure
== SC_DPMEASURE_ALL
)
2142 nMemberMeasure
= nSubCount
;
2144 OSL_ENSURE( rFilterCxt
.mnCol
< rSequence
.getLength(), "bumm" );
2145 sheet::DataResult
& rRes
= rSequence
.getArray()[rFilterCxt
.mnCol
];
2147 if ( HasData( nMemberMeasure
, aLocalSubState
) )
2149 if ( HasError( nMemberMeasure
, aLocalSubState
) )
2152 rRes
.Flags
|= sheet::DataResultFlags::ERROR
;
2156 rRes
.Value
= GetAggregate( nMemberMeasure
, aLocalSubState
);
2157 rRes
.Flags
|= sheet::DataResultFlags::HASDATA
;
2161 if ( bHasChild
|| bIsSubTotalRow
)
2162 rRes
.Flags
|= sheet::DataResultFlags::SUBTOTAL
;
2164 rFilterCxt
.maFilterSet
.add(rFilterCxt
.maFilters
, rRes
.Value
);
2165 rFilterCxt
.mnCol
+= 1;
2169 // add extra space again if subtracted from GetSize above,
2170 // add to own size if no children
2171 rFilterCxt
.mnCol
+= nExtraSpace
;
2172 rFilterCxt
.mnCol
+= nMoveSubTotal
;
2175 void ScDPDataMember::UpdateDataRow(
2176 const ScDPResultMember
* pRefMember
, tools::Long nMeasure
, bool bIsSubTotalRow
,
2177 const ScDPSubTotalState
& rSubState
)
2179 OSL_ENSURE( pRefMember
== pResultMember
|| !pResultMember
, "bla" );
2181 // Calculate must be called even if not visible (for use as reference value)
2182 const ScDPDataDimension
* pDataChild
= GetChildDimension();
2183 const ScDPResultDimension
* pRefChild
= pRefMember
->GetChildDimension();
2185 // leave space for children even if the DataMember hasn't been initialized
2186 // (pDataChild is null then, this happens when no values for it are in this row)
2187 bool bHasChild
= ( pRefChild
!= nullptr );
2189 // process subtotals even if not shown
2190 tools::Long nUserSubCount
= pRefMember
->GetSubTotalCount();
2192 // Calculate at least automatic if no subtotals are selected,
2193 // show only own values if there's no child dimension (innermost).
2194 if ( !nUserSubCount
|| !bHasChild
)
2197 ScDPSubTotalState
aLocalSubState(rSubState
); // keep row state, modify column
2199 tools::Long nMemberMeasure
= nMeasure
;
2200 tools::Long nSubSize
= pResultData
->GetCountForMeasure(nMeasure
);
2202 for (tools::Long nUserPos
=0; nUserPos
<nUserSubCount
; nUserPos
++) // including hidden "automatic"
2204 if ( pChildDimension
&& nUserSubCount
> 1 )
2206 const ScDPLevel
* pForceLevel
= pResultMember
? pResultMember
->GetParentLevel() : nullptr;
2207 aLocalSubState
.nColSubTotalFunc
= nUserPos
;
2208 aLocalSubState
.eColForce
= lcl_GetForceFunc( pForceLevel
, nUserPos
);
2211 for ( tools::Long nSubCount
=0; nSubCount
<nSubSize
; nSubCount
++ )
2213 if ( nMeasure
== SC_DPMEASURE_ALL
)
2214 nMemberMeasure
= nSubCount
;
2217 ScDPAggData
* pAggData
= GetAggData( nMemberMeasure
, aLocalSubState
);
2220 //TODO: aLocalSubState?
2221 ScSubTotalFunc eFunc
= pResultData
->GetMeasureFunction( nMemberMeasure
);
2222 sheet::DataPilotFieldReference aReferenceValue
= pResultData
->GetMeasureRefVal( nMemberMeasure
);
2223 sal_Int32 eRefType
= aReferenceValue
.ReferenceType
;
2225 // calculate the result first - for all members, regardless of reference value
2226 pAggData
->Calculate( eFunc
, aLocalSubState
);
2228 if ( eRefType
== sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE
||
2229 eRefType
== sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE
||
2230 eRefType
== sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE
)
2232 // copy the result into auxiliary value, so differences can be
2233 // calculated in any order
2234 pAggData
->SetAuxiliary( pAggData
->GetResult() );
2236 // column/row percentage/index is now in UpdateRunningTotals, so it doesn't disturb sorting
2241 if ( bHasChild
) // child dimension must be processed last, so the row total is known
2244 pDataChild
->UpdateDataRow( pRefChild
, nMeasure
, bIsSubTotalRow
, rSubState
);
2248 void ScDPDataMember::SortMembers( ScDPResultMember
* pRefMember
)
2250 OSL_ENSURE( pRefMember
== pResultMember
|| !pResultMember
, "bla" );
2252 if ( pRefMember
->IsVisible() ) //TODO: here or in ScDPDataDimension ???
2254 ScDPDataDimension
* pDataChild
= GetChildDimension();
2255 ScDPResultDimension
* pRefChild
= pRefMember
->GetChildDimension();
2256 if ( pRefChild
&& pDataChild
)
2257 pDataChild
->SortMembers( pRefChild
); // sorting is done at the dimension
2261 void ScDPDataMember::DoAutoShow( ScDPResultMember
* pRefMember
)
2263 OSL_ENSURE( pRefMember
== pResultMember
|| !pResultMember
, "bla" );
2265 if ( pRefMember
->IsVisible() ) //TODO: here or in ScDPDataDimension ???
2267 ScDPDataDimension
* pDataChild
= GetChildDimension();
2268 ScDPResultDimension
* pRefChild
= pRefMember
->GetChildDimension();
2269 if ( pRefChild
&& pDataChild
)
2270 pDataChild
->DoAutoShow( pRefChild
); // sorting is done at the dimension
2274 void ScDPDataMember::ResetResults()
2278 ScDPDataDimension
* pDataChild
= GetChildDimension();
2280 pDataChild
->ResetResults();
2283 void ScDPDataMember::UpdateRunningTotals(
2284 const ScDPResultMember
* pRefMember
, tools::Long nMeasure
, bool bIsSubTotalRow
,
2285 const ScDPSubTotalState
& rSubState
, ScDPRunningTotalState
& rRunning
,
2286 ScDPRowTotals
& rTotals
, const ScDPResultMember
& rRowParent
)
2288 OSL_ENSURE( pRefMember
== pResultMember
|| !pResultMember
, "bla" );
2290 const ScDPDataDimension
* pDataChild
= GetChildDimension();
2291 const ScDPResultDimension
* pRefChild
= pRefMember
->GetChildDimension();
2293 bool bIsRoot
= ( pResultMember
== nullptr || pResultMember
->GetParentLevel() == nullptr );
2295 // leave space for children even if the DataMember hasn't been initialized
2296 // (pDataChild is null then, this happens when no values for it are in this row)
2297 bool bHasChild
= ( pRefChild
!= nullptr );
2299 tools::Long nUserSubCount
= pRefMember
->GetSubTotalCount();
2301 // Calculate at least automatic if no subtotals are selected,
2302 // show only own values if there's no child dimension (innermost).
2303 if ( !nUserSubCount
|| !bHasChild
)
2306 ScDPSubTotalState
aLocalSubState(rSubState
); // keep row state, modify column
2308 tools::Long nMemberMeasure
= nMeasure
;
2309 tools::Long nSubSize
= pResultData
->GetCountForMeasure(nMeasure
);
2311 for (tools::Long nUserPos
=0; nUserPos
<nUserSubCount
; nUserPos
++) // including hidden "automatic"
2313 if ( pChildDimension
&& nUserSubCount
> 1 )
2315 const ScDPLevel
* pForceLevel
= pResultMember
? pResultMember
->GetParentLevel() : nullptr;
2316 aLocalSubState
.nColSubTotalFunc
= nUserPos
;
2317 aLocalSubState
.eColForce
= lcl_GetForceFunc( pForceLevel
, nUserPos
);
2320 for ( tools::Long nSubCount
=0; nSubCount
<nSubSize
; nSubCount
++ )
2322 if ( nMeasure
== SC_DPMEASURE_ALL
)
2323 nMemberMeasure
= nSubCount
;
2326 ScDPAggData
* pAggData
= GetAggData( nMemberMeasure
, aLocalSubState
);
2329 //TODO: aLocalSubState?
2330 sheet::DataPilotFieldReference aReferenceValue
= pResultData
->GetMeasureRefVal( nMemberMeasure
);
2331 sal_Int32 eRefType
= aReferenceValue
.ReferenceType
;
2333 if ( eRefType
== sheet::DataPilotFieldReferenceType::RUNNING_TOTAL
||
2334 eRefType
== sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE
||
2335 eRefType
== sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE
||
2336 eRefType
== sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE
)
2338 bool bRunningTotal
= ( eRefType
== sheet::DataPilotFieldReferenceType::RUNNING_TOTAL
);
2340 ( aReferenceValue
.ReferenceItemType
!= sheet::DataPilotFieldReferenceItemType::NAMED
&& !bRunningTotal
);
2341 tools::Long nRelativeDir
= bRelative
?
2342 ( ( aReferenceValue
.ReferenceItemType
== sheet::DataPilotFieldReferenceItemType::PREVIOUS
) ? -1 : 1 ) : 0;
2344 const ScDPRunningTotalState::IndexArray
& rColVisible
= rRunning
.GetColVisible();
2345 const ScDPRunningTotalState::IndexArray
& rColSorted
= rRunning
.GetColSorted();
2346 const ScDPRunningTotalState::IndexArray
& rRowVisible
= rRunning
.GetRowVisible();
2347 const ScDPRunningTotalState::IndexArray
& rRowSorted
= rRunning
.GetRowSorted();
2349 OUString aRefFieldName
= aReferenceValue
.ReferenceField
;
2351 //TODO: aLocalSubState?
2352 sheet::DataPilotFieldOrientation nRefOrient
= pResultData
->GetMeasureRefOrient( nMemberMeasure
);
2353 bool bRefDimInCol
= ( nRefOrient
== sheet::DataPilotFieldOrientation_COLUMN
);
2354 bool bRefDimInRow
= ( nRefOrient
== sheet::DataPilotFieldOrientation_ROW
);
2356 ScDPResultDimension
* pSelectDim
= nullptr;
2357 sal_Int32 nRowPos
= 0;
2358 sal_Int32 nColPos
= 0;
2360 // find the reference field in column or row dimensions
2362 if ( bRefDimInRow
) // look in row dimensions
2364 pSelectDim
= rRunning
.GetRowResRoot()->GetChildDimension();
2365 while ( pSelectDim
&& pSelectDim
->GetName() != aRefFieldName
)
2367 tools::Long nIndex
= rRowSorted
[nRowPos
];
2368 if ( nIndex
>= 0 && nIndex
< pSelectDim
->GetMemberCount() )
2369 pSelectDim
= pSelectDim
->GetMember(nIndex
)->GetChildDimension();
2371 pSelectDim
= nullptr;
2374 // child dimension of innermost member?
2375 if ( pSelectDim
&& rRowSorted
[nRowPos
] < 0 )
2376 pSelectDim
= nullptr;
2379 if ( bRefDimInCol
) // look in column dimensions
2381 pSelectDim
= rRunning
.GetColResRoot()->GetChildDimension();
2382 while ( pSelectDim
&& pSelectDim
->GetName() != aRefFieldName
)
2384 tools::Long nIndex
= rColSorted
[nColPos
];
2385 if ( nIndex
>= 0 && nIndex
< pSelectDim
->GetMemberCount() )
2386 pSelectDim
= pSelectDim
->GetMember(nIndex
)->GetChildDimension();
2388 pSelectDim
= nullptr;
2391 // child dimension of innermost member?
2392 if ( pSelectDim
&& rColSorted
[nColPos
] < 0 )
2393 pSelectDim
= nullptr;
2396 bool bNoDetailsInRef
= false;
2397 if ( pSelectDim
&& bRunningTotal
)
2400 // If details are hidden for this member in the reference dimension,
2401 // don't show or sum up the value. Otherwise, for following members,
2402 // the running totals of details and subtotals wouldn't match.
2404 tools::Long nMyIndex
= bRefDimInCol
? rColSorted
[nColPos
] : rRowSorted
[nRowPos
];
2405 if ( nMyIndex
>= 0 && nMyIndex
< pSelectDim
->GetMemberCount() )
2407 const ScDPResultMember
* pMyRefMember
= pSelectDim
->GetMember(nMyIndex
);
2408 if ( pMyRefMember
&& pMyRefMember
->HasHiddenDetails() )
2410 pSelectDim
= nullptr; // don't calculate
2411 bNoDetailsInRef
= true; // show error, not empty
2418 // Difference/Percentage from previous/next:
2419 // If details are hidden for this member in the innermost column/row
2420 // dimension (the orientation of the reference dimension), show an
2422 // - If the no-details dimension is the reference dimension, its
2423 // members will be skipped when finding the previous/next member,
2424 // so there must be no results for its members.
2425 // - If the no-details dimension is outside of the reference dimension,
2426 // no calculation in the reference dimension is possible.
2427 // - Otherwise, the error isn't strictly necessary, but shown for
2430 bool bInnerNoDetails
= bRefDimInCol
? HasHiddenDetails() :
2431 ( !bRefDimInRow
|| rRowParent
.HasHiddenDetails() );
2432 if ( bInnerNoDetails
)
2434 pSelectDim
= nullptr;
2435 bNoDetailsInRef
= true; // show error, not empty
2439 if ( !bRefDimInCol
&& !bRefDimInRow
) // invalid dimension specified
2440 bNoDetailsInRef
= true; // pSelectDim is then already NULL
2442 // get the member for the reference item and do the calculation
2444 if ( bRunningTotal
)
2446 // running total in (dimension) -> find first existing member
2450 ScDPDataMember
* pSelectMember
;
2452 pSelectMember
= ScDPResultDimension::GetColReferenceMember( nullptr, nullptr,
2453 nColPos
, rRunning
);
2456 const sal_Int32
* pRowSorted
= rRowSorted
.data();
2457 const sal_Int32
* pColSorted
= rColSorted
.data();
2458 pRowSorted
+= nRowPos
+ 1; // including the reference dimension
2459 pSelectMember
= pSelectDim
->GetRowReferenceMember(
2460 nullptr, nullptr, pRowSorted
, pColSorted
);
2463 if ( pSelectMember
)
2465 // The running total is kept as the auxiliary value in
2466 // the first available member for the reference dimension.
2467 // Members are visited in final order, so each one's result
2468 // can be used and then modified.
2470 ScDPAggData
* pSelectData
= pSelectMember
->
2471 GetAggData( nMemberMeasure
, aLocalSubState
);
2474 double fTotal
= pSelectData
->GetAuxiliary();
2475 fTotal
+= pAggData
->GetResult();
2476 pSelectData
->SetAuxiliary( fTotal
);
2477 pAggData
->SetResult( fTotal
);
2478 pAggData
->SetEmpty(false); // always display
2482 pAggData
->SetError();
2484 else if (bNoDetailsInRef
)
2485 pAggData
->SetError();
2487 pAggData
->SetEmpty(true); // empty (dim set to 0 above)
2491 // difference/percentage -> find specified member
2495 OUString aRefItemName
= aReferenceValue
.ReferenceItemName
;
2496 ScDPRelativePos
aRefItemPos( 0, nRelativeDir
); // nBasePos is modified later
2498 const OUString
* pRefName
= nullptr;
2499 const ScDPRelativePos
* pRefPos
= nullptr;
2501 pRefPos
= &aRefItemPos
;
2503 pRefName
= &aRefItemName
;
2505 ScDPDataMember
* pSelectMember
;
2508 aRefItemPos
.nBasePos
= rColVisible
[nColPos
]; // without sort order applied
2509 pSelectMember
= ScDPResultDimension::GetColReferenceMember( pRefPos
, pRefName
,
2510 nColPos
, rRunning
);
2514 aRefItemPos
.nBasePos
= rRowVisible
[nRowPos
]; // without sort order applied
2515 const sal_Int32
* pRowSorted
= rRowSorted
.data();
2516 const sal_Int32
* pColSorted
= rColSorted
.data();
2517 pRowSorted
+= nRowPos
+ 1; // including the reference dimension
2518 pSelectMember
= pSelectDim
->GetRowReferenceMember(
2519 pRefPos
, pRefName
, pRowSorted
, pColSorted
);
2522 // difference or perc.difference is empty for the reference item itself
2523 if ( pSelectMember
== this &&
2524 eRefType
!= sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE
)
2526 pAggData
->SetEmpty(true);
2528 else if ( pSelectMember
)
2530 const ScDPAggData
* pOtherAggData
= pSelectMember
->
2531 GetConstAggData( nMemberMeasure
, aLocalSubState
);
2532 OSL_ENSURE( pOtherAggData
, "no agg data" );
2533 if ( pOtherAggData
)
2535 // Reference member may be visited before or after this one,
2536 // so the auxiliary value is used for the original result.
2538 double fOtherResult
= pOtherAggData
->GetAuxiliary();
2539 double fThisResult
= pAggData
->GetResult();
2540 bool bError
= false;
2543 case sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE
:
2544 fThisResult
= fThisResult
- fOtherResult
;
2546 case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE
:
2547 if ( fOtherResult
== 0.0 )
2550 fThisResult
= fThisResult
/ fOtherResult
;
2552 case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE
:
2553 if ( fOtherResult
== 0.0 )
2556 fThisResult
= ( fThisResult
- fOtherResult
) / fOtherResult
;
2559 OSL_FAIL("invalid calculation type");
2563 pAggData
->SetError();
2567 pAggData
->SetResult(fThisResult
);
2568 pAggData
->SetEmpty(false); // always display
2570 //TODO: errors in data?
2573 else if (bRelative
&& !bNoDetailsInRef
)
2574 pAggData
->SetEmpty(true); // empty
2576 pAggData
->SetError(); // error
2578 else if (bNoDetailsInRef
)
2579 pAggData
->SetError(); // error
2581 pAggData
->SetEmpty(true); // empty
2584 else if ( eRefType
== sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE
||
2585 eRefType
== sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE
||
2586 eRefType
== sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE
||
2587 eRefType
== sheet::DataPilotFieldReferenceType::INDEX
)
2590 // set total values when they are encountered (always before their use)
2592 ScDPAggData
* pColTotalData
= pRefMember
->GetColTotal( nMemberMeasure
);
2593 ScDPAggData
* pRowTotalData
= rTotals
.GetRowTotal( nMemberMeasure
);
2594 ScDPAggData
* pGrandTotalData
= rTotals
.GetGrandTotal( nMemberMeasure
);
2596 double fTotalValue
= pAggData
->HasError() ? 0 : pAggData
->GetResult();
2598 if ( bIsRoot
&& rTotals
.IsInColRoot() && pGrandTotalData
)
2599 pGrandTotalData
->SetAuxiliary( fTotalValue
);
2601 if ( bIsRoot
&& pRowTotalData
)
2602 pRowTotalData
->SetAuxiliary( fTotalValue
);
2604 if ( rTotals
.IsInColRoot() && pColTotalData
)
2605 pColTotalData
->SetAuxiliary( fTotalValue
);
2607 // find relation to total values
2611 case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE
:
2612 case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE
:
2613 case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE
:
2616 if ( eRefType
== sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE
)
2617 nTotal
= pRowTotalData
? pRowTotalData
->GetAuxiliary() : 0.0;
2618 else if ( eRefType
== sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE
)
2619 nTotal
= pColTotalData
? pColTotalData
->GetAuxiliary() : 0.0;
2621 nTotal
= pGrandTotalData
? pGrandTotalData
->GetAuxiliary() : 0.0;
2623 if ( nTotal
== 0.0 )
2624 pAggData
->SetError();
2626 pAggData
->SetResult( pAggData
->GetResult() / nTotal
);
2629 case sheet::DataPilotFieldReferenceType::INDEX
:
2631 double nColTotal
= pColTotalData
? pColTotalData
->GetAuxiliary() : 0.0;
2632 double nRowTotal
= pRowTotalData
? pRowTotalData
->GetAuxiliary() : 0.0;
2633 double nGrandTotal
= pGrandTotalData
? pGrandTotalData
->GetAuxiliary() : 0.0;
2634 if ( nRowTotal
== 0.0 || nColTotal
== 0.0 )
2635 pAggData
->SetError();
2637 pAggData
->SetResult(
2638 ( pAggData
->GetResult() * nGrandTotal
) /
2639 ( nRowTotal
* nColTotal
) );
2649 if ( bHasChild
) // child dimension must be processed last, so the row total is known
2652 pDataChild
->UpdateRunningTotals( pRefChild
, nMeasure
,
2653 bIsSubTotalRow
, rSubState
, rRunning
, rTotals
, rRowParent
);
2657 #if DUMP_PIVOT_TABLE
2658 void ScDPDataMember::DumpState( const ScDPResultMember
* pRefMember
, ScDocument
* pDoc
, ScAddress
& rPos
) const
2660 dumpRow(u
"ScDPDataMember"_ustr
, GetName(), &aAggregate
, pDoc
, rPos
);
2661 SCROW nStartRow
= rPos
.Row();
2663 const ScDPDataDimension
* pDataChild
= GetChildDimension();
2664 const ScDPResultDimension
* pRefChild
= pRefMember
->GetChildDimension();
2665 if ( pDataChild
&& pRefChild
)
2666 pDataChild
->DumpState( pRefChild
, pDoc
, rPos
);
2668 indent(pDoc
, nStartRow
, rPos
);
2671 void ScDPDataMember::Dump(int nIndent
) const
2673 std::string
aIndent(nIndent
*2, ' ');
2674 std::cout
<< aIndent
<< "-- data member '"
2675 << (pResultMember
? pResultMember
->GetName() : OUString()) << "'" << std::endl
;
2676 for (const ScDPAggData
* pAgg
= &aAggregate
; pAgg
; pAgg
= pAgg
->GetExistingChild())
2677 pAgg
->Dump(nIndent
+1);
2679 if (pChildDimension
)
2680 pChildDimension
->Dump(nIndent
+1);
2684 // Helper class to select the members to include in
2685 // ScDPResultDimension::InitFrom or LateInitFrom if groups are used
2689 class ScDPGroupCompare
2692 const ScDPResultData
* pResultData
;
2693 const ScDPInitState
& rInitState
;
2694 tools::Long nDimSource
;
2697 tools::Long nGroupBase
;
2699 ScDPGroupCompare( const ScDPResultData
* pData
, const ScDPInitState
& rState
, tools::Long nDimension
);
2701 bool IsIncluded( const ScDPMember
& rMember
) { return bIncludeAll
|| TestIncluded( rMember
); }
2702 bool TestIncluded( const ScDPMember
& rMember
);
2707 ScDPGroupCompare::ScDPGroupCompare( const ScDPResultData
* pData
, const ScDPInitState
& rState
, tools::Long nDimension
) :
2708 pResultData( pData
),
2709 rInitState( rState
),
2710 nDimSource( nDimension
)
2712 bIsBase
= pResultData
->IsBaseForGroup( nDimSource
);
2713 nGroupBase
= pResultData
->GetGroupBase( nDimSource
); //TODO: get together in one call?
2715 // if bIncludeAll is set, TestIncluded doesn't need to be called
2716 bIncludeAll
= !( bIsBase
|| nGroupBase
>= 0 );
2719 bool ScDPGroupCompare::TestIncluded( const ScDPMember
& rMember
)
2721 bool bInclude
= true;
2724 // need to check all previous groups
2725 //TODO: get array of groups (or indexes) before loop?
2726 ScDPItemData
aMemberData(rMember
.FillItemData());
2728 const std::vector
<ScDPInitState::Member
>& rMemStates
= rInitState
.GetMembers();
2729 bInclude
= std::all_of(rMemStates
.begin(), rMemStates
.end(),
2730 [this, &aMemberData
](const ScDPInitState::Member
& rMem
) {
2731 return (pResultData
->GetGroupBase(rMem
.mnSrcIndex
) != nDimSource
)
2732 || pResultData
->IsInGroup(rMem
.mnNameIndex
, rMem
.mnSrcIndex
, aMemberData
, nDimSource
);
2735 else if ( nGroupBase
>= 0 )
2737 // base isn't used in preceding fields
2738 // -> look for other groups using the same base
2740 //TODO: get array of groups (or indexes) before loop?
2741 ScDPItemData
aMemberData(rMember
.FillItemData());
2742 const std::vector
<ScDPInitState::Member
>& rMemStates
= rInitState
.GetMembers();
2743 bInclude
= std::all_of(rMemStates
.begin(), rMemStates
.end(),
2744 [this, &aMemberData
](const ScDPInitState::Member
& rMem
) {
2745 // coverity[copy_paste_error : FALSE] - same base (hierarchy between
2746 // the two groups is irrelevant)
2747 return (pResultData
->GetGroupBase(rMem
.mnSrcIndex
) != nGroupBase
)
2748 || pResultData
->HasCommonElement(rMem
.mnNameIndex
, rMem
.mnSrcIndex
, aMemberData
, nDimSource
);
2755 ScDPResultDimension::ScDPResultDimension( const ScDPResultData
* pData
) :
2756 pResultData( pData
),
2758 bIsDataLayout( false ),
2759 bSortByData( false ),
2760 bSortAscending( false ),
2762 bAutoTopItems( false ),
2763 bInitialized( false ),
2769 ScDPResultDimension::~ScDPResultDimension()
2773 ScDPResultMember
*ScDPResultDimension::FindMember( SCROW iData
) const
2777 SAL_WARN_IF(maMemberArray
.empty(), "sc.core", "MemberArray is empty");
2778 return !maMemberArray
.empty() ? maMemberArray
[0].get() : nullptr;
2781 MemberHash::const_iterator aRes
= maMemberHash
.find( iData
);
2782 if( aRes
!= maMemberHash
.end()) {
2783 if ( aRes
->second
->IsNamedItem( iData
) )
2784 return aRes
->second
;
2785 OSL_FAIL("problem! hash result is not the same as IsNamedItem");
2789 unsigned int nCount
= maMemberArray
.size();
2790 for( i
= 0; i
< nCount
; i
++ )
2792 ScDPResultMember
* pResultMember
= maMemberArray
[i
].get();
2793 if ( pResultMember
->IsNamedItem( iData
) )
2794 return pResultMember
;
2799 void ScDPResultDimension::InitFrom(
2800 const vector
<ScDPDimension
*>& ppDim
, const vector
<ScDPLevel
*>& ppLev
,
2801 size_t nPos
, ScDPInitState
& rInitState
, bool bInitChild
)
2803 if (nPos
>= ppDim
.size() || nPos
>= ppLev
.size())
2805 bInitialized
= true;
2809 ScDPDimension
* pThisDim
= ppDim
[nPos
];
2810 ScDPLevel
* pThisLevel
= ppLev
[nPos
];
2812 if (!pThisDim
|| !pThisLevel
)
2814 bInitialized
= true;
2818 bIsDataLayout
= pThisDim
->getIsDataLayoutDimension(); // member
2819 aDimensionName
= pThisDim
->getName(); // member
2821 // Check the autoshow setting. If it's enabled, store the settings.
2822 const sheet::DataPilotFieldAutoShowInfo
& rAutoInfo
= pThisLevel
->GetAutoShow();
2823 if ( rAutoInfo
.IsEnabled
)
2826 bAutoTopItems
= ( rAutoInfo
.ShowItemsMode
== sheet::DataPilotFieldShowItemsMode::FROM_TOP
);
2827 nAutoMeasure
= pThisLevel
->GetAutoMeasure();
2828 nAutoCount
= rAutoInfo
.ItemCount
;
2831 // Check the sort info, and store the settings if appropriate.
2832 const sheet::DataPilotFieldSortInfo
& rSortInfo
= pThisLevel
->GetSortInfo();
2833 if ( rSortInfo
.Mode
== sheet::DataPilotFieldSortMode::DATA
)
2836 bSortAscending
= rSortInfo
.IsAscending
;
2837 nSortMeasure
= pThisLevel
->GetSortMeasure();
2840 // global order is used to initialize aMembers, so it doesn't have to be looked at later
2841 const ScMemberSortOrder
& rGlobalOrder
= pThisLevel
->GetGlobalOrder();
2843 tools::Long nDimSource
= pThisDim
->GetDimension(); //TODO: check GetSourceDim?
2844 ScDPGroupCompare
aCompare( pResultData
, rInitState
, nDimSource
);
2846 // Now, go through all members and initialize them.
2847 ScDPMembers
* pMembers
= pThisLevel
->GetMembersObject();
2848 tools::Long nMembCount
= pMembers
->getCount();
2849 for ( tools::Long i
=0; i
<nMembCount
; i
++ )
2851 tools::Long nSorted
= rGlobalOrder
.empty() ? i
: rGlobalOrder
[i
];
2853 ScDPMember
* pMember
= pMembers
->getByIndex(nSorted
);
2854 if ( aCompare
.IsIncluded( *pMember
) )
2856 ScDPParentDimData
aData( i
, pThisDim
, pThisLevel
, pMember
);
2857 ScDPResultMember
* pNew
= AddMember( aData
);
2859 rInitState
.AddMember(nDimSource
, pNew
->GetDataId());
2860 pNew
->InitFrom( ppDim
, ppLev
, nPos
+1, rInitState
, bInitChild
);
2861 rInitState
.RemoveMember();
2864 bInitialized
= true;
2867 void ScDPResultDimension::LateInitFrom(
2868 LateInitParams
& rParams
, const vector
<SCROW
>& pItemData
, size_t nPos
, ScDPInitState
& rInitState
)
2870 if ( rParams
.IsEnd( nPos
) )
2872 if (nPos
>= pItemData
.size())
2874 SAL_WARN("sc.core", "pos " << nPos
<< ", but vector size is " << pItemData
.size());
2877 SCROW rThisData
= pItemData
[nPos
];
2878 ScDPDimension
* pThisDim
= rParams
.GetDim( nPos
);
2879 ScDPLevel
* pThisLevel
= rParams
.GetLevel( nPos
);
2881 if (!pThisDim
|| !pThisLevel
)
2884 tools::Long nDimSource
= pThisDim
->GetDimension(); //TODO: check GetSourceDim?
2886 bool bShowEmpty
= pThisLevel
->getShowEmpty();
2888 if ( !bInitialized
)
2889 { // init some values
2890 // create all members at the first call (preserve order)
2891 bIsDataLayout
= pThisDim
->getIsDataLayoutDimension();
2892 aDimensionName
= pThisDim
->getName();
2894 const sheet::DataPilotFieldAutoShowInfo
& rAutoInfo
= pThisLevel
->GetAutoShow();
2895 if ( rAutoInfo
.IsEnabled
)
2898 bAutoTopItems
= ( rAutoInfo
.ShowItemsMode
== sheet::DataPilotFieldShowItemsMode::FROM_TOP
);
2899 nAutoMeasure
= pThisLevel
->GetAutoMeasure();
2900 nAutoCount
= rAutoInfo
.ItemCount
;
2903 const sheet::DataPilotFieldSortInfo
& rSortInfo
= pThisLevel
->GetSortInfo();
2904 if ( rSortInfo
.Mode
== sheet::DataPilotFieldSortMode::DATA
)
2907 bSortAscending
= rSortInfo
.IsAscending
;
2908 nSortMeasure
= pThisLevel
->GetSortMeasure();
2912 bool bLateInitAllMembers
= bIsDataLayout
|| rParams
.GetInitAllChild() || bShowEmpty
;
2914 if ( !bLateInitAllMembers
)
2916 ResultMembers
& rMembers
= pResultData
->GetDimResultMembers(nDimSource
, pThisDim
, pThisLevel
);
2917 bLateInitAllMembers
= rMembers
.IsHasHideDetailsMembers();
2919 SAL_INFO("sc.core", aDimensionName
<< (rMembers
.IsHasHideDetailsMembers() ? " HasHideDetailsMembers" : ""));
2921 rMembers
.SetHasHideDetailsMembers( false );
2924 bool bNewAllMembers
= (!rParams
.IsRow()) || nPos
== 0 || bLateInitAllMembers
;
2926 if (bNewAllMembers
)
2928 // global order is used to initialize aMembers, so it doesn't have to be looked at later
2929 if ( !bInitialized
)
2930 { //init all members
2931 const ScMemberSortOrder
& rGlobalOrder
= pThisLevel
->GetGlobalOrder();
2933 ScDPGroupCompare
aCompare( pResultData
, rInitState
, nDimSource
);
2934 ScDPMembers
* pMembers
= pThisLevel
->GetMembersObject();
2935 tools::Long nMembCount
= pMembers
->getCount();
2936 for ( tools::Long i
=0; i
<nMembCount
; i
++ )
2938 tools::Long nSorted
= rGlobalOrder
.empty() ? i
: rGlobalOrder
[i
];
2940 ScDPMember
* pMember
= pMembers
->getByIndex(nSorted
);
2941 if ( aCompare
.IsIncluded( *pMember
) )
2942 { // add all members
2943 ScDPParentDimData
aData( i
, pThisDim
, pThisLevel
, pMember
);
2947 bInitialized
= true; // don't call again, even if no members were included
2949 // initialize only specific member (or all if "show empty" flag is set)
2950 if ( bLateInitAllMembers
)
2952 tools::Long nCount
= maMemberArray
.size();
2953 for (tools::Long i
=0; i
<nCount
; i
++)
2955 ScDPResultMember
* pResultMember
= maMemberArray
[i
].get();
2958 bool bAllChildren
= false;
2961 bAllChildren
= !pResultMember
->IsNamedItem( rThisData
);
2963 rParams
.SetInitAllChildren( bAllChildren
);
2964 rInitState
.AddMember( nDimSource
, pResultMember
->GetDataId() );
2965 pResultMember
->LateInitFrom( rParams
, pItemData
, nPos
+1, rInitState
);
2966 rInitState
.RemoveMember();
2971 ScDPResultMember
* pResultMember
= FindMember( rThisData
);
2972 if( nullptr != pResultMember
)
2974 rInitState
.AddMember( nDimSource
, pResultMember
->GetDataId() );
2975 pResultMember
->LateInitFrom( rParams
, pItemData
, nPos
+1, rInitState
);
2976 rInitState
.RemoveMember();
2981 InitWithMembers( rParams
, pItemData
, nPos
, rInitState
);
2984 tools::Long
ScDPResultDimension::GetSize(tools::Long nMeasure
) const
2986 tools::Long nMemberCount
= maMemberArray
.size();
2990 tools::Long nTotal
= 0;
2993 OSL_ENSURE(nMeasure
== SC_DPMEASURE_ALL
|| pResultData
->GetMeasureCount() == 1,
2994 "DataLayout dimension twice?");
2995 // repeat first member...
2996 nTotal
= nMemberCount
* maMemberArray
[0]->GetSize(0); // all measures have equal size
3001 for (tools::Long nMem
=0; nMem
<nMemberCount
; nMem
++)
3002 nTotal
+= maMemberArray
[nMem
]->GetSize(nMeasure
);
3007 bool ScDPResultDimension::IsValidEntry( const vector
< SCROW
>& aMembers
) const
3009 if (aMembers
.empty())
3012 const ScDPResultMember
* pMember
= FindMember( aMembers
[0] );
3013 if ( nullptr != pMember
)
3014 return pMember
->IsValidEntry( aMembers
);
3015 #if OSL_DEBUG_LEVEL > 0
3016 SAL_INFO("sc.core", "IsValidEntry: Member not found, DimNam = " << GetName());
3021 void ScDPResultDimension::ProcessData( const vector
< SCROW
>& aMembers
,
3022 const ScDPResultDimension
* pDataDim
,
3023 const vector
< SCROW
>& aDataMembers
,
3024 const vector
<ScDPValue
>& aValues
) const
3026 if (aMembers
.empty())
3029 ScDPResultMember
* pMember
= FindMember( aMembers
[0] );
3030 if ( nullptr != pMember
)
3032 vector
<SCROW
> aChildMembers
;
3033 if (aMembers
.size() > 1)
3035 vector
<SCROW
>::const_iterator itr
= aMembers
.begin();
3036 aChildMembers
.insert(aChildMembers
.begin(), ++itr
, aMembers
.end());
3038 pMember
->ProcessData( aChildMembers
, pDataDim
, aDataMembers
, aValues
);
3042 OSL_FAIL("ProcessData: Member not found");
3045 void ScDPResultDimension::FillMemberResults( uno::Sequence
<sheet::MemberResult
>* pSequences
,
3046 tools::Long nStart
, tools::Long nMeasure
)
3048 tools::Long nPos
= nStart
;
3049 tools::Long nCount
= maMemberArray
.size();
3051 for (tools::Long i
=0; i
<nCount
; i
++)
3053 tools::Long nSorted
= aMemberOrder
.empty() ? i
: aMemberOrder
[i
];
3055 ScDPResultMember
* pMember
= maMemberArray
[nSorted
].get();
3056 // in data layout dimension, use first member with different measures/names
3057 if ( bIsDataLayout
)
3059 bool bTotalResult
= false;
3060 OUString aMbrName
= pResultData
->GetMeasureDimensionName( nSorted
);
3061 OUString aMbrCapt
= pResultData
->GetMeasureString( nSorted
, false, SUBTOTAL_FUNC_NONE
, bTotalResult
);
3062 maMemberArray
[0]->FillMemberResults( pSequences
, nPos
, nSorted
, false, &aMbrName
, &aMbrCapt
);
3064 else if ( pMember
->IsVisible() )
3066 pMember
->FillMemberResults( pSequences
, nPos
, nMeasure
, false, nullptr, nullptr );
3072 void ScDPResultDimension::FillDataResults(
3073 const ScDPResultMember
* pRefMember
, ScDPResultFilterContext
& rFilterCxt
,
3074 uno::Sequence
< uno::Sequence
<sheet::DataResult
> >& rSequence
, tools::Long nMeasure
) const
3076 FilterStack
aFilterStack(rFilterCxt
.maFilters
);
3077 aFilterStack
.pushDimName(GetName(), bIsDataLayout
);
3079 tools::Long nMemberMeasure
= nMeasure
;
3080 tools::Long nCount
= maMemberArray
.size();
3081 for (tools::Long i
=0; i
<nCount
; i
++)
3083 tools::Long nSorted
= aMemberOrder
.empty() ? i
: aMemberOrder
[i
];
3085 const ScDPResultMember
* pMember
;
3088 OSL_ENSURE(nMeasure
== SC_DPMEASURE_ALL
|| pResultData
->GetMeasureCount() == 1,
3089 "DataLayout dimension twice?");
3090 pMember
= maMemberArray
[0].get();
3091 nMemberMeasure
= nSorted
;
3094 pMember
= maMemberArray
[nSorted
].get();
3096 if ( pMember
->IsVisible() )
3097 pMember
->FillDataResults(pRefMember
, rFilterCxt
, rSequence
, nMemberMeasure
);
3101 void ScDPResultDimension::UpdateDataResults( const ScDPResultMember
* pRefMember
, tools::Long nMeasure
) const
3103 tools::Long nMemberMeasure
= nMeasure
;
3104 tools::Long nCount
= maMemberArray
.size();
3105 for (tools::Long i
=0; i
<nCount
; i
++)
3107 const ScDPResultMember
* pMember
;
3110 OSL_ENSURE(nMeasure
== SC_DPMEASURE_ALL
|| pResultData
->GetMeasureCount() == 1,
3111 "DataLayout dimension twice?");
3112 pMember
= maMemberArray
[0].get();
3116 pMember
= maMemberArray
[i
].get();
3118 if ( pMember
->IsVisible() )
3119 pMember
->UpdateDataResults( pRefMember
, nMemberMeasure
);
3123 void ScDPResultDimension::SortMembers( ScDPResultMember
* pRefMember
)
3125 tools::Long nCount
= maMemberArray
.size();
3131 OSL_ENSURE( aMemberOrder
.empty(), "sort twice?" );
3132 aMemberOrder
.resize( nCount
);
3133 for (tools::Long nPos
=0; nPos
<nCount
; nPos
++)
3134 aMemberOrder
[nPos
] = nPos
;
3136 ScDPRowMembersOrder
aComp( *this, nSortMeasure
, bSortAscending
);
3137 ::std::sort( aMemberOrder
.begin(), aMemberOrder
.end(), aComp
);
3142 // for data layout, call only once - sorting measure is always taken from settings
3143 tools::Long nLoopCount
= bIsDataLayout
? std::min
<tools::Long
>(1, nCount
) : nCount
;
3144 for (tools::Long i
=0; i
<nLoopCount
; i
++)
3146 ScDPResultMember
* pMember
= maMemberArray
[i
].get();
3147 if ( pMember
->IsVisible() )
3148 pMember
->SortMembers( pRefMember
);
3152 void ScDPResultDimension::DoAutoShow( ScDPResultMember
* pRefMember
)
3154 tools::Long nCount
= maMemberArray
.size();
3156 // handle children first, before changing the visible state
3158 // for data layout, call only once - sorting measure is always taken from settings
3159 tools::Long nLoopCount
= bIsDataLayout
? 1 : nCount
;
3160 for (tools::Long i
=0; i
<nLoopCount
; i
++)
3162 ScDPResultMember
* pMember
= maMemberArray
[i
].get();
3163 if ( pMember
->IsVisible() )
3164 pMember
->DoAutoShow( pRefMember
);
3167 if ( !(bAutoShow
&& nAutoCount
> 0 && nAutoCount
< nCount
) )
3170 // establish temporary order, hide remaining members
3172 ScMemberSortOrder aAutoOrder
;
3173 aAutoOrder
.resize( nCount
);
3175 for (nPos
=0; nPos
<nCount
; nPos
++)
3176 aAutoOrder
[nPos
] = nPos
;
3178 ScDPRowMembersOrder
aComp( *this, nAutoMeasure
, !bAutoTopItems
);
3179 ::std::sort( aAutoOrder
.begin(), aAutoOrder
.end(), aComp
);
3181 // look for equal values to the last included one
3183 tools::Long nIncluded
= nAutoCount
;
3184 const ScDPResultMember
* pMember1
= maMemberArray
[aAutoOrder
[nIncluded
- 1]].get();
3185 const ScDPDataMember
* pDataMember1
= pMember1
->IsVisible() ? pMember1
->GetDataRoot() : nullptr;
3186 bool bContinue
= true;
3190 if ( nIncluded
< nCount
)
3192 const ScDPResultMember
* pMember2
= maMemberArray
[aAutoOrder
[nIncluded
]].get();
3193 const ScDPDataMember
* pDataMember2
= pMember2
->IsVisible() ? pMember2
->GetDataRoot() : nullptr;
3195 if ( lcl_IsEqual( pDataMember1
, pDataMember2
, nAutoMeasure
) )
3197 ++nIncluded
; // include more members if values are equal
3203 // hide the remaining members
3205 for (nPos
= nIncluded
; nPos
< nCount
; nPos
++)
3207 ScDPResultMember
* pMember
= maMemberArray
[aAutoOrder
[nPos
]].get();
3208 pMember
->SetAutoHidden();
3212 void ScDPResultDimension::ResetResults()
3214 tools::Long nCount
= maMemberArray
.size();
3215 for (tools::Long i
=0; i
<nCount
; i
++)
3217 // sort order doesn't matter
3218 ScDPResultMember
* pMember
= maMemberArray
[bIsDataLayout
? 0 : i
].get();
3219 pMember
->ResetResults();
3223 tools::Long
ScDPResultDimension::GetSortedIndex( tools::Long nUnsorted
) const
3225 return aMemberOrder
.empty() ? nUnsorted
: aMemberOrder
[nUnsorted
];
3228 void ScDPResultDimension::UpdateRunningTotals( const ScDPResultMember
* pRefMember
, tools::Long nMeasure
,
3229 ScDPRunningTotalState
& rRunning
, ScDPRowTotals
& rTotals
) const
3231 const ScDPResultMember
* pMember
;
3232 tools::Long nMemberMeasure
= nMeasure
;
3233 tools::Long nCount
= maMemberArray
.size();
3234 for (tools::Long i
=0; i
<nCount
; i
++)
3236 tools::Long nSorted
= aMemberOrder
.empty() ? i
: aMemberOrder
[i
];
3240 OSL_ENSURE(nMeasure
== SC_DPMEASURE_ALL
|| pResultData
->GetMeasureCount() == 1,
3241 "DataLayout dimension twice?");
3242 pMember
= maMemberArray
[0].get();
3243 nMemberMeasure
= nSorted
;
3246 pMember
= maMemberArray
[nSorted
].get();
3248 if ( pMember
->IsVisible() )
3250 if ( bIsDataLayout
)
3251 rRunning
.AddRowIndex( 0, 0 );
3253 rRunning
.AddRowIndex( i
, nSorted
);
3254 pMember
->UpdateRunningTotals( pRefMember
, nMemberMeasure
, rRunning
, rTotals
);
3255 rRunning
.RemoveRowIndex();
3260 ScDPDataMember
* ScDPResultDimension::GetRowReferenceMember(
3261 const ScDPRelativePos
* pRelativePos
, const OUString
* pName
,
3262 const sal_Int32
* pRowIndexes
, const sal_Int32
* pColIndexes
) const
3264 // get named, previous/next, or first member of this dimension (first existing if pRelativePos and pName are NULL)
3266 OSL_ENSURE( pRelativePos
== nullptr || pName
== nullptr, "can't use position and name" );
3268 ScDPDataMember
* pColMember
= nullptr;
3270 bool bFirstExisting
= ( pRelativePos
== nullptr && pName
== nullptr );
3271 tools::Long nMemberCount
= maMemberArray
.size();
3272 tools::Long nMemberIndex
= 0; // unsorted
3273 tools::Long nDirection
= 1; // forward if no relative position is used
3276 nDirection
= pRelativePos
->nDirection
;
3277 nMemberIndex
= pRelativePos
->nBasePos
+ nDirection
; // bounds are handled below
3279 OSL_ENSURE( nDirection
== 1 || nDirection
== -1, "Direction must be 1 or -1" );
3283 // search for named member
3285 const ScDPResultMember
* pRowMember
= maMemberArray
[GetSortedIndex(nMemberIndex
)].get();
3287 //TODO: use ScDPItemData, as in ScDPDimension::IsValidPage?
3288 while ( pRowMember
&& pRowMember
->GetName() != *pName
)
3291 if ( nMemberIndex
< nMemberCount
)
3292 pRowMember
= maMemberArray
[GetSortedIndex(nMemberIndex
)].get();
3294 pRowMember
= nullptr;
3298 bool bContinue
= true;
3299 while ( bContinue
&& nMemberIndex
>= 0 && nMemberIndex
< nMemberCount
)
3301 const ScDPResultMember
* pRowMember
= maMemberArray
[GetSortedIndex(nMemberIndex
)].get();
3303 // get child members by given indexes
3305 const sal_Int32
* pNextRowIndex
= pRowIndexes
;
3306 while ( *pNextRowIndex
>= 0 && pRowMember
)
3308 const ScDPResultDimension
* pRowChild
= pRowMember
->GetChildDimension();
3309 if ( pRowChild
&& *pNextRowIndex
< pRowChild
->GetMemberCount() )
3310 pRowMember
= pRowChild
->GetMember( *pNextRowIndex
);
3312 pRowMember
= nullptr;
3316 if ( pRowMember
&& pRelativePos
)
3318 // Skip the member if it has hidden details
3319 // (because when looking for the details, it is skipped, too).
3320 // Also skip if the member is invisible because it has no data,
3321 // for consistent ordering.
3322 if ( pRowMember
->HasHiddenDetails() || !pRowMember
->IsVisible() )
3323 pRowMember
= nullptr;
3328 pColMember
= pRowMember
->GetDataRoot();
3330 const sal_Int32
* pNextColIndex
= pColIndexes
;
3331 while ( *pNextColIndex
>= 0 && pColMember
)
3333 ScDPDataDimension
* pColChild
= pColMember
->GetChildDimension();
3334 if ( pColChild
&& *pNextColIndex
< pColChild
->GetMemberCount() )
3335 pColMember
= pColChild
->GetMember( *pNextColIndex
);
3337 pColMember
= nullptr;
3342 // continue searching only if looking for first existing or relative position
3343 bContinue
= ( pColMember
== nullptr && ( bFirstExisting
|| pRelativePos
) );
3344 nMemberIndex
+= nDirection
;
3350 ScDPDataMember
* ScDPResultDimension::GetColReferenceMember(
3351 const ScDPRelativePos
* pRelativePos
, const OUString
* pName
,
3352 sal_Int32 nRefDimPos
, const ScDPRunningTotalState
& rRunning
)
3354 OSL_ENSURE( pRelativePos
== nullptr || pName
== nullptr, "can't use position and name" );
3356 const sal_Int32
* pColIndexes
= rRunning
.GetColSorted().data();
3357 const sal_Int32
* pRowIndexes
= rRunning
.GetRowSorted().data();
3359 // get own row member using all indexes
3361 const ScDPResultMember
* pRowMember
= rRunning
.GetRowResRoot();
3362 ScDPDataMember
* pColMember
= nullptr;
3364 const sal_Int32
* pNextRowIndex
= pRowIndexes
;
3365 while ( *pNextRowIndex
>= 0 && pRowMember
)
3367 const ScDPResultDimension
* pRowChild
= pRowMember
->GetChildDimension();
3368 if ( pRowChild
&& *pNextRowIndex
< pRowChild
->GetMemberCount() )
3369 pRowMember
= pRowChild
->GetMember( *pNextRowIndex
);
3371 pRowMember
= nullptr;
3375 // get column (data) members before the reference field
3376 //TODO: pass rRowParent from ScDPDataMember::UpdateRunningTotals instead
3380 pColMember
= pRowMember
->GetDataRoot();
3382 const sal_Int32
* pNextColIndex
= pColIndexes
;
3383 sal_Int32 nColSkipped
= 0;
3384 while ( *pNextColIndex
>= 0 && pColMember
&& nColSkipped
< nRefDimPos
)
3386 ScDPDataDimension
* pColChild
= pColMember
->GetChildDimension();
3387 if ( pColChild
&& *pNextColIndex
< pColChild
->GetMemberCount() )
3388 pColMember
= pColChild
->GetMember( *pNextColIndex
);
3390 pColMember
= nullptr;
3396 // get column member for the reference field
3400 ScDPDataDimension
* pReferenceDim
= pColMember
->GetChildDimension();
3401 if ( pReferenceDim
)
3403 tools::Long nReferenceCount
= pReferenceDim
->GetMemberCount();
3405 bool bFirstExisting
= ( pRelativePos
== nullptr && pName
== nullptr );
3406 tools::Long nMemberIndex
= 0; // unsorted
3407 tools::Long nDirection
= 1; // forward if no relative position is used
3408 pColMember
= nullptr; // don't use parent dimension's member if none found
3411 nDirection
= pRelativePos
->nDirection
;
3412 nMemberIndex
= pRelativePos
->nBasePos
+ nDirection
; // bounds are handled below
3416 // search for named member
3418 pColMember
= pReferenceDim
->GetMember( pReferenceDim
->GetSortedIndex( nMemberIndex
) );
3420 //TODO: use ScDPItemData, as in ScDPDimension::IsValidPage?
3421 while ( pColMember
&& pColMember
->GetName() != *pName
)
3424 if ( nMemberIndex
< nReferenceCount
)
3425 pColMember
= pReferenceDim
->GetMember( pReferenceDim
->GetSortedIndex( nMemberIndex
) );
3427 pColMember
= nullptr;
3431 bool bContinue
= true;
3432 while ( bContinue
&& nMemberIndex
>= 0 && nMemberIndex
< nReferenceCount
)
3434 pColMember
= pReferenceDim
->GetMember( pReferenceDim
->GetSortedIndex( nMemberIndex
) );
3436 // get column members below the reference field
3438 const sal_Int32
* pNextColIndex
= pColIndexes
+ nRefDimPos
+ 1;
3439 while ( *pNextColIndex
>= 0 && pColMember
)
3441 ScDPDataDimension
* pColChild
= pColMember
->GetChildDimension();
3442 if ( pColChild
&& *pNextColIndex
< pColChild
->GetMemberCount() )
3443 pColMember
= pColChild
->GetMember( *pNextColIndex
);
3445 pColMember
= nullptr;
3449 if ( pColMember
&& pRelativePos
)
3451 // Skip the member if it has hidden details
3452 // (because when looking for the details, it is skipped, too).
3453 // Also skip if the member is invisible because it has no data,
3454 // for consistent ordering.
3455 if ( pColMember
->HasHiddenDetails() || !pColMember
->IsVisible() )
3456 pColMember
= nullptr;
3459 // continue searching only if looking for first existing or relative position
3460 bContinue
= ( pColMember
== nullptr && ( bFirstExisting
|| pRelativePos
) );
3461 nMemberIndex
+= nDirection
;
3465 pColMember
= nullptr;
3471 #if DUMP_PIVOT_TABLE
3472 void ScDPResultDimension::DumpState( const ScDPResultMember
* pRefMember
, ScDocument
* pDoc
, ScAddress
& rPos
) const
3474 OUString aDimName
= bIsDataLayout
? u
"(data layout)"_ustr
: GetName();
3475 dumpRow(u
"ScDPResultDimension"_ustr
, aDimName
, nullptr, pDoc
, rPos
);
3477 SCROW nStartRow
= rPos
.Row();
3479 tools::Long nCount
= bIsDataLayout
? 1 : maMemberArray
.size();
3480 for (tools::Long i
=0; i
<nCount
; i
++)
3482 const ScDPResultMember
* pMember
= maMemberArray
[i
].get();
3483 pMember
->DumpState( pRefMember
, pDoc
, rPos
);
3486 indent(pDoc
, nStartRow
, rPos
);
3489 void ScDPResultDimension::Dump(int nIndent
) const
3491 std::string
aIndent(nIndent
*2, ' ');
3492 std::cout
<< aIndent
<< "-- dimension '" << GetName() << "'" << std::endl
;
3493 for (const auto& rxMember
: maMemberArray
)
3495 const ScDPResultMember
* p
= rxMember
.get();
3501 tools::Long
ScDPResultDimension::GetMemberCount() const
3503 return maMemberArray
.size();
3506 const ScDPResultMember
* ScDPResultDimension::GetMember(tools::Long n
) const
3508 return maMemberArray
[n
].get();
3510 ScDPResultMember
* ScDPResultDimension::GetMember(tools::Long n
)
3512 return maMemberArray
[n
].get();
3515 ScDPResultDimension
* ScDPResultDimension::GetFirstChildDimension() const
3517 if ( !maMemberArray
.empty() )
3518 return maMemberArray
[0]->GetChildDimension();
3523 void ScDPResultDimension::FillVisibilityData(ScDPResultVisibilityData
& rData
) const
3528 for (const auto& rxMember
: maMemberArray
)
3530 ScDPResultMember
* pMember
= rxMember
.get();
3531 if (pMember
->IsValid())
3533 ScDPItemData
aItem(pMember
->FillItemData());
3534 rData
.addVisibleMember(GetName(), aItem
);
3535 pMember
->FillVisibilityData(rData
);
3540 ScDPDataDimension::ScDPDataDimension( const ScDPResultData
* pData
) :
3541 pResultData( pData
),
3542 pResultDimension( nullptr ),
3543 bIsDataLayout( false )
3547 ScDPDataDimension::~ScDPDataDimension()
3551 void ScDPDataDimension::InitFrom( const ScDPResultDimension
* pDim
)
3556 pResultDimension
= pDim
;
3557 bIsDataLayout
= pDim
->IsDataLayout();
3559 // Go through all result members under the given result dimension, and
3560 // create a new data member instance for each result member.
3561 tools::Long nCount
= pDim
->GetMemberCount();
3562 for (tools::Long i
=0; i
<nCount
; i
++)
3564 const ScDPResultMember
* pResMem
= pDim
->GetMember(i
);
3566 ScDPDataMember
* pNew
= new ScDPDataMember( pResultData
, pResMem
);
3567 maMembers
.emplace_back( pNew
);
3569 if ( !pResultData
->IsLateInit() )
3571 // with LateInit, pResMem hasn't necessarily been initialized yet,
3572 // so InitFrom for the new result member is called from its ProcessData method
3574 const ScDPResultDimension
* pChildDim
= pResMem
->GetChildDimension();
3576 pNew
->InitFrom( pChildDim
);
3581 void ScDPDataDimension::ProcessData( const vector
< SCROW
>& aDataMembers
, const vector
<ScDPValue
>& aValues
,
3582 const ScDPSubTotalState
& rSubState
)
3584 // the ScDPItemData array must contain enough entries for all dimensions - this isn't checked
3586 tools::Long nCount
= maMembers
.size();
3587 for (tools::Long i
=0; i
<nCount
; i
++)
3589 ScDPDataMember
* pMember
= maMembers
[static_cast<sal_uInt16
>(i
)].get();
3591 // always first member for data layout dim
3592 if ( bIsDataLayout
|| ( !aDataMembers
.empty() && pMember
->IsNamedItem(aDataMembers
[0]) ) )
3594 vector
<SCROW
> aChildDataMembers
;
3595 if (aDataMembers
.size() > 1)
3597 vector
<SCROW
>::const_iterator itr
= aDataMembers
.begin();
3598 aChildDataMembers
.insert(aChildDataMembers
.begin(), ++itr
, aDataMembers
.end());
3600 pMember
->ProcessData( aChildDataMembers
, aValues
, rSubState
);
3605 OSL_FAIL("ProcessData: Member not found");
3608 void ScDPDataDimension::FillDataRow(
3609 const ScDPResultDimension
* pRefDim
, ScDPResultFilterContext
& rFilterCxt
,
3610 uno::Sequence
<sheet::DataResult
>& rSequence
, tools::Long nMeasure
, bool bIsSubTotalRow
,
3611 const ScDPSubTotalState
& rSubState
) const
3614 bool bDataLayout
= false;
3615 if (pResultDimension
)
3617 aDimName
= pResultDimension
->GetName();
3618 bDataLayout
= pResultDimension
->IsDataLayout();
3621 FilterStack
aFilterStack(rFilterCxt
.maFilters
);
3622 aFilterStack
.pushDimName(aDimName
, bDataLayout
);
3625 OSL_ENSURE( static_cast<size_t>(pRefDim
->GetMemberCount()) == maMembers
.size(), "dimensions don't match" );
3626 OSL_ENSURE( pRefDim
== pResultDimension
, "wrong dim" );
3628 const ScMemberSortOrder
& rMemberOrder
= pRefDim
->GetMemberOrder();
3630 tools::Long nMemberMeasure
= nMeasure
;
3631 tools::Long nCount
= maMembers
.size();
3632 for (tools::Long i
=0; i
<nCount
; i
++)
3634 tools::Long nSorted
= rMemberOrder
.empty() ? i
: rMemberOrder
[i
];
3636 tools::Long nMemberPos
= nSorted
;
3639 OSL_ENSURE(nMeasure
== SC_DPMEASURE_ALL
|| pResultData
->GetMeasureCount() == 1,
3640 "DataLayout dimension twice?");
3642 nMemberMeasure
= nSorted
;
3645 const ScDPResultMember
* pRefMember
= pRefDim
->GetMember(nMemberPos
);
3646 if ( pRefMember
->IsVisible() ) //TODO: here or in ScDPDataMember::FillDataRow ???
3648 const ScDPDataMember
* pDataMember
= maMembers
[static_cast<sal_uInt16
>(nMemberPos
)].get();
3649 pDataMember
->FillDataRow(pRefMember
, rFilterCxt
, rSequence
, nMemberMeasure
, bIsSubTotalRow
, rSubState
);
3654 void ScDPDataDimension::UpdateDataRow( const ScDPResultDimension
* pRefDim
,
3655 tools::Long nMeasure
, bool bIsSubTotalRow
,
3656 const ScDPSubTotalState
& rSubState
) const
3659 OSL_ENSURE( static_cast<size_t>(pRefDim
->GetMemberCount()) == maMembers
.size(), "dimensions don't match" );
3660 OSL_ENSURE( pRefDim
== pResultDimension
, "wrong dim" );
3662 tools::Long nMemberMeasure
= nMeasure
;
3663 tools::Long nCount
= maMembers
.size();
3664 for (tools::Long i
=0; i
<nCount
; i
++)
3666 tools::Long nMemberPos
= i
;
3669 OSL_ENSURE(nMeasure
== SC_DPMEASURE_ALL
|| pResultData
->GetMeasureCount() == 1,
3670 "DataLayout dimension twice?");
3675 // Calculate must be called even if the member is not visible (for use as reference value)
3676 const ScDPResultMember
* pRefMember
= pRefDim
->GetMember(nMemberPos
);
3677 ScDPDataMember
* pDataMember
= maMembers
[static_cast<sal_uInt16
>(nMemberPos
)].get();
3678 pDataMember
->UpdateDataRow( pRefMember
, nMemberMeasure
, bIsSubTotalRow
, rSubState
);
3682 void ScDPDataDimension::SortMembers( ScDPResultDimension
* pRefDim
)
3684 tools::Long nCount
= maMembers
.size();
3686 if ( pRefDim
->IsSortByData() )
3690 ScMemberSortOrder
& rMemberOrder
= pRefDim
->GetMemberOrder();
3691 OSL_ENSURE( rMemberOrder
.empty(), "sort twice?" );
3692 rMemberOrder
.resize( nCount
);
3693 for (tools::Long nPos
=0; nPos
<nCount
; nPos
++)
3694 rMemberOrder
[nPos
] = nPos
;
3696 ScDPColMembersOrder
aComp( *this, pRefDim
->GetSortMeasure(), pRefDim
->IsSortAscending() );
3697 ::std::sort( rMemberOrder
.begin(), rMemberOrder
.end(), aComp
);
3702 OSL_ENSURE( pRefDim
&& static_cast<size_t>(pRefDim
->GetMemberCount()) == maMembers
.size(), "dimensions don't match" );
3703 OSL_ENSURE( pRefDim
== pResultDimension
, "wrong dim" );
3705 // for data layout, call only once - sorting measure is always taken from settings
3706 tools::Long nLoopCount
= bIsDataLayout
? 1 : nCount
;
3707 for (tools::Long i
=0; i
<nLoopCount
; i
++)
3709 ScDPResultMember
* pRefMember
= pRefDim
->GetMember(i
);
3710 if ( pRefMember
->IsVisible() ) //TODO: here or in ScDPDataMember ???
3712 ScDPDataMember
* pDataMember
= maMembers
[static_cast<sal_uInt16
>(i
)].get();
3713 pDataMember
->SortMembers( pRefMember
);
3718 void ScDPDataDimension::DoAutoShow( ScDPResultDimension
* pRefDim
)
3720 tools::Long nCount
= maMembers
.size();
3722 // handle children first, before changing the visible state
3725 OSL_ENSURE( static_cast<size_t>(pRefDim
->GetMemberCount()) == maMembers
.size(), "dimensions don't match" );
3726 OSL_ENSURE( pRefDim
== pResultDimension
, "wrong dim" );
3728 // for data layout, call only once - sorting measure is always taken from settings
3729 tools::Long nLoopCount
= bIsDataLayout
? 1 : nCount
;
3730 for (tools::Long i
=0; i
<nLoopCount
; i
++)
3732 ScDPResultMember
* pRefMember
= pRefDim
->GetMember(i
);
3733 if ( pRefMember
->IsVisible() ) //TODO: here or in ScDPDataMember ???
3735 ScDPDataMember
* pDataMember
= maMembers
[i
].get();
3736 pDataMember
->DoAutoShow( pRefMember
);
3740 if ( !(pRefDim
->IsAutoShow() && pRefDim
->GetAutoCount() > 0 && pRefDim
->GetAutoCount() < nCount
) )
3743 // establish temporary order, hide remaining members
3745 ScMemberSortOrder aAutoOrder
;
3746 aAutoOrder
.resize( nCount
);
3748 for (nPos
=0; nPos
<nCount
; nPos
++)
3749 aAutoOrder
[nPos
] = nPos
;
3751 ScDPColMembersOrder
aComp( *this, pRefDim
->GetAutoMeasure(), !pRefDim
->IsAutoTopItems() );
3752 ::std::sort( aAutoOrder
.begin(), aAutoOrder
.end(), aComp
);
3754 // look for equal values to the last included one
3756 tools::Long nIncluded
= pRefDim
->GetAutoCount();
3757 ScDPDataMember
* pDataMember1
= maMembers
[aAutoOrder
[nIncluded
- 1]].get();
3758 if ( !pDataMember1
->IsVisible() )
3759 pDataMember1
= nullptr;
3760 bool bContinue
= true;
3764 if ( nIncluded
< nCount
)
3766 ScDPDataMember
* pDataMember2
= maMembers
[aAutoOrder
[nIncluded
]].get();
3767 if ( !pDataMember2
->IsVisible() )
3768 pDataMember2
= nullptr;
3770 if ( lcl_IsEqual( pDataMember1
, pDataMember2
, pRefDim
->GetAutoMeasure() ) )
3772 ++nIncluded
; // include more members if values are equal
3778 // hide the remaining members
3780 for (nPos
= nIncluded
; nPos
< nCount
; nPos
++)
3782 ScDPResultMember
* pMember
= pRefDim
->GetMember(aAutoOrder
[nPos
]);
3783 pMember
->SetAutoHidden();
3787 void ScDPDataDimension::ResetResults()
3789 tools::Long nCount
= maMembers
.size();
3790 for (tools::Long i
=0; i
<nCount
; i
++)
3792 // sort order doesn't matter
3794 tools::Long nMemberPos
= bIsDataLayout
? 0 : i
;
3795 ScDPDataMember
* pDataMember
= maMembers
[nMemberPos
].get();
3796 pDataMember
->ResetResults();
3800 tools::Long
ScDPDataDimension::GetSortedIndex( tools::Long nUnsorted
) const
3802 if (!pResultDimension
)
3805 const ScMemberSortOrder
& rMemberOrder
= pResultDimension
->GetMemberOrder();
3806 return rMemberOrder
.empty() ? nUnsorted
: rMemberOrder
[nUnsorted
];
3809 void ScDPDataDimension::UpdateRunningTotals( const ScDPResultDimension
* pRefDim
,
3810 tools::Long nMeasure
, bool bIsSubTotalRow
,
3811 const ScDPSubTotalState
& rSubState
, ScDPRunningTotalState
& rRunning
,
3812 ScDPRowTotals
& rTotals
, const ScDPResultMember
& rRowParent
) const
3815 OSL_ENSURE( static_cast<size_t>(pRefDim
->GetMemberCount()) == maMembers
.size(), "dimensions don't match" );
3816 OSL_ENSURE( pRefDim
== pResultDimension
, "wrong dim" );
3818 tools::Long nMemberMeasure
= nMeasure
;
3819 tools::Long nCount
= maMembers
.size();
3820 for (tools::Long i
=0; i
<nCount
; i
++)
3822 const ScMemberSortOrder
& rMemberOrder
= pRefDim
->GetMemberOrder();
3823 tools::Long nSorted
= rMemberOrder
.empty() ? i
: rMemberOrder
[i
];
3825 tools::Long nMemberPos
= nSorted
;
3828 OSL_ENSURE(nMeasure
== SC_DPMEASURE_ALL
|| pResultData
->GetMeasureCount() == 1,
3829 "DataLayout dimension twice?");
3831 nMemberMeasure
= nSorted
;
3834 const ScDPResultMember
* pRefMember
= pRefDim
->GetMember(nMemberPos
);
3835 if ( pRefMember
->IsVisible() )
3837 if ( bIsDataLayout
)
3838 rRunning
.AddColIndex( 0, 0 );
3840 rRunning
.AddColIndex( i
, nSorted
);
3842 ScDPDataMember
* pDataMember
= maMembers
[nMemberPos
].get();
3843 pDataMember
->UpdateRunningTotals(
3844 pRefMember
, nMemberMeasure
, bIsSubTotalRow
, rSubState
, rRunning
, rTotals
, rRowParent
);
3846 rRunning
.RemoveColIndex();
3851 #if DUMP_PIVOT_TABLE
3852 void ScDPDataDimension::DumpState( const ScDPResultDimension
* pRefDim
, ScDocument
* pDoc
, ScAddress
& rPos
) const
3854 OUString aDimName
= bIsDataLayout
? u
"(data layout)"_ustr
: u
"(unknown)"_ustr
;
3855 dumpRow(u
"ScDPDataDimension"_ustr
, aDimName
, nullptr, pDoc
, rPos
);
3857 SCROW nStartRow
= rPos
.Row();
3859 tools::Long nCount
= bIsDataLayout
? 1 : maMembers
.size();
3860 for (tools::Long i
=0; i
<nCount
; i
++)
3862 const ScDPResultMember
* pRefMember
= pRefDim
->GetMember(i
);
3863 const ScDPDataMember
* pDataMember
= maMembers
[i
].get();
3864 pDataMember
->DumpState( pRefMember
, pDoc
, rPos
);
3867 indent(pDoc
, nStartRow
, rPos
);
3870 void ScDPDataDimension::Dump(int nIndent
) const
3872 std::string
aIndent(nIndent
*2, ' ');
3873 std::cout
<< aIndent
<< "-- data dimension '"
3874 << (pResultDimension
? pResultDimension
->GetName() : OUString()) << "'" << std::endl
;
3875 for (auto& rxMember
: maMembers
)
3876 rxMember
->Dump(nIndent
+1);
3880 tools::Long
ScDPDataDimension::GetMemberCount() const
3882 return maMembers
.size();
3885 const ScDPDataMember
* ScDPDataDimension::GetMember(tools::Long n
) const
3887 return maMembers
[n
].get();
3890 ScDPDataMember
* ScDPDataDimension::GetMember(tools::Long n
)
3892 return maMembers
[n
].get();
3895 ScDPResultVisibilityData::ScDPResultVisibilityData(
3896 ScDPSource
* pSource
) :
3901 ScDPResultVisibilityData::~ScDPResultVisibilityData()
3905 void ScDPResultVisibilityData::addVisibleMember(const OUString
& rDimName
, const ScDPItemData
& rMemberItem
)
3907 DimMemberType::iterator itr
= maDimensions
.find(rDimName
);
3908 if (itr
== maDimensions
.end())
3910 pair
<DimMemberType::iterator
, bool> r
= maDimensions
.emplace(
3911 rDimName
, VisibleMemberType());
3914 // insertion failed.
3919 VisibleMemberType
& rMem
= itr
->second
;
3920 rMem
.insert(rMemberItem
);
3923 void ScDPResultVisibilityData::fillFieldFilters(vector
<ScDPFilteredCache::Criterion
>& rFilters
) const
3925 typedef std::unordered_map
<OUString
, tools::Long
> FieldNameMapType
;
3926 FieldNameMapType aFieldNames
;
3927 ScDPTableData
* pData
= mpSource
->GetData();
3928 sal_Int32 nColumnCount
= pData
->GetColumnCount();
3929 for (sal_Int32 i
= 0; i
< nColumnCount
; ++i
)
3931 aFieldNames
.emplace(pData
->getDimensionName(i
), i
);
3934 const ScDPDimensions
* pDims
= mpSource
->GetDimensionsObject();
3935 for (const auto& [rDimName
, rMem
] : maDimensions
)
3937 ScDPFilteredCache::Criterion aCri
;
3938 FieldNameMapType::const_iterator itrField
= aFieldNames
.find(rDimName
);
3939 if (itrField
== aFieldNames
.end())
3940 // This should never happen!
3943 tools::Long nDimIndex
= itrField
->second
;
3944 aCri
.mnFieldIndex
= static_cast<sal_Int32
>(nDimIndex
);
3945 aCri
.mpFilter
= std::make_shared
<ScDPFilteredCache::GroupFilter
>();
3947 ScDPFilteredCache::GroupFilter
* pGrpFilter
=
3948 static_cast<ScDPFilteredCache::GroupFilter
*>(aCri
.mpFilter
.get());
3950 for (const ScDPItemData
& rMemItem
: rMem
)
3952 pGrpFilter
->addMatchItem(rMemItem
);
3955 ScDPDimension
* pDim
= pDims
->getByIndex(nDimIndex
);
3956 ScDPMembers
* pMembers
= pDim
->GetHierarchiesObject()->getByIndex(0)->
3957 GetLevelsObject()->getByIndex(0)->GetMembersObject();
3958 if (pGrpFilter
->getMatchItemCount() < o3tl::make_unsigned(pMembers
->getCount()))
3959 rFilters
.push_back(aCri
);
3963 size_t ScDPResultVisibilityData::MemberHash::operator() (const ScDPItemData
& r
) const
3966 return static_cast<size_t>(::rtl::math::approxFloor(r
.GetValue()));
3968 return r
.GetString().hashCode();
3970 SCROW
ScDPResultMember::GetDataId( ) const
3972 const ScDPMember
* pMemberDesc
= GetDPMember();
3974 return pMemberDesc
->GetItemDataId();
3978 ScDPResultMember
* ScDPResultDimension::AddMember(const ScDPParentDimData
&aData
)
3980 ScDPResultMember
* pMember
= new ScDPResultMember( pResultData
, aData
);
3981 SCROW nDataIndex
= pMember
->GetDataId();
3982 maMemberArray
.emplace_back( pMember
);
3984 maMemberHash
.emplace( nDataIndex
, pMember
);
3988 ScDPResultMember
* ScDPResultDimension::InsertMember(const ScDPParentDimData
*pMemberData
)
3991 if ( !lcl_SearchMember( maMemberArray
, pMemberData
->mnOrder
, nInsert
) )
3993 ScDPResultMember
* pNew
= new ScDPResultMember( pResultData
, *pMemberData
);
3994 maMemberArray
.emplace( maMemberArray
.begin()+nInsert
, pNew
);
3996 SCROW nDataIndex
= pMemberData
->mpMemberDesc
->GetItemDataId();
3997 maMemberHash
.emplace( nDataIndex
, pNew
);
4000 return maMemberArray
[ nInsert
].get();
4003 void ScDPResultDimension::InitWithMembers(
4004 LateInitParams
& rParams
, const std::vector
<SCROW
>& pItemData
, size_t nPos
,
4005 ScDPInitState
& rInitState
)
4007 if ( rParams
.IsEnd( nPos
) )
4009 ScDPDimension
* pThisDim
= rParams
.GetDim( nPos
);
4010 ScDPLevel
* pThisLevel
= rParams
.GetLevel( nPos
);
4011 SCROW nDataID
= pItemData
[nPos
];
4013 if (!(pThisDim
&& pThisLevel
))
4016 tools::Long nDimSource
= pThisDim
->GetDimension(); //TODO: check GetSourceDim?
4018 // create all members at the first call (preserve order)
4019 ResultMembers
& rMembers
= pResultData
->GetDimResultMembers(nDimSource
, pThisDim
, pThisLevel
);
4020 ScDPGroupCompare
aCompare( pResultData
, rInitState
, nDimSource
);
4021 // initialize only specific member (or all if "show empty" flag is set)
4022 ScDPResultMember
* pResultMember
= nullptr;
4024 pResultMember
= FindMember( nDataID
);
4026 bInitialized
= true;
4028 if ( pResultMember
== nullptr )
4029 { //only insert found item
4030 const ScDPParentDimData
* pMemberData
= rMembers
.FindMember( nDataID
);
4031 if ( pMemberData
&& aCompare
.IsIncluded( *( pMemberData
->mpMemberDesc
) ) )
4032 pResultMember
= InsertMember( pMemberData
);
4034 if ( pResultMember
)
4036 rInitState
.AddMember( nDimSource
, pResultMember
->GetDataId() );
4037 pResultMember
->LateInitFrom(rParams
, pItemData
, nPos
+1, rInitState
);
4038 rInitState
.RemoveMember();
4042 ScDPParentDimData::ScDPParentDimData() :
4043 mnOrder(-1), mpParentDim(nullptr), mpParentLevel(nullptr), mpMemberDesc(nullptr) {}
4045 ScDPParentDimData::ScDPParentDimData(
4046 SCROW nIndex
, const ScDPDimension
* pDim
, const ScDPLevel
* pLev
, const ScDPMember
* pMember
) :
4047 mnOrder(nIndex
), mpParentDim(pDim
), mpParentLevel(pLev
), mpMemberDesc(pMember
) {}
4049 const ScDPParentDimData
* ResultMembers::FindMember( SCROW nIndex
) const
4051 auto aRes
= maMemberHash
.find( nIndex
);
4052 if( aRes
!= maMemberHash
.end()) {
4053 if ( aRes
->second
.mpMemberDesc
&& aRes
->second
.mpMemberDesc
->GetItemDataId()==nIndex
)
4054 return &aRes
->second
;
4058 void ResultMembers::InsertMember( const ScDPParentDimData
& rNew
)
4060 if ( !rNew
.mpMemberDesc
->getShowDetails() )
4061 mbHasHideDetailsMember
= true;
4062 maMemberHash
.emplace( rNew
.mpMemberDesc
->GetItemDataId(), rNew
);
4065 ResultMembers::ResultMembers():
4066 mbHasHideDetailsMember( false )
4069 ResultMembers::~ResultMembers()
4073 LateInitParams::LateInitParams(
4074 const vector
<ScDPDimension
*>& ppDim
, const vector
<ScDPLevel
*>& ppLev
, bool bRow
) :
4078 mbInitChild( true ),
4079 mbAllChildren( false )
4083 bool LateInitParams::IsEnd( size_t nPos
) const
4085 return nPos
>= mppDim
.size();
4088 void ScDPResultDimension::CheckShowEmpty( bool bShow
)
4090 tools::Long nCount
= maMemberArray
.size();
4092 for (tools::Long i
=0; i
<nCount
; i
++)
4094 ScDPResultMember
* pMember
= maMemberArray
.at(i
).get();
4095 pMember
->CheckShowEmpty(bShow
);
4100 void ScDPResultMember::CheckShowEmpty( bool bShow
)
4104 ScDPResultDimension
* pChildDim
= GetChildDimension();
4106 pChildDim
->CheckShowEmpty();
4108 else if (IsValid() && bInitialized
)
4110 bShow
= bShow
|| (GetParentLevel() && GetParentLevel()->getShowEmpty());
4114 ScDPResultDimension
* pChildDim
= GetChildDimension();
4116 pChildDim
->CheckShowEmpty(true);
4121 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */