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