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 <dpgroup.hxx>
22 #include <dpcache.hxx>
23 #include <document.hxx>
24 #include <dpfilteredcache.hxx>
27 #include <osl/diagnose.h>
28 #include <rtl/math.hxx>
29 #include <svl/numformat.hxx>
31 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
33 #include <unordered_map>
34 #include <unordered_set>
39 using namespace ::com::sun::star
;
40 using ::com::sun::star::uno::Any
;
41 using ::com::sun::star::uno::Sequence
;
44 using ::std::shared_ptr
;
46 const sal_uInt16 SC_DP_LEAPYEAR
= 1648; // arbitrary leap year for date calculations
50 class ScDPGroupNumFilter
: public ScDPFilteredCache::FilterBase
53 ScDPGroupNumFilter(std::vector
<ScDPItemData
>&& rValues
, const ScDPNumGroupInfo
& rInfo
);
55 virtual bool match(const ScDPItemData
&rCellData
) const override
;
56 virtual std::vector
<ScDPItemData
> getMatchValues() const override
;
58 std::vector
<ScDPItemData
> maValues
;
59 ScDPNumGroupInfo maNumInfo
;
64 ScDPGroupNumFilter::ScDPGroupNumFilter( std::vector
<ScDPItemData
>&& rValues
, const ScDPNumGroupInfo
& rInfo
) :
65 maValues(std::move(rValues
)), maNumInfo(rInfo
) {}
67 bool ScDPGroupNumFilter::match(const ScDPItemData
& rCellData
) const
69 if (rCellData
.GetType() != ScDPItemData::Value
)
72 for (const auto& rValue
: maValues
)
74 double fVal
= rValue
.GetValue();
77 if (std::signbit(fVal
))
79 // Less than the min value.
80 if (rCellData
.GetValue() < maNumInfo
.mfStart
)
84 // Greater than the max value.
85 if (maNumInfo
.mfEnd
< rCellData
.GetValue())
92 double high
= low
+ maNumInfo
.mfStep
;
93 if (maNumInfo
.mbIntegerOnly
)
96 if (low
<= rCellData
.GetValue() && rCellData
.GetValue() < high
)
103 std::vector
<ScDPItemData
> ScDPGroupNumFilter::getMatchValues() const
105 return std::vector
<ScDPItemData
>();
110 class ScDPGroupDateFilter
: public ScDPFilteredCache::FilterBase
114 std::vector
<ScDPItemData
>&& rValues
, const Date
& rNullDate
, const ScDPNumGroupInfo
& rNumInfo
);
116 virtual bool match(const ScDPItemData
& rCellData
) const override
;
117 virtual std::vector
<ScDPItemData
> getMatchValues() const override
;
120 std::vector
<ScDPItemData
> maValues
;
122 ScDPNumGroupInfo maNumInfo
;
127 ScDPGroupDateFilter::ScDPGroupDateFilter(
128 std::vector
<ScDPItemData
>&& rValues
, const Date
& rNullDate
, const ScDPNumGroupInfo
& rNumInfo
) :
129 maValues(std::move(rValues
)),
130 maNullDate(rNullDate
),
135 bool ScDPGroupDateFilter::match( const ScDPItemData
& rCellData
) const
137 using namespace ::com::sun::star::sheet
;
138 using ::rtl::math::approxFloor
;
139 using ::rtl::math::approxEqual
;
141 if ( !rCellData
.IsValue() )
144 for (const ScDPItemData
& rValue
: maValues
)
146 if (rValue
.GetType() != ScDPItemData::GroupValue
)
149 sal_Int32 nGroupType
= rValue
.GetGroupValue().mnGroupType
;
150 sal_Int32 nValue
= rValue
.GetGroupValue().mnValue
;
152 // Start and end dates are inclusive. (An end date without a time value
153 // is included, while an end date with a time value is not.)
155 if (rCellData
.GetValue() < maNumInfo
.mfStart
&& !approxEqual(rCellData
.GetValue(), maNumInfo
.mfStart
))
157 if (nValue
== ScDPItemData::DateFirst
)
162 if (rCellData
.GetValue() > maNumInfo
.mfEnd
&& !approxEqual(rCellData
.GetValue(), maNumInfo
.mfEnd
))
164 if (nValue
== ScDPItemData::DateLast
)
169 if (nGroupType
== DataPilotFieldGroupBy::HOURS
|| nGroupType
== DataPilotFieldGroupBy::MINUTES
||
170 nGroupType
== DataPilotFieldGroupBy::SECONDS
)
173 // (do as in the cell functions, ScInterpreter::ScGetHour() etc.)
175 sal_uInt16 nHour
, nMinute
, nSecond
;
176 double fFractionOfSecond
;
177 tools::Time::GetClock( rCellData
.GetValue(), nHour
, nMinute
, nSecond
, fFractionOfSecond
, 0);
181 case DataPilotFieldGroupBy::HOURS
:
187 case DataPilotFieldGroupBy::MINUTES
:
189 if (nMinute
== nValue
)
193 case DataPilotFieldGroupBy::SECONDS
:
195 if (nSecond
== nValue
)
200 OSL_FAIL("invalid time part");
206 Date date
= maNullDate
+ static_cast<sal_Int32
>(approxFloor(rCellData
.GetValue()));
209 case DataPilotFieldGroupBy::YEARS
:
211 sal_Int32 year
= static_cast<sal_Int32
>(date
.GetYear());
216 case DataPilotFieldGroupBy::QUARTERS
:
218 sal_Int32 qtr
= 1 + (static_cast<sal_Int32
>(date
.GetMonth()) - 1) / 3;
223 case DataPilotFieldGroupBy::MONTHS
:
225 sal_Int32 month
= static_cast<sal_Int32
>(date
.GetMonth());
230 case DataPilotFieldGroupBy::DAYS
:
232 Date
yearStart(1, 1, date
.GetYear());
233 sal_Int32 days
= (date
- yearStart
) + 1; // Jan 01 has value 1
234 if (days
>= 60 && !date
.IsLeapYear())
236 // This is not a leap year. Adjust the value accordingly.
244 OSL_FAIL("invalid date part");
251 std::vector
<ScDPItemData
> ScDPGroupDateFilter::getMatchValues() const
253 return std::vector
<ScDPItemData
>();
258 bool isDateInGroup(const ScDPItemData
& rGroupItem
, const ScDPItemData
& rChildItem
)
260 if (rGroupItem
.GetType() != ScDPItemData::GroupValue
|| rChildItem
.GetType() != ScDPItemData::GroupValue
)
263 sal_Int32 nGroupPart
= rGroupItem
.GetGroupValue().mnGroupType
;
264 sal_Int32 nGroupValue
= rGroupItem
.GetGroupValue().mnValue
;
265 sal_Int32 nChildPart
= rChildItem
.GetGroupValue().mnGroupType
;
266 sal_Int32 nChildValue
= rChildItem
.GetGroupValue().mnValue
;
268 if (nGroupValue
== ScDPItemData::DateFirst
|| nGroupValue
== ScDPItemData::DateLast
||
269 nChildValue
== ScDPItemData::DateFirst
|| nChildValue
== ScDPItemData::DateLast
)
271 // first/last entry matches only itself
272 return nGroupValue
== nChildValue
;
275 switch (nChildPart
) // inner part
277 case css::sheet::DataPilotFieldGroupBy::MONTHS
:
278 // a month is only contained in its quarter
279 if (nGroupPart
== css::sheet::DataPilotFieldGroupBy::QUARTERS
)
280 // months and quarters are both 1-based
281 return (nGroupValue
- 1 == (nChildValue
- 1) / 3);
283 case css::sheet::DataPilotFieldGroupBy::DAYS
:
284 // a day is only contained in its quarter or month
285 if (nGroupPart
== css::sheet::DataPilotFieldGroupBy::MONTHS
||
286 nGroupPart
== css::sheet::DataPilotFieldGroupBy::QUARTERS
)
288 Date
aDate(1, 1, SC_DP_LEAPYEAR
);
289 aDate
.AddDays(nChildValue
- 1); // days are 1-based
290 sal_Int32 nCompare
= aDate
.GetMonth();
291 if (nGroupPart
== css::sheet::DataPilotFieldGroupBy::QUARTERS
)
292 nCompare
= ( ( nCompare
- 1 ) / 3 ) + 1; // get quarter from date
294 return nGroupValue
== nCompare
;
306 ScDPGroupItem::ScDPGroupItem( const ScDPItemData
& rName
) :
311 void ScDPGroupItem::AddElement( const ScDPItemData
& rName
)
313 aElements
.push_back( rName
);
316 bool ScDPGroupItem::HasElement( const ScDPItemData
& rData
) const
318 return std::any_of(aElements
.begin(), aElements
.end(),
319 [&rData
](const ScDPItemData
& rElement
) { return rElement
.IsCaseInsEqual(rData
); });
322 bool ScDPGroupItem::HasCommonElement( const ScDPGroupItem
& rOther
) const
324 return std::any_of(aElements
.begin(), aElements
.end(),
325 [&rOther
](const ScDPItemData
& rElement
) { return rOther
.HasElement(rElement
); });
328 void ScDPGroupItem::FillGroupFilter( ScDPFilteredCache::GroupFilter
& rFilter
) const
330 for (const auto& rElement
: aElements
)
331 rFilter
.addMatchItem(rElement
);
334 ScDPGroupDimension::ScDPGroupDimension( tools::Long nSource
, OUString aNewName
) :
335 nSourceDim( nSource
),
337 aGroupName(std::move( aNewName
)),
338 mbDateDimension(false)
342 ScDPGroupDimension::~ScDPGroupDimension()
344 maMemberEntries
.clear();
347 ScDPGroupDimension::ScDPGroupDimension( const ScDPGroupDimension
& rOther
) :
348 nSourceDim( rOther
.nSourceDim
),
349 nGroupDim( rOther
.nGroupDim
),
350 aGroupName( rOther
.aGroupName
),
351 aItems( rOther
.aItems
),
352 mbDateDimension(rOther
.mbDateDimension
)
356 ScDPGroupDimension
& ScDPGroupDimension::operator=( const ScDPGroupDimension
& rOther
)
358 nSourceDim
= rOther
.nSourceDim
;
359 nGroupDim
= rOther
.nGroupDim
;
360 aGroupName
= rOther
.aGroupName
;
361 aItems
= rOther
.aItems
;
362 mbDateDimension
= rOther
.mbDateDimension
;
366 void ScDPGroupDimension::AddItem( const ScDPGroupItem
& rItem
)
368 aItems
.push_back( rItem
);
371 void ScDPGroupDimension::SetGroupDim( tools::Long nDim
)
376 const std::vector
<SCROW
>& ScDPGroupDimension::GetColumnEntries(
377 const ScDPFilteredCache
& rCacheTable
) const
379 if (!maMemberEntries
.empty())
380 return maMemberEntries
;
382 rCacheTable
.getCache().GetGroupDimMemberIds(nGroupDim
, maMemberEntries
);
383 return maMemberEntries
;
386 const ScDPGroupItem
* ScDPGroupDimension::GetGroupForData( const ScDPItemData
& rData
) const
388 auto aIter
= std::find_if(aItems
.begin(), aItems
.end(),
389 [&rData
](const ScDPGroupItem
& rItem
) { return rItem
.HasElement(rData
); });
390 if (aIter
!= aItems
.end())
396 const ScDPGroupItem
* ScDPGroupDimension::GetGroupForName( const ScDPItemData
& rName
) const
398 auto aIter
= std::find_if(aItems
.begin(), aItems
.end(),
399 [&rName
](const ScDPGroupItem
& rItem
) { return rItem
.GetName().IsCaseInsEqual(rName
); });
400 if (aIter
!= aItems
.end())
406 const ScDPGroupItem
* ScDPGroupDimension::GetGroupByIndex( size_t nIndex
) const
408 if (nIndex
>= aItems
.size())
411 return &aItems
[nIndex
];
414 void ScDPGroupDimension::DisposeData()
416 maMemberEntries
.clear();
419 void ScDPGroupDimension::SetDateDimension()
421 mbDateDimension
= true;
424 ScDPNumGroupDimension::ScDPNumGroupDimension() : mbDateDimension(false) {}
426 ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupInfo
& rInfo
) :
427 aGroupInfo(rInfo
), mbDateDimension(false) {}
429 ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupDimension
& rOther
) :
430 aGroupInfo(rOther
.aGroupInfo
), mbDateDimension(rOther
.mbDateDimension
) {}
432 ScDPNumGroupDimension
& ScDPNumGroupDimension::operator=( const ScDPNumGroupDimension
& rOther
)
434 aGroupInfo
= rOther
.aGroupInfo
;
435 mbDateDimension
= rOther
.mbDateDimension
;
439 void ScDPNumGroupDimension::DisposeData()
441 aGroupInfo
= ScDPNumGroupInfo();
442 maMemberEntries
.clear();
445 ScDPNumGroupDimension::~ScDPNumGroupDimension()
449 void ScDPNumGroupDimension::SetDateDimension()
451 aGroupInfo
.mbEnable
= true; //TODO: or query both?
452 mbDateDimension
= true;
455 const std::vector
<SCROW
>& ScDPNumGroupDimension::GetNumEntries(
456 SCCOL nSourceDim
, const ScDPCache
* pCache
) const
458 if (!maMemberEntries
.empty())
459 return maMemberEntries
;
461 pCache
->GetGroupDimMemberIds(nSourceDim
, maMemberEntries
);
462 return maMemberEntries
;
465 ScDPGroupTableData::ScDPGroupTableData( const shared_ptr
<ScDPTableData
>& pSource
, ScDocument
* pDocument
) :
466 ScDPTableData(pDocument
),
467 pSourceData( pSource
),
470 OSL_ENSURE( pSource
, "ScDPGroupTableData: pSource can't be NULL" );
473 nSourceCount
= pSource
->GetColumnCount(); // real columns, excluding data layout
474 pNumGroups
.reset( new ScDPNumGroupDimension
[nSourceCount
] );
477 ScDPGroupTableData::~ScDPGroupTableData()
481 void ScDPGroupTableData::AddGroupDimension( const ScDPGroupDimension
& rGroup
)
483 ScDPGroupDimension
aNewGroup( rGroup
);
484 aNewGroup
.SetGroupDim( GetColumnCount() ); // new dimension will be at the end
485 aGroups
.push_back( aNewGroup
);
488 void ScDPGroupTableData::SetNumGroupDimension( sal_Int32 nIndex
, const ScDPNumGroupDimension
& rGroup
)
490 if ( nIndex
< nSourceCount
)
492 pNumGroups
[nIndex
] = rGroup
;
494 // automatic minimum / maximum is handled in GetNumEntries
498 sal_Int32
ScDPGroupTableData::GetDimensionIndex( std::u16string_view rName
)
500 for (tools::Long i
= 0; i
< nSourceCount
; ++i
) // nSourceCount excludes data layout
501 if (pSourceData
->getDimensionName(i
) == rName
) //TODO: ignore case?
506 sal_Int32
ScDPGroupTableData::GetColumnCount()
508 return nSourceCount
+ aGroups
.size();
511 bool ScDPGroupTableData::IsNumGroupDimension( tools::Long nDimension
) const
513 return ( nDimension
< nSourceCount
&& pNumGroups
[nDimension
].GetInfo().mbEnable
);
516 void ScDPGroupTableData::GetNumGroupInfo(tools::Long nDimension
, ScDPNumGroupInfo
& rInfo
)
518 if ( nDimension
< nSourceCount
)
519 rInfo
= pNumGroups
[nDimension
].GetInfo();
521 sal_Int32
ScDPGroupTableData::GetMembersCount( sal_Int32 nDim
)
523 const std::vector
< SCROW
>& members
= GetColumnEntries( nDim
);
524 return members
.size();
526 const std::vector
< SCROW
>& ScDPGroupTableData::GetColumnEntries( sal_Int32 nColumn
)
528 if ( nColumn
>= nSourceCount
)
530 if ( getIsDataLayoutDimension( nColumn
) ) // data layout dimension?
531 nColumn
= nSourceCount
; // index of data layout in source data
534 const ScDPGroupDimension
& rGroupDim
= aGroups
[nColumn
- nSourceCount
];
535 return rGroupDim
.GetColumnEntries( GetCacheTable() );
539 if ( IsNumGroupDimension( nColumn
) )
541 // dimension number is unchanged for numerical groups
542 return pNumGroups
[nColumn
].GetNumEntries(
543 static_cast<SCCOL
>(nColumn
), &GetCacheTable().getCache());
546 return pSourceData
->GetColumnEntries( nColumn
);
549 const ScDPItemData
* ScDPGroupTableData::GetMemberById( sal_Int32 nDim
, sal_Int32 nId
)
551 return pSourceData
->GetMemberById( nDim
, nId
);
554 OUString
ScDPGroupTableData::getDimensionName(sal_Int32 nColumn
)
556 if ( nColumn
>= nSourceCount
)
558 if ( nColumn
== sal::static_int_cast
<tools::Long
>( nSourceCount
+ aGroups
.size() ) ) // data layout dimension?
559 nColumn
= nSourceCount
; // index of data layout in source data
561 return aGroups
[nColumn
- nSourceCount
].GetName();
564 return pSourceData
->getDimensionName( nColumn
);
567 bool ScDPGroupTableData::getIsDataLayoutDimension(sal_Int32 nColumn
)
569 // position of data layout dimension is moved from source data
570 return ( nColumn
== sal::static_int_cast
<tools::Long
>( nSourceCount
+ aGroups
.size() ) ); // data layout dimension?
573 bool ScDPGroupTableData::IsDateDimension(sal_Int32 nDim
)
575 if ( nDim
>= nSourceCount
)
577 if ( nDim
== sal::static_int_cast
<tools::Long
>( nSourceCount
+ aGroups
.size() ) ) // data layout dimension?
578 nDim
= nSourceCount
; // index of data layout in source data
580 nDim
= aGroups
[nDim
- nSourceCount
].GetSourceDim(); // look at original dimension
583 return pSourceData
->IsDateDimension( nDim
);
586 sal_uInt32
ScDPGroupTableData::GetNumberFormat(sal_Int32 nDim
)
588 if ( nDim
>= nSourceCount
)
590 if ( nDim
== sal::static_int_cast
<tools::Long
>( nSourceCount
+ aGroups
.size() ) ) // data layout dimension?
591 nDim
= nSourceCount
; // index of data layout in source data
593 nDim
= aGroups
[nDim
- nSourceCount
].GetSourceDim(); // look at original dimension
596 return pSourceData
->GetNumberFormat( nDim
);
599 void ScDPGroupTableData::DisposeData()
601 for ( auto& rGroup
: aGroups
)
602 rGroup
.DisposeData();
604 for ( tools::Long i
=0; i
<nSourceCount
; i
++ )
605 pNumGroups
[i
].DisposeData();
607 pSourceData
->DisposeData();
610 void ScDPGroupTableData::SetEmptyFlags( bool bIgnoreEmptyRows
, bool bRepeatIfEmpty
)
612 pSourceData
->SetEmptyFlags( bIgnoreEmptyRows
, bRepeatIfEmpty
);
615 bool ScDPGroupTableData::IsRepeatIfEmpty()
617 return pSourceData
->IsRepeatIfEmpty();
620 void ScDPGroupTableData::CreateCacheTable()
622 pSourceData
->CreateCacheTable();
627 class FindCaseInsensitive
629 ScDPItemData maValue
;
631 explicit FindCaseInsensitive(const ScDPItemData
& rVal
) : maValue(rVal
) {}
633 bool operator() (const ScDPItemData
& rItem
) const
635 return maValue
.IsCaseInsEqual(rItem
);
641 void ScDPGroupTableData::ModifyFilterCriteria(vector
<ScDPFilteredCache::Criterion
>& rCriteria
)
643 // Build dimension ID to object map for group dimensions.
644 typedef std::unordered_map
<tools::Long
, const ScDPGroupDimension
*> GroupFieldMapType
;
645 GroupFieldMapType aGroupFieldIds
;
647 for (const auto& rGroup
: aGroups
)
649 aGroupFieldIds
.emplace(rGroup
.GetGroupDim(), &rGroup
);
652 vector
<ScDPFilteredCache::Criterion
> aNewCriteria
;
653 aNewCriteria
.reserve(rCriteria
.size() + aGroups
.size());
655 // Go through all the filtered field names and process them appropriately.
657 const ScDPCache
& rCache
= GetCacheTable().getCache();
658 GroupFieldMapType::const_iterator itrGrpEnd
= aGroupFieldIds
.end();
659 for (const auto& rCriterion
: rCriteria
)
661 std::vector
<ScDPItemData
> aMatchValues
= rCriterion
.mpFilter
->getMatchValues();
663 GroupFieldMapType::const_iterator itrGrp
= aGroupFieldIds
.find(rCriterion
.mnFieldIndex
);
664 if (itrGrp
== itrGrpEnd
)
666 if (IsNumGroupDimension(rCriterion
.mnFieldIndex
))
668 // internal number group field
669 const ScDPNumGroupInfo
* pNumInfo
= rCache
.GetNumGroupInfo(rCriterion
.mnFieldIndex
);
671 // Number group dimension without num info? Something is wrong...
674 ScDPFilteredCache::Criterion aCri
;
675 aCri
.mnFieldIndex
= rCriterion
.mnFieldIndex
;
676 const ScDPNumGroupDimension
& rNumGrpDim
= pNumGroups
[rCriterion
.mnFieldIndex
];
678 if (rNumGrpDim
.IsDateDimension())
682 std::make_shared
<ScDPGroupDateFilter
>(
683 std::move(aMatchValues
), pDoc
->GetFormatTable()->GetNullDate(), *pNumInfo
);
687 // This dimension is grouped by numeric ranges.
689 std::make_shared
<ScDPGroupNumFilter
>(std::move(aMatchValues
), *pNumInfo
);
692 aNewCriteria
.push_back(aCri
);
696 // This is a regular source field.
697 aNewCriteria
.push_back(rCriterion
);
702 // This is an ordinary group field or external number group field.
704 const ScDPGroupDimension
* pGrpDim
= itrGrp
->second
;
705 tools::Long nSrcDim
= pGrpDim
->GetSourceDim();
706 tools::Long nGrpDim
= pGrpDim
->GetGroupDim();
707 const ScDPNumGroupInfo
* pNumInfo
= rCache
.GetNumGroupInfo(nGrpDim
);
709 if (pGrpDim
->IsDateDimension() && pNumInfo
)
711 // external number group
712 ScDPFilteredCache::Criterion aCri
;
713 aCri
.mnFieldIndex
= nSrcDim
; // use the source dimension, not the group dimension.
715 std::make_shared
<ScDPGroupDateFilter
>(
716 std::move(aMatchValues
), pDoc
->GetFormatTable()->GetNullDate(), *pNumInfo
);
718 aNewCriteria
.push_back(aCri
);
724 ScDPFilteredCache::Criterion aCri
;
725 aCri
.mnFieldIndex
= nSrcDim
;
726 aCri
.mpFilter
= std::make_shared
<ScDPFilteredCache::GroupFilter
>();
727 ScDPFilteredCache::GroupFilter
* pGrpFilter
=
728 static_cast<ScDPFilteredCache::GroupFilter
*>(aCri
.mpFilter
.get());
730 size_t nGroupItemCount
= pGrpDim
->GetItemCount();
731 for (size_t i
= 0; i
< nGroupItemCount
; ++i
)
733 const ScDPGroupItem
* pGrpItem
= pGrpDim
->GetGroupByIndex(i
);
737 // Make sure this group name equals one of the match values.
738 if (std::none_of(aMatchValues
.begin(), aMatchValues
.end(), FindCaseInsensitive(pGrpItem
->GetName())))
741 pGrpItem
->FillGroupFilter(*pGrpFilter
);
744 aNewCriteria
.push_back(aCri
);
748 rCriteria
.swap(aNewCriteria
);
751 void ScDPGroupTableData::FilterCacheTable(std::vector
<ScDPFilteredCache::Criterion
>&& rCriteria
, std::unordered_set
<sal_Int32
>&& rCatDims
)
753 ModifyFilterCriteria(rCriteria
);
754 pSourceData
->FilterCacheTable(std::move(rCriteria
), std::move(rCatDims
));
757 void ScDPGroupTableData::GetDrillDownData(std::vector
<ScDPFilteredCache::Criterion
>&& rCriteria
, std::unordered_set
<sal_Int32
>&&rCatDims
, Sequence
< Sequence
<Any
> >& rData
)
759 ModifyFilterCriteria(rCriteria
);
760 pSourceData
->GetDrillDownData(std::move(rCriteria
), std::move(rCatDims
), rData
);
763 void ScDPGroupTableData::CalcResults(CalcInfo
& rInfo
, bool bAutoShow
)
765 // #i111435# Inside FillRowDataFromCacheTable/GetItemData, virtual methods
766 // getIsDataLayoutDimension and GetSourceDim are used, so it has to be called
767 // with original rInfo, containing dimension indexes of the grouped data.
769 const ScDPFilteredCache
& rCacheTable
= pSourceData
->GetCacheTable();
770 sal_Int32 nRowSize
= rCacheTable
.getRowSize();
771 for (sal_Int32 nRow
= 0; nRow
< nRowSize
; ++nRow
)
774 if (!rCacheTable
.isRowActive(nRow
, &nLastRow
))
781 FillRowDataFromCacheTable(nRow
, rCacheTable
, rInfo
, aData
);
783 if ( !rInfo
.aColLevelDims
.empty() )
784 FillGroupValues(aData
.aColData
, rInfo
.aColLevelDims
);
785 if ( !rInfo
.aRowLevelDims
.empty() )
786 FillGroupValues(aData
.aRowData
, rInfo
.aRowLevelDims
);
787 if ( !rInfo
.aPageDims
.empty() )
788 FillGroupValues(aData
.aPageData
, rInfo
.aPageDims
);
790 ProcessRowData(rInfo
, aData
, bAutoShow
);
794 const ScDPFilteredCache
& ScDPGroupTableData::GetCacheTable() const
796 return pSourceData
->GetCacheTable();
799 void ScDPGroupTableData::ReloadCacheTable()
801 pSourceData
->ReloadCacheTable();
804 void ScDPGroupTableData::FillGroupValues(vector
<SCROW
>& rItems
, const vector
<sal_Int32
>& rDims
)
806 sal_Int32 nGroupedColumns
= aGroups
.size();
808 const ScDPCache
& rCache
= GetCacheTable().getCache();
810 for (tools::Long nColumn
: rDims
)
812 bool bDateDim
= false;
814 sal_Int32 nSourceDim
= nColumn
;
815 if ( nColumn
>= nSourceCount
&& nColumn
< nSourceCount
+ nGroupedColumns
)
817 const ScDPGroupDimension
& rGroupDim
= aGroups
[nColumn
- nSourceCount
];
818 nSourceDim
= rGroupDim
.GetSourceDim();
819 bDateDim
= rGroupDim
.IsDateDimension();
820 if (!bDateDim
) // date is handled below
822 const ScDPItemData
& rItem
= *GetMemberById(nSourceDim
, rItems
[i
]);
823 const ScDPGroupItem
* pGroupItem
= rGroupDim
.GetGroupForData(rItem
);
827 rCache
.GetIdByItemData(nColumn
, pGroupItem
->GetName());
830 rItems
[i
] = rCache
.GetIdByItemData(nColumn
, rItem
);
833 else if ( IsNumGroupDimension( nColumn
) )
835 bDateDim
= pNumGroups
[nColumn
].IsDateDimension();
836 if (!bDateDim
) // date is handled below
838 const ScDPItemData
* pData
= rCache
.GetItemDataById(nSourceDim
, rItems
[i
]);
839 if (pData
->GetType() == ScDPItemData::Value
)
841 ScDPNumGroupInfo aNumInfo
;
842 GetNumGroupInfo(nColumn
, aNumInfo
);
843 double fGroupValue
= ScDPUtil::getNumGroupStartValue(pData
->GetValue(), aNumInfo
);
844 ScDPItemData aItemData
;
845 aItemData
.SetRangeStart(fGroupValue
);
846 rItems
[i
] = rCache
.GetIdByItemData(nSourceDim
, aItemData
);
848 // else (textual) keep original value
852 const ScDPNumGroupInfo
* pNumInfo
= rCache
.GetNumGroupInfo(nColumn
);
854 if (bDateDim
&& pNumInfo
)
856 // This is a date group dimension.
857 sal_Int32 nDatePart
= rCache
.GetGroupType(nColumn
);
858 const ScDPItemData
* pData
= rCache
.GetItemDataById(nSourceDim
, rItems
[i
]);
859 if (pData
->GetType() == ScDPItemData::Value
)
861 SvNumberFormatter
* pFormatter
= pDoc
->GetFormatTable();
862 sal_Int32 nPartValue
= ScDPUtil::getDatePartValue(
863 pData
->GetValue(), pNumInfo
, nDatePart
, pFormatter
);
865 ScDPItemData
aItem(nDatePart
, nPartValue
);
866 rItems
[i
] = rCache
.GetIdByItemData(nColumn
, aItem
);
874 bool ScDPGroupTableData::IsBaseForGroup(sal_Int32 nDim
) const
876 return std::any_of(aGroups
.begin(), aGroups
.end(),
877 [&nDim
](const ScDPGroupDimension
& rDim
) { return rDim
.GetSourceDim() == nDim
; });
880 sal_Int32
ScDPGroupTableData::GetGroupBase(sal_Int32 nGroupDim
) const
882 auto aIter
= std::find_if(aGroups
.begin(), aGroups
.end(),
883 [&nGroupDim
](const ScDPGroupDimension
& rDim
) { return rDim
.GetGroupDim() == nGroupDim
; });
884 if (aIter
!= aGroups
.end())
885 return aIter
->GetSourceDim();
890 bool ScDPGroupTableData::IsNumOrDateGroup(sal_Int32 nDimension
) const
892 // Virtual method from ScDPTableData, used in result data to force text labels.
894 if ( nDimension
< nSourceCount
)
896 return pNumGroups
[nDimension
].GetInfo().mbEnable
||
897 pNumGroups
[nDimension
].IsDateDimension();
900 auto aIter
= std::find_if(aGroups
.begin(), aGroups
.end(),
901 [&nDimension
](const ScDPGroupDimension
& rDim
) { return rDim
.GetGroupDim() == nDimension
; });
902 if (aIter
!= aGroups
.end())
903 return aIter
->IsDateDimension();
908 bool ScDPGroupTableData::IsInGroup( const ScDPItemData
& rGroupData
, sal_Int32 nGroupIndex
,
909 const ScDPItemData
& rBaseData
, sal_Int32 nBaseIndex
) const
911 auto aIter
= std::find_if(aGroups
.begin(), aGroups
.end(),
912 [&nGroupIndex
, &nBaseIndex
](const ScDPGroupDimension
& rDim
) {
913 return rDim
.GetGroupDim() == nGroupIndex
&& rDim
.GetSourceDim() == nBaseIndex
; });
914 if (aIter
!= aGroups
.end())
916 const ScDPGroupDimension
& rDim
= *aIter
;
917 if (rDim
.IsDateDimension())
919 return isDateInGroup(rGroupData
, rBaseData
);
923 // If the item is in a group, only that group is valid.
924 // If the item is not in any group, its own name is valid.
926 const ScDPGroupItem
* pGroup
= rDim
.GetGroupForData( rBaseData
);
927 return pGroup
? pGroup
->GetName().IsCaseInsEqual( rGroupData
) :
928 rGroupData
.IsCaseInsEqual( rBaseData
);
932 OSL_FAIL("IsInGroup: no group dimension found");
936 bool ScDPGroupTableData::HasCommonElement( const ScDPItemData
& rFirstData
, sal_Int32 nFirstIndex
,
937 const ScDPItemData
& rSecondData
, sal_Int32 nSecondIndex
) const
939 const ScDPGroupDimension
* pFirstDim
= nullptr;
940 const ScDPGroupDimension
* pSecondDim
= nullptr;
941 for ( const auto& rDim
: aGroups
)
943 const ScDPGroupDimension
* pDim
= &rDim
;
944 if ( pDim
->GetGroupDim() == nFirstIndex
)
946 else if ( pDim
->GetGroupDim() == nSecondIndex
)
949 if ( pFirstDim
&& pSecondDim
)
951 bool bFirstDate
= pFirstDim
->IsDateDimension();
952 bool bSecondDate
= pSecondDim
->IsDateDimension();
953 if (bFirstDate
|| bSecondDate
)
955 // If one is a date group dimension, the other one must be, too.
956 if (!bFirstDate
|| !bSecondDate
)
958 OSL_FAIL( "mix of date and non-date groups" );
962 return isDateInGroup(rFirstData
, rSecondData
);
965 const ScDPGroupItem
* pFirstItem
= pFirstDim
->GetGroupForName( rFirstData
);
966 const ScDPGroupItem
* pSecondItem
= pSecondDim
->GetGroupForName( rSecondData
);
967 if ( pFirstItem
&& pSecondItem
)
969 // two existing groups -> sal_True if they have a common element
970 return pFirstItem
->HasCommonElement( *pSecondItem
);
972 else if ( pFirstItem
)
974 // "automatic" group contains only its own name
975 return pFirstItem
->HasElement( rSecondData
);
977 else if ( pSecondItem
)
979 // "automatic" group contains only its own name
980 return pSecondItem
->HasElement( rFirstData
);
984 // no groups -> sal_True if equal
985 return rFirstData
.IsCaseInsEqual( rSecondData
);
989 OSL_FAIL("HasCommonElement: no group dimension found");
993 sal_Int32
ScDPGroupTableData::GetSourceDim( sal_Int32 nDim
)
995 if ( getIsDataLayoutDimension( nDim
) )
997 if ( nDim
>= nSourceCount
&& nDim
< nSourceCount
+static_cast<tools::Long
>(aGroups
.size()) )
999 const ScDPGroupDimension
& rGroupDim
= aGroups
[nDim
- nSourceCount
];
1000 return rGroupDim
.GetSourceDim();
1005 sal_Int32
ScDPGroupTableData::Compare(sal_Int32 nDim
, sal_Int32 nDataId1
, sal_Int32 nDataId2
)
1007 if ( getIsDataLayoutDimension(nDim
) )
1009 const ScDPItemData
* rItem1
= GetMemberById(nDim
, nDataId1
);
1010 const ScDPItemData
* rItem2
= GetMemberById(nDim
, nDataId2
);
1011 if (rItem1
== nullptr || rItem2
== nullptr)
1013 return ScDPItemData::Compare( *rItem1
,*rItem2
);
1016 #if DUMP_PIVOT_TABLE
1018 void ScDPGroupTableData::Dump() const
1020 cout
<< "--- ScDPGroupTableData" << endl
;
1021 for (tools::Long i
= 0; i
< nSourceCount
; ++i
)
1023 cout
<< "* dimension: " << i
<< endl
;
1024 const ScDPNumGroupDimension
& rGrp
= pNumGroups
[i
];
1025 rGrp
.GetInfo().Dump();
1027 cout
<< "---" << endl
;
1031 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */