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