tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / core / data / dpgroup.cxx
blob336388a36daf4b2a166ef71f6ad9b35148fb525a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
25 #include <dputil.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>
35 #include <utility>
36 #include <vector>
37 #include <algorithm>
39 using namespace ::com::sun::star;
40 using ::com::sun::star::uno::Any;
41 using ::com::sun::star::uno::Sequence;
43 using ::std::vector;
44 using ::std::shared_ptr;
46 const sal_uInt16 SC_DP_LEAPYEAR = 1648; // arbitrary leap year for date calculations
48 namespace {
50 class ScDPGroupNumFilter : public ScDPFilteredCache::FilterBase
52 public:
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;
57 private:
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)
70 return false;
72 for (const auto& rValue : maValues)
74 double fVal = rValue.GetValue();
75 if (std::isinf(fVal))
77 if (std::signbit(fVal))
79 // Less than the min value.
80 if (rCellData.GetValue() < maNumInfo.mfStart)
81 return true;
84 // Greater than the max value.
85 if (maNumInfo.mfEnd < rCellData.GetValue())
86 return true;
88 continue;
91 double low = fVal;
92 double high = low + maNumInfo.mfStep;
93 if (maNumInfo.mbIntegerOnly)
94 high += 1.0;
96 if (low <= rCellData.GetValue() && rCellData.GetValue() < high)
97 return true;
100 return false;
103 std::vector<ScDPItemData> ScDPGroupNumFilter::getMatchValues() const
105 return std::vector<ScDPItemData>();
108 namespace {
110 class ScDPGroupDateFilter : public ScDPFilteredCache::FilterBase
112 public:
113 ScDPGroupDateFilter(
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;
119 private:
120 std::vector<ScDPItemData> maValues;
121 Date maNullDate;
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),
131 maNumInfo(rNumInfo)
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() )
142 return false;
144 for (const ScDPItemData& rValue : maValues)
146 if (rValue.GetType() != ScDPItemData::GroupValue)
147 continue;
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)
158 return true;
159 continue;
162 if (rCellData.GetValue() > maNumInfo.mfEnd && !approxEqual(rCellData.GetValue(), maNumInfo.mfEnd))
164 if (nValue == ScDPItemData::DateLast)
165 return true;
166 continue;
169 if (nGroupType == DataPilotFieldGroupBy::HOURS || nGroupType == DataPilotFieldGroupBy::MINUTES ||
170 nGroupType == DataPilotFieldGroupBy::SECONDS)
172 // handle time
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);
179 switch (nGroupType)
181 case DataPilotFieldGroupBy::HOURS:
183 if (nHour == nValue)
184 return true;
186 break;
187 case DataPilotFieldGroupBy::MINUTES:
189 if (nMinute == nValue)
190 return true;
192 break;
193 case DataPilotFieldGroupBy::SECONDS:
195 if (nSecond == nValue)
196 return true;
198 break;
199 default:
200 OSL_FAIL("invalid time part");
203 continue;
206 Date date = maNullDate + static_cast<sal_Int32>(approxFloor(rCellData.GetValue()));
207 switch (nGroupType)
209 case DataPilotFieldGroupBy::YEARS:
211 sal_Int32 year = static_cast<sal_Int32>(date.GetYear());
212 if (year == nValue)
213 return true;
215 break;
216 case DataPilotFieldGroupBy::QUARTERS:
218 sal_Int32 qtr = 1 + (static_cast<sal_Int32>(date.GetMonth()) - 1) / 3;
219 if (qtr == nValue)
220 return true;
222 break;
223 case DataPilotFieldGroupBy::MONTHS:
225 sal_Int32 month = static_cast<sal_Int32>(date.GetMonth());
226 if (month == nValue)
227 return true;
229 break;
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.
237 ++days;
239 if (days == nValue)
240 return true;
242 break;
243 default:
244 OSL_FAIL("invalid date part");
248 return false;
251 std::vector<ScDPItemData> ScDPGroupDateFilter::getMatchValues() const
253 return std::vector<ScDPItemData>();
256 namespace {
258 bool isDateInGroup(const ScDPItemData& rGroupItem, const ScDPItemData& rChildItem)
260 if (rGroupItem.GetType() != ScDPItemData::GroupValue || rChildItem.GetType() != ScDPItemData::GroupValue)
261 return false;
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);
282 break;
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;
296 break;
297 default:
301 return true;
306 ScDPGroupItem::ScDPGroupItem( const ScDPItemData& rName ) :
307 aGroupName( 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 ),
336 nGroupDim( -1 ),
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;
363 return *this;
366 void ScDPGroupDimension::AddItem( const ScDPGroupItem& rItem )
368 aItems.push_back( rItem );
371 void ScDPGroupDimension::SetGroupDim( tools::Long nDim )
373 nGroupDim = 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())
391 return &*aIter;
393 return nullptr;
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())
401 return &*aIter;
403 return nullptr;
406 const ScDPGroupItem* ScDPGroupDimension::GetGroupByIndex( size_t nIndex ) const
408 if (nIndex >= aItems.size())
409 return nullptr;
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;
436 return *this;
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 ),
468 pDoc( pDocument )
470 OSL_ENSURE( pSource, "ScDPGroupTableData: pSource can't be NULL" );
472 CreateCacheTable();
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?
502 return i;
503 return -1; // none
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
532 else
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
560 else
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
579 else
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
592 else
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();
625 namespace {
627 class FindCaseInsensitive
629 ScDPItemData maValue;
630 public:
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);
670 if (!pNumInfo)
671 // Number group dimension without num info? Something is wrong...
672 continue;
674 ScDPFilteredCache::Criterion aCri;
675 aCri.mnFieldIndex = rCriterion.mnFieldIndex;
676 const ScDPNumGroupDimension& rNumGrpDim = pNumGroups[rCriterion.mnFieldIndex];
678 if (rNumGrpDim.IsDateDimension())
680 // grouped by dates.
681 aCri.mpFilter =
682 std::make_shared<ScDPGroupDateFilter>(
683 std::move(aMatchValues), pDoc->GetFormatTable()->GetNullDate(), *pNumInfo);
685 else
687 // This dimension is grouped by numeric ranges.
688 aCri.mpFilter =
689 std::make_shared<ScDPGroupNumFilter>(std::move(aMatchValues), *pNumInfo);
692 aNewCriteria.push_back(aCri);
694 else
696 // This is a regular source field.
697 aNewCriteria.push_back(rCriterion);
700 else
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.
714 aCri.mpFilter =
715 std::make_shared<ScDPGroupDateFilter>(
716 std::move(aMatchValues), pDoc->GetFormatTable()->GetNullDate(), *pNumInfo);
718 aNewCriteria.push_back(aCri);
720 else
722 // normal group
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);
734 if (!pGrpItem)
735 continue;
737 // Make sure this group name equals one of the match values.
738 if (std::none_of(aMatchValues.begin(), aMatchValues.end(), FindCaseInsensitive(pGrpItem->GetName())))
739 continue;
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)
773 sal_Int32 nLastRow;
774 if (!rCacheTable.isRowActive(nRow, &nLastRow))
776 nRow = nLastRow;
777 continue;
780 CalcRowData aData;
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();
809 size_t i = 0;
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);
824 if (pGroupItem)
826 rItems[i] =
827 rCache.GetIdByItemData(nColumn, pGroupItem->GetName());
829 else
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);
870 ++i;
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();
887 return -1; // none
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();
905 return false;
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);
921 else
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");
933 return true;
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 )
945 pFirstDim = pDim;
946 else if ( pDim->GetGroupDim() == nSecondIndex )
947 pSecondDim = pDim;
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" );
959 return true;
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 );
982 else
984 // no groups -> sal_True if equal
985 return rFirstData.IsCaseInsEqual( rSecondData );
989 OSL_FAIL("HasCommonElement: no group dimension found");
990 return true;
993 sal_Int32 ScDPGroupTableData::GetSourceDim( sal_Int32 nDim )
995 if ( getIsDataLayoutDimension( nDim ) )
996 return nSourceCount;
997 if ( nDim >= nSourceCount && nDim < nSourceCount +static_cast<tools::Long>(aGroups.size()) )
999 const ScDPGroupDimension& rGroupDim = aGroups[nDim - nSourceCount];
1000 return rGroupDim.GetSourceDim();
1002 return nDim;
1005 sal_Int32 ScDPGroupTableData::Compare(sal_Int32 nDim, sal_Int32 nDataId1, sal_Int32 nDataId2)
1007 if ( getIsDataLayoutDimension(nDim) )
1008 return 0;
1009 const ScDPItemData* rItem1 = GetMemberById(nDim, nDataId1);
1010 const ScDPItemData* rItem2 = GetMemberById(nDim, nDataId2);
1011 if (rItem1 == nullptr || rItem2 == nullptr)
1012 return 0;
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;
1029 #endif
1031 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */