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 
30 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
31 
32 #include <unordered_map>
33 #include <unordered_set>
34 #include <vector>
35 #include <algorithm>
36 
37 using namespace ::com::sun::star;
38 using ::com::sun::star::uno::Any;
39 using ::com::sun::star::uno::Sequence;
40 
41 using ::std::vector;
42 using ::std::shared_ptr;
43 
44 const sal_uInt16 SC_DP_LEAPYEAR = 1648; // arbitrary leap year for date calculations
45 
46 namespace {
47 
48 class ScDPGroupNumFilter : public ScDPFilteredCache::FilterBase
49 {
50 public:
51  ScDPGroupNumFilter(const std::vector<ScDPItemData>& rValues, const ScDPNumGroupInfo& rInfo);
52 
53  virtual bool match(const ScDPItemData &rCellData) const override;
54  virtual std::vector<ScDPItemData> getMatchValues() const override;
55 private:
56  std::vector<ScDPItemData> maValues;
57  ScDPNumGroupInfo maNumInfo;
58 };
59 
60 }
61 
62 ScDPGroupNumFilter::ScDPGroupNumFilter(const std::vector<ScDPItemData>& rValues, const ScDPNumGroupInfo& rInfo) :
63  maValues(rValues), maNumInfo(rInfo) {}
64 
65 bool ScDPGroupNumFilter::match(const ScDPItemData& rCellData) const
66 {
67  if (rCellData.GetType() != ScDPItemData::Value)
68  return false;
69 
70  for (const auto& rValue : maValues)
71  {
72  double fVal = rValue.GetValue();
73  if (std::isinf(fVal))
74  {
75  if (std::signbit(fVal))
76  {
77  // Less than the min value.
78  if (rCellData.GetValue() < maNumInfo.mfStart)
79  return true;
80  }
81 
82  // Greater than the max value.
83  if (maNumInfo.mfEnd < rCellData.GetValue())
84  return true;
85 
86  continue;
87  }
88 
89  double low = fVal;
90  double high = low + maNumInfo.mfStep;
91  if (maNumInfo.mbIntegerOnly)
92  high += 1.0;
93 
94  if (low <= rCellData.GetValue() && rCellData.GetValue() < high)
95  return true;
96  }
97 
98  return false;
99 }
100 
101 std::vector<ScDPItemData> ScDPGroupNumFilter::getMatchValues() const
102 {
103  return std::vector<ScDPItemData>();
104 }
105 
106 namespace {
107 
108 class ScDPGroupDateFilter : public ScDPFilteredCache::FilterBase
109 {
110 public:
111  ScDPGroupDateFilter(
112  const std::vector<ScDPItemData>& rValues, const Date& rNullDate, const ScDPNumGroupInfo& rNumInfo);
113 
114  virtual bool match(const ScDPItemData & rCellData) const override;
115  virtual std::vector<ScDPItemData> getMatchValues() const override;
116 
117 private:
118  std::vector<ScDPItemData> maValues;
119  Date maNullDate;
120  ScDPNumGroupInfo maNumInfo;
121 };
122 
123 }
124 
125 ScDPGroupDateFilter::ScDPGroupDateFilter(
126  const std::vector<ScDPItemData>& rValues, const Date& rNullDate, const ScDPNumGroupInfo& rNumInfo) :
127  maValues(rValues),
128  maNullDate(rNullDate),
129  maNumInfo(rNumInfo)
130 {
131 }
132 
133 bool ScDPGroupDateFilter::match( const ScDPItemData & rCellData ) const
134 {
135  using namespace ::com::sun::star::sheet;
136  using ::rtl::math::approxFloor;
137  using ::rtl::math::approxEqual;
138 
139  if ( !rCellData.IsValue() )
140  return false;
141 
142  for (const ScDPItemData& rValue : maValues)
143  {
144  if (rValue.GetType() != ScDPItemData::GroupValue)
145  continue;
146 
147  sal_Int32 nGroupType = rValue.GetGroupValue().mnGroupType;
148  sal_Int32 nValue = rValue.GetGroupValue().mnValue;
149 
150  // Start and end dates are inclusive. (An end date without a time value
151  // is included, while an end date with a time value is not.)
152 
153  if (rCellData.GetValue() < maNumInfo.mfStart && !approxEqual(rCellData.GetValue(), maNumInfo.mfStart))
154  {
155  if (nValue == ScDPItemData::DateFirst)
156  return true;
157  continue;
158  }
159 
160  if (rCellData.GetValue() > maNumInfo.mfEnd && !approxEqual(rCellData.GetValue(), maNumInfo.mfEnd))
161  {
162  if (nValue == ScDPItemData::DateLast)
163  return true;
164  continue;
165  }
166 
167  if (nGroupType == DataPilotFieldGroupBy::HOURS || nGroupType == DataPilotFieldGroupBy::MINUTES ||
168  nGroupType == DataPilotFieldGroupBy::SECONDS)
169  {
170  // handle time
171  // (do as in the cell functions, ScInterpreter::ScGetHour() etc.)
172 
173  sal_uInt16 nHour, nMinute, nSecond;
174  double fFractionOfSecond;
175  tools::Time::GetClock( rCellData.GetValue(), nHour, nMinute, nSecond, fFractionOfSecond, 0);
176 
177  switch (nGroupType)
178  {
179  case DataPilotFieldGroupBy::HOURS:
180  {
181  if (nHour == nValue)
182  return true;
183  }
184  break;
185  case DataPilotFieldGroupBy::MINUTES:
186  {
187  if (nMinute == nValue)
188  return true;
189  }
190  break;
191  case DataPilotFieldGroupBy::SECONDS:
192  {
193  if (nSecond == nValue)
194  return true;
195  }
196  break;
197  default:
198  OSL_FAIL("invalid time part");
199  }
200 
201  continue;
202  }
203 
204  Date date = maNullDate + static_cast<sal_Int32>(approxFloor(rCellData.GetValue()));
205  switch (nGroupType)
206  {
207  case DataPilotFieldGroupBy::YEARS:
208  {
209  sal_Int32 year = static_cast<sal_Int32>(date.GetYear());
210  if (year == nValue)
211  return true;
212  }
213  break;
214  case DataPilotFieldGroupBy::QUARTERS:
215  {
216  sal_Int32 qtr = 1 + (static_cast<sal_Int32>(date.GetMonth()) - 1) / 3;
217  if (qtr == nValue)
218  return true;
219  }
220  break;
221  case DataPilotFieldGroupBy::MONTHS:
222  {
223  sal_Int32 month = static_cast<sal_Int32>(date.GetMonth());
224  if (month == nValue)
225  return true;
226  }
227  break;
228  case DataPilotFieldGroupBy::DAYS:
229  {
230  Date yearStart(1, 1, date.GetYear());
231  sal_Int32 days = (date - yearStart) + 1; // Jan 01 has value 1
232  if (days >= 60 && !date.IsLeapYear())
233  {
234  // This is not a leap year. Adjust the value accordingly.
235  ++days;
236  }
237  if (days == nValue)
238  return true;
239  }
240  break;
241  default:
242  OSL_FAIL("invalid date part");
243  }
244  }
245 
246  return false;
247 }
248 
249 std::vector<ScDPItemData> ScDPGroupDateFilter::getMatchValues() const
250 {
251  return std::vector<ScDPItemData>();
252 }
253 
254 namespace {
255 
256 bool isDateInGroup(const ScDPItemData& rGroupItem, const ScDPItemData& rChildItem)
257 {
258  if (rGroupItem.GetType() != ScDPItemData::GroupValue || rChildItem.GetType() != ScDPItemData::GroupValue)
259  return false;
260 
261  sal_Int32 nGroupPart = rGroupItem.GetGroupValue().mnGroupType;
262  sal_Int32 nGroupValue = rGroupItem.GetGroupValue().mnValue;
263  sal_Int32 nChildPart = rChildItem.GetGroupValue().mnGroupType;
264  sal_Int32 nChildValue = rChildItem.GetGroupValue().mnValue;
265 
266  if (nGroupValue == ScDPItemData::DateFirst || nGroupValue == ScDPItemData::DateLast ||
267  nChildValue == ScDPItemData::DateFirst || nChildValue == ScDPItemData::DateLast)
268  {
269  // first/last entry matches only itself
270  return nGroupValue == nChildValue;
271  }
272 
273  switch (nChildPart) // inner part
274  {
275  case css::sheet::DataPilotFieldGroupBy::MONTHS:
276  // a month is only contained in its quarter
277  if (nGroupPart == css::sheet::DataPilotFieldGroupBy::QUARTERS)
278  // months and quarters are both 1-based
279  return (nGroupValue - 1 == (nChildValue - 1) / 3);
280  break;
281  case css::sheet::DataPilotFieldGroupBy::DAYS:
282  // a day is only contained in its quarter or month
283  if (nGroupPart == css::sheet::DataPilotFieldGroupBy::MONTHS ||
284  nGroupPart == css::sheet::DataPilotFieldGroupBy::QUARTERS)
285  {
286  Date aDate(1, 1, SC_DP_LEAPYEAR);
287  aDate.AddDays(nChildValue - 1); // days are 1-based
288  sal_Int32 nCompare = aDate.GetMonth();
289  if (nGroupPart == css::sheet::DataPilotFieldGroupBy::QUARTERS)
290  nCompare = ( ( nCompare - 1 ) / 3 ) + 1; // get quarter from date
291 
292  return nGroupValue == nCompare;
293  }
294  break;
295  default:
296  ;
297  }
298 
299  return true;
300 }
301 
302 }
303 
305  aGroupName( rName )
306 {
307 }
308 
310 {
311  aElements.push_back( rName );
312 }
313 
314 bool ScDPGroupItem::HasElement( const ScDPItemData& rData ) const
315 {
316  return std::any_of(aElements.begin(), aElements.end(),
317  [&rData](const ScDPItemData& rElement) { return rElement.IsCaseInsEqual(rData); });
318 }
319 
321 {
322  return std::any_of(aElements.begin(), aElements.end(),
323  [&rOther](const ScDPItemData& rElement) { return rOther.HasElement(rElement); });
324 }
325 
327 {
328  for (const auto& rElement : aElements)
329  rFilter.addMatchItem(rElement);
330 }
331 
332 ScDPGroupDimension::ScDPGroupDimension( long nSource, const OUString& rNewName ) :
333  nSourceDim( nSource ),
334  nGroupDim( -1 ),
335  aGroupName( rNewName ),
336  mbDateDimension(false)
337 {
338 }
339 
341 {
342  maMemberEntries.clear();
343 }
344 
346  nSourceDim( rOther.nSourceDim ),
347  nGroupDim( rOther.nGroupDim ),
348  aGroupName( rOther.aGroupName ),
349  aItems( rOther.aItems ),
350  mbDateDimension(rOther.mbDateDimension)
351 {
352 }
353 
355 {
356  nSourceDim = rOther.nSourceDim;
357  nGroupDim = rOther.nGroupDim;
358  aGroupName = rOther.aGroupName;
359  aItems = rOther.aItems;
361  return *this;
362 }
363 
365 {
366  aItems.push_back( rItem );
367 }
368 
370 {
371  nGroupDim = nDim;
372 }
373 
374 const std::vector<SCROW>& ScDPGroupDimension::GetColumnEntries(
375  const ScDPFilteredCache& rCacheTable) const
376 {
377  if (!maMemberEntries.empty())
378  return maMemberEntries;
379 
381  return maMemberEntries;
382 }
383 
385 {
386  auto aIter = std::find_if(aItems.begin(), aItems.end(),
387  [&rData](const ScDPGroupItem& rItem) { return rItem.HasElement(rData); });
388  if (aIter != aItems.end())
389  return &*aIter;
390 
391  return nullptr;
392 }
393 
395 {
396  auto aIter = std::find_if(aItems.begin(), aItems.end(),
397  [&rName](const ScDPGroupItem& rItem) { return rItem.GetName().IsCaseInsEqual(rName); });
398  if (aIter != aItems.end())
399  return &*aIter;
400 
401  return nullptr;
402 }
403 
405 {
406  if (nIndex >= aItems.size())
407  return nullptr;
408 
409  return &aItems[nIndex];
410 }
411 
413 {
414  maMemberEntries.clear();
415 }
416 
418 {
419  mbDateDimension = true;
420 }
421 
422 ScDPNumGroupDimension::ScDPNumGroupDimension() : mbDateDimension(false) {}
423 
425  aGroupInfo(rInfo), mbDateDimension(false) {}
426 
428  aGroupInfo(rOther.aGroupInfo), mbDateDimension(rOther.mbDateDimension) {}
429 
431 {
432  aGroupInfo = rOther.aGroupInfo;
434  return *this;
435 }
436 
438 {
440  maMemberEntries.clear();
441 }
442 
444 {
445 }
446 
448 {
449  aGroupInfo.mbEnable = true; //TODO: or query both?
450  mbDateDimension = true;
451 }
452 
453 const std::vector<SCROW>& ScDPNumGroupDimension::GetNumEntries(
454  SCCOL nSourceDim, const ScDPCache* pCache) const
455 {
456  if (!maMemberEntries.empty())
457  return maMemberEntries;
458 
459  pCache->GetGroupDimMemberIds(nSourceDim, maMemberEntries);
460  return maMemberEntries;
461 }
462 
463 ScDPGroupTableData::ScDPGroupTableData( const shared_ptr<ScDPTableData>& pSource, ScDocument* pDocument ) :
464  ScDPTableData(pDocument),
465  pSourceData( pSource ),
466  pDoc( pDocument )
467 {
468  OSL_ENSURE( pSource, "ScDPGroupTableData: pSource can't be NULL" );
469 
471  nSourceCount = pSource->GetColumnCount(); // real columns, excluding data layout
473 }
474 
476 {
477 }
478 
480 {
481  ScDPGroupDimension aNewGroup( rGroup );
482  aNewGroup.SetGroupDim( GetColumnCount() ); // new dimension will be at the end
483  aGroups.push_back( aNewGroup );
484 }
485 
487 {
488  if ( nIndex < nSourceCount )
489  {
490  pNumGroups[nIndex] = rGroup;
491 
492  // automatic minimum / maximum is handled in GetNumEntries
493  }
494 }
495 
496 long ScDPGroupTableData::GetDimensionIndex( const OUString& rName )
497 {
498  for (long i = 0; i < nSourceCount; ++i) // nSourceCount excludes data layout
499  if (pSourceData->getDimensionName(i) == rName) //TODO: ignore case?
500  return i;
501  return -1; // none
502 }
503 
505 {
506  return nSourceCount + aGroups.size();
507 }
508 
509 bool ScDPGroupTableData::IsNumGroupDimension( long nDimension ) const
510 {
511  return ( nDimension < nSourceCount && pNumGroups[nDimension].GetInfo().mbEnable );
512 }
513 
515 {
516  if ( nDimension < nSourceCount )
517  rInfo = pNumGroups[nDimension].GetInfo();
518 }
520 {
521  const std::vector< SCROW >& members = GetColumnEntries( nDim );
522  return members.size();
523 }
524 const std::vector< SCROW >& ScDPGroupTableData::GetColumnEntries( long nColumn )
525 {
526  if ( nColumn >= nSourceCount )
527  {
528  if ( getIsDataLayoutDimension( nColumn) ) // data layout dimension?
529  nColumn = nSourceCount; // index of data layout in source data
530  else
531  {
532  const ScDPGroupDimension& rGroupDim = aGroups[nColumn - nSourceCount];
533  return rGroupDim.GetColumnEntries( GetCacheTable() );
534  }
535  }
536 
537  if ( IsNumGroupDimension( nColumn ) )
538  {
539  // dimension number is unchanged for numerical groups
540  return pNumGroups[nColumn].GetNumEntries(
541  static_cast<SCCOL>(nColumn), &GetCacheTable().getCache());
542  }
543 
544  return pSourceData->GetColumnEntries( nColumn );
545 }
546 
547 const ScDPItemData* ScDPGroupTableData::GetMemberById( long nDim, long nId )
548 {
549  return pSourceData->GetMemberById( nDim, nId );
550 }
551 
553 {
554  if ( nColumn >= nSourceCount )
555  {
556  if ( nColumn == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ) // data layout dimension?
557  nColumn = nSourceCount; // index of data layout in source data
558  else
559  return aGroups[nColumn - nSourceCount].GetName();
560  }
561 
562  return pSourceData->getDimensionName( nColumn );
563 }
564 
566 {
567  // position of data layout dimension is moved from source data
568  return ( nColumn == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ); // data layout dimension?
569 }
570 
572 {
573  if ( nDim >= nSourceCount )
574  {
575  if ( nDim == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ) // data layout dimension?
576  nDim = nSourceCount; // index of data layout in source data
577  else
578  nDim = aGroups[nDim - nSourceCount].GetSourceDim(); // look at original dimension
579  }
580 
581  return pSourceData->IsDateDimension( nDim );
582 }
583 
585 {
586  if ( nDim >= nSourceCount )
587  {
588  if ( nDim == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ) // data layout dimension?
589  nDim = nSourceCount; // index of data layout in source data
590  else
591  nDim = aGroups[nDim - nSourceCount].GetSourceDim(); // look at original dimension
592  }
593 
594  return pSourceData->GetNumberFormat( nDim );
595 }
596 
598 {
599  for ( auto& rGroup : aGroups )
600  rGroup.DisposeData();
601 
602  for ( long i=0; i<nSourceCount; i++ )
604 
605  pSourceData->DisposeData();
606 }
607 
608 void ScDPGroupTableData::SetEmptyFlags( bool bIgnoreEmptyRows, bool bRepeatIfEmpty )
609 {
610  pSourceData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
611 }
612 
614 {
615  return pSourceData->IsRepeatIfEmpty();
616 }
617 
619 {
620  pSourceData->CreateCacheTable();
621 }
622 
623 namespace {
624 
625 class FindCaseInsensitive
626 {
628 public:
629  explicit FindCaseInsensitive(const ScDPItemData& rVal) : maValue(rVal) {}
630 
631  bool operator() (const ScDPItemData& rItem) const
632  {
633  return maValue.IsCaseInsEqual(rItem);
634  }
635 };
636 
637 }
638 
639 void ScDPGroupTableData::ModifyFilterCriteria(vector<ScDPFilteredCache::Criterion>& rCriteria)
640 {
641  // Build dimension ID to object map for group dimensions.
642  typedef std::unordered_map<long, const ScDPGroupDimension*> GroupFieldMapType;
643  GroupFieldMapType aGroupFieldIds;
644 
645  for (const auto& rGroup : aGroups)
646  {
647  aGroupFieldIds.emplace(rGroup.GetGroupDim(), &rGroup);
648  }
649 
650  vector<ScDPFilteredCache::Criterion> aNewCriteria;
651  aNewCriteria.reserve(rCriteria.size() + aGroups.size());
652 
653  // Go through all the filtered field names and process them appropriately.
654 
655  const ScDPCache& rCache = GetCacheTable().getCache();
656  GroupFieldMapType::const_iterator itrGrpEnd = aGroupFieldIds.end();
657  for (const auto& rCriterion : rCriteria)
658  {
659  std::vector<ScDPItemData> aMatchValues = rCriterion.mpFilter->getMatchValues();
660 
661  GroupFieldMapType::const_iterator itrGrp = aGroupFieldIds.find(rCriterion.mnFieldIndex);
662  if (itrGrp == itrGrpEnd)
663  {
664  if (IsNumGroupDimension(rCriterion.mnFieldIndex))
665  {
666  // internal number group field
667  const ScDPNumGroupInfo* pNumInfo = rCache.GetNumGroupInfo(rCriterion.mnFieldIndex);
668  if (!pNumInfo)
669  // Number group dimension without num info? Something is wrong...
670  continue;
671 
673  aCri.mnFieldIndex = rCriterion.mnFieldIndex;
674  const ScDPNumGroupDimension& rNumGrpDim = pNumGroups[rCriterion.mnFieldIndex];
675 
676  if (rNumGrpDim.IsDateDimension())
677  {
678  // grouped by dates.
679  aCri.mpFilter =
680  std::make_shared<ScDPGroupDateFilter>(
681  aMatchValues, pDoc->GetFormatTable()->GetNullDate(), *pNumInfo);
682  }
683  else
684  {
685  // This dimension is grouped by numeric ranges.
686  aCri.mpFilter =
687  std::make_shared<ScDPGroupNumFilter>(aMatchValues, *pNumInfo);
688  }
689 
690  aNewCriteria.push_back(aCri);
691  }
692  else
693  {
694  // This is a regular source field.
695  aNewCriteria.push_back(rCriterion);
696  }
697  }
698  else
699  {
700  // This is an ordinary group field or external number group field.
701 
702  const ScDPGroupDimension* pGrpDim = itrGrp->second;
703  long nSrcDim = pGrpDim->GetSourceDim();
704  long nGrpDim = pGrpDim->GetGroupDim();
705  const ScDPNumGroupInfo* pNumInfo = rCache.GetNumGroupInfo(nGrpDim);
706 
707  if (pGrpDim->IsDateDimension() && pNumInfo)
708  {
709  // external number group
711  aCri.mnFieldIndex = nSrcDim; // use the source dimension, not the group dimension.
712  aCri.mpFilter =
713  std::make_shared<ScDPGroupDateFilter>(
714  aMatchValues, pDoc->GetFormatTable()->GetNullDate(), *pNumInfo);
715 
716  aNewCriteria.push_back(aCri);
717  }
718  else
719  {
720  // normal group
721 
723  aCri.mnFieldIndex = nSrcDim;
724  aCri.mpFilter = std::make_shared<ScDPFilteredCache::GroupFilter>();
725  ScDPFilteredCache::GroupFilter* pGrpFilter =
726  static_cast<ScDPFilteredCache::GroupFilter*>(aCri.mpFilter.get());
727 
728  size_t nGroupItemCount = pGrpDim->GetItemCount();
729  for (size_t i = 0; i < nGroupItemCount; ++i)
730  {
731  const ScDPGroupItem* pGrpItem = pGrpDim->GetGroupByIndex(i);
732  if (!pGrpItem)
733  continue;
734 
735  // Make sure this group name equals one of the match values.
736  if (std::none_of(aMatchValues.begin(), aMatchValues.end(), FindCaseInsensitive(pGrpItem->GetName())))
737  continue;
738 
739  pGrpItem->FillGroupFilter(*pGrpFilter);
740  }
741 
742  aNewCriteria.push_back(aCri);
743  }
744  }
745  }
746  rCriteria.swap(aNewCriteria);
747 }
748 
749 void ScDPGroupTableData::FilterCacheTable(const vector<ScDPFilteredCache::Criterion>& rCriteria, const std::unordered_set<sal_Int32>& rCatDims)
750 {
751  vector<ScDPFilteredCache::Criterion> aNewCriteria(rCriteria);
752  ModifyFilterCriteria(aNewCriteria);
753  pSourceData->FilterCacheTable(aNewCriteria, rCatDims);
754 }
755 
756 void ScDPGroupTableData::GetDrillDownData(const vector<ScDPFilteredCache::Criterion>& rCriteria, const std::unordered_set<sal_Int32>& rCatDims, Sequence< Sequence<Any> >& rData)
757 {
758  vector<ScDPFilteredCache::Criterion> aNewCriteria(rCriteria);
759  ModifyFilterCriteria(aNewCriteria);
760  pSourceData->GetDrillDownData(aNewCriteria, rCatDims, rData);
761 }
762 
763 void 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 
804 void ScDPGroupTableData::FillGroupValues(vector<SCROW>& rItems, const vector<long>& rDims)
805 {
806  long nGroupedColumns = aGroups.size();
807 
808  const ScDPCache& rCache = GetCacheTable().getCache();
809  size_t i = 0;
810  for (long nColumn : rDims)
811  {
812  bool bDateDim = false;
813 
814  long 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 
875 {
876  return std::any_of(aGroups.begin(), aGroups.end(),
877  [&nDim](const ScDPGroupDimension& rDim) { return rDim.GetSourceDim() == nDim; });
878 }
879 
880 long ScDPGroupTableData::GetGroupBase(long 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 
890 bool ScDPGroupTableData::IsNumOrDateGroup(long 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 
908 bool ScDPGroupTableData::IsInGroup( const ScDPItemData& rGroupData, long nGroupIndex,
909  const ScDPItemData& rBaseData, long 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 
936 bool ScDPGroupTableData::HasCommonElement( const ScDPItemData& rFirstData, long nFirstIndex,
937  const ScDPItemData& rSecondData, long 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 
994 {
995  if ( getIsDataLayoutDimension( nDim ) )
996  return nSourceCount;
997  if ( nDim >= nSourceCount && nDim < nSourceCount +static_cast<long>(aGroups.size()) )
998  {
999  const ScDPGroupDimension& rGroupDim = aGroups[nDim - nSourceCount];
1000  return rGroupDim.GetSourceDim();
1001  }
1002  return nDim;
1003 }
1004 
1005 long ScDPGroupTableData::Compare(long nDim, long nDataId1, long 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 
1018 void ScDPGroupTableData::Dump() const
1019 {
1020  cout << "--- ScDPGroupTableData" << endl;
1021  for (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: */
virtual void FilterCacheTable(const std::vector< ScDPFilteredCache::Criterion > &rCriteria, const std::unordered_set< sal_Int32 > &rDataDims) override
Definition: dpgroup.cxx:749
virtual bool IsBaseForGroup(long nDim) const override
Definition: dpgroup.cxx:874
void FillGroupValues(std::vector< SCROW > &rItems, const std::vector< long > &rDims)
Definition: dpgroup.cxx:804
virtual void CalcResults(CalcInfo &rInfo, bool bAutoShow) override
Definition: dpgroup.cxx:763
const sal_uInt16 SC_DP_LEAPYEAR
Definition: dpgroup.cxx:44
OUString aGroupName
Definition: dpgroup.hxx:57
const ScDPNumGroupInfo & GetInfo() const
Definition: dpgroup.hxx:102
void AddGroupDimension(const ScDPGroupDimension &rGroup)
Definition: dpgroup.cxx:479
::std::vector< long > aColLevelDims
Definition: dptabdat.hxx:71
const std::vector< SCROW > & GetColumnEntries(const ScDPFilteredCache &rCacheTable) const
Definition: dpgroup.cxx:374
const char aData[]
long GetGroupDim() const
Definition: dpgroup.hxx:72
sal_Int32 getRowSize() const
sal_Int32 month
std::unique_ptr< ContentProperties > pData
sal_Int32 GetGroupType(long nDim) const
Return a group type identifier.
Definition: dpcache.cxx:1394
void SetNumGroupDimension(long nIndex, const ScDPNumGroupDimension &rGroup)
Definition: dpgroup.cxx:486
ScDPGroupTableData(const std::shared_ptr< ScDPTableData > &pSource, ScDocument *pDocument)
Definition: dpgroup.cxx:463
bool HasElement(const ScDPItemData &rData) const
Definition: dpgroup.cxx:314
std::vector< ScDPGroupDimension > aGroups
Definition: dpgroup.hxx:120
std::vector< ScDPGroupItem > aItems
Definition: dpgroup.hxx:58
PyObject_HEAD PyUNO_callable_Internals * members
const ContentProperties & rData
bool IsLeapYear() const
virtual ~ScDPGroupTableData() override
Definition: dpgroup.cxx:475
This class represents the cached data part of the datapilot cache table implementation.
Definition: dpcache.hxx:47
virtual bool IsNumOrDateGroup(long nDim) const override
Definition: dpgroup.cxx:890
std::unique_ptr< ScDPNumGroupDimension[]> pNumGroups
Definition: dpgroup.hxx:122
ValueVectorType maValues
virtual void Dump() const override
css::uno::Any const & rValue
interface class used for filtering of rows.
virtual bool IsDateDimension(long nDim) override
Definition: dpgroup.cxx:571
virtual long Compare(long nDim, long nDataId1, long nDataId2) override
Definition: dpgroup.cxx:1005
virtual void GetDrillDownData(const std::vector< ScDPFilteredCache::Criterion > &rCriteria, const std::unordered_set< sal_Int32 > &rCatDims, css::uno::Sequence< css::uno::Sequence< css::uno::Any > > &rData) override
Definition: dpgroup.cxx:756
sal_uInt16 GetMonth() const
virtual long GetColumnCount() override
use (new) typed collection instead of ScStrCollection or separate Str and ValueCollection ...
Definition: dpgroup.cxx:504
multi-item (group) filter.
ScDocument * pDoc
Definition: dpgroup.hxx:123
void Dump() const
static sal_Int32 Compare(const ScDPItemData &rA, const ScDPItemData &rB)
Definition: dpitemdata.cxx:30
virtual long GetGroupBase(long nGroupDim) const override
Definition: dpgroup.cxx:880
virtual bool getIsDataLayoutDimension(long nColumn) override
Definition: dpgroup.cxx:565
std::shared_ptr< FilterBase > mpFilter
static double getNumGroupStartValue(double fValue, const ScDPNumGroupInfo &rInfo)
Definition: dputil.cxx:153
void SetDateDimension()
Definition: dpgroup.cxx:417
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:85
sal_Int16 GetYear() const
::std::vector< long > aRowLevelDims
Definition: dptabdat.hxx:74
virtual OUString getDimensionName(long nColumn) override
Definition: dpgroup.cxx:552
SC_DLLPUBLIC SvNumberFormatter * GetFormatTable() const
Definition: documen2.cxx:438
Type GetType() const
Definition: dpitemdata.hxx:68
When assigning a string value, you can also assign an interned string whose life-cycle is managed by ...
Definition: dpitemdata.hxx:29
void AddElement(const ScDPItemData &rName)
Definition: dpgroup.cxx:309
virtual const std::vector< SCROW > & GetColumnEntries(long nColumn) override
Definition: dpgroup.cxx:524
const ScDPGroupItem * GetGroupByIndex(size_t nIndex) const
Definition: dpgroup.cxx:404
int i
This structure stores dimension information used when calculating results.
Definition: dptabdat.hxx:69
ScDPGroupItem(const ScDPItemData &rName)
Definition: dpgroup.cxx:304
void GetGroupDimMemberIds(long nDim, std::vector< SCROW > &rIds) const
Definition: dpcache.cxx:1322
long GetSourceDim() const
Definition: dpgroup.hxx:71
sal_Int16 SCCOL
Definition: types.hxx:22
::std::vector< SCROW > aRowData
Definition: dptabdat.hxx:145
virtual void CreateCacheTable() override
Definition: dpgroup.cxx:618
virtual bool IsInGroup(const ScDPItemData &rGroupData, long nGroupIndex, const ScDPItemData &rBaseData, long nBaseIndex) const override
Definition: dpgroup.cxx:908
GroupValueAttr GetGroupValue() const
Definition: dpitemdata.cxx:355
std::vector< SCROW > maMemberEntries
Definition: dpgroup.hxx:59
static void ProcessRowData(CalcInfo &rInfo, const CalcRowData &rData, bool bAutoShow)
Definition: dptabdat.cxx:168
virtual const ScDPItemData * GetMemberById(long nDim, long nId) override
Definition: dpgroup.cxx:547
void FillGroupFilter(ScDPFilteredCache::GroupFilter &rFilter) const
Definition: dpgroup.cxx:326
ScDPNumGroupDimension & operator=(const ScDPNumGroupDimension &rOther)
Definition: dpgroup.cxx:430
virtual void ReloadCacheTable() override
Definition: dpgroup.cxx:799
virtual void DisposeData() override
Definition: dpgroup.cxx:597
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
void addMatchItem(const ScDPItemData &rItem)
std::vector< SCROW > maMemberEntries
Definition: dpgroup.hxx:91
virtual std::vector< ScDPItemData > getMatchValues() const =0
void AddItem(const ScDPGroupItem &rItem)
Definition: dpgroup.cxx:364
SCROW GetIdByItemData(long nDim, const ScDPItemData &rItem) const
Definition: dpcache.cxx:1124
::std::vector< long > aPageDims
Definition: dptabdat.hxx:77
std::shared_ptr< ScDPTableData > pSourceData
Definition: dpgroup.hxx:117
Base class that abstracts different data source types of a datapilot table.
Definition: dptabdat.hxx:56
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:37
::std::vector< SCROW > aPageData
Definition: dptabdat.hxx:146
virtual void SetEmptyFlags(bool bIgnoreEmptyRows, bool bRepeatIfEmpty) override
Definition: dpgroup.cxx:608
virtual long GetSourceDim(long nDim) override
Definition: dpgroup.cxx:993
SvStream & endl(SvStream &rStr)
virtual long GetMembersCount(long nDim) override
Definition: dpgroup.cxx:519
static const sal_Int32 DateFirst
Definition: dpitemdata.hxx:36
bool HasCommonElement(const ScDPGroupItem &rOther) const
Definition: dpgroup.cxx:320
static sal_Int32 getDatePartValue(double fValue, const ScDPNumGroupInfo *pInfo, sal_Int32 nDatePart, const SvNumberFormatter *pFormatter)
Definition: dputil.cxx:293
ScDPGroupDimension & operator=(const ScDPGroupDimension &rOther)
Definition: dpgroup.cxx:354
const std::vector< SCROW > & GetNumEntries(SCCOL nSourceDim, const ScDPCache *pCache) const
Definition: dpgroup.cxx:453
double GetValue() const
Definition: dpitemdata.cxx:347
ScDPItemDataVec aElements
Definition: dpgroup.hxx:39
bool IsValue() const
Definition: dpitemdata.cxx:322
void GetNumGroupInfo(long nDimension, ScDPNumGroupInfo &rInfo)
Definition: dpgroup.cxx:514
void ModifyFilterCriteria(::std::vector< ScDPFilteredCache::Criterion > &rCriteria)
Definition: dpgroup.cxx:639
ScDPGroupDimension(long nSource, const OUString &rNewName)
Definition: dpgroup.cxx:332
virtual bool HasCommonElement(const ScDPItemData &rFirstData, long nFirstIndex, const ScDPItemData &rSecondData, long nSecondIndex) const override
Definition: dpgroup.cxx:936
ScDPNumGroupInfo aGroupInfo
Definition: dpgroup.hxx:90
virtual sal_uInt32 GetNumberFormat(long nDim) override
Definition: dpgroup.cxx:584
const ScDPGroupItem * GetGroupForData(const ScDPItemData &rData) const
Definition: dpgroup.cxx:384
const ScDPNumGroupInfo * GetNumGroupInfo(long nDim) const
Definition: dpcache.cxx:1373
const ScDPItemData * GetItemDataById(long nDim, SCROW nId) const
Definition: dpcache.cxx:985
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
virtual const ScDPFilteredCache & GetCacheTable() const override
Definition: dpgroup.cxx:794
const ScDPGroupItem * GetGroupForName(const ScDPItemData &rName) const
Definition: dpgroup.cxx:394
size_t GetItemCount() const
Definition: dpgroup.hxx:82
long GetDimensionIndex(const OUString &rName)
Definition: dpgroup.cxx:496
void SetGroupDim(long nDim)
Definition: dpgroup.cxx:369
bool IsNumGroupDimension(long nDimension) const
Definition: dpgroup.cxx:509
double maValue
const ScDPCache & getCache() const
sal_Int16 nValue
const ScDPItemData & GetName() const
Definition: dpgroup.hxx:46
bool IsDateDimension() const
Definition: dpgroup.hxx:110
virtual bool IsRepeatIfEmpty() override
Definition: dpgroup.cxx:613
void SetRangeStart(double fVal)
Definition: dpitemdata.cxx:160