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