/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 class ScDPGroupNumFilter : public ScDPFilteredCache::FilterBase { public: ScDPGroupNumFilter(const std::vector& rValues, const ScDPNumGroupInfo& rInfo); virtual bool match(const ScDPItemData &rCellData) const override; virtual std::vector getMatchValues() const override; private: std::vector maValues; ScDPNumGroupInfo maNumInfo; }; ScDPGroupNumFilter::ScDPGroupNumFilter(const std::vector& rValues, const ScDPNumGroupInfo& rInfo) : maValues(rValues), maNumInfo(rInfo) {} bool ScDPGroupNumFilter::match(const ScDPItemData& rCellData) const { if (rCellData.GetType() != ScDPItemData::Value) return false; std::vector::const_iterator it = maValues.begin(), itEnd = maValues.end(); for (; it != itEnd; ++it) { double fVal = it->GetValue(); if (rtl::math::isInf(fVal)) { if (rtl::math::isSignBitSet(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 ScDPGroupNumFilter::getMatchValues() const { return std::vector(); } class ScDPGroupDateFilter : public ScDPFilteredCache::FilterBase { public: ScDPGroupDateFilter( const std::vector& rValues, const Date& rNullDate, const ScDPNumGroupInfo& rNumInfo); virtual bool match(const ScDPItemData & rCellData) const override; virtual std::vector getMatchValues() const override; private: std::vector maValues; Date maNullDate; ScDPNumGroupInfo maNumInfo; }; ScDPGroupDateFilter::ScDPGroupDateFilter( const std::vector& rValues, const Date& rNullDate, const ScDPNumGroupInfo& rNumInfo) : maValues(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; std::vector::const_iterator it = maValues.begin(), itEnd = maValues.end(); for (; it != itEnd; ++it) { const ScDPItemData& rValue = *it; 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(approxFloor(rCellData.GetValue())); switch (nGroupType) { case DataPilotFieldGroupBy::YEARS: { sal_Int32 year = static_cast(date.GetYear()); if (year == nValue) return true; } break; case DataPilotFieldGroupBy::QUARTERS: { sal_Int32 qtr = 1 + (static_cast(date.GetMonth()) - 1) / 3; if (qtr == nValue) return true; } break; case DataPilotFieldGroupBy::MONTHS: { sal_Int32 month = static_cast(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 ScDPGroupDateFilter::getMatchValues() const { return std::vector(); } 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 ) { } ScDPGroupItem::~ScDPGroupItem() { } void ScDPGroupItem::AddElement( const ScDPItemData& rName ) { aElements.push_back( rName ); } bool ScDPGroupItem::HasElement( const ScDPItemData& rData ) const { for ( ScDPItemDataVec::const_iterator aIter(aElements.begin()); aIter != aElements.end(); ++aIter ) if ( aIter->IsCaseInsEqual( rData ) ) return true; return false; } bool ScDPGroupItem::HasCommonElement( const ScDPGroupItem& rOther ) const { for ( ScDPItemDataVec::const_iterator aIter(aElements.begin()); aIter != aElements.end(); ++aIter ) if ( rOther.HasElement( *aIter ) ) return true; return false; } void ScDPGroupItem::FillGroupFilter( ScDPFilteredCache::GroupFilter& rFilter ) const { ScDPItemDataVec::const_iterator itrEnd = aElements.end(); for (ScDPItemDataVec::const_iterator itr = aElements.begin(); itr != itrEnd; ++itr) rFilter.addMatchItem(*itr); } ScDPGroupDimension::ScDPGroupDimension( 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( long nDim ) { nGroupDim = nDim; } const std::vector& 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 { for (ScDPGroupItemVec::const_iterator aIter = aItems.begin(); aIter != aItems.end(); ++aIter) if (aIter->HasElement(rData)) return &*aIter; return nullptr; } const ScDPGroupItem* ScDPGroupDimension::GetGroupForName( const ScDPItemData& rName ) const { for ( ScDPGroupItemVec::const_iterator aIter(aItems.begin()); aIter != aItems.end(); ++aIter ) if ( aIter->GetName().IsCaseInsEqual( rName ) ) 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& ScDPNumGroupDimension::GetNumEntries( SCCOL nSourceDim, const ScDPCache* pCache) const { if (!maMemberEntries.empty()) return maMemberEntries; pCache->GetGroupDimMemberIds(nSourceDim, maMemberEntries); return maMemberEntries; } ScDPGroupTableData::ScDPGroupTableData( const shared_ptr& 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 ); aGroupNames.insert(aNewGroup.GetName()); } void ScDPGroupTableData::SetNumGroupDimension( long nIndex, const ScDPNumGroupDimension& rGroup ) { if ( nIndex < nSourceCount ) { pNumGroups[nIndex] = rGroup; // automatic minimum / maximum is handled in GetNumEntries } } long ScDPGroupTableData::GetDimensionIndex( const OUString& rName ) { for (long i = 0; i < nSourceCount; ++i) // nSourceCount excludes data layout if (pSourceData->getDimensionName(i) == rName) //TODO: ignore case? return i; return -1; // none } long ScDPGroupTableData::GetColumnCount() { return nSourceCount + aGroups.size(); } bool ScDPGroupTableData::IsNumGroupDimension( long nDimension ) const { return ( nDimension < nSourceCount && pNumGroups[nDimension].GetInfo().mbEnable ); } void ScDPGroupTableData::GetNumGroupInfo(long nDimension, ScDPNumGroupInfo& rInfo) { if ( nDimension < nSourceCount ) rInfo = pNumGroups[nDimension].GetInfo(); } long ScDPGroupTableData::GetMembersCount( long nDim ) { const std::vector< SCROW >& members = GetColumnEntries( nDim ); return members.size(); } const std::vector< SCROW >& ScDPGroupTableData::GetColumnEntries( long 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(nColumn), &GetCacheTable().getCache()); } return pSourceData->GetColumnEntries( nColumn ); } const ScDPItemData* ScDPGroupTableData::GetMemberById( long nDim, long nId ) { return pSourceData->GetMemberById( nDim, nId ); } OUString ScDPGroupTableData::getDimensionName(long nColumn) { if ( nColumn >= nSourceCount ) { if ( nColumn == sal::static_int_cast( 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(long nColumn) { // position of data layout dimension is moved from source data return ( nColumn == sal::static_int_cast( nSourceCount + aGroups.size() ) ); // data layout dimension? } bool ScDPGroupTableData::IsDateDimension(long nDim) { if ( nDim >= nSourceCount ) { if ( nDim == sal::static_int_cast( 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(long nDim) { if ( nDim >= nSourceCount ) { if ( nDim == sal::static_int_cast( 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 ( ScDPGroupDimensionVec::iterator aIter(aGroups.begin()); aIter != aGroups.end(); ++aIter ) aIter->DisposeData(); for ( long i=0; iDisposeData(); } 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& rCriteria) { // Build dimension ID to object map for group dimensions. typedef std::unordered_map GroupFieldMapType; GroupFieldMapType aGroupFieldIds; { ScDPGroupDimensionVec::const_iterator itr = aGroups.begin(), itrEnd = aGroups.end(); for (; itr != itrEnd; ++itr) { aGroupFieldIds.emplace(itr->GetGroupDim(), &(*itr)); } } vector aNewCriteria; aNewCriteria.reserve(rCriteria.size() + aGroups.size()); // Go through all the filtered field names and process them appropriately. const ScDPCache& rCache = GetCacheTable().getCache(); vector::const_iterator itrEnd = rCriteria.end(); GroupFieldMapType::const_iterator itrGrpEnd = aGroupFieldIds.end(); for (vector::const_iterator itr = rCriteria.begin(); itr != itrEnd; ++itr) { std::vector aMatchValues = itr->mpFilter->getMatchValues(); GroupFieldMapType::const_iterator itrGrp = aGroupFieldIds.find(itr->mnFieldIndex); if (itrGrp == itrGrpEnd) { if (IsNumGroupDimension(itr->mnFieldIndex)) { // internal number group field const ScDPNumGroupInfo* pNumInfo = rCache.GetNumGroupInfo(itr->mnFieldIndex); if (!pNumInfo) // Number group dimension without num info? Something is wrong... continue; ScDPFilteredCache::Criterion aCri; aCri.mnFieldIndex = itr->mnFieldIndex; const ScDPNumGroupDimension& rNumGrpDim = pNumGroups[itr->mnFieldIndex]; if (rNumGrpDim.IsDateDimension()) { // grouped by dates. aCri.mpFilter.reset( new ScDPGroupDateFilter( aMatchValues, pDoc->GetFormatTable()->GetNullDate(), *pNumInfo)); } else { // This dimension is grouped by numeric ranges. aCri.mpFilter.reset( new ScDPGroupNumFilter(aMatchValues, *pNumInfo)); } aNewCriteria.push_back(aCri); } else { // This is a regular source field. aNewCriteria.push_back(*itr); } } else { // This is an ordinary group field or external number group field. const ScDPGroupDimension* pGrpDim = itrGrp->second; long nSrcDim = pGrpDim->GetSourceDim(); 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.reset( new ScDPGroupDateFilter( aMatchValues, pDoc->GetFormatTable()->GetNullDate(), *pNumInfo)); aNewCriteria.push_back(aCri); } else { // normal group ScDPFilteredCache::Criterion aCri; aCri.mnFieldIndex = nSrcDim; aCri.mpFilter.reset(new ScDPFilteredCache::GroupFilter()); ScDPFilteredCache::GroupFilter* pGrpFilter = static_cast(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(const vector& rCriteria, const std::unordered_set& rCatDims) { vector aNewCriteria(rCriteria); ModifyFilterCriteria(aNewCriteria); pSourceData->FilterCacheTable(aNewCriteria, rCatDims); } void ScDPGroupTableData::GetDrillDownData(const vector& rCriteria, const std::unordered_set& rCatDims, Sequence< Sequence >& rData) { vector aNewCriteria(rCriteria); ModifyFilterCriteria(aNewCriteria); pSourceData->GetDrillDownData(aNewCriteria, 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& rItems, const vector& rDims) { long nGroupedColumns = aGroups.size(); const ScDPCache& rCache = GetCacheTable().getCache(); vector::const_iterator it = rDims.begin(), itEnd = rDims.end(); for (size_t i = 0; it != itEnd; ++it, ++i) { long nColumn = *it; bool bDateDim = false; long 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); } } } } bool ScDPGroupTableData::IsBaseForGroup(long nDim) const { for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); ++aIter ) { const ScDPGroupDimension& rDim = *aIter; if ( rDim.GetSourceDim() == nDim ) return true; } return false; } long ScDPGroupTableData::GetGroupBase(long nGroupDim) const { for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); ++aIter ) { const ScDPGroupDimension& rDim = *aIter; if ( rDim.GetGroupDim() == nGroupDim ) return rDim.GetSourceDim(); } return -1; // none } bool ScDPGroupTableData::IsNumOrDateGroup(long 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(); } for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); ++aIter ) { const ScDPGroupDimension& rDim = *aIter; if ( rDim.GetGroupDim() == nDimension ) return rDim.IsDateDimension(); } return false; } bool ScDPGroupTableData::IsInGroup( const ScDPItemData& rGroupData, long nGroupIndex, const ScDPItemData& rBaseData, long nBaseIndex ) const { for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); ++aIter ) { const ScDPGroupDimension& rDim = *aIter; if ( rDim.GetGroupDim() == nGroupIndex && rDim.GetSourceDim() == nBaseIndex ) { 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, long nFirstIndex, const ScDPItemData& rSecondData, long nSecondIndex ) const { const ScDPGroupDimension* pFirstDim = nullptr; const ScDPGroupDimension* pSecondDim = nullptr; for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); ++aIter ) { const ScDPGroupDimension* pDim = &(*aIter); 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; } long ScDPGroupTableData::GetSourceDim( long nDim ) { if ( getIsDataLayoutDimension( nDim ) ) return nSourceCount; if ( nDim >= nSourceCount && nDim < nSourceCount +static_cast(aGroups.size()) ) { const ScDPGroupDimension& rGroupDim = aGroups[nDim - nSourceCount]; return rGroupDim.GetSourceDim(); } return nDim; } long ScDPGroupTableData::Compare(long nDim, long nDataId1, long nDataId2) { if ( getIsDataLayoutDimension(nDim) ) return 0; return ScDPItemData::Compare( *GetMemberById(nDim, nDataId1),*GetMemberById(nDim, nDataId2) ); } #if DUMP_PIVOT_TABLE void ScDPGroupTableData::Dump() const { cout << "--- ScDPGroupTableData" << endl; for (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: */