diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sc/source/core/data/dpgroup.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sc/source/core/data/dpgroup.cxx')
-rw-r--r-- | sc/source/core/data/dpgroup.cxx | 1030 |
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: */ |