summaryrefslogtreecommitdiffstats
path: root/sc/source/core/data/dpgroup.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/data/dpgroup.cxx')
-rw-r--r--sc/source/core/data/dpgroup.cxx1030
1 files changed, 1030 insertions, 0 deletions
diff --git a/sc/source/core/data/dpgroup.cxx b/sc/source/core/data/dpgroup.cxx
new file mode 100644
index 000000000..52bc6476f
--- /dev/null
+++ b/sc/source/core/data/dpgroup.cxx
@@ -0,0 +1,1030 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dpgroup.hxx>
+
+#include <dpcache.hxx>
+#include <document.hxx>
+#include <dpfilteredcache.hxx>
+#include <dputil.hxx>
+
+#include <osl/diagnose.h>
+#include <rtl/math.hxx>
+#include <svl/numformat.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+#include <algorithm>
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Sequence;
+
+using ::std::vector;
+using ::std::shared_ptr;
+
+const sal_uInt16 SC_DP_LEAPYEAR = 1648; // arbitrary leap year for date calculations
+
+namespace {
+
+class ScDPGroupNumFilter : public ScDPFilteredCache::FilterBase
+{
+public:
+ ScDPGroupNumFilter(std::vector<ScDPItemData>&& rValues, const ScDPNumGroupInfo& rInfo);
+
+ virtual bool match(const ScDPItemData &rCellData) const override;
+ virtual std::vector<ScDPItemData> getMatchValues() const override;
+private:
+ std::vector<ScDPItemData> maValues;
+ ScDPNumGroupInfo maNumInfo;
+};
+
+}
+
+ScDPGroupNumFilter::ScDPGroupNumFilter( std::vector<ScDPItemData>&& rValues, const ScDPNumGroupInfo& rInfo) :
+ maValues(std::move(rValues)), maNumInfo(rInfo) {}
+
+bool ScDPGroupNumFilter::match(const ScDPItemData& rCellData) const
+{
+ if (rCellData.GetType() != ScDPItemData::Value)
+ return false;
+
+ for (const auto& rValue : maValues)
+ {
+ double fVal = rValue.GetValue();
+ if (std::isinf(fVal))
+ {
+ if (std::signbit(fVal))
+ {
+ // Less than the min value.
+ if (rCellData.GetValue() < maNumInfo.mfStart)
+ return true;
+ }
+
+ // Greater than the max value.
+ if (maNumInfo.mfEnd < rCellData.GetValue())
+ return true;
+
+ continue;
+ }
+
+ double low = fVal;
+ double high = low + maNumInfo.mfStep;
+ if (maNumInfo.mbIntegerOnly)
+ high += 1.0;
+
+ if (low <= rCellData.GetValue() && rCellData.GetValue() < high)
+ return true;
+ }
+
+ return false;
+}
+
+std::vector<ScDPItemData> ScDPGroupNumFilter::getMatchValues() const
+{
+ return std::vector<ScDPItemData>();
+}
+
+namespace {
+
+class ScDPGroupDateFilter : public ScDPFilteredCache::FilterBase
+{
+public:
+ ScDPGroupDateFilter(
+ std::vector<ScDPItemData>&& rValues, const Date& rNullDate, const ScDPNumGroupInfo& rNumInfo);
+
+ virtual bool match(const ScDPItemData & rCellData) const override;
+ virtual std::vector<ScDPItemData> getMatchValues() const override;
+
+private:
+ std::vector<ScDPItemData> maValues;
+ Date maNullDate;
+ ScDPNumGroupInfo maNumInfo;
+};
+
+}
+
+ScDPGroupDateFilter::ScDPGroupDateFilter(
+ std::vector<ScDPItemData>&& rValues, const Date& rNullDate, const ScDPNumGroupInfo& rNumInfo) :
+ maValues(std::move(rValues)),
+ maNullDate(rNullDate),
+ maNumInfo(rNumInfo)
+{
+}
+
+bool ScDPGroupDateFilter::match( const ScDPItemData & rCellData ) const
+{
+ using namespace ::com::sun::star::sheet;
+ using ::rtl::math::approxFloor;
+ using ::rtl::math::approxEqual;
+
+ if ( !rCellData.IsValue() )
+ return false;
+
+ for (const ScDPItemData& rValue : maValues)
+ {
+ if (rValue.GetType() != ScDPItemData::GroupValue)
+ continue;
+
+ sal_Int32 nGroupType = rValue.GetGroupValue().mnGroupType;
+ sal_Int32 nValue = rValue.GetGroupValue().mnValue;
+
+ // Start and end dates are inclusive. (An end date without a time value
+ // is included, while an end date with a time value is not.)
+
+ if (rCellData.GetValue() < maNumInfo.mfStart && !approxEqual(rCellData.GetValue(), maNumInfo.mfStart))
+ {
+ if (nValue == ScDPItemData::DateFirst)
+ return true;
+ continue;
+ }
+
+ if (rCellData.GetValue() > maNumInfo.mfEnd && !approxEqual(rCellData.GetValue(), maNumInfo.mfEnd))
+ {
+ if (nValue == ScDPItemData::DateLast)
+ return true;
+ continue;
+ }
+
+ if (nGroupType == DataPilotFieldGroupBy::HOURS || nGroupType == DataPilotFieldGroupBy::MINUTES ||
+ nGroupType == DataPilotFieldGroupBy::SECONDS)
+ {
+ // handle time
+ // (do as in the cell functions, ScInterpreter::ScGetHour() etc.)
+
+ sal_uInt16 nHour, nMinute, nSecond;
+ double fFractionOfSecond;
+ tools::Time::GetClock( rCellData.GetValue(), nHour, nMinute, nSecond, fFractionOfSecond, 0);
+
+ switch (nGroupType)
+ {
+ case DataPilotFieldGroupBy::HOURS:
+ {
+ if (nHour == nValue)
+ return true;
+ }
+ break;
+ case DataPilotFieldGroupBy::MINUTES:
+ {
+ if (nMinute == nValue)
+ return true;
+ }
+ break;
+ case DataPilotFieldGroupBy::SECONDS:
+ {
+ if (nSecond == nValue)
+ return true;
+ }
+ break;
+ default:
+ OSL_FAIL("invalid time part");
+ }
+
+ continue;
+ }
+
+ Date date = maNullDate + static_cast<sal_Int32>(approxFloor(rCellData.GetValue()));
+ switch (nGroupType)
+ {
+ case DataPilotFieldGroupBy::YEARS:
+ {
+ sal_Int32 year = static_cast<sal_Int32>(date.GetYear());
+ if (year == nValue)
+ return true;
+ }
+ break;
+ case DataPilotFieldGroupBy::QUARTERS:
+ {
+ sal_Int32 qtr = 1 + (static_cast<sal_Int32>(date.GetMonth()) - 1) / 3;
+ if (qtr == nValue)
+ return true;
+ }
+ break;
+ case DataPilotFieldGroupBy::MONTHS:
+ {
+ sal_Int32 month = static_cast<sal_Int32>(date.GetMonth());
+ if (month == nValue)
+ return true;
+ }
+ break;
+ case DataPilotFieldGroupBy::DAYS:
+ {
+ Date yearStart(1, 1, date.GetYear());
+ sal_Int32 days = (date - yearStart) + 1; // Jan 01 has value 1
+ if (days >= 60 && !date.IsLeapYear())
+ {
+ // This is not a leap year. Adjust the value accordingly.
+ ++days;
+ }
+ if (days == nValue)
+ return true;
+ }
+ break;
+ default:
+ OSL_FAIL("invalid date part");
+ }
+ }
+
+ return false;
+}
+
+std::vector<ScDPItemData> ScDPGroupDateFilter::getMatchValues() const
+{
+ return std::vector<ScDPItemData>();
+}
+
+namespace {
+
+bool isDateInGroup(const ScDPItemData& rGroupItem, const ScDPItemData& rChildItem)
+{
+ if (rGroupItem.GetType() != ScDPItemData::GroupValue || rChildItem.GetType() != ScDPItemData::GroupValue)
+ return false;
+
+ sal_Int32 nGroupPart = rGroupItem.GetGroupValue().mnGroupType;
+ sal_Int32 nGroupValue = rGroupItem.GetGroupValue().mnValue;
+ sal_Int32 nChildPart = rChildItem.GetGroupValue().mnGroupType;
+ sal_Int32 nChildValue = rChildItem.GetGroupValue().mnValue;
+
+ if (nGroupValue == ScDPItemData::DateFirst || nGroupValue == ScDPItemData::DateLast ||
+ nChildValue == ScDPItemData::DateFirst || nChildValue == ScDPItemData::DateLast)
+ {
+ // first/last entry matches only itself
+ return nGroupValue == nChildValue;
+ }
+
+ switch (nChildPart) // inner part
+ {
+ case css::sheet::DataPilotFieldGroupBy::MONTHS:
+ // a month is only contained in its quarter
+ if (nGroupPart == css::sheet::DataPilotFieldGroupBy::QUARTERS)
+ // months and quarters are both 1-based
+ return (nGroupValue - 1 == (nChildValue - 1) / 3);
+ break;
+ case css::sheet::DataPilotFieldGroupBy::DAYS:
+ // a day is only contained in its quarter or month
+ if (nGroupPart == css::sheet::DataPilotFieldGroupBy::MONTHS ||
+ nGroupPart == css::sheet::DataPilotFieldGroupBy::QUARTERS)
+ {
+ Date aDate(1, 1, SC_DP_LEAPYEAR);
+ aDate.AddDays(nChildValue - 1); // days are 1-based
+ sal_Int32 nCompare = aDate.GetMonth();
+ if (nGroupPart == css::sheet::DataPilotFieldGroupBy::QUARTERS)
+ nCompare = ( ( nCompare - 1 ) / 3 ) + 1; // get quarter from date
+
+ return nGroupValue == nCompare;
+ }
+ break;
+ default:
+ ;
+ }
+
+ return true;
+}
+
+}
+
+ScDPGroupItem::ScDPGroupItem( const ScDPItemData& rName ) :
+ aGroupName( rName )
+{
+}
+
+void ScDPGroupItem::AddElement( const ScDPItemData& rName )
+{
+ aElements.push_back( rName );
+}
+
+bool ScDPGroupItem::HasElement( const ScDPItemData& rData ) const
+{
+ return std::any_of(aElements.begin(), aElements.end(),
+ [&rData](const ScDPItemData& rElement) { return rElement.IsCaseInsEqual(rData); });
+}
+
+bool ScDPGroupItem::HasCommonElement( const ScDPGroupItem& rOther ) const
+{
+ return std::any_of(aElements.begin(), aElements.end(),
+ [&rOther](const ScDPItemData& rElement) { return rOther.HasElement(rElement); });
+}
+
+void ScDPGroupItem::FillGroupFilter( ScDPFilteredCache::GroupFilter& rFilter ) const
+{
+ for (const auto& rElement : aElements)
+ rFilter.addMatchItem(rElement);
+}
+
+ScDPGroupDimension::ScDPGroupDimension( tools::Long nSource, const OUString& rNewName ) :
+ nSourceDim( nSource ),
+ nGroupDim( -1 ),
+ aGroupName( rNewName ),
+ mbDateDimension(false)
+{
+}
+
+ScDPGroupDimension::~ScDPGroupDimension()
+{
+ maMemberEntries.clear();
+}
+
+ScDPGroupDimension::ScDPGroupDimension( const ScDPGroupDimension& rOther ) :
+ nSourceDim( rOther.nSourceDim ),
+ nGroupDim( rOther.nGroupDim ),
+ aGroupName( rOther.aGroupName ),
+ aItems( rOther.aItems ),
+ mbDateDimension(rOther.mbDateDimension)
+{
+}
+
+ScDPGroupDimension& ScDPGroupDimension::operator=( const ScDPGroupDimension& rOther )
+{
+ nSourceDim = rOther.nSourceDim;
+ nGroupDim = rOther.nGroupDim;
+ aGroupName = rOther.aGroupName;
+ aItems = rOther.aItems;
+ mbDateDimension = rOther.mbDateDimension;
+ return *this;
+}
+
+void ScDPGroupDimension::AddItem( const ScDPGroupItem& rItem )
+{
+ aItems.push_back( rItem );
+}
+
+void ScDPGroupDimension::SetGroupDim( tools::Long nDim )
+{
+ nGroupDim = nDim;
+}
+
+const std::vector<SCROW>& ScDPGroupDimension::GetColumnEntries(
+ const ScDPFilteredCache& rCacheTable) const
+{
+ if (!maMemberEntries.empty())
+ return maMemberEntries;
+
+ rCacheTable.getCache().GetGroupDimMemberIds(nGroupDim, maMemberEntries);
+ return maMemberEntries;
+}
+
+const ScDPGroupItem* ScDPGroupDimension::GetGroupForData( const ScDPItemData& rData ) const
+{
+ auto aIter = std::find_if(aItems.begin(), aItems.end(),
+ [&rData](const ScDPGroupItem& rItem) { return rItem.HasElement(rData); });
+ if (aIter != aItems.end())
+ return &*aIter;
+
+ return nullptr;
+}
+
+const ScDPGroupItem* ScDPGroupDimension::GetGroupForName( const ScDPItemData& rName ) const
+{
+ auto aIter = std::find_if(aItems.begin(), aItems.end(),
+ [&rName](const ScDPGroupItem& rItem) { return rItem.GetName().IsCaseInsEqual(rName); });
+ if (aIter != aItems.end())
+ return &*aIter;
+
+ return nullptr;
+}
+
+const ScDPGroupItem* ScDPGroupDimension::GetGroupByIndex( size_t nIndex ) const
+{
+ if (nIndex >= aItems.size())
+ return nullptr;
+
+ return &aItems[nIndex];
+}
+
+void ScDPGroupDimension::DisposeData()
+{
+ maMemberEntries.clear();
+}
+
+void ScDPGroupDimension::SetDateDimension()
+{
+ mbDateDimension = true;
+}
+
+ScDPNumGroupDimension::ScDPNumGroupDimension() : mbDateDimension(false) {}
+
+ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupInfo& rInfo ) :
+ aGroupInfo(rInfo), mbDateDimension(false) {}
+
+ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupDimension& rOther ) :
+ aGroupInfo(rOther.aGroupInfo), mbDateDimension(rOther.mbDateDimension) {}
+
+ScDPNumGroupDimension& ScDPNumGroupDimension::operator=( const ScDPNumGroupDimension& rOther )
+{
+ aGroupInfo = rOther.aGroupInfo;
+ mbDateDimension = rOther.mbDateDimension;
+ return *this;
+}
+
+void ScDPNumGroupDimension::DisposeData()
+{
+ aGroupInfo = ScDPNumGroupInfo();
+ maMemberEntries.clear();
+}
+
+ScDPNumGroupDimension::~ScDPNumGroupDimension()
+{
+}
+
+void ScDPNumGroupDimension::SetDateDimension()
+{
+ aGroupInfo.mbEnable = true; //TODO: or query both?
+ mbDateDimension = true;
+}
+
+const std::vector<SCROW>& ScDPNumGroupDimension::GetNumEntries(
+ SCCOL nSourceDim, const ScDPCache* pCache) const
+{
+ if (!maMemberEntries.empty())
+ return maMemberEntries;
+
+ pCache->GetGroupDimMemberIds(nSourceDim, maMemberEntries);
+ return maMemberEntries;
+}
+
+ScDPGroupTableData::ScDPGroupTableData( const shared_ptr<ScDPTableData>& pSource, ScDocument* pDocument ) :
+ ScDPTableData(pDocument),
+ pSourceData( pSource ),
+ pDoc( pDocument )
+{
+ OSL_ENSURE( pSource, "ScDPGroupTableData: pSource can't be NULL" );
+
+ CreateCacheTable();
+ nSourceCount = pSource->GetColumnCount(); // real columns, excluding data layout
+ pNumGroups.reset( new ScDPNumGroupDimension[nSourceCount] );
+}
+
+ScDPGroupTableData::~ScDPGroupTableData()
+{
+}
+
+void ScDPGroupTableData::AddGroupDimension( const ScDPGroupDimension& rGroup )
+{
+ ScDPGroupDimension aNewGroup( rGroup );
+ aNewGroup.SetGroupDim( GetColumnCount() ); // new dimension will be at the end
+ aGroups.push_back( aNewGroup );
+}
+
+void ScDPGroupTableData::SetNumGroupDimension( sal_Int32 nIndex, const ScDPNumGroupDimension& rGroup )
+{
+ if ( nIndex < nSourceCount )
+ {
+ pNumGroups[nIndex] = rGroup;
+
+ // automatic minimum / maximum is handled in GetNumEntries
+ }
+}
+
+sal_Int32 ScDPGroupTableData::GetDimensionIndex( std::u16string_view rName )
+{
+ for (tools::Long i = 0; i < nSourceCount; ++i) // nSourceCount excludes data layout
+ if (pSourceData->getDimensionName(i) == rName) //TODO: ignore case?
+ return i;
+ return -1; // none
+}
+
+sal_Int32 ScDPGroupTableData::GetColumnCount()
+{
+ return nSourceCount + aGroups.size();
+}
+
+bool ScDPGroupTableData::IsNumGroupDimension( tools::Long nDimension ) const
+{
+ return ( nDimension < nSourceCount && pNumGroups[nDimension].GetInfo().mbEnable );
+}
+
+void ScDPGroupTableData::GetNumGroupInfo(tools::Long nDimension, ScDPNumGroupInfo& rInfo)
+{
+ if ( nDimension < nSourceCount )
+ rInfo = pNumGroups[nDimension].GetInfo();
+}
+sal_Int32 ScDPGroupTableData::GetMembersCount( sal_Int32 nDim )
+{
+ const std::vector< SCROW >& members = GetColumnEntries( nDim );
+ return members.size();
+}
+const std::vector< SCROW >& ScDPGroupTableData::GetColumnEntries( sal_Int32 nColumn )
+{
+ if ( nColumn >= nSourceCount )
+ {
+ if ( getIsDataLayoutDimension( nColumn) ) // data layout dimension?
+ nColumn = nSourceCount; // index of data layout in source data
+ else
+ {
+ const ScDPGroupDimension& rGroupDim = aGroups[nColumn - nSourceCount];
+ return rGroupDim.GetColumnEntries( GetCacheTable() );
+ }
+ }
+
+ if ( IsNumGroupDimension( nColumn ) )
+ {
+ // dimension number is unchanged for numerical groups
+ return pNumGroups[nColumn].GetNumEntries(
+ static_cast<SCCOL>(nColumn), &GetCacheTable().getCache());
+ }
+
+ return pSourceData->GetColumnEntries( nColumn );
+}
+
+const ScDPItemData* ScDPGroupTableData::GetMemberById( sal_Int32 nDim, sal_Int32 nId )
+{
+ return pSourceData->GetMemberById( nDim, nId );
+}
+
+OUString ScDPGroupTableData::getDimensionName(sal_Int32 nColumn)
+{
+ if ( nColumn >= nSourceCount )
+ {
+ if ( nColumn == sal::static_int_cast<tools::Long>( nSourceCount + aGroups.size() ) ) // data layout dimension?
+ nColumn = nSourceCount; // index of data layout in source data
+ else
+ return aGroups[nColumn - nSourceCount].GetName();
+ }
+
+ return pSourceData->getDimensionName( nColumn );
+}
+
+bool ScDPGroupTableData::getIsDataLayoutDimension(sal_Int32 nColumn)
+{
+ // position of data layout dimension is moved from source data
+ return ( nColumn == sal::static_int_cast<tools::Long>( nSourceCount + aGroups.size() ) ); // data layout dimension?
+}
+
+bool ScDPGroupTableData::IsDateDimension(sal_Int32 nDim)
+{
+ if ( nDim >= nSourceCount )
+ {
+ if ( nDim == sal::static_int_cast<tools::Long>( nSourceCount + aGroups.size() ) ) // data layout dimension?
+ nDim = nSourceCount; // index of data layout in source data
+ else
+ nDim = aGroups[nDim - nSourceCount].GetSourceDim(); // look at original dimension
+ }
+
+ return pSourceData->IsDateDimension( nDim );
+}
+
+sal_uInt32 ScDPGroupTableData::GetNumberFormat(sal_Int32 nDim)
+{
+ if ( nDim >= nSourceCount )
+ {
+ if ( nDim == sal::static_int_cast<tools::Long>( nSourceCount + aGroups.size() ) ) // data layout dimension?
+ nDim = nSourceCount; // index of data layout in source data
+ else
+ nDim = aGroups[nDim - nSourceCount].GetSourceDim(); // look at original dimension
+ }
+
+ return pSourceData->GetNumberFormat( nDim );
+}
+
+void ScDPGroupTableData::DisposeData()
+{
+ for ( auto& rGroup : aGroups )
+ rGroup.DisposeData();
+
+ for ( tools::Long i=0; i<nSourceCount; i++ )
+ pNumGroups[i].DisposeData();
+
+ pSourceData->DisposeData();
+}
+
+void ScDPGroupTableData::SetEmptyFlags( bool bIgnoreEmptyRows, bool bRepeatIfEmpty )
+{
+ pSourceData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
+}
+
+bool ScDPGroupTableData::IsRepeatIfEmpty()
+{
+ return pSourceData->IsRepeatIfEmpty();
+}
+
+void ScDPGroupTableData::CreateCacheTable()
+{
+ pSourceData->CreateCacheTable();
+}
+
+namespace {
+
+class FindCaseInsensitive
+{
+ ScDPItemData maValue;
+public:
+ explicit FindCaseInsensitive(const ScDPItemData& rVal) : maValue(rVal) {}
+
+ bool operator() (const ScDPItemData& rItem) const
+ {
+ return maValue.IsCaseInsEqual(rItem);
+ }
+};
+
+}
+
+void ScDPGroupTableData::ModifyFilterCriteria(vector<ScDPFilteredCache::Criterion>& rCriteria)
+{
+ // Build dimension ID to object map for group dimensions.
+ typedef std::unordered_map<tools::Long, const ScDPGroupDimension*> GroupFieldMapType;
+ GroupFieldMapType aGroupFieldIds;
+
+ for (const auto& rGroup : aGroups)
+ {
+ aGroupFieldIds.emplace(rGroup.GetGroupDim(), &rGroup);
+ }
+
+ vector<ScDPFilteredCache::Criterion> aNewCriteria;
+ aNewCriteria.reserve(rCriteria.size() + aGroups.size());
+
+ // Go through all the filtered field names and process them appropriately.
+
+ const ScDPCache& rCache = GetCacheTable().getCache();
+ GroupFieldMapType::const_iterator itrGrpEnd = aGroupFieldIds.end();
+ for (const auto& rCriterion : rCriteria)
+ {
+ std::vector<ScDPItemData> aMatchValues = rCriterion.mpFilter->getMatchValues();
+
+ GroupFieldMapType::const_iterator itrGrp = aGroupFieldIds.find(rCriterion.mnFieldIndex);
+ if (itrGrp == itrGrpEnd)
+ {
+ if (IsNumGroupDimension(rCriterion.mnFieldIndex))
+ {
+ // internal number group field
+ const ScDPNumGroupInfo* pNumInfo = rCache.GetNumGroupInfo(rCriterion.mnFieldIndex);
+ if (!pNumInfo)
+ // Number group dimension without num info? Something is wrong...
+ continue;
+
+ ScDPFilteredCache::Criterion aCri;
+ aCri.mnFieldIndex = rCriterion.mnFieldIndex;
+ const ScDPNumGroupDimension& rNumGrpDim = pNumGroups[rCriterion.mnFieldIndex];
+
+ if (rNumGrpDim.IsDateDimension())
+ {
+ // grouped by dates.
+ aCri.mpFilter =
+ std::make_shared<ScDPGroupDateFilter>(
+ std::move(aMatchValues), pDoc->GetFormatTable()->GetNullDate(), *pNumInfo);
+ }
+ else
+ {
+ // This dimension is grouped by numeric ranges.
+ aCri.mpFilter =
+ std::make_shared<ScDPGroupNumFilter>(std::move(aMatchValues), *pNumInfo);
+ }
+
+ aNewCriteria.push_back(aCri);
+ }
+ else
+ {
+ // This is a regular source field.
+ aNewCriteria.push_back(rCriterion);
+ }
+ }
+ else
+ {
+ // This is an ordinary group field or external number group field.
+
+ const ScDPGroupDimension* pGrpDim = itrGrp->second;
+ tools::Long nSrcDim = pGrpDim->GetSourceDim();
+ tools::Long nGrpDim = pGrpDim->GetGroupDim();
+ const ScDPNumGroupInfo* pNumInfo = rCache.GetNumGroupInfo(nGrpDim);
+
+ if (pGrpDim->IsDateDimension() && pNumInfo)
+ {
+ // external number group
+ ScDPFilteredCache::Criterion aCri;
+ aCri.mnFieldIndex = nSrcDim; // use the source dimension, not the group dimension.
+ aCri.mpFilter =
+ std::make_shared<ScDPGroupDateFilter>(
+ std::move(aMatchValues), pDoc->GetFormatTable()->GetNullDate(), *pNumInfo);
+
+ aNewCriteria.push_back(aCri);
+ }
+ else
+ {
+ // normal group
+
+ ScDPFilteredCache::Criterion aCri;
+ aCri.mnFieldIndex = nSrcDim;
+ aCri.mpFilter = std::make_shared<ScDPFilteredCache::GroupFilter>();
+ ScDPFilteredCache::GroupFilter* pGrpFilter =
+ static_cast<ScDPFilteredCache::GroupFilter*>(aCri.mpFilter.get());
+
+ size_t nGroupItemCount = pGrpDim->GetItemCount();
+ for (size_t i = 0; i < nGroupItemCount; ++i)
+ {
+ const ScDPGroupItem* pGrpItem = pGrpDim->GetGroupByIndex(i);
+ if (!pGrpItem)
+ continue;
+
+ // Make sure this group name equals one of the match values.
+ if (std::none_of(aMatchValues.begin(), aMatchValues.end(), FindCaseInsensitive(pGrpItem->GetName())))
+ continue;
+
+ pGrpItem->FillGroupFilter(*pGrpFilter);
+ }
+
+ aNewCriteria.push_back(aCri);
+ }
+ }
+ }
+ rCriteria.swap(aNewCriteria);
+}
+
+void ScDPGroupTableData::FilterCacheTable(std::vector<ScDPFilteredCache::Criterion>&& rCriteria, std::unordered_set<sal_Int32>&& rCatDims)
+{
+ ModifyFilterCriteria(rCriteria);
+ pSourceData->FilterCacheTable(std::move(rCriteria), std::move(rCatDims));
+}
+
+void ScDPGroupTableData::GetDrillDownData(std::vector<ScDPFilteredCache::Criterion>&& rCriteria, std::unordered_set<sal_Int32>&&rCatDims, Sequence< Sequence<Any> >& rData)
+{
+ ModifyFilterCriteria(rCriteria);
+ pSourceData->GetDrillDownData(std::move(rCriteria), std::move(rCatDims), rData);
+}
+
+void ScDPGroupTableData::CalcResults(CalcInfo& rInfo, bool bAutoShow)
+{
+ // #i111435# Inside FillRowDataFromCacheTable/GetItemData, virtual methods
+ // getIsDataLayoutDimension and GetSourceDim are used, so it has to be called
+ // with original rInfo, containing dimension indexes of the grouped data.
+
+ const ScDPFilteredCache& rCacheTable = pSourceData->GetCacheTable();
+ sal_Int32 nRowSize = rCacheTable.getRowSize();
+ for (sal_Int32 nRow = 0; nRow < nRowSize; ++nRow)
+ {
+ sal_Int32 nLastRow;
+ if (!rCacheTable.isRowActive(nRow, &nLastRow))
+ {
+ nRow = nLastRow;
+ continue;
+ }
+
+ CalcRowData aData;
+ FillRowDataFromCacheTable(nRow, rCacheTable, rInfo, aData);
+
+ if ( !rInfo.aColLevelDims.empty() )
+ FillGroupValues(aData.aColData, rInfo.aColLevelDims);
+ if ( !rInfo.aRowLevelDims.empty() )
+ FillGroupValues(aData.aRowData, rInfo.aRowLevelDims);
+ if ( !rInfo.aPageDims.empty() )
+ FillGroupValues(aData.aPageData, rInfo.aPageDims);
+
+ ProcessRowData(rInfo, aData, bAutoShow);
+ }
+}
+
+const ScDPFilteredCache& ScDPGroupTableData::GetCacheTable() const
+{
+ return pSourceData->GetCacheTable();
+}
+
+void ScDPGroupTableData::ReloadCacheTable()
+{
+ pSourceData->ReloadCacheTable();
+}
+
+void ScDPGroupTableData::FillGroupValues(vector<SCROW>& rItems, const vector<sal_Int32>& rDims)
+{
+ sal_Int32 nGroupedColumns = aGroups.size();
+
+ const ScDPCache& rCache = GetCacheTable().getCache();
+ size_t i = 0;
+ for (tools::Long nColumn : rDims)
+ {
+ bool bDateDim = false;
+
+ sal_Int32 nSourceDim = nColumn;
+ if ( nColumn >= nSourceCount && nColumn < nSourceCount + nGroupedColumns )
+ {
+ const ScDPGroupDimension& rGroupDim = aGroups[nColumn - nSourceCount];
+ nSourceDim= rGroupDim.GetSourceDim();
+ bDateDim = rGroupDim.IsDateDimension();
+ if (!bDateDim) // date is handled below
+ {
+ const ScDPItemData& rItem = *GetMemberById(nSourceDim, rItems[i]);
+ const ScDPGroupItem* pGroupItem = rGroupDim.GetGroupForData(rItem);
+ if (pGroupItem)
+ {
+ rItems[i] =
+ rCache.GetIdByItemData(nColumn, pGroupItem->GetName());
+ }
+ else
+ rItems[i] = rCache.GetIdByItemData(nColumn, rItem);
+ }
+ }
+ else if ( IsNumGroupDimension( nColumn ) )
+ {
+ bDateDim = pNumGroups[nColumn].IsDateDimension();
+ if (!bDateDim) // date is handled below
+ {
+ const ScDPItemData* pData = rCache.GetItemDataById(nSourceDim, rItems[i]);
+ if (pData->GetType() == ScDPItemData::Value)
+ {
+ ScDPNumGroupInfo aNumInfo;
+ GetNumGroupInfo(nColumn, aNumInfo);
+ double fGroupValue = ScDPUtil::getNumGroupStartValue(pData->GetValue(), aNumInfo);
+ ScDPItemData aItemData;
+ aItemData.SetRangeStart(fGroupValue);
+ rItems[i] = rCache.GetIdByItemData(nSourceDim, aItemData);
+ }
+ // else (textual) keep original value
+ }
+ }
+
+ const ScDPNumGroupInfo* pNumInfo = rCache.GetNumGroupInfo(nColumn);
+
+ if (bDateDim && pNumInfo)
+ {
+ // This is a date group dimension.
+ sal_Int32 nDatePart = rCache.GetGroupType(nColumn);
+ const ScDPItemData* pData = rCache.GetItemDataById(nSourceDim, rItems[i]);
+ if (pData->GetType() == ScDPItemData::Value)
+ {
+ SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
+ sal_Int32 nPartValue = ScDPUtil::getDatePartValue(
+ pData->GetValue(), pNumInfo, nDatePart, pFormatter);
+
+ ScDPItemData aItem(nDatePart, nPartValue);
+ rItems[i] = rCache.GetIdByItemData(nColumn, aItem);
+ }
+ }
+
+ ++i;
+ }
+}
+
+bool ScDPGroupTableData::IsBaseForGroup(sal_Int32 nDim) const
+{
+ return std::any_of(aGroups.begin(), aGroups.end(),
+ [&nDim](const ScDPGroupDimension& rDim) { return rDim.GetSourceDim() == nDim; });
+}
+
+sal_Int32 ScDPGroupTableData::GetGroupBase(sal_Int32 nGroupDim) const
+{
+ auto aIter = std::find_if(aGroups.begin(), aGroups.end(),
+ [&nGroupDim](const ScDPGroupDimension& rDim) { return rDim.GetGroupDim() == nGroupDim; });
+ if (aIter != aGroups.end())
+ return aIter->GetSourceDim();
+
+ return -1; // none
+}
+
+bool ScDPGroupTableData::IsNumOrDateGroup(sal_Int32 nDimension) const
+{
+ // Virtual method from ScDPTableData, used in result data to force text labels.
+
+ if ( nDimension < nSourceCount )
+ {
+ return pNumGroups[nDimension].GetInfo().mbEnable ||
+ pNumGroups[nDimension].IsDateDimension();
+ }
+
+ auto aIter = std::find_if(aGroups.begin(), aGroups.end(),
+ [&nDimension](const ScDPGroupDimension& rDim) { return rDim.GetGroupDim() == nDimension; });
+ if (aIter != aGroups.end())
+ return aIter->IsDateDimension();
+
+ return false;
+}
+
+bool ScDPGroupTableData::IsInGroup( const ScDPItemData& rGroupData, sal_Int32 nGroupIndex,
+ const ScDPItemData& rBaseData, sal_Int32 nBaseIndex ) const
+{
+ auto aIter = std::find_if(aGroups.begin(), aGroups.end(),
+ [&nGroupIndex, &nBaseIndex](const ScDPGroupDimension& rDim) {
+ return rDim.GetGroupDim() == nGroupIndex && rDim.GetSourceDim() == nBaseIndex; });
+ if (aIter != aGroups.end())
+ {
+ const ScDPGroupDimension& rDim = *aIter;
+ if (rDim.IsDateDimension())
+ {
+ return isDateInGroup(rGroupData, rBaseData);
+ }
+ else
+ {
+ // If the item is in a group, only that group is valid.
+ // If the item is not in any group, its own name is valid.
+
+ const ScDPGroupItem* pGroup = rDim.GetGroupForData( rBaseData );
+ return pGroup ? pGroup->GetName().IsCaseInsEqual( rGroupData ) :
+ rGroupData.IsCaseInsEqual( rBaseData );
+ }
+ }
+
+ OSL_FAIL("IsInGroup: no group dimension found");
+ return true;
+}
+
+bool ScDPGroupTableData::HasCommonElement( const ScDPItemData& rFirstData, sal_Int32 nFirstIndex,
+ const ScDPItemData& rSecondData, sal_Int32 nSecondIndex ) const
+{
+ const ScDPGroupDimension* pFirstDim = nullptr;
+ const ScDPGroupDimension* pSecondDim = nullptr;
+ for ( const auto& rDim : aGroups )
+ {
+ const ScDPGroupDimension* pDim = &rDim;
+ if ( pDim->GetGroupDim() == nFirstIndex )
+ pFirstDim = pDim;
+ else if ( pDim->GetGroupDim() == nSecondIndex )
+ pSecondDim = pDim;
+ }
+ if ( pFirstDim && pSecondDim )
+ {
+ bool bFirstDate = pFirstDim->IsDateDimension();
+ bool bSecondDate = pSecondDim->IsDateDimension();
+ if (bFirstDate || bSecondDate)
+ {
+ // If one is a date group dimension, the other one must be, too.
+ if (!bFirstDate || !bSecondDate)
+ {
+ OSL_FAIL( "mix of date and non-date groups" );
+ return true;
+ }
+
+ return isDateInGroup(rFirstData, rSecondData);
+ }
+
+ const ScDPGroupItem* pFirstItem = pFirstDim->GetGroupForName( rFirstData );
+ const ScDPGroupItem* pSecondItem = pSecondDim->GetGroupForName( rSecondData );
+ if ( pFirstItem && pSecondItem )
+ {
+ // two existing groups -> sal_True if they have a common element
+ return pFirstItem->HasCommonElement( *pSecondItem );
+ }
+ else if ( pFirstItem )
+ {
+ // "automatic" group contains only its own name
+ return pFirstItem->HasElement( rSecondData );
+ }
+ else if ( pSecondItem )
+ {
+ // "automatic" group contains only its own name
+ return pSecondItem->HasElement( rFirstData );
+ }
+ else
+ {
+ // no groups -> sal_True if equal
+ return rFirstData.IsCaseInsEqual( rSecondData );
+ }
+ }
+
+ OSL_FAIL("HasCommonElement: no group dimension found");
+ return true;
+}
+
+sal_Int32 ScDPGroupTableData::GetSourceDim( sal_Int32 nDim )
+{
+ if ( getIsDataLayoutDimension( nDim ) )
+ return nSourceCount;
+ if ( nDim >= nSourceCount && nDim < nSourceCount +static_cast<tools::Long>(aGroups.size()) )
+ {
+ const ScDPGroupDimension& rGroupDim = aGroups[nDim - nSourceCount];
+ return rGroupDim.GetSourceDim();
+ }
+ return nDim;
+}
+
+sal_Int32 ScDPGroupTableData::Compare(sal_Int32 nDim, sal_Int32 nDataId1, sal_Int32 nDataId2)
+{
+ if ( getIsDataLayoutDimension(nDim) )
+ return 0;
+ const ScDPItemData* rItem1 = GetMemberById(nDim, nDataId1);
+ const ScDPItemData* rItem2 = GetMemberById(nDim, nDataId2);
+ if (rItem1 == nullptr || rItem2 == nullptr)
+ return 0;
+ return ScDPItemData::Compare( *rItem1,*rItem2);
+}
+
+#if DUMP_PIVOT_TABLE
+
+void ScDPGroupTableData::Dump() const
+{
+ cout << "--- ScDPGroupTableData" << endl;
+ for (tools::Long i = 0; i < nSourceCount; ++i)
+ {
+ cout << "* dimension: " << i << endl;
+ const ScDPNumGroupDimension& rGrp = pNumGroups[i];
+ rGrp.GetInfo().Dump();
+ }
+ cout << "---" << endl;
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */