LibreOffice Module sc (master) 1
dpgroup.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <dpgroup.hxx>
21
22#include <dpcache.hxx>
23#include <document.hxx>
24#include <dpfilteredcache.hxx>
25#include <dputil.hxx>
26
27#include <osl/diagnose.h>
28#include <rtl/math.hxx>
29#include <svl/numformat.hxx>
30
31#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
32
33#include <unordered_map>
34#include <unordered_set>
35#include <utility>
36#include <vector>
37#include <algorithm>
38
39using namespace ::com::sun::star;
40using ::com::sun::star::uno::Any;
41using ::com::sun::star::uno::Sequence;
42
43using ::std::vector;
44using ::std::shared_ptr;
45
46const sal_uInt16 SC_DP_LEAPYEAR = 1648; // arbitrary leap year for date calculations
47
48namespace {
49
50class ScDPGroupNumFilter : public ScDPFilteredCache::FilterBase
51{
52public:
53 ScDPGroupNumFilter(std::vector<ScDPItemData>&& rValues, const ScDPNumGroupInfo& rInfo);
54
55 virtual bool match(const ScDPItemData &rCellData) const override;
56 virtual std::vector<ScDPItemData> getMatchValues() const override;
57private:
58 std::vector<ScDPItemData> maValues;
59 ScDPNumGroupInfo maNumInfo;
60};
61
62}
63
64ScDPGroupNumFilter::ScDPGroupNumFilter( std::vector<ScDPItemData>&& rValues, const ScDPNumGroupInfo& rInfo) :
65 maValues(std::move(rValues)), maNumInfo(rInfo) {}
66
67bool ScDPGroupNumFilter::match(const ScDPItemData& rCellData) const
68{
69 if (rCellData.GetType() != ScDPItemData::Value)
70 return false;
71
72 for (const auto& rValue : maValues)
73 {
74 double fVal = rValue.GetValue();
75 if (std::isinf(fVal))
76 {
77 if (std::signbit(fVal))
78 {
79 // Less than the min value.
80 if (rCellData.GetValue() < maNumInfo.mfStart)
81 return true;
82 }
83
84 // Greater than the max value.
85 if (maNumInfo.mfEnd < rCellData.GetValue())
86 return true;
87
88 continue;
89 }
90
91 double low = fVal;
92 double high = low + maNumInfo.mfStep;
93 if (maNumInfo.mbIntegerOnly)
94 high += 1.0;
95
96 if (low <= rCellData.GetValue() && rCellData.GetValue() < high)
97 return true;
98 }
99
100 return false;
101}
102
103std::vector<ScDPItemData> ScDPGroupNumFilter::getMatchValues() const
104{
105 return std::vector<ScDPItemData>();
106}
107
108namespace {
109
110class ScDPGroupDateFilter : public ScDPFilteredCache::FilterBase
111{
112public:
113 ScDPGroupDateFilter(
114 std::vector<ScDPItemData>&& rValues, const Date& rNullDate, const ScDPNumGroupInfo& rNumInfo);
115
116 virtual bool match(const ScDPItemData & rCellData) const override;
117 virtual std::vector<ScDPItemData> getMatchValues() const override;
118
119private:
120 std::vector<ScDPItemData> maValues;
121 Date maNullDate;
122 ScDPNumGroupInfo maNumInfo;
123};
124
125}
126
127ScDPGroupDateFilter::ScDPGroupDateFilter(
128 std::vector<ScDPItemData>&& rValues, const Date& rNullDate, const ScDPNumGroupInfo& rNumInfo) :
129 maValues(std::move(rValues)),
130 maNullDate(rNullDate),
131 maNumInfo(rNumInfo)
132{
133}
134
135bool ScDPGroupDateFilter::match( const ScDPItemData & rCellData ) const
136{
137 using namespace ::com::sun::star::sheet;
138 using ::rtl::math::approxFloor;
139 using ::rtl::math::approxEqual;
140
141 if ( !rCellData.IsValue() )
142 return false;
143
144 for (const ScDPItemData& rValue : maValues)
145 {
146 if (rValue.GetType() != ScDPItemData::GroupValue)
147 continue;
148
149 sal_Int32 nGroupType = rValue.GetGroupValue().mnGroupType;
150 sal_Int32 nValue = rValue.GetGroupValue().mnValue;
151
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.)
154
155 if (rCellData.GetValue() < maNumInfo.mfStart && !approxEqual(rCellData.GetValue(), maNumInfo.mfStart))
156 {
157 if (nValue == ScDPItemData::DateFirst)
158 return true;
159 continue;
160 }
161
162 if (rCellData.GetValue() > maNumInfo.mfEnd && !approxEqual(rCellData.GetValue(), maNumInfo.mfEnd))
163 {
164 if (nValue == ScDPItemData::DateLast)
165 return true;
166 continue;
167 }
168
169 if (nGroupType == DataPilotFieldGroupBy::HOURS || nGroupType == DataPilotFieldGroupBy::MINUTES ||
170 nGroupType == DataPilotFieldGroupBy::SECONDS)
171 {
172 // handle time
173 // (do as in the cell functions, ScInterpreter::ScGetHour() etc.)
174
175 sal_uInt16 nHour, nMinute, nSecond;
176 double fFractionOfSecond;
177 tools::Time::GetClock( rCellData.GetValue(), nHour, nMinute, nSecond, fFractionOfSecond, 0);
178
179 switch (nGroupType)
180 {
181 case DataPilotFieldGroupBy::HOURS:
182 {
183 if (nHour == nValue)
184 return true;
185 }
186 break;
187 case DataPilotFieldGroupBy::MINUTES:
188 {
189 if (nMinute == nValue)
190 return true;
191 }
192 break;
193 case DataPilotFieldGroupBy::SECONDS:
194 {
195 if (nSecond == nValue)
196 return true;
197 }
198 break;
199 default:
200 OSL_FAIL("invalid time part");
201 }
202
203 continue;
204 }
205
206 Date date = maNullDate + static_cast<sal_Int32>(approxFloor(rCellData.GetValue()));
207 switch (nGroupType)
208 {
209 case DataPilotFieldGroupBy::YEARS:
210 {
211 sal_Int32 year = static_cast<sal_Int32>(date.GetYear());
212 if (year == nValue)
213 return true;
214 }
215 break;
216 case DataPilotFieldGroupBy::QUARTERS:
217 {
218 sal_Int32 qtr = 1 + (static_cast<sal_Int32>(date.GetMonth()) - 1) / 3;
219 if (qtr == nValue)
220 return true;
221 }
222 break;
223 case DataPilotFieldGroupBy::MONTHS:
224 {
225 sal_Int32 month = static_cast<sal_Int32>(date.GetMonth());
226 if (month == nValue)
227 return true;
228 }
229 break;
230 case DataPilotFieldGroupBy::DAYS:
231 {
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())
235 {
236 // This is not a leap year. Adjust the value accordingly.
237 ++days;
238 }
239 if (days == nValue)
240 return true;
241 }
242 break;
243 default:
244 OSL_FAIL("invalid date part");
245 }
246 }
247
248 return false;
249}
250
251std::vector<ScDPItemData> ScDPGroupDateFilter::getMatchValues() const
252{
253 return std::vector<ScDPItemData>();
254}
255
256namespace {
257
258bool isDateInGroup(const ScDPItemData& rGroupItem, const ScDPItemData& rChildItem)
259{
260 if (rGroupItem.GetType() != ScDPItemData::GroupValue || rChildItem.GetType() != ScDPItemData::GroupValue)
261 return false;
262
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;
267
268 if (nGroupValue == ScDPItemData::DateFirst || nGroupValue == ScDPItemData::DateLast ||
269 nChildValue == ScDPItemData::DateFirst || nChildValue == ScDPItemData::DateLast)
270 {
271 // first/last entry matches only itself
272 return nGroupValue == nChildValue;
273 }
274
275 switch (nChildPart) // inner part
276 {
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)
287 {
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
293
294 return nGroupValue == nCompare;
295 }
296 break;
297 default:
298 ;
299 }
300
301 return true;
302}
303
304}
305
307 aGroupName( rName )
308{
309}
310
312{
313 aElements.push_back( rName );
314}
315
316bool ScDPGroupItem::HasElement( const ScDPItemData& rData ) const
317{
318 return std::any_of(aElements.begin(), aElements.end(),
319 [&rData](const ScDPItemData& rElement) { return rElement.IsCaseInsEqual(rData); });
320}
321
323{
324 return std::any_of(aElements.begin(), aElements.end(),
325 [&rOther](const ScDPItemData& rElement) { return rOther.HasElement(rElement); });
326}
327
329{
330 for (const auto& rElement : aElements)
331 rFilter.addMatchItem(rElement);
332}
333
335 nSourceDim( nSource ),
336 nGroupDim( -1 ),
337 aGroupName(std::move( aNewName )),
338 mbDateDimension(false)
339{
340}
341
343{
344 maMemberEntries.clear();
345}
346
348 nSourceDim( rOther.nSourceDim ),
349 nGroupDim( rOther.nGroupDim ),
350 aGroupName( rOther.aGroupName ),
351 aItems( rOther.aItems ),
352 mbDateDimension(rOther.mbDateDimension)
353{
354}
355
357{
358 nSourceDim = rOther.nSourceDim;
359 nGroupDim = rOther.nGroupDim;
360 aGroupName = rOther.aGroupName;
361 aItems = rOther.aItems;
363 return *this;
364}
365
367{
368 aItems.push_back( rItem );
369}
370
372{
373 nGroupDim = nDim;
374}
375
376const std::vector<SCROW>& ScDPGroupDimension::GetColumnEntries(
377 const ScDPFilteredCache& rCacheTable) const
378{
379 if (!maMemberEntries.empty())
380 return maMemberEntries;
381
383 return maMemberEntries;
384}
385
387{
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;
392
393 return nullptr;
394}
395
397{
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;
402
403 return nullptr;
404}
405
407{
408 if (nIndex >= aItems.size())
409 return nullptr;
410
411 return &aItems[nIndex];
412}
413
415{
416 maMemberEntries.clear();
417}
418
420{
421 mbDateDimension = true;
422}
423
425
427 aGroupInfo(rInfo), mbDateDimension(false) {}
428
430 aGroupInfo(rOther.aGroupInfo), mbDateDimension(rOther.mbDateDimension) {}
431
433{
434 aGroupInfo = rOther.aGroupInfo;
436 return *this;
437}
438
440{
442 maMemberEntries.clear();
443}
444
446{
447}
448
450{
451 aGroupInfo.mbEnable = true; //TODO: or query both?
452 mbDateDimension = true;
453}
454
455const std::vector<SCROW>& ScDPNumGroupDimension::GetNumEntries(
456 SCCOL nSourceDim, const ScDPCache* pCache) const
457{
458 if (!maMemberEntries.empty())
459 return maMemberEntries;
460
461 pCache->GetGroupDimMemberIds(nSourceDim, maMemberEntries);
462 return maMemberEntries;
463}
464
465ScDPGroupTableData::ScDPGroupTableData( const shared_ptr<ScDPTableData>& pSource, ScDocument* pDocument ) :
466 ScDPTableData(pDocument),
467 pSourceData( pSource ),
468 pDoc( pDocument )
469{
470 OSL_ENSURE( pSource, "ScDPGroupTableData: pSource can't be NULL" );
471
473 nSourceCount = pSource->GetColumnCount(); // real columns, excluding data layout
475}
476
478{
479}
480
482{
483 ScDPGroupDimension aNewGroup( rGroup );
484 aNewGroup.SetGroupDim( GetColumnCount() ); // new dimension will be at the end
485 aGroups.push_back( aNewGroup );
486}
487
489{
490 if ( nIndex < nSourceCount )
491 {
492 pNumGroups[nIndex] = rGroup;
493
494 // automatic minimum / maximum is handled in GetNumEntries
495 }
496}
497
498sal_Int32 ScDPGroupTableData::GetDimensionIndex( std::u16string_view rName )
499{
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
504}
505
507{
508 return nSourceCount + aGroups.size();
509}
510
512{
513 return ( nDimension < nSourceCount && pNumGroups[nDimension].GetInfo().mbEnable );
514}
515
517{
518 if ( nDimension < nSourceCount )
519 rInfo = pNumGroups[nDimension].GetInfo();
520}
521sal_Int32 ScDPGroupTableData::GetMembersCount( sal_Int32 nDim )
522{
523 const std::vector< SCROW >& members = GetColumnEntries( nDim );
524 return members.size();
525}
526const std::vector< SCROW >& ScDPGroupTableData::GetColumnEntries( sal_Int32 nColumn )
527{
528 if ( nColumn >= nSourceCount )
529 {
530 if ( getIsDataLayoutDimension( nColumn) ) // data layout dimension?
531 nColumn = nSourceCount; // index of data layout in source data
532 else
533 {
534 const ScDPGroupDimension& rGroupDim = aGroups[nColumn - nSourceCount];
535 return rGroupDim.GetColumnEntries( GetCacheTable() );
536 }
537 }
538
539 if ( IsNumGroupDimension( nColumn ) )
540 {
541 // dimension number is unchanged for numerical groups
542 return pNumGroups[nColumn].GetNumEntries(
543 static_cast<SCCOL>(nColumn), &GetCacheTable().getCache());
544 }
545
546 return pSourceData->GetColumnEntries( nColumn );
547}
548
549const ScDPItemData* ScDPGroupTableData::GetMemberById( sal_Int32 nDim, sal_Int32 nId )
550{
551 return pSourceData->GetMemberById( nDim, nId );
552}
553
554OUString ScDPGroupTableData::getDimensionName(sal_Int32 nColumn)
555{
556 if ( nColumn >= nSourceCount )
557 {
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();
562 }
563
564 return pSourceData->getDimensionName( nColumn );
565}
566
568{
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?
571}
572
574{
575 if ( nDim >= nSourceCount )
576 {
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
581 }
582
583 return pSourceData->IsDateDimension( nDim );
584}
585
586sal_uInt32 ScDPGroupTableData::GetNumberFormat(sal_Int32 nDim)
587{
588 if ( nDim >= nSourceCount )
589 {
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
594 }
595
596 return pSourceData->GetNumberFormat( nDim );
597}
598
600{
601 for ( auto& rGroup : aGroups )
602 rGroup.DisposeData();
603
604 for ( tools::Long i=0; i<nSourceCount; i++ )
606
607 pSourceData->DisposeData();
608}
609
610void ScDPGroupTableData::SetEmptyFlags( bool bIgnoreEmptyRows, bool bRepeatIfEmpty )
611{
612 pSourceData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
613}
614
616{
617 return pSourceData->IsRepeatIfEmpty();
618}
619
621{
622 pSourceData->CreateCacheTable();
623}
624
625namespace {
626
627class FindCaseInsensitive
628{
630public:
631 explicit FindCaseInsensitive(const ScDPItemData& rVal) : maValue(rVal) {}
632
633 bool operator() (const ScDPItemData& rItem) const
634 {
635 return maValue.IsCaseInsEqual(rItem);
636 }
637};
638
639}
640
641void ScDPGroupTableData::ModifyFilterCriteria(vector<ScDPFilteredCache::Criterion>& rCriteria)
642{
643 // Build dimension ID to object map for group dimensions.
644 typedef std::unordered_map<tools::Long, const ScDPGroupDimension*> GroupFieldMapType;
645 GroupFieldMapType aGroupFieldIds;
646
647 for (const auto& rGroup : aGroups)
648 {
649 aGroupFieldIds.emplace(rGroup.GetGroupDim(), &rGroup);
650 }
651
652 vector<ScDPFilteredCache::Criterion> aNewCriteria;
653 aNewCriteria.reserve(rCriteria.size() + aGroups.size());
654
655 // Go through all the filtered field names and process them appropriately.
656
657 const ScDPCache& rCache = GetCacheTable().getCache();
658 GroupFieldMapType::const_iterator itrGrpEnd = aGroupFieldIds.end();
659 for (const auto& rCriterion : rCriteria)
660 {
661 std::vector<ScDPItemData> aMatchValues = rCriterion.mpFilter->getMatchValues();
662
663 GroupFieldMapType::const_iterator itrGrp = aGroupFieldIds.find(rCriterion.mnFieldIndex);
664 if (itrGrp == itrGrpEnd)
665 {
666 if (IsNumGroupDimension(rCriterion.mnFieldIndex))
667 {
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;
673
675 aCri.mnFieldIndex = rCriterion.mnFieldIndex;
676 const ScDPNumGroupDimension& rNumGrpDim = pNumGroups[rCriterion.mnFieldIndex];
677
678 if (rNumGrpDim.IsDateDimension())
679 {
680 // grouped by dates.
681 aCri.mpFilter =
682 std::make_shared<ScDPGroupDateFilter>(
683 std::move(aMatchValues), pDoc->GetFormatTable()->GetNullDate(), *pNumInfo);
684 }
685 else
686 {
687 // This dimension is grouped by numeric ranges.
688 aCri.mpFilter =
689 std::make_shared<ScDPGroupNumFilter>(std::move(aMatchValues), *pNumInfo);
690 }
691
692 aNewCriteria.push_back(aCri);
693 }
694 else
695 {
696 // This is a regular source field.
697 aNewCriteria.push_back(rCriterion);
698 }
699 }
700 else
701 {
702 // This is an ordinary group field or external number group field.
703
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);
708
709 if (pGrpDim->IsDateDimension() && pNumInfo)
710 {
711 // external number group
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);
717
718 aNewCriteria.push_back(aCri);
719 }
720 else
721 {
722 // normal group
723
725 aCri.mnFieldIndex = nSrcDim;
726 aCri.mpFilter = std::make_shared<ScDPFilteredCache::GroupFilter>();
728 static_cast<ScDPFilteredCache::GroupFilter*>(aCri.mpFilter.get());
729
730 size_t nGroupItemCount = pGrpDim->GetItemCount();
731 for (size_t i = 0; i < nGroupItemCount; ++i)
732 {
733 const ScDPGroupItem* pGrpItem = pGrpDim->GetGroupByIndex(i);
734 if (!pGrpItem)
735 continue;
736
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;
740
741 pGrpItem->FillGroupFilter(*pGrpFilter);
742 }
743
744 aNewCriteria.push_back(aCri);
745 }
746 }
747 }
748 rCriteria.swap(aNewCriteria);
749}
750
751void ScDPGroupTableData::FilterCacheTable(std::vector<ScDPFilteredCache::Criterion>&& rCriteria, std::unordered_set<sal_Int32>&& rCatDims)
752{
753 ModifyFilterCriteria(rCriteria);
754 pSourceData->FilterCacheTable(std::move(rCriteria), std::move(rCatDims));
755}
756
757void ScDPGroupTableData::GetDrillDownData(std::vector<ScDPFilteredCache::Criterion>&& rCriteria, std::unordered_set<sal_Int32>&&rCatDims, Sequence< Sequence<Any> >& rData)
758{
759 ModifyFilterCriteria(rCriteria);
760 pSourceData->GetDrillDownData(std::move(rCriteria), std::move(rCatDims), rData);
761}
762
763void ScDPGroupTableData::CalcResults(CalcInfo& rInfo, bool bAutoShow)
764{
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.
768
769 const ScDPFilteredCache& rCacheTable = pSourceData->GetCacheTable();
770 sal_Int32 nRowSize = rCacheTable.getRowSize();
771 for (sal_Int32 nRow = 0; nRow < nRowSize; ++nRow)
772 {
773 sal_Int32 nLastRow;
774 if (!rCacheTable.isRowActive(nRow, &nLastRow))
775 {
776 nRow = nLastRow;
777 continue;
778 }
779
781 FillRowDataFromCacheTable(nRow, rCacheTable, rInfo, aData);
782
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);
789
790 ProcessRowData(rInfo, aData, bAutoShow);
791 }
792}
793
795{
796 return pSourceData->GetCacheTable();
797}
798
800{
801 pSourceData->ReloadCacheTable();
802}
803
804void ScDPGroupTableData::FillGroupValues(vector<SCROW>& rItems, const vector<sal_Int32>& rDims)
805{
806 sal_Int32 nGroupedColumns = aGroups.size();
807
808 const ScDPCache& rCache = GetCacheTable().getCache();
809 size_t i = 0;
810 for (tools::Long nColumn : rDims)
811 {
812 bool bDateDim = false;
813
814 sal_Int32 nSourceDim = nColumn;
815 if ( nColumn >= nSourceCount && nColumn < nSourceCount + nGroupedColumns )
816 {
817 const ScDPGroupDimension& rGroupDim = aGroups[nColumn - nSourceCount];
818 nSourceDim= rGroupDim.GetSourceDim();
819 bDateDim = rGroupDim.IsDateDimension();
820 if (!bDateDim) // date is handled below
821 {
822 const ScDPItemData& rItem = *GetMemberById(nSourceDim, rItems[i]);
823 const ScDPGroupItem* pGroupItem = rGroupDim.GetGroupForData(rItem);
824 if (pGroupItem)
825 {
826 rItems[i] =
827 rCache.GetIdByItemData(nColumn, pGroupItem->GetName());
828 }
829 else
830 rItems[i] = rCache.GetIdByItemData(nColumn, rItem);
831 }
832 }
833 else if ( IsNumGroupDimension( nColumn ) )
834 {
835 bDateDim = pNumGroups[nColumn].IsDateDimension();
836 if (!bDateDim) // date is handled below
837 {
838 const ScDPItemData* pData = rCache.GetItemDataById(nSourceDim, rItems[i]);
839 if (pData->GetType() == ScDPItemData::Value)
840 {
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);
847 }
848 // else (textual) keep original value
849 }
850 }
851
852 const ScDPNumGroupInfo* pNumInfo = rCache.GetNumGroupInfo(nColumn);
853
854 if (bDateDim && pNumInfo)
855 {
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)
860 {
861 SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
862 sal_Int32 nPartValue = ScDPUtil::getDatePartValue(
863 pData->GetValue(), pNumInfo, nDatePart, pFormatter);
864
865 ScDPItemData aItem(nDatePart, nPartValue);
866 rItems[i] = rCache.GetIdByItemData(nColumn, aItem);
867 }
868 }
869
870 ++i;
871 }
872}
873
874bool ScDPGroupTableData::IsBaseForGroup(sal_Int32 nDim) const
875{
876 return std::any_of(aGroups.begin(), aGroups.end(),
877 [&nDim](const ScDPGroupDimension& rDim) { return rDim.GetSourceDim() == nDim; });
878}
879
880sal_Int32 ScDPGroupTableData::GetGroupBase(sal_Int32 nGroupDim) const
881{
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();
886
887 return -1; // none
888}
889
890bool ScDPGroupTableData::IsNumOrDateGroup(sal_Int32 nDimension) const
891{
892 // Virtual method from ScDPTableData, used in result data to force text labels.
893
894 if ( nDimension < nSourceCount )
895 {
896 return pNumGroups[nDimension].GetInfo().mbEnable ||
897 pNumGroups[nDimension].IsDateDimension();
898 }
899
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();
904
905 return false;
906}
907
908bool ScDPGroupTableData::IsInGroup( const ScDPItemData& rGroupData, sal_Int32 nGroupIndex,
909 const ScDPItemData& rBaseData, sal_Int32 nBaseIndex ) const
910{
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())
915 {
916 const ScDPGroupDimension& rDim = *aIter;
917 if (rDim.IsDateDimension())
918 {
919 return isDateInGroup(rGroupData, rBaseData);
920 }
921 else
922 {
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.
925
926 const ScDPGroupItem* pGroup = rDim.GetGroupForData( rBaseData );
927 return pGroup ? pGroup->GetName().IsCaseInsEqual( rGroupData ) :
928 rGroupData.IsCaseInsEqual( rBaseData );
929 }
930 }
931
932 OSL_FAIL("IsInGroup: no group dimension found");
933 return true;
934}
935
936bool ScDPGroupTableData::HasCommonElement( const ScDPItemData& rFirstData, sal_Int32 nFirstIndex,
937 const ScDPItemData& rSecondData, sal_Int32 nSecondIndex ) const
938{
939 const ScDPGroupDimension* pFirstDim = nullptr;
940 const ScDPGroupDimension* pSecondDim = nullptr;
941 for ( const auto& rDim : aGroups )
942 {
943 const ScDPGroupDimension* pDim = &rDim;
944 if ( pDim->GetGroupDim() == nFirstIndex )
945 pFirstDim = pDim;
946 else if ( pDim->GetGroupDim() == nSecondIndex )
947 pSecondDim = pDim;
948 }
949 if ( pFirstDim && pSecondDim )
950 {
951 bool bFirstDate = pFirstDim->IsDateDimension();
952 bool bSecondDate = pSecondDim->IsDateDimension();
953 if (bFirstDate || bSecondDate)
954 {
955 // If one is a date group dimension, the other one must be, too.
956 if (!bFirstDate || !bSecondDate)
957 {
958 OSL_FAIL( "mix of date and non-date groups" );
959 return true;
960 }
961
962 return isDateInGroup(rFirstData, rSecondData);
963 }
964
965 const ScDPGroupItem* pFirstItem = pFirstDim->GetGroupForName( rFirstData );
966 const ScDPGroupItem* pSecondItem = pSecondDim->GetGroupForName( rSecondData );
967 if ( pFirstItem && pSecondItem )
968 {
969 // two existing groups -> sal_True if they have a common element
970 return pFirstItem->HasCommonElement( *pSecondItem );
971 }
972 else if ( pFirstItem )
973 {
974 // "automatic" group contains only its own name
975 return pFirstItem->HasElement( rSecondData );
976 }
977 else if ( pSecondItem )
978 {
979 // "automatic" group contains only its own name
980 return pSecondItem->HasElement( rFirstData );
981 }
982 else
983 {
984 // no groups -> sal_True if equal
985 return rFirstData.IsCaseInsEqual( rSecondData );
986 }
987 }
988
989 OSL_FAIL("HasCommonElement: no group dimension found");
990 return true;
991}
992
993sal_Int32 ScDPGroupTableData::GetSourceDim( sal_Int32 nDim )
994{
995 if ( getIsDataLayoutDimension( nDim ) )
996 return nSourceCount;
997 if ( nDim >= nSourceCount && nDim < nSourceCount +static_cast<tools::Long>(aGroups.size()) )
998 {
999 const ScDPGroupDimension& rGroupDim = aGroups[nDim - nSourceCount];
1000 return rGroupDim.GetSourceDim();
1001 }
1002 return nDim;
1003}
1004
1005sal_Int32 ScDPGroupTableData::Compare(sal_Int32 nDim, sal_Int32 nDataId1, sal_Int32 nDataId2)
1006{
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);
1014}
1015
1016#if DUMP_PIVOT_TABLE
1017
1018void ScDPGroupTableData::Dump() const
1019{
1020 cout << "--- ScDPGroupTableData" << endl;
1021 for (tools::Long i = 0; i < nSourceCount; ++i)
1022 {
1023 cout << "* dimension: " << i << endl;
1024 const ScDPNumGroupDimension& rGrp = pNumGroups[i];
1025 rGrp.GetInfo().Dump();
1026 }
1027 cout << "---" << endl;
1028}
1029#endif
1030
1031/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
ValueVectorType maValues
sal_Int32 year
sal_Int32 month
sal_Int16 GetYear() const
bool IsLeapYear() const
sal_uInt16 GetMonth() const
This class represents the cached data part of the datapilot cache table implementation.
Definition: dpcache.hxx:48
sal_Int32 GetGroupType(tools::Long nDim) const
Return a group type identifier.
Definition: dpcache.cxx:1389
const ScDPItemData * GetItemDataById(tools::Long nDim, SCROW nId) const
Definition: dpcache.cxx:983
SCROW GetIdByItemData(tools::Long nDim, const ScDPItemData &rItem) const
Definition: dpcache.cxx:1122
void GetGroupDimMemberIds(tools::Long nDim, std::vector< SCROW > &rIds) const
Definition: dpcache.cxx:1317
const ScDPNumGroupInfo * GetNumGroupInfo(tools::Long nDim) const
Definition: dpcache.cxx:1368
interface class used for filtering of rows.
virtual std::vector< ScDPItemData > getMatchValues() const =0
virtual bool match(const ScDPItemData &rCellData) const =0
returns true if the matching condition is met for a single cell value, or false otherwise.
multi-item (group) filter.
void addMatchItem(const ScDPItemData &rItem)
This class is only a wrapper to the actual cache, to provide filtering on the raw data based on the q...
sal_Int32 getRowSize() const
bool isRowActive(sal_Int32 nRow, sal_Int32 *pLastRow=nullptr) const
Check whether a specified row is active or not.
const ScDPCache & getCache() const
tools::Long GetSourceDim() const
Definition: dpgroup.hxx:70
ScDPGroupDimension & operator=(const ScDPGroupDimension &rOther)
Definition: dpgroup.cxx:356
ScDPGroupDimension(tools::Long nSource, OUString aNewName)
Definition: dpgroup.cxx:334
void SetDateDimension()
Definition: dpgroup.cxx:419
tools::Long nSourceDim
Definition: dpgroup.hxx:54
const ScDPGroupItem * GetGroupByIndex(size_t nIndex) const
Definition: dpgroup.cxx:406
bool IsDateDimension() const
Definition: dpgroup.hxx:84
tools::Long nGroupDim
Definition: dpgroup.hxx:55
const ScDPGroupItem * GetGroupForData(const ScDPItemData &rData) const
Definition: dpgroup.cxx:386
tools::Long GetGroupDim() const
Definition: dpgroup.hxx:71
size_t GetItemCount() const
Definition: dpgroup.hxx:81
OUString aGroupName
Definition: dpgroup.hxx:56
const ScDPGroupItem * GetGroupForName(const ScDPItemData &rName) const
Definition: dpgroup.cxx:396
std::vector< SCROW > maMemberEntries
Definition: dpgroup.hxx:58
std::vector< ScDPGroupItem > aItems
Definition: dpgroup.hxx:57
const std::vector< SCROW > & GetColumnEntries(const ScDPFilteredCache &rCacheTable) const
Definition: dpgroup.cxx:376
void SetGroupDim(tools::Long nDim)
Definition: dpgroup.cxx:371
void AddItem(const ScDPGroupItem &rItem)
Definition: dpgroup.cxx:366
ScDPItemDataVec aElements
Definition: dpgroup.hxx:38
void AddElement(const ScDPItemData &rName)
Definition: dpgroup.cxx:311
bool HasElement(const ScDPItemData &rData) const
Definition: dpgroup.cxx:316
void FillGroupFilter(ScDPFilteredCache::GroupFilter &rFilter) const
Definition: dpgroup.cxx:328
ScDPGroupItem(const ScDPItemData &rName)
Definition: dpgroup.cxx:306
const ScDPItemData & GetName() const
Definition: dpgroup.hxx:45
bool HasCommonElement(const ScDPGroupItem &rOther) const
Definition: dpgroup.cxx:322
virtual sal_Int32 GetSourceDim(sal_Int32 nDim) override
Definition: dpgroup.cxx:993
virtual const ScDPItemData * GetMemberById(sal_Int32 nDim, sal_Int32 nId) override
Definition: dpgroup.cxx:549
virtual sal_Int32 GetMembersCount(sal_Int32 nDim) override
Definition: dpgroup.cxx:521
virtual bool IsNumOrDateGroup(sal_Int32 nDim) const override
Definition: dpgroup.cxx:890
virtual bool HasCommonElement(const ScDPItemData &rFirstData, sal_Int32 nFirstIndex, const ScDPItemData &rSecondData, sal_Int32 nSecondIndex) const override
Definition: dpgroup.cxx:936
virtual void FilterCacheTable(std::vector< ScDPFilteredCache::Criterion > &&rCriteria, std::unordered_set< sal_Int32 > &&rDataDims) override
Definition: dpgroup.cxx:751
sal_Int32 GetDimensionIndex(std::u16string_view rName)
Definition: dpgroup.cxx:498
std::shared_ptr< ScDPTableData > pSourceData
Definition: dpgroup.hxx:116
virtual void CalcResults(CalcInfo &rInfo, bool bAutoShow) override
Definition: dpgroup.cxx:763
virtual void SetEmptyFlags(bool bIgnoreEmptyRows, bool bRepeatIfEmpty) override
Definition: dpgroup.cxx:610
virtual void DisposeData() override
Definition: dpgroup.cxx:599
virtual void CreateCacheTable() override
Definition: dpgroup.cxx:620
virtual bool IsDateDimension(sal_Int32 nDim) override
Definition: dpgroup.cxx:573
virtual sal_Int32 GetGroupBase(sal_Int32 nGroupDim) const override
Definition: dpgroup.cxx:880
virtual bool getIsDataLayoutDimension(sal_Int32 nColumn) override
Definition: dpgroup.cxx:567
sal_Int32 nSourceCount
Definition: dpgroup.hxx:117
void FillGroupValues(std::vector< SCROW > &rItems, const std::vector< sal_Int32 > &rDims)
Definition: dpgroup.cxx:804
virtual sal_uInt32 GetNumberFormat(sal_Int32 nDim) override
Definition: dpgroup.cxx:586
virtual bool IsRepeatIfEmpty() override
Definition: dpgroup.cxx:615
virtual bool IsBaseForGroup(sal_Int32 nDim) const override
Definition: dpgroup.cxx:874
void GetNumGroupInfo(tools::Long nDimension, ScDPNumGroupInfo &rInfo)
Definition: dpgroup.cxx:516
virtual void Dump() const override
bool IsNumGroupDimension(tools::Long nDimension) const
Definition: dpgroup.cxx:511
void SetNumGroupDimension(sal_Int32 nIndex, const ScDPNumGroupDimension &rGroup)
Definition: dpgroup.cxx:488
virtual const std::vector< SCROW > & GetColumnEntries(sal_Int32 nColumn) override
Definition: dpgroup.cxx:526
virtual const ScDPFilteredCache & GetCacheTable() const override
Definition: dpgroup.cxx:794
void AddGroupDimension(const ScDPGroupDimension &rGroup)
Definition: dpgroup.cxx:481
virtual ~ScDPGroupTableData() override
Definition: dpgroup.cxx:477
std::unique_ptr< ScDPNumGroupDimension[]> pNumGroups
Definition: dpgroup.hxx:121
ScDocument * pDoc
Definition: dpgroup.hxx:122
virtual bool IsInGroup(const ScDPItemData &rGroupData, sal_Int32 nGroupIndex, const ScDPItemData &rBaseData, sal_Int32 nBaseIndex) const override
Definition: dpgroup.cxx:908
virtual void GetDrillDownData(std::vector< ScDPFilteredCache::Criterion > &&rCriteria, std::unordered_set< sal_Int32 > &&rCatDims, css::uno::Sequence< css::uno::Sequence< css::uno::Any > > &rData) override
Definition: dpgroup.cxx:757
void ModifyFilterCriteria(::std::vector< ScDPFilteredCache::Criterion > &rCriteria)
Definition: dpgroup.cxx:641
virtual void ReloadCacheTable() override
Definition: dpgroup.cxx:799
ScDPGroupTableData(const std::shared_ptr< ScDPTableData > &pSource, ScDocument *pDocument)
Definition: dpgroup.cxx:465
std::vector< ScDPGroupDimension > aGroups
Definition: dpgroup.hxx:119
virtual OUString getDimensionName(sal_Int32 nColumn) override
Definition: dpgroup.cxx:554
virtual sal_Int32 GetColumnCount() override
use (new) typed collection instead of ScStrCollection or separate Str and ValueCollection
Definition: dpgroup.cxx:506
virtual sal_Int32 Compare(sal_Int32 nDim, sal_Int32 nDataId1, sal_Int32 nDataId2) override
Definition: dpgroup.cxx:1005
When assigning a string value, you can also assign an interned string whose life-cycle is managed by ...
Definition: dpitemdata.hxx:29
double GetValue() const
Definition: dpitemdata.cxx:347
bool IsValue() const
Definition: dpitemdata.cxx:322
Type GetType() const
Definition: dpitemdata.hxx:67
void SetRangeStart(double fVal)
Definition: dpitemdata.cxx:160
static const sal_Int32 DateFirst
Definition: dpitemdata.hxx:35
static sal_Int32 Compare(const ScDPItemData &rA, const ScDPItemData &rB)
Definition: dpitemdata.cxx:30
bool IsCaseInsEqual(const ScDPItemData &r) const
Definition: dpitemdata.cxx:187
static const sal_Int32 DateLast
Definition: dpitemdata.hxx:36
GroupValueAttr GetGroupValue() const
Definition: dpitemdata.cxx:355
ScDPNumGroupDimension & operator=(const ScDPNumGroupDimension &rOther)
Definition: dpgroup.cxx:432
std::vector< SCROW > maMemberEntries
Definition: dpgroup.hxx:90
ScDPNumGroupInfo aGroupInfo
Definition: dpgroup.hxx:89
const std::vector< SCROW > & GetNumEntries(SCCOL nSourceDim, const ScDPCache *pCache) const
Definition: dpgroup.cxx:455
bool IsDateDimension() const
Definition: dpgroup.hxx:109
const ScDPNumGroupInfo & GetInfo() const
Definition: dpgroup.hxx:101
Base class that abstracts different data source types of a datapilot table.
Definition: dptabdat.hxx:57
static void ProcessRowData(CalcInfo &rInfo, const CalcRowData &rData, bool bAutoShow)
Definition: dptabdat.cxx:168
void FillRowDataFromCacheTable(sal_Int32 nRow, const ScDPFilteredCache &rCacheTable, const CalcInfo &rInfo, CalcRowData &rData)
Definition: dptabdat.cxx:140
static sal_Int32 getDatePartValue(double fValue, const ScDPNumGroupInfo *pInfo, sal_Int32 nDatePart, const SvNumberFormatter *pFormatter)
Definition: dputil.cxx:287
static double getNumGroupStartValue(double fValue, const ScDPNumGroupInfo &rInfo)
Definition: dputil.cxx:153
SC_DLLPUBLIC SvNumberFormatter * GetFormatTable() const
Definition: documen2.cxx:467
const Date & GetNullDate() const
static void GetClock(double fTimeInDays, sal_uInt16 &nHour, sal_uInt16 &nMinute, sal_uInt16 &nSecond, double &fFractionOfSecond, int nFractionDecimals)
const sal_uInt16 SC_DP_LEAPYEAR
Definition: dpgroup.cxx:46
double maValue
sal_Int16 nValue
sal_Int32 nIndex
std::unique_ptr< sal_Int32[]> pData
constexpr OUStringLiteral aData
int i
long Long
sal_Int16 nId
PyObject_HEAD PyUNO_callable_Internals * members
TOOLS_DLLPUBLIC SvStream & endl(SvStream &rStr)
single filtering criterion.
std::shared_ptr< FilterBase > mpFilter
void Dump() const
This structure stores dimension information used when calculating results.
Definition: dptabdat.hxx:70
::std::vector< sal_Int32 > aPageDims
Definition: dptabdat.hxx:77
::std::vector< sal_Int32 > aRowLevelDims
Definition: dptabdat.hxx:74
::std::vector< sal_Int32 > aColLevelDims
Definition: dptabdat.hxx:71
This structure stores vector arrays that hold intermediate data for each row during cache table itera...
Definition: dptabdat.hxx:143
sal_Int16 SCCOL
Definition: types.hxx:21