LibreOffice Module sc (master)  1
dpcache.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 <dpcache.hxx>
21 
22 #include <document.hxx>
23 #include <queryentry.hxx>
24 #include <queryparam.hxx>
25 #include <dpobject.hxx>
26 #include <globstr.hrc>
27 #include <scresid.hxx>
28 #include <docoptio.hxx>
29 #include <dpitemdata.hxx>
30 #include <dputil.hxx>
31 #include <dpnumgroupinfo.hxx>
32 #include <columniterator.hxx>
33 #include <cellvalue.hxx>
34 
36 #include <rtl/math.hxx>
37 #include <unotools/charclass.hxx>
38 #include <unotools/textsearch.hxx>
41 #include <svl/zforlist.hxx>
42 #include <o3tl/safeint.hxx>
43 #include <osl/diagnose.h>
44 
45 #if DUMP_PIVOT_TABLE
46 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
47 #endif
48 
49 // TODO : Threaded pivot cache operation is disabled until we can figure out
50 // ways to make the edit engine and number formatter codes thread-safe in a
51 // proper fashion.
52 #define ENABLE_THREADED_PIVOT_CACHE 0
53 
54 #if ENABLE_THREADED_PIVOT_CACHE
55 #include <thread>
56 #include <future>
57 #include <queue>
58 #endif
59 
60 using namespace ::com::sun::star;
61 
62 using ::com::sun::star::uno::Exception;
63 
64 ScDPCache::GroupItems::GroupItems() : mnGroupType(0) {}
65 
66 ScDPCache::GroupItems::GroupItems(const ScDPNumGroupInfo& rInfo, sal_Int32 nGroupType) :
67  maInfo(rInfo), mnGroupType(nGroupType) {}
68 
69 ScDPCache::Field::Field() : mnNumFormat(0) {}
70 
72  mrDoc( rDoc ),
73  mnColumnCount ( 0 ),
74  maEmptyRows(0, rDoc.GetSheetLimits().GetMaxRowCount(), true),
75  mnDataSize(-1),
76  mnRowCount(0),
77  mbDisposing(false)
78 {
79 }
80 
81 namespace {
82 
83 struct ClearObjectSource
84 {
85  void operator() (ScDPObject* p) const
86  {
87  p->ClearTableData();
88  }
89 };
90 
91 }
92 
94 {
95  // Make sure no live ScDPObject instances hold reference to this cache any
96  // more.
97  mbDisposing = true;
98  std::for_each(maRefObjects.begin(), maRefObjects.end(), ClearObjectSource());
99 }
100 
101 namespace {
102 
107 class MacroInterpretIncrementer
108 {
109 public:
110  explicit MacroInterpretIncrementer(ScDocument& rDoc) :
111  mrDoc(rDoc)
112  {
113  mrDoc.IncMacroInterpretLevel();
114  }
115  ~MacroInterpretIncrementer()
116  {
117  mrDoc.DecMacroInterpretLevel();
118  }
119 private:
120  ScDocument& mrDoc;
121 };
122 
123 rtl_uString* internString( ScDPCache::StringSetType& rPool, const OUString& rStr )
124 {
125  return rPool.insert(rStr).first->pData;
126 }
127 
128 OUString createLabelString( const ScDocument& rDoc, SCCOL nCol, const ScRefCellValue& rCell )
129 {
130  OUString aDocStr = rCell.getRawString(rDoc);
131 
132  if (aDocStr.isEmpty())
133  {
134  // Replace an empty label string with column name.
135 
136  ScAddress aColAddr(nCol, 0, 0);
137  aDocStr = ScResId(STR_COLUMN) + " " + aColAddr.Format(ScRefFlags::COL_VALID);
138  }
139  return aDocStr;
140 }
141 
142 void initFromCell(
143  ScDPCache::StringSetType& rStrPool, const ScDocument& rDoc, const ScAddress& rPos,
144  const ScRefCellValue& rCell, ScDPItemData& rData, sal_uInt32& rNumFormat)
145 {
146  OUString aDocStr = rCell.getRawString(rDoc);
147  rNumFormat = 0;
148 
149  if (rCell.hasError())
150  {
151  rData.SetErrorStringInterned(internString(rStrPool, rDoc.GetString(rPos.Col(), rPos.Row(), rPos.Tab())));
152  }
153  else if (rCell.hasNumeric())
154  {
155  double fVal = rCell.getRawValue();
156  rNumFormat = rDoc.GetNumberFormat(rPos);
157  rData.SetValue(fVal);
158  }
159  else if (!rCell.isEmpty())
160  {
161  rData.SetStringInterned(internString(rStrPool, aDocStr));
162  }
163  else
164  rData.SetEmpty();
165 }
166 
167 struct Bucket
168 {
170  SCROW mnOrderIndex;
171  SCROW mnDataIndex;
172  Bucket() :
173  mnOrderIndex(0), mnDataIndex(0) {}
174  Bucket(const ScDPItemData& rValue, SCROW nData) :
175  maValue(rValue), mnOrderIndex(0), mnDataIndex(nData) {}
176 };
177 
178 #if DEBUG_PIVOT_TABLE
179 #include <iostream>
180 using std::cout;
181 using std::endl;
182 
183 struct PrintBucket
184 {
185  void operator() (const Bucket& v) const
186  {
187  cout << "value: " << v.maValue.GetValue() << " order index: " << v.mnOrderIndex << " data index: " << v.mnDataIndex << endl;
188  }
189 };
190 
191 #endif
192 
193 struct LessByValue
194 {
195  bool operator() (const Bucket& left, const Bucket& right) const
196  {
197  return left.maValue < right.maValue;
198  }
199 };
200 
201 struct LessByOrderIndex
202 {
203  bool operator() (const Bucket& left, const Bucket& right) const
204  {
205  return left.mnOrderIndex < right.mnOrderIndex;
206  }
207 };
208 
209 struct LessByDataIndex
210 {
211  bool operator() (const Bucket& left, const Bucket& right) const
212  {
213  return left.mnDataIndex < right.mnDataIndex;
214  }
215 };
216 
217 struct EqualByOrderIndex
218 {
219  bool operator() (const Bucket& left, const Bucket& right) const
220  {
221  return left.mnOrderIndex == right.mnOrderIndex;
222  }
223 };
224 
225 class PushBackValue
226 {
228 public:
229  explicit PushBackValue(ScDPCache::ScDPItemDataVec& _items) : mrItems(_items) {}
230  void operator() (const Bucket& v)
231  {
232  mrItems.push_back(v.maValue);
233  }
234 };
235 
236 class PushBackOrderIndex
237 {
239 public:
240  explicit PushBackOrderIndex(ScDPCache::IndexArrayType& _items) : mrData(_items) {}
241  void operator() (const Bucket& v)
242  {
243  mrData.push_back(v.mnOrderIndex);
244  }
245 };
246 
247 void processBuckets(std::vector<Bucket>& aBuckets, ScDPCache::Field& rField)
248 {
249  if (aBuckets.empty())
250  return;
251 
252  // Sort by the value.
253  comphelper::parallelSort(aBuckets.begin(), aBuckets.end(), LessByValue());
254 
255  {
256  // Set order index such that unique values have identical index value.
257  SCROW nCurIndex = 0;
258  std::vector<Bucket>::iterator it = aBuckets.begin(), itEnd = aBuckets.end();
259  ScDPItemData aPrev = it->maValue;
260  it->mnOrderIndex = nCurIndex;
261  for (++it; it != itEnd; ++it)
262  {
263  if (!aPrev.IsCaseInsEqual(it->maValue))
264  ++nCurIndex;
265 
266  it->mnOrderIndex = nCurIndex;
267  aPrev = it->maValue;
268  }
269  }
270 
271  // Re-sort the bucket this time by the data index.
272  comphelper::parallelSort(aBuckets.begin(), aBuckets.end(), LessByDataIndex());
273 
274  // Copy the order index series into the field object.
275  rField.maData.reserve(aBuckets.size());
276  std::for_each(aBuckets.begin(), aBuckets.end(), PushBackOrderIndex(rField.maData));
277 
278  // Sort by the value again.
279  comphelper::parallelSort(aBuckets.begin(), aBuckets.end(), LessByOrderIndex());
280 
281  // Unique by value.
282  std::vector<Bucket>::iterator itUniqueEnd =
283  std::unique(aBuckets.begin(), aBuckets.end(), EqualByOrderIndex());
284 
285  // Copy the unique values into items.
286  std::vector<Bucket>::iterator itBeg = aBuckets.begin();
287  size_t nLen = distance(itBeg, itUniqueEnd);
288  rField.maItems.reserve(nLen);
289  std::for_each(itBeg, itUniqueEnd, PushBackValue(rField.maItems));
290 }
291 
292 struct InitColumnData
293 {
294  ScDPCache::EmptyRowsType maEmptyRows;
295  OUString maLabel;
296 
297  ScDPCache::StringSetType* mpStrPool;
298  ScDPCache::Field* mpField;
299 
300  SCCOL mnCol;
301 
302  InitColumnData(ScSheetLimits const & rSheetLimits) :
303  maEmptyRows(0, rSheetLimits.GetMaxRowCount(), true),
304  mpStrPool(nullptr),
305  mpField(nullptr),
306  mnCol(-1) {}
307 
308  void init( SCCOL nCol, ScDPCache::StringSetType* pStrPool, ScDPCache::Field* pField )
309  {
310  mpStrPool = pStrPool;
311  mpField = pField;
312  mnCol = nCol;
313  }
314 };
315 
316 struct InitDocData
317 {
318  ScDocument& mrDoc;
319  SCTAB mnDocTab;
320  SCROW mnStartRow;
321  SCROW mnEndRow;
322  bool mbTailEmptyRows;
323 
324  InitDocData(ScDocument& rDoc) :
325  mrDoc(rDoc),
326  mnDocTab(-1),
327  mnStartRow(-1),
328  mnEndRow(-1),
329  mbTailEmptyRows(false) {}
330 };
331 
332 typedef std::unordered_set<OUString> LabelSet;
333 
334 void normalizeAddLabel(const OUString& rLabel, std::vector<OUString>& rLabels, LabelSet& rExistingNames)
335 {
336  const OUString aLabelLower = ScGlobal::getCharClassPtr()->lowercase(rLabel);
337  sal_Int32 nSuffix = 1;
338  OUString aNewLabel = rLabel;
339  OUString aNewLabelLower = aLabelLower;
340  while (true)
341  {
342  if (!rExistingNames.count(aNewLabelLower))
343  {
344  // this is a unique label.
345  rLabels.push_back(aNewLabel);
346  rExistingNames.insert(aNewLabelLower);
347  break;
348  }
349 
350  // This name already exists.
351  aNewLabel = rLabel + OUString::number(++nSuffix);
352  aNewLabelLower = aLabelLower + OUString::number(nSuffix);
353  }
354 }
355 
356 std::vector<OUString> normalizeLabels(const std::vector<InitColumnData>& rColData)
357 {
358  std::vector<OUString> aLabels(1u, ScResId(STR_PIVOT_DATA));
359 
360  LabelSet aExistingNames;
361 
362  for (const InitColumnData& rCol : rColData)
363  normalizeAddLabel(rCol.maLabel, aLabels, aExistingNames);
364 
365  return aLabels;
366 }
367 
368 std::vector<OUString> normalizeLabels(const ScDPCache::DBConnector& rDB, const sal_Int32 nLabelCount)
369 {
370  std::vector<OUString> aLabels(1u, ScResId(STR_PIVOT_DATA));
371  aLabels.reserve(nLabelCount + 1);
372 
373  LabelSet aExistingNames;
374 
375  for (sal_Int32 nCol = 0; nCol < nLabelCount; ++nCol)
376  {
377  OUString aColTitle = rDB.getColumnLabel(nCol);
378  normalizeAddLabel(aColTitle, aLabels, aExistingNames);
379  }
380 
381  return aLabels;
382 }
383 
384 void initColumnFromDoc( InitDocData& rDocData, InitColumnData &rColData )
385 {
386  ScDPCache::Field& rField = *rColData.mpField;
387  ScDocument& rDoc = rDocData.mrDoc;
388  SCTAB nDocTab = rDocData.mnDocTab;
389  SCCOL nCol = rColData.mnCol;
390  SCROW nStartRow = rDocData.mnStartRow;
391  SCROW nEndRow = rDocData.mnEndRow;
392  bool bTailEmptyRows = rDocData.mbTailEmptyRows;
393 
394  std::optional<sc::ColumnIterator> pIter =
395  rDoc.GetColumnIterator(nDocTab, nCol, nStartRow, nEndRow);
396  assert(pIter);
397  assert(pIter->hasCell());
398 
400 
401  rColData.maLabel = createLabelString(rDoc, nCol, pIter->getCell());
402  pIter->next();
403 
404  std::vector<Bucket> aBuckets;
405  aBuckets.reserve(nEndRow-nStartRow); // skip the topmost label cell.
406 
407  // Push back all original values.
408  for (SCROW i = 0, n = nEndRow-nStartRow; i < n; ++i, pIter->next())
409  {
410  assert(pIter->hasCell());
411 
412  sal_uInt32 nNumFormat = 0;
413  ScAddress aPos(nCol, pIter->getRow(), nDocTab);
414  initFromCell(*rColData.mpStrPool, rDoc, aPos, pIter->getCell(), aData, nNumFormat);
415 
416  aBuckets.emplace_back(aData, i);
417 
418  if (!aData.IsEmpty())
419  {
420  rColData.maEmptyRows.insert_back(i, i+1, false);
421  if (nNumFormat)
422  // Only take non-default number format.
423  rField.mnNumFormat = nNumFormat;
424  }
425  }
426 
427  processBuckets(aBuckets, rField);
428 
429  if (bTailEmptyRows)
430  {
431  // If the last item is not empty, append one. Note that the items
432  // are sorted, and empty item should come last when sorted.
433  if (rField.maItems.empty() || !rField.maItems.back().IsEmpty())
434  {
435  aData.SetEmpty();
436  rField.maItems.push_back(aData);
437  }
438  }
439 }
440 
441 #if ENABLE_THREADED_PIVOT_CACHE
442 
443 class ThreadQueue
444 {
445  using FutureType = std::future<void>;
446  std::queue<FutureType> maQueue;
447  std::mutex maMutex;
448  std::condition_variable maCond;
449 
450  size_t mnMaxQueue;
451 
452 public:
453  ThreadQueue( size_t nMaxQueue ) : mnMaxQueue(nMaxQueue) {}
454 
455  void push( std::function<void()> aFunc )
456  {
457  std::unique_lock<std::mutex> lock(maMutex);
458 
459  while (maQueue.size() >= mnMaxQueue)
460  maCond.wait(lock);
461 
462  FutureType f = std::async(std::launch::async, aFunc);
463  maQueue.push(std::move(f));
464  lock.unlock();
465 
466  maCond.notify_one();
467  }
468 
469  void waitForOne()
470  {
471  std::unique_lock<std::mutex> lock(maMutex);
472 
473  while (maQueue.empty())
474  maCond.wait(lock);
475 
476  FutureType ret = std::move(maQueue.front());
477  maQueue.pop();
478  lock.unlock();
479 
480  ret.get(); // This may throw if an exception was thrown on the async thread.
481 
482  maCond.notify_one();
483  }
484 };
485 
486 class ThreadScopedGuard
487 {
488  std::thread maThread;
489 public:
490  ThreadScopedGuard(std::thread thread) : maThread(std::move(thread)) {}
491  ThreadScopedGuard(ThreadScopedGuard&& other) : maThread(std::move(other.maThread)) {}
492 
493  ThreadScopedGuard(const ThreadScopedGuard&) = delete;
494  ThreadScopedGuard& operator= (const ThreadScopedGuard&) = delete;
495 
496  ~ThreadScopedGuard()
497  {
498  maThread.join();
499  }
500 };
501 
502 #endif
503 
504 }
505 
506 void ScDPCache::InitFromDoc(ScDocument& rDoc, const ScRange& rRange)
507 {
508  Clear();
509 
510  InitDocData aDocData(rDoc);
511 
512  // Make sure the formula cells within the data range are interpreted
513  // during this call, for this method may be called from the interpretation
514  // of GETPIVOTDATA, which disables nested formula interpretation without
515  // increasing the macro level.
516  MacroInterpretIncrementer aMacroInc(rDoc);
517 
518  aDocData.mnStartRow = rRange.aStart.Row(); // start of data
519  aDocData.mnEndRow = rRange.aEnd.Row();
520 
521  // Sanity check
522  if (!GetDoc().ValidRow(aDocData.mnStartRow) || !GetDoc().ValidRow(aDocData.mnEndRow) || aDocData.mnEndRow <= aDocData.mnStartRow)
523  return;
524 
525  SCCOL nStartCol = rRange.aStart.Col();
526  SCCOL nEndCol = rRange.aEnd.Col();
527  aDocData.mnDocTab = rRange.aStart.Tab();
528 
529  mnColumnCount = nEndCol - nStartCol + 1;
530 
531  // this row count must include the trailing empty rows.
532  mnRowCount = aDocData.mnEndRow - aDocData.mnStartRow; // skip the topmost label row.
533 
534  // Skip trailing empty rows if exists.
535  SCCOL nCol1 = nStartCol, nCol2 = nEndCol;
536  SCROW nRow1 = aDocData.mnStartRow, nRow2 = aDocData.mnEndRow;
537  rDoc.ShrinkToDataArea(aDocData.mnDocTab, nCol1, nRow1, nCol2, nRow2);
538  aDocData.mbTailEmptyRows = aDocData.mnEndRow > nRow2; // Trailing empty rows exist.
539  aDocData.mnEndRow = nRow2;
540 
541  if (aDocData.mnEndRow <= aDocData.mnStartRow)
542  {
543  // Check this again since the end row position has changed. It's
544  // possible that the new end row becomes lower than the start row
545  // after the shrinkage.
546  Clear();
547  return;
548  }
549 
551  std::vector<InitColumnData> aColData(mnColumnCount, InitColumnData(rDoc.GetSheetLimits()));
552  maFields.reserve(mnColumnCount);
553  for (SCCOL i = 0; i < mnColumnCount; ++i)
554  maFields.push_back(std::make_unique<Field>());
555 
556  maLabelNames.reserve(mnColumnCount+1);
557 
558  // Ensure that none of the formula cells in the data range are dirty.
559  rDoc.EnsureFormulaCellResults(rRange);
560 
561 #if ENABLE_THREADED_PIVOT_CACHE
562  ThreadQueue aQueue(std::thread::hardware_concurrency());
563 
564  auto aFuncLaunchFieldThreads = [&]()
565  {
566  for (sal_uInt16 nCol = nStartCol; nCol <= nEndCol; ++nCol)
567  {
568  size_t nDim = nCol - nStartCol;
569  InitColumnData& rColData = aColData[nDim];
570  rColData.init(nCol, &maStringPools[nDim], maFields[nDim].get());
571 
572  auto func = [&aDocData,&rColData]()
573  {
574  initColumnFromDoc(aDocData, rColData);
575  };
576 
577  aQueue.push(std::move(func));
578  }
579  };
580 
581  {
582  // Launch a separate thread that in turn spawns async threads to populate the fields.
583  std::thread t(aFuncLaunchFieldThreads);
584  ThreadScopedGuard sg(std::move(t));
585 
586  // Wait for all the async threads to complete on the main thread.
587  for (SCCOL i = 0; i < mnColumnCount; ++i)
588  aQueue.waitForOne();
589  }
590 
591 #else
592  for (sal_uInt16 nCol = nStartCol; nCol <= nEndCol; ++nCol)
593  {
594  size_t nDim = nCol - nStartCol;
595  InitColumnData& rColData = aColData[nDim];
596  rColData.init(nCol, &maStringPools[nDim], maFields[nDim].get());
597 
598  initColumnFromDoc(aDocData, rColData);
599  }
600 #endif
601 
602  maLabelNames = normalizeLabels(aColData);
603 
604  // Merge all non-empty rows data.
605  for (const InitColumnData& rCol : aColData)
606  {
607  EmptyRowsType::const_segment_iterator it = rCol.maEmptyRows.begin_segment();
608  EmptyRowsType::const_segment_iterator ite = rCol.maEmptyRows.end_segment();
609  EmptyRowsType::const_iterator pos = maEmptyRows.begin();
610 
611  for (; it != ite; ++it)
612  {
613  if (!it->value)
614  // Non-empty segment found. Record it.
615  pos = maEmptyRows.insert(pos, it->start, it->end, false).first;
616  }
617  }
618 
619  PostInit();
620 }
621 
623 {
624  Clear();
625 
626  try
627  {
630  maFields.clear();
631  maFields.reserve(mnColumnCount);
632  for (SCCOL i = 0; i < mnColumnCount; ++i)
633  maFields.push_back(std::make_unique<Field>());
634 
635  // Get column titles and types.
636  maLabelNames = normalizeLabels(rDB, mnColumnCount);
637 
638  std::vector<Bucket> aBuckets;
639  ScDPItemData aData;
640  for (sal_Int32 nCol = 0; nCol < mnColumnCount; ++nCol)
641  {
642  if (!rDB.first())
643  continue;
644 
645  aBuckets.clear();
646  Field& rField = *maFields[nCol];
647  SCROW nRow = 0;
648  do
649  {
650  SvNumFormatType nFormatType = SvNumFormatType::UNDEFINED;
651  aData.SetEmpty();
652  rDB.getValue(nCol, aData, nFormatType);
653  aBuckets.emplace_back(aData, nRow);
654  if (!aData.IsEmpty())
655  {
656  maEmptyRows.insert_back(nRow, nRow+1, false);
657  SvNumberFormatter* pFormatter = mrDoc.GetFormatTable();
658  rField.mnNumFormat = pFormatter ? pFormatter->GetStandardFormat(nFormatType) : 0;
659  }
660 
661  ++nRow;
662  }
663  while (rDB.next());
664 
665  processBuckets(aBuckets, rField);
666  }
667 
668  rDB.finish();
669 
670  if (!maFields.empty())
671  mnRowCount = maFields[0]->maData.size();
672 
673  PostInit();
674  return true;
675  }
676  catch (const Exception&)
677  {
678  return false;
679  }
680 }
681 
682 bool ScDPCache::ValidQuery( SCROW nRow, const ScQueryParam &rParam) const
683 {
684  if (!rParam.GetEntryCount())
685  return true;
686 
687  if (!rParam.GetEntry(0).bDoQuery)
688  return true;
689 
690  bool bMatchWholeCell = mrDoc.GetDocOptions().IsMatchWholeCell();
691 
692  SCSIZE nEntryCount = rParam.GetEntryCount();
693  std::vector<bool> aPassed(nEntryCount, false);
694 
695  tools::Long nPos = -1;
696  CollatorWrapper* pCollator = (rParam.bCaseSens ? ScGlobal::GetCaseCollator() :
698  ::utl::TransliterationWrapper* pTransliteration = (rParam.bCaseSens ?
700 
701  for (size_t i = 0; i < nEntryCount && rParam.GetEntry(i).bDoQuery; ++i)
702  {
703  const ScQueryEntry& rEntry = rParam.GetEntry(i);
704  const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
705  // we can only handle one single direct query
706  // #i115431# nField in QueryParam is the sheet column, not the field within the source range
707  SCCOL nQueryCol = static_cast<SCCOL>(rEntry.nField);
708  if ( nQueryCol < rParam.nCol1 )
709  nQueryCol = rParam.nCol1;
710  if ( nQueryCol > rParam.nCol2 )
711  nQueryCol = rParam.nCol2;
712  SCCOL nSourceField = nQueryCol - rParam.nCol1;
713  SCROW nId = GetItemDataId( nSourceField, nRow, false );
714  const ScDPItemData* pCellData = GetItemDataById( nSourceField, nId );
715 
716  bool bOk = false;
717 
718  if (rEntry.GetQueryItem().meType == ScQueryEntry::ByEmpty)
719  {
720  if (rEntry.IsQueryByEmpty())
721  bOk = pCellData->IsEmpty();
722  else
723  {
724  assert(rEntry.IsQueryByNonEmpty());
725  bOk = !pCellData->IsEmpty();
726  }
727  }
728  else if (rEntry.GetQueryItem().meType != ScQueryEntry::ByString && pCellData->IsValue())
729  { // by Value
730  double nCellVal = pCellData->GetValue();
731 
732  switch (rEntry.eOp)
733  {
734  case SC_EQUAL :
735  bOk = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
736  break;
737  case SC_LESS :
738  bOk = (nCellVal < rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
739  break;
740  case SC_GREATER :
741  bOk = (nCellVal > rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
742  break;
743  case SC_LESS_EQUAL :
744  bOk = (nCellVal < rItem.mfVal) || ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
745  break;
746  case SC_GREATER_EQUAL :
747  bOk = (nCellVal > rItem.mfVal) || ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
748  break;
749  case SC_NOT_EQUAL :
750  bOk = !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
751  break;
752  default:
753  bOk= false;
754  break;
755  }
756  }
757  else if ((rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL)
759  && pCellData->HasStringData() )
760  )
761  { // by String
762  OUString aCellStr = pCellData->GetString();
763 
764  bool bRealWildOrRegExp = (rParam.eSearchType != utl::SearchParam::SearchType::Normal &&
765  ((rEntry.eOp == SC_EQUAL) || (rEntry.eOp == SC_NOT_EQUAL)));
766  if (bRealWildOrRegExp)
767  {
768  sal_Int32 nStart = 0;
769  sal_Int32 nEnd = aCellStr.getLength();
770 
771  bool bMatch = rEntry.GetSearchTextPtr( rParam.eSearchType, rParam.bCaseSens, bMatchWholeCell )
772  ->SearchForward( aCellStr, &nStart, &nEnd );
773  // from 614 on, nEnd is behind the found text
774  if (bMatch && bMatchWholeCell
775  && (nStart != 0 || nEnd != aCellStr.getLength()))
776  bMatch = false; // RegExp must match entire cell string
777 
778  bOk = ((rEntry.eOp == SC_NOT_EQUAL) ? !bMatch : bMatch);
779  }
780  else
781  {
782  if (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL)
783  {
784  if (bMatchWholeCell)
785  {
786  // TODO: Use shared string for fast equality check.
787  OUString aStr = rEntry.GetQueryItem().maString.getString();
788  bOk = pTransliteration->isEqual(aCellStr, aStr);
789  bool bHasStar = false;
790  sal_Int32 nIndex;
791  if (( nIndex = aStr.indexOf('*') ) != -1)
792  bHasStar = true;
793  if (bHasStar && (nIndex>0))
794  {
795  for (sal_Int32 j=0;(j<nIndex) && (j< aCellStr.getLength()) ; j++)
796  {
797  if (aCellStr[j] == aStr[j])
798  {
799  bOk=true;
800  }
801  else
802  {
803  bOk=false;
804  break;
805  }
806  }
807  }
808  }
809  else
810  {
811  OUString aQueryStr = rEntry.GetQueryItem().maString.getString();
812  css::uno::Sequence< sal_Int32 > xOff;
813  const LanguageType nLang = ScGlobal::xSysLocale->GetLanguageTag().getLanguageType();
814  OUString aCell = pTransliteration->transliterate(
815  aCellStr, nLang, 0, aCellStr.getLength(), &xOff);
816  OUString aQuer = pTransliteration->transliterate(
817  aQueryStr, nLang, 0, aQueryStr.getLength(), &xOff);
818  bOk = (aCell.indexOf( aQuer ) != -1);
819  }
820  if (rEntry.eOp == SC_NOT_EQUAL)
821  bOk = !bOk;
822  }
823  else
824  { // use collator here because data was probably sorted
825  sal_Int32 nCompare = pCollator->compareString(
826  aCellStr, rEntry.GetQueryItem().maString.getString());
827  switch (rEntry.eOp)
828  {
829  case SC_LESS :
830  bOk = (nCompare < 0);
831  break;
832  case SC_GREATER :
833  bOk = (nCompare > 0);
834  break;
835  case SC_LESS_EQUAL :
836  bOk = (nCompare <= 0);
837  break;
838  case SC_GREATER_EQUAL :
839  bOk = (nCompare >= 0);
840  break;
841  case SC_NOT_EQUAL:
842  OSL_FAIL("SC_NOT_EQUAL");
843  break;
844  case SC_TOPVAL:
845  case SC_BOTVAL:
846  case SC_TOPPERC:
847  case SC_BOTPERC:
848  default:
849  break;
850  }
851  }
852  }
853  }
854 
855  if (nPos == -1)
856  {
857  nPos++;
858  aPassed[nPos] = bOk;
859  }
860  else
861  {
862  if (rEntry.eConnect == SC_AND)
863  {
864  aPassed[nPos] = aPassed[nPos] && bOk;
865  }
866  else
867  {
868  nPos++;
869  aPassed[nPos] = bOk;
870  }
871  }
872  }
873 
874  for (tools::Long j=1; j <= nPos; j++)
875  aPassed[0] = aPassed[0] || aPassed[j];
876 
877  bool bRet = aPassed[0];
878  return bRet;
879 }
880 
882 {
883  return mrDoc;
884 }
885 
887 {
888  return mnColumnCount;
889 }
890 
891 bool ScDPCache::IsRowEmpty(SCROW nRow) const
892 {
893  bool bEmpty = true;
894  maEmptyRows.search_tree(nRow, bEmpty);
895  return bEmpty;
896 }
897 
899 {
900  if (nDim < 0)
901  return nullptr;
902 
903  tools::Long nSourceCount = static_cast<tools::Long>(maFields.size());
904  if (nDim < nSourceCount)
905  return maFields[nDim]->mpGroup.get();
906 
907  nDim -= nSourceCount;
908  if (nDim < static_cast<tools::Long>(maGroupFields.size()))
909  return maGroupFields[nDim].get();
910 
911  return nullptr;
912 }
913 
914 OUString ScDPCache::GetDimensionName(std::vector<OUString>::size_type nDim) const
915 {
916  OSL_ENSURE(nDim < maLabelNames.size()-1 , "ScDPTableDataCache::GetDimensionName");
917  OSL_ENSURE(maLabelNames.size() == static_cast <sal_uInt16> (mnColumnCount+1), "ScDPTableDataCache::GetDimensionName");
918 
919  if ( nDim+1 < maLabelNames.size() )
920  {
921  return maLabelNames[nDim+1];
922  }
923  else
924  return OUString();
925 }
926 
928 {
929  OSL_ENSURE(!maFields.empty(), "Cache not initialized!");
930 
931  maEmptyRows.build_tree();
932  auto it = maEmptyRows.rbegin();
933  OSL_ENSURE(it != maEmptyRows.rend(), "corrupt flat_segment_tree instance!");
934  mnDataSize = maFields[0]->maData.size();
935  ++it; // Skip the first position.
936  OSL_ENSURE(it != maEmptyRows.rend(), "buggy version of flat_segment_tree is used.");
937  if (it->second)
938  {
939  SCROW nLastNonEmpty = it->first - 1;
940  if (nLastNonEmpty+1 < mnDataSize)
941  mnDataSize = nLastNonEmpty+1;
942  }
943 }
944 
946 {
947  mnColumnCount = 0;
948  mnRowCount = 0;
949  maFields.clear();
950  maLabelNames.clear();
951  maGroupFields.clear();
952  maEmptyRows.clear();
953  maStringPools.clear();
954 }
955 
956 SCROW ScDPCache::GetItemDataId(sal_uInt16 nDim, SCROW nRow, bool bRepeatIfEmpty) const
957 {
958  OSL_ENSURE(nDim < mnColumnCount, "ScDPTableDataCache::GetItemDataId ");
959 
960  const Field& rField = *maFields[nDim];
961  if (o3tl::make_unsigned(nRow) >= rField.maData.size())
962  {
963  // nRow is in the trailing empty rows area.
964  if (bRepeatIfEmpty)
965  nRow = rField.maData.size()-1; // Move to the last non-empty row.
966  else
967  // Return the last item, which should always be empty if the
968  // initialization has skipped trailing empty rows.
969  return rField.maItems.size()-1;
970 
971  }
972  else if (bRepeatIfEmpty)
973  {
974  while (nRow > 0 && rField.maItems[rField.maData[nRow]].IsEmpty())
975  --nRow;
976  }
977 
978  return rField.maData[nRow];
979 }
980 
982 {
983  if (nDim < 0 || nId < 0)
984  return nullptr;
985 
986  size_t nSourceCount = maFields.size();
987  size_t nDimPos = static_cast<size_t>(nDim);
988  size_t nItemId = static_cast<size_t>(nId);
989  if (nDimPos < nSourceCount)
990  {
991  // source field.
992  const Field& rField = *maFields[nDimPos];
993  if (nItemId < rField.maItems.size())
994  return &rField.maItems[nItemId];
995 
996  if (!rField.mpGroup)
997  return nullptr;
998 
999  nItemId -= rField.maItems.size();
1000  const ScDPItemDataVec& rGI = rField.mpGroup->maItems;
1001  if (nItemId >= rGI.size())
1002  return nullptr;
1003 
1004  return &rGI[nItemId];
1005  }
1006 
1007  // Try group fields.
1008  nDimPos -= nSourceCount;
1009  if (nDimPos >= maGroupFields.size())
1010  return nullptr;
1011 
1012  const ScDPItemDataVec& rGI = maGroupFields[nDimPos]->maItems;
1013  if (nItemId >= rGI.size())
1014  return nullptr;
1015 
1016  return &rGI[nItemId];
1017 }
1018 
1020 {
1021  return maFields.size();
1022 }
1023 
1025 {
1026  return maGroupFields.size();
1027 }
1028 
1030 {
1031  return mnRowCount;
1032 }
1033 
1035 {
1036  OSL_ENSURE(mnDataSize <= GetRowCount(), "Data size should never be larger than the row count.");
1037  return mnDataSize >= 0 ? mnDataSize : 0;
1038 }
1039 
1041 {
1042  if (nDim >= maFields.size())
1043  return nullptr;
1044 
1045  return &maFields[nDim]->maData;
1046 }
1047 
1049 {
1050  OSL_ENSURE( nDim>=0 && nDim < mnColumnCount ," nDim < mnColumnCount ");
1051  return maFields.at(nDim)->maItems;
1052 }
1053 
1054 sal_uInt32 ScDPCache::GetNumberFormat( tools::Long nDim ) const
1055 {
1056  if ( nDim >= mnColumnCount )
1057  return 0;
1058 
1059  // TODO: Find a way to determine the dominant number format in presence of
1060  // multiple number formats in the same field.
1061  return maFields[nDim]->mnNumFormat;
1062 }
1063 
1065 {
1066  if (nDim >= mnColumnCount)
1067  return false;
1068 
1069  SvNumberFormatter* pFormatter = mrDoc.GetFormatTable();
1070  if (!pFormatter)
1071  return false;
1072 
1073  SvNumFormatType eType = pFormatter->GetType(maFields[nDim]->mnNumFormat);
1074  return (eType == SvNumFormatType::DATE) || (eType == SvNumFormatType::DATETIME);
1075 }
1076 
1078 {
1079  OSL_ENSURE( nDim>=0 && nDim < mnColumnCount ," ScDPTableDataCache::GetDimMemberCount : out of bound ");
1080  return maFields[nDim]->maItems.size();
1081 }
1082 
1083 SCCOL ScDPCache::GetDimensionIndex(std::u16string_view sName) const
1084 {
1085  for (size_t i = 1; i < maLabelNames.size(); ++i)
1086  {
1087  if (maLabelNames[i] == sName)
1088  return static_cast<SCCOL>(i-1);
1089  }
1090  return -1;
1091 }
1092 
1093 rtl_uString* ScDPCache::InternString( size_t nDim, const OUString& rStr )
1094 {
1095  assert(nDim < maStringPools.size());
1096  return internString(maStringPools[nDim], rStr);
1097 }
1098 
1100 {
1101  maRefObjects.insert(pObj);
1102 }
1103 
1105 {
1106  if (mbDisposing)
1107  // Object being deleted.
1108  return;
1109 
1110  maRefObjects.erase(pObj);
1111  if (maRefObjects.empty())
1113 }
1114 
1116 {
1117  return maRefObjects;
1118 }
1119 
1121 {
1122  if (nDim < 0)
1123  return -1;
1124 
1125  if (nDim < mnColumnCount)
1126  {
1127  // source field.
1128  const ScDPItemDataVec& rItems = maFields[nDim]->maItems;
1129  for (size_t i = 0, n = rItems.size(); i < n; ++i)
1130  {
1131  if (rItems[i] == rItem)
1132  return i;
1133  }
1134 
1135  if (!maFields[nDim]->mpGroup)
1136  return -1;
1137 
1138  // grouped source field.
1139  const ScDPItemDataVec& rGI = maFields[nDim]->mpGroup->maItems;
1140  for (size_t i = 0, n = rGI.size(); i < n; ++i)
1141  {
1142  if (rGI[i] == rItem)
1143  return rItems.size() + i;
1144  }
1145  return -1;
1146  }
1147 
1148  // group field.
1149  nDim -= mnColumnCount;
1150  if (o3tl::make_unsigned(nDim) < maGroupFields.size())
1151  {
1152  const ScDPItemDataVec& rGI = maGroupFields[nDim]->maItems;
1153  for (size_t i = 0, n = rGI.size(); i < n; ++i)
1154  {
1155  if (rGI[i] == rItem)
1156  return i;
1157  }
1158  }
1159 
1160  return -1;
1161 }
1162 
1163 // static
1164 sal_uInt32 ScDPCache::GetLocaleIndependentFormat( SvNumberFormatter& rFormatter, sal_uInt32 nNumFormat )
1165 {
1166  // For a date or date+time format use ISO format so it works across locales
1167  // and can be matched against string based item queries. For time use 24h
1168  // format. All others use General format, no currency, percent, ...
1169  // Use en-US locale for all.
1170  switch (rFormatter.GetType( nNumFormat))
1171  {
1172  case SvNumFormatType::DATE:
1174  case SvNumFormatType::TIME:
1175  return rFormatter.GetFormatIndex( NF_TIME_HHMMSS, LANGUAGE_ENGLISH_US);
1176  case SvNumFormatType::DATETIME:
1178  default:
1180  }
1181 }
1182 
1183 // static
1185 {
1186  return rtl::math::doubleToUString( fValue, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true);
1187 }
1188 
1189 // static
1191  SvNumberFormatter& rFormatter, sal_uInt32 nNumFormat )
1192 {
1193  nNumFormat = GetLocaleIndependentFormat( rFormatter, nNumFormat);
1194  if ((nNumFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
1196 
1197  OUString aStr;
1198  const Color* pColor = nullptr;
1199  rFormatter.GetOutputString( fValue, nNumFormat, aStr, &pColor);
1200  return aStr;
1201 }
1202 
1203 OUString ScDPCache::GetFormattedString(tools::Long nDim, const ScDPItemData& rItem, bool bLocaleIndependent) const
1204 {
1205  if (nDim < 0)
1206  return rItem.GetString();
1207 
1208  ScDPItemData::Type eType = rItem.GetType();
1209  if (eType == ScDPItemData::Value)
1210  {
1211  // Format value using the stored number format.
1212  SvNumberFormatter* pFormatter = mrDoc.GetFormatTable();
1213  if (pFormatter)
1214  {
1215  sal_uInt32 nNumFormat = GetNumberFormat(nDim);
1216  if (bLocaleIndependent)
1217  return GetLocaleIndependentFormattedString( rItem.GetValue(), *pFormatter, nNumFormat);
1218 
1219  OUString aStr;
1220  const Color* pColor = nullptr;
1221  pFormatter->GetOutputString(rItem.GetValue(), nNumFormat, aStr, &pColor);
1222  return aStr;
1223  }
1224 
1225  // Last resort...
1227  }
1228 
1229  if (eType == ScDPItemData::GroupValue)
1230  {
1232  double fStart = 0.0, fEnd = 0.0;
1233  const GroupItems* p = GetGroupItems(nDim);
1234  if (p)
1235  {
1236  fStart = p->maInfo.mfStart;
1237  fEnd = p->maInfo.mfEnd;
1238  }
1240  aAttr.mnGroupType, aAttr.mnValue, mrDoc.GetFormatTable(), fStart, fEnd);
1241  }
1242 
1243  if (eType == ScDPItemData::RangeStart)
1244  {
1245  double fVal = rItem.GetValue();
1246  const GroupItems* p = GetGroupItems(nDim);
1247  if (!p)
1248  return rItem.GetString();
1249 
1251  return ScDPUtil::getNumGroupName(fVal, p->maInfo, cDecSep, mrDoc.GetFormatTable());
1252  }
1253 
1254  return rItem.GetString();
1255 }
1256 
1258 {
1259  return mrDoc.GetFormatTable();
1260 }
1261 
1263 {
1264  maGroupFields.push_back(std::make_unique<GroupItems>());
1265  return static_cast<tools::Long>(maFields.size() + maGroupFields.size() - 1);
1266 }
1267 
1268 void ScDPCache::ResetGroupItems(tools::Long nDim, const ScDPNumGroupInfo& rNumInfo, sal_Int32 nGroupType)
1269 {
1270  if (nDim < 0)
1271  return;
1272 
1273  tools::Long nSourceCount = static_cast<tools::Long>(maFields.size());
1274  if (nDim < nSourceCount)
1275  {
1276  maFields.at(nDim)->mpGroup.reset(new GroupItems(rNumInfo, nGroupType));
1277  return;
1278  }
1279 
1280  nDim -= nSourceCount;
1281  if (nDim < static_cast<tools::Long>(maGroupFields.size()))
1282  {
1283  GroupItems& rGI = *maGroupFields[nDim];
1284  rGI.maItems.clear();
1285  rGI.maInfo = rNumInfo;
1286  rGI.mnGroupType = nGroupType;
1287  }
1288 }
1289 
1291 {
1292  if (nDim < 0)
1293  return -1;
1294 
1295  tools::Long nSourceCount = static_cast<tools::Long>(maFields.size());
1296  if (nDim < nSourceCount)
1297  {
1298  GroupItems& rGI = *maFields.at(nDim)->mpGroup;
1299  rGI.maItems.push_back(rData);
1300  SCROW nId = maFields[nDim]->maItems.size() + rGI.maItems.size() - 1;
1301  return nId;
1302  }
1303 
1304  nDim -= nSourceCount;
1305  if (nDim < static_cast<tools::Long>(maGroupFields.size()))
1306  {
1307  ScDPItemDataVec& rItems = maGroupFields.at(nDim)->maItems;
1308  rItems.push_back(rData);
1309  return rItems.size()-1;
1310  }
1311 
1312  return -1;
1313 }
1314 
1315 void ScDPCache::GetGroupDimMemberIds(tools::Long nDim, std::vector<SCROW>& rIds) const
1316 {
1317  if (nDim < 0)
1318  return;
1319 
1320  tools::Long nSourceCount = static_cast<tools::Long>(maFields.size());
1321  if (nDim < nSourceCount)
1322  {
1323  if (!maFields.at(nDim)->mpGroup)
1324  return;
1325 
1326  size_t nOffset = maFields[nDim]->maItems.size();
1327  const ScDPItemDataVec& rGI = maFields[nDim]->mpGroup->maItems;
1328  for (size_t i = 0, n = rGI.size(); i < n; ++i)
1329  rIds.push_back(static_cast<SCROW>(i + nOffset));
1330 
1331  return;
1332  }
1333 
1334  nDim -= nSourceCount;
1335  if (nDim < static_cast<tools::Long>(maGroupFields.size()))
1336  {
1337  const ScDPItemDataVec& rGI = maGroupFields.at(nDim)->maItems;
1338  for (size_t i = 0, n = rGI.size(); i < n; ++i)
1339  rIds.push_back(static_cast<SCROW>(i));
1340  }
1341 }
1342 
1343 namespace {
1344 
1345 struct ClearGroupItems
1346 {
1347  void operator() (const std::unique_ptr<ScDPCache::Field>& r) const
1348  {
1349  r->mpGroup.reset();
1350  }
1351 };
1352 
1353 }
1354 
1356 {
1357  maGroupFields.clear();
1358 }
1359 
1361 {
1362  ClearGroupFields();
1363  std::for_each(maFields.begin(), maFields.end(), ClearGroupItems());
1364 }
1365 
1367 {
1368  if (nDim < 0)
1369  return nullptr;
1370 
1371  tools::Long nSourceCount = static_cast<tools::Long>(maFields.size());
1372  if (nDim < nSourceCount)
1373  {
1374  if (!maFields.at(nDim)->mpGroup)
1375  return nullptr;
1376 
1377  return &maFields[nDim]->mpGroup->maInfo;
1378  }
1379 
1380  nDim -= nSourceCount;
1381  if (nDim < static_cast<tools::Long>(maGroupFields.size()))
1382  return &maGroupFields.at(nDim)->maInfo;
1383 
1384  return nullptr;
1385 }
1386 
1388 {
1389  if (nDim < 0)
1390  return 0;
1391 
1392  tools::Long nSourceCount = static_cast<tools::Long>(maFields.size());
1393  if (nDim < nSourceCount)
1394  {
1395  if (!maFields.at(nDim)->mpGroup)
1396  return 0;
1397 
1398  return maFields[nDim]->mpGroup->mnGroupType;
1399  }
1400 
1401  nDim -= nSourceCount;
1402  if (nDim < static_cast<tools::Long>(maGroupFields.size()))
1403  return maGroupFields.at(nDim)->mnGroupType;
1404 
1405  return 0;
1406 }
1407 
1408 #if DUMP_PIVOT_TABLE
1409 
1410 namespace {
1411 
1412 void dumpItems(const ScDPCache& rCache, tools::Long nDim, const ScDPCache::ScDPItemDataVec& rItems, size_t nOffset)
1413 {
1414  for (size_t i = 0; i < rItems.size(); ++i)
1415  cout << " " << (i+nOffset) << ": " << rCache.GetFormattedString(nDim, rItems[i], false) << endl;
1416 }
1417 
1418 void dumpSourceData(const ScDPCache& rCache, tools::Long nDim, const ScDPCache::ScDPItemDataVec& rItems, const ScDPCache::IndexArrayType& rArray)
1419 {
1420  for (const auto& rIndex : rArray)
1421  cout << " '" << rCache.GetFormattedString(nDim, rItems[rIndex], false) << "'" << endl;
1422 }
1423 
1424 const char* getGroupTypeName(sal_Int32 nType)
1425 {
1426  static const char* pNames[] = {
1427  "", "years", "quarters", "months", "days", "hours", "minutes", "seconds"
1428  };
1429 
1430  switch (nType)
1431  {
1432  case sheet::DataPilotFieldGroupBy::YEARS: return pNames[1];
1433  case sheet::DataPilotFieldGroupBy::QUARTERS: return pNames[2];
1434  case sheet::DataPilotFieldGroupBy::MONTHS: return pNames[3];
1435  case sheet::DataPilotFieldGroupBy::DAYS: return pNames[4];
1436  case sheet::DataPilotFieldGroupBy::HOURS: return pNames[5];
1437  case sheet::DataPilotFieldGroupBy::MINUTES: return pNames[6];
1438  case sheet::DataPilotFieldGroupBy::SECONDS: return pNames[7];
1439  default:
1440  ;
1441  }
1442 
1443  return pNames[0];
1444 }
1445 
1446 }
1447 
1448 void ScDPCache::Dump() const
1449 {
1450  // Change these flags to fit your debugging needs.
1451  bool bDumpItems = false;
1452  bool bDumpSourceData = false;
1453 
1454  cout << "--- pivot cache dump" << endl;
1455  {
1456  size_t i = 0;
1457  for (const auto& rxField : maFields)
1458  {
1459  const Field& fld = *rxField;
1460  cout << "* source dimension: " << GetDimensionName(i) << " (ID = " << i << ")" << endl;
1461  cout << " item count: " << fld.maItems.size() << endl;
1462  if (bDumpItems)
1463  dumpItems(*this, i, fld.maItems, 0);
1464  if (fld.mpGroup)
1465  {
1466  cout << " group item count: " << fld.mpGroup->maItems.size() << endl;
1467  cout << " group type: " << getGroupTypeName(fld.mpGroup->mnGroupType) << endl;
1468  if (bDumpItems)
1469  dumpItems(*this, i, fld.mpGroup->maItems, fld.maItems.size());
1470  }
1471 
1472  if (bDumpSourceData)
1473  {
1474  cout << " source data (re-constructed):" << endl;
1475  dumpSourceData(*this, i, fld.maItems, fld.maData);
1476  }
1477 
1478  ++i;
1479  }
1480  }
1481 
1482  {
1483  size_t i = maFields.size();
1484  for (const auto& rxGroupField : maGroupFields)
1485  {
1486  const GroupItems& gi = *rxGroupField;
1487  cout << "* group dimension: (unnamed) (ID = " << i << ")" << endl;
1488  cout << " item count: " << gi.maItems.size() << endl;
1489  cout << " group type: " << getGroupTypeName(gi.mnGroupType) << endl;
1490  if (bDumpItems)
1491  dumpItems(*this, i, gi.maItems, 0);
1492  ++i;
1493  }
1494  }
1495 
1496  {
1497  struct { SCROW start; SCROW end; bool empty; } aRange;
1498  cout << "* empty rows: " << endl;
1499  mdds::flat_segment_tree<SCROW, bool>::const_iterator it = maEmptyRows.begin(), itEnd = maEmptyRows.end();
1500  if (it != itEnd)
1501  {
1502  aRange.start = it->first;
1503  aRange.empty = it->second;
1504 
1505  for (++it; it != itEnd; ++it)
1506  {
1507  aRange.end = it->first-1;
1508  cout << " rows " << aRange.start << "-" << aRange.end << ": " << (aRange.empty ? "empty" : "not-empty") << endl;
1509  aRange.start = it->first;
1510  aRange.empty = it->second;
1511  }
1512  }
1513  }
1514 
1515  cout << "---" << endl;
1516 }
1517 
1518 #endif
1519 
1520 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void SetValue(double fVal)
Definition: dpitemdata.cxx:153
ScDPNumGroupInfo maInfo
Definition: dpcache.hxx:59
size_t GetGroupFieldCount() const
Definition: dpcache.cxx:1024
static sal_uInt32 GetLocaleIndependentFormat(SvNumberFormatter &rFormatter, sal_uInt32 nNumFormat)
Definition: dpcache.cxx:1164
sal_Int32 nIndex
OUString getString() const
virtual bool first()=0
ScDPItemDataVec maItems
Definition: dpcache.hxx:58
ScAddress aStart
Definition: address.hxx:499
bool hasError() const
Definition: cellvalue.cxx:627
OUString GetFormattedString(tools::Long nDim, const ScDPItemData &rItem, bool bLocaleIndependent) const
Definition: dpcache.cxx:1203
EmptyRowsType maEmptyRows
Definition: dpcache.hxx:127
sal_Int32 compareString(const OUString &s1, const OUString &s2) const
tools::Long GetDimMemberCount(tools::Long nDim) const
Definition: dpcache.cxx:1077
sal_Int32 mnGroupType
Definition: dpcache.hxx:60
OUString GetDimensionName(std::vector< OUString >::size_type nDim) const
Definition: dpcache.cxx:914
SCROW Row() const
Definition: address.hxx:261
static OUString GetLocaleIndependentFormattedString(double fValue, SvNumberFormatter &rFormatter, sal_uInt32 nNumFormat)
Definition: dpcache.cxx:1190
#define LANGUAGE_ENGLISH_US
OUString getRawString(const ScDocument &rDoc) const
Retrieve a string value without modifying the states of any objects in the referenced document store...
Definition: cellvalue.cxx:665
std::vector< StringSetType > maStringPools
Definition: dpcache.hxx:124
const Item & GetQueryItem() const
Definition: queryentry.hxx:84
static SC_DLLPUBLIC::utl::TransliterationWrapper * GetpTransliteration()
Definition: global.cxx:975
virtual bool next()=0
size_t GetFieldCount() const
Definition: dpcache.cxx:1019
sal_uInt32 GetFormatIndex(NfIndexTableOffset, LanguageType eLnge=LANGUAGE_DONTKNOW)
::utl::TransliterationWrapper * GetCaseTransliteration()
Definition: global.cxx:987
bool isEmpty() const
Definition: cellvalue.cxx:670
std::optional< sc::ColumnIterator > GetColumnIterator(SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2) const
Definition: document10.cxx:966
SCROW SetGroupItem(tools::Long nDim, const ScDPItemData &rData)
Definition: dpcache.cxx:1290
ScDocument & GetDoc() const
Definition: dpcache.cxx:881
SCROW GetIdByItemData(tools::Long nDim, const ScDPItemData &rItem) const
Definition: dpcache.cxx:1120
ScDPCache(const ScDPCache &)=delete
long Long
const GroupItems * GetGroupItems(tools::Long nDim) const
Definition: dpcache.cxx:898
static OUString getNumGroupName(double fValue, const ScDPNumGroupInfo &rInfo, sal_Unicode cDecSep, SvNumberFormatter *pFormatter)
Definition: dputil.cxx:260
sal_Int64 n
SC_DLLPUBLIC ScDPCollection * GetDPCollection()
Definition: documen3.cxx:365
static SC_DLLPUBLIC CollatorWrapper * GetCollator()
Definition: global.cxx:1040
sal_Int32 mnCol
SvNumFormatType GetType(sal_uInt32 nFIndex) const
rtl_uString * InternString(size_t nDim, const OUString &rStr)
Definition: dpcache.cxx:1093
sal_Int16 nId
SC_DLLPUBLIC void GetNumberFormat(SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt32 &rFormat) const
Definition: document.cxx:3672
SCCOLROW nField
Definition: queryentry.hxx:60
ScAddress aEnd
Definition: address.hxx:500
bool IsQueryByEmpty() const
Definition: queryentry.cxx:86
const ScDPItemDataVec & GetDimMemberValues(SCCOL nDim) const
Definition: dpcache.cxx:1048
This is very similar to ScCellValue, except that it references the original value instead of copying ...
Definition: cellvalue.hxx:103
This class represents the cached data part of the datapilot cache table implementation.
Definition: dpcache.hxx:47
void InitFromDoc(ScDocument &rDoc, const ScRange &rRange)
Definition: dpcache.cxx:506
::osl::Mutex maMutex
bool IsEmpty() const
Definition: dpitemdata.cxx:317
std::vector< sal_Int8 > maData
SCROW GetItemDataId(sal_uInt16 nDim, SCROW nRow, bool bRepeatIfEmpty) const
Definition: dpcache.cxx:956
SCCOL GetDimensionIndex(std::u16string_view sName) const
Definition: dpcache.cxx:1083
SCCOL mnColumnCount
Definition: dpcache.hxx:112
std::unordered_set< OUString > StringSetType
Definition: dpcache.hxx:50
NF_NUMBER_STANDARD
sal_uInt16 sal_Unicode
bool IsRowEmpty(SCROW nRow) const
Definition: dpcache.cxx:891
ObjectFormatterData & mrData
static SC_DLLPUBLIC OUString getDateGroupName(sal_Int32 nDatePart, sal_Int32 nValue, SvNumberFormatter *pFormatter, double fStart, double fEnd)
Definition: dputil.cxx:102
sal_uInt32 mnNumFormat
Definition: dpcache.hxx:87
size_t SCSIZE
size_t typedef to be able to find places where code was changed from USHORT to size_t and is used to ...
Definition: address.hxx:44
size_t pos
sal_Int32 GetGroupType(tools::Long nDim) const
Return a group type identifier.
Definition: dpcache.cxx:1387
tools::Long AppendGroupField()
Definition: dpcache.cxx:1262
SCROW GetRowCount() const
Row count is the number of records plus any trailing empty rows in case the source data is sheet and ...
Definition: dpcache.cxx:1029
SC_DLLPUBLIC SCSIZE GetEntryCount() const
Definition: queryparam.cxx:119
SC_DLLPUBLIC OUString GetString(SCCOL nCol, SCROW nRow, SCTAB nTab, const ScInterpreterContext *pContext=nullptr) const
Definition: document.cxx:3517
const ScDPItemData * GetItemDataById(tools::Long nDim, SCROW nId) const
Definition: dpcache.cxx:981
SC_DLLPUBLIC const ScQueryEntry & GetEntry(SCSIZE n) const
Definition: queryparam.cxx:124
void AddReference(ScDPObject *pObj) const
Definition: dpcache.cxx:1099
SCTAB Tab() const
Definition: address.hxx:270
NF_DATE_ISO_YYYYMMDD
virtual OUString getColumnLabel(tools::Long nCol) const =0
NF_TIME_HHMMSS
void SetStringInterned(rtl_uString *pS)
Definition: dpitemdata.cxx:145
const ScDPNumGroupInfo * GetNumGroupInfo(tools::Long nDim) const
Definition: dpcache.cxx:1366
void Clear()
Definition: dpcache.cxx:945
~ScDPCache()
Definition: dpcache.cxx:93
ScDPObjectSet maRefObjects
All pivot table objects that references this cache.
Definition: dpcache.hxx:117
utl::TextSearch * GetSearchTextPtr(utl::SearchParam::SearchType eSearchType, bool bCaseSens, bool bWildMatchSel) const
creates pSearchParam and pSearchText if necessary
Definition: queryentry.cxx:193
std::unique_ptr< GroupItems > mpGroup
Optional items for grouped field.
Definition: dpcache.hxx:73
constexpr OUStringLiteral aData
bool IsMatchWholeCell() const
Definition: docoptio.hxx:55
bool SearchForward(const OUString &rStr, sal_Int32 *pStart, sal_Int32 *pEnd, css::util::SearchResult *pRes=nullptr)
double distance
DocumentType eType
std::vector< OUString > maLabelNames
Definition: dpcache.hxx:126
SC_DLLPUBLIC const ScDocOptions & GetDocOptions() const
Definition: documen3.cxx:1939
Interface for connecting to database source.
Definition: dpcache.hxx:97
const OUString & getNumDecimalSep() const
double getRawValue() const
Retrieve a numeric value without modifying the states of any objects in the referenced document store...
Definition: cellvalue.cxx:646
void ClearGroupFields()
Definition: dpcache.cxx:1355
SC_DLLPUBLIC SvNumberFormatter * GetFormatTable() const
Definition: documen2.cxx:440
SCROW mnDataSize
Definition: dpcache.hxx:128
Type GetType() const
Definition: dpitemdata.hxx:67
When assigning a string value, you can also assign an interned string whose life-cycle is managed by ...
Definition: dpitemdata.hxx:28
int i
bool IsQueryByNonEmpty() const
Definition: queryentry.cxx:108
ScDocument & mrDoc
Definition: dpcache.hxx:111
sal_Int16 SCCOL
Definition: types.hxx:21
void ResetGroupItems(tools::Long nDim, const ScDPNumGroupInfo &rNumInfo, sal_Int32 nGroupType)
Definition: dpcache.cxx:1268
SCROW GetDataSize() const
Data size is the number of records without any trailing empty rows for sheet source data...
Definition: dpcache.cxx:1034
ScSheetLimits & GetSheetLimits() const
Definition: document.hxx:873
SvNumberFormatter * GetNumberFormatter() const
Definition: dpcache.cxx:1257
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
void SetErrorStringInterned(rtl_uString *pS)
Definition: dpitemdata.cxx:181
std::vector< ScDPItemData > ScDPItemDataVec
Definition: dpcache.hxx:52
tools::Long GetColumnCount() const
Definition: dpcache.cxx:886
bool isEqual(const OUString &rStr1, const OUString &rStr2) const
GroupValueAttr GetGroupValue() const
Definition: dpitemdata.cxx:355
bool ShrinkToDataArea(SCTAB nTab, SCCOL &rStartCol, SCROW &rStartRow, SCCOL &rEndCol, SCROW &rEndRow) const
Shrink a range to only include data area.
Definition: document.cxx:1031
SvNumFormatType
void ClearTableData()
Definition: dpobject.cxx:800
void ClearAllFields()
Definition: dpcache.cxx:1360
static CollatorWrapper * GetCaseCollator()
Definition: global.cxx:1051
const_iterator end() const
bool hasNumeric() const
Definition: cellvalue.cxx:622
#define SV_COUNTRY_LANGUAGE_OFFSET
bool empty() const
OUString lowercase(const OUString &rStr, sal_Int32 nPos, sal_Int32 nCount) const
svl::SharedString maString
Definition: queryentry.hxx:49
virtual void finish()=0
enumrange< T >::Iterator end(enumrange< T >)
SCCOL Col() const
Definition: address.hxx:266
XPropertyListType t
GroupFieldsType maGroupFields
Definition: dpcache.hxx:123
void RemoveCache(const ScDPCache *pCache)
Only to be called from ScDPCache::RemoveReference().
Definition: dpobject.cxx:3829
const_iterator begin() const
IndexArrayType maData
Original source data represented as indices to the unique value list.
Definition: dpcache.hxx:85
const IndexArrayType * GetFieldIndexArray(size_t nDim) const
Definition: dpcache.cxx:1040
void PostInit()
Definition: dpcache.cxx:927
static SC_DLLPUBLIC const LocaleDataWrapper * getLocaleDataPtr()
Definition: global.cxx:1000
static std::unique_ptr< SvtSysLocale > xSysLocale
Definition: global.hxx:535
SC_DLLPUBLIC bool EnsureFormulaCellResults(const ScRange &rRange, bool bSkipRunning=false)
Make sure all of the formula cells in the specified range have been fully calculated.
Definition: document10.cxx:984
bool mbDisposing
Definition: dpcache.hxx:131
bool InitFromDataBase(DBConnector &rDB)
Definition: dpcache.cxx:622
sal_Int32 SCROW
Definition: types.hxx:17
ScDPItemDataVec maItems
Unique values in the field, stored in ascending order.
Definition: dpcache.hxx:78
static OUString GetLocaleIndependentFormattedNumberString(double fValue)
Definition: dpcache.cxx:1184
OUString ScResId(std::string_view aId)
Definition: scdll.cxx:89
void RemoveReference(ScDPObject *pObj) const
Definition: dpcache.cxx:1104
bool IsDateDimension(tools::Long nDim) const
Definition: dpcache.cxx:1064
SvStream & endl(SvStream &rStr)
OUString transliterate(const OUString &rStr, sal_Int32 nStart, sal_Int32 nLen) const
sal_uInt32 GetStandardFormat(SvNumFormatType eType, LanguageType eLnge=LANGUAGE_DONTKNOW)
if(aStr!=aBuf) UpdateName_Impl(m_xFollowLb.get()
const ScDPObjectSet & GetAllReferences() const
Definition: dpcache.cxx:1115
sal_uInt32 GetNumberFormat(tools::Long nDim) const
Definition: dpcache.cxx:1054
bool HasStringData() const
Definition: dpitemdata.cxx:366
FieldsType maFields
Definition: dpcache.hxx:122
NF_DATETIME_ISO_YYYYMMDD_HHMMSS
static SC_DLLPUBLIC const CharClass * getCharClassPtr()
Definition: global.cxx:1009
void * p
QPRO_FUNC_TYPE nType
Definition: qproform.cxx:400
double GetValue() const
Definition: dpitemdata.cxx:347
void Dump() const
ScQueryConnect eConnect
Definition: queryentry.hxx:62
virtual tools::Long getColumnCount() const =0
bool IsValue() const
Definition: dpitemdata.cxx:322
FILE * init(int, char **)
std::vector< SCROW > IndexArrayType
Definition: dpcache.hxx:54
bool ValidRow(SCROW nRow, SCROW nMaxRow)
Definition: address.hxx:98
void(* f)(TrueTypeTable *)
OUString GetString() const
Definition: dpitemdata.cxx:327
virtual void getValue(tools::Long nCol, ScDPItemData &rData, SvNumFormatType &rNumType) const =0
bool IsCaseInsEqual(const ScDPItemData &r) const
Definition: dpitemdata.cxx:187
std::pair< const_iterator, bool > insert(Value &&x)
void GetGroupDimMemberIds(tools::Long nDim, std::vector< SCROW > &rIds) const
Definition: dpcache.cxx:1315
ScQueryOp eOp
Definition: queryentry.hxx:61
void parallelSort(const RandItr aBegin, const RandItr aEnd, Compare aComp=Compare())
aStr
mdds::flat_segment_tree< SCROW, bool > EmptyRowsType
Definition: dpcache.hxx:51
std::shared_ptr< osl::Mutex > const & lock()
Each instance of this struct represents a single filtering criteria.
Definition: queryentry.hxx:33
void GetOutputString(const double &fOutNumber, sal_uInt32 nFIndex, OUString &sOutString, const Color **ppColor, bool bUseStarFormat=false)
void SetEmpty()
Definition: dpitemdata.cxx:131
utl::SearchParam::SearchType eSearchType
Definition: queryparam.hxx:43
double maValue
sal_uInt16 nPos
sal_Int16 SCTAB
Definition: types.hxx:22
size_type erase(const Value &x)
SCROW mnRowCount
Definition: dpcache.hxx:129
bool ValidQuery(SCROW nRow, const ScQueryParam &rQueryParam) const
Definition: dpcache.cxx:682