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  mpDoc( pDoc ),
73  mnColumnCount ( 0 ),
74  maEmptyRows(0, MAXROWCOUNT, 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* pDoc) :
111  mpDoc(pDoc)
112  {
113  mpDoc->IncMacroInterpretLevel();
114  }
115  ~MacroInterpretIncrementer()
116  {
117  mpDoc->DecMacroInterpretLevel();
118  }
119 private:
120  ScDocument* mpDoc;
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* pDoc, SCCOL nCol, const ScRefCellValue& rCell )
129 {
130  OUString aDocStr = rCell.getRawString(pDoc);
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* pDoc, const ScAddress& rPos,
148  const ScRefCellValue& rCell, ScDPItemData& rData, sal_uInt32& rNumFormat)
149 {
150  OUString aDocStr = rCell.getRawString(pDoc);
151  rNumFormat = 0;
152 
153  if (rCell.hasError())
154  {
155  rData.SetErrorStringInterned(internString(rStrPool, pDoc->GetString(rPos.Col(), rPos.Row(), rPos.Tab())));
156  }
157  else if (rCell.hasNumeric())
158  {
159  double fVal = rCell.getRawValue();
160  rNumFormat = pDoc->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() :
307  maEmptyRows(0, MAXROWCOUNT, 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* mpDoc;
323  SCTAB mnDocTab;
324  SCROW mnStartRow;
325  SCROW mnEndRow;
326  bool mbTailEmptyRows;
327 
328  InitDocData() :
329  mpDoc(nullptr),
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* pDoc = rDocData.mpDoc;
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  pDoc->GetColumnIterator(nDocTab, nCol, nStartRow, nEndRow);
400  assert(pIter);
401  assert(pIter->hasCell());
402 
404 
405  rColData.maLabel = createLabelString(pDoc, 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, pDoc, 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* pDoc, const ScRange& rRange)
511 {
512  Clear();
513 
514  InitDocData aDocData;
515  aDocData.mpDoc = pDoc;
516 
517  // Make sure the formula cells within the data range are interpreted
518  // during this call, for this method may be called from the interpretation
519  // of GETPIVOTDATA, which disables nested formula interpretation without
520  // increasing the macro level.
521  MacroInterpretIncrementer aMacroInc(pDoc);
522 
523  aDocData.mnStartRow = rRange.aStart.Row(); // start of data
524  aDocData.mnEndRow = rRange.aEnd.Row();
525 
526  // Sanity check
527  if (!GetDoc()->ValidRow(aDocData.mnStartRow) || !GetDoc()->ValidRow(aDocData.mnEndRow) || aDocData.mnEndRow <= aDocData.mnStartRow)
528  return;
529 
530  SCCOL nStartCol = rRange.aStart.Col();
531  SCCOL nEndCol = rRange.aEnd.Col();
532  aDocData.mnDocTab = rRange.aStart.Tab();
533 
534  mnColumnCount = nEndCol - nStartCol + 1;
535 
536  // this row count must include the trailing empty rows.
537  mnRowCount = aDocData.mnEndRow - aDocData.mnStartRow; // skip the topmost label row.
538 
539  // Skip trailing empty rows if exists.
540  SCCOL nCol1 = nStartCol, nCol2 = nEndCol;
541  SCROW nRow1 = aDocData.mnStartRow, nRow2 = aDocData.mnEndRow;
542  pDoc->ShrinkToDataArea(aDocData.mnDocTab, nCol1, nRow1, nCol2, nRow2);
543  aDocData.mbTailEmptyRows = aDocData.mnEndRow > nRow2; // Trailing empty rows exist.
544  aDocData.mnEndRow = nRow2;
545 
546  if (aDocData.mnEndRow <= aDocData.mnStartRow)
547  {
548  // Check this again since the end row position has changed. It's
549  // possible that the new end row becomes lower than the start row
550  // after the shrinkage.
551  Clear();
552  return;
553  }
554 
556  std::vector<InitColumnData> aColData(mnColumnCount);
557  maFields.reserve(mnColumnCount);
558  for (SCCOL i = 0; i < mnColumnCount; ++i)
559  maFields.push_back(std::make_unique<Field>());
560 
561  maLabelNames.reserve(mnColumnCount+1);
562 
563  // Ensure that none of the formula cells in the data range are dirty.
564  pDoc->EnsureFormulaCellResults(rRange);
565 
566 #if ENABLE_THREADED_PIVOT_CACHE
567  ThreadQueue aQueue(std::thread::hardware_concurrency());
568 
569  auto aFuncLaunchFieldThreads = [&]()
570  {
571  for (sal_uInt16 nCol = nStartCol; nCol <= nEndCol; ++nCol)
572  {
573  size_t nDim = nCol - nStartCol;
574  InitColumnData& rColData = aColData[nDim];
575  rColData.init(nCol, &maStringPools[nDim], maFields[nDim].get());
576 
577  auto func = [&aDocData,&rColData]()
578  {
579  initColumnFromDoc(aDocData, rColData);
580  };
581 
582  aQueue.push(std::move(func));
583  }
584  };
585 
586  {
587  // Launch a separate thread that in turn spawns async threads to populate the fields.
588  std::thread t(aFuncLaunchFieldThreads);
589  ThreadScopedGuard sg(std::move(t));
590 
591  // Wait for all the async threads to complete on the main thread.
592  for (SCCOL i = 0; i < mnColumnCount; ++i)
593  aQueue.waitForOne();
594  }
595 
596 #else
597  for (sal_uInt16 nCol = nStartCol; nCol <= nEndCol; ++nCol)
598  {
599  size_t nDim = nCol - nStartCol;
600  InitColumnData& rColData = aColData[nDim];
601  rColData.init(nCol, &maStringPools[nDim], maFields[nDim].get());
602 
603  initColumnFromDoc(aDocData, rColData);
604  }
605 #endif
606 
607  maLabelNames = normalizeLabels(aColData);
608 
609  // Merge all non-empty rows data.
610  for (const InitColumnData& rCol : aColData)
611  {
612  EmptyRowsType::const_segment_iterator it = rCol.maEmptyRows.begin_segment();
613  EmptyRowsType::const_segment_iterator ite = rCol.maEmptyRows.end_segment();
614  EmptyRowsType::const_iterator pos = maEmptyRows.begin();
615 
616  for (; it != ite; ++it)
617  {
618  if (!it->value)
619  // Non-empty segment found. Record it.
620  pos = maEmptyRows.insert(pos, it->start, it->end, false).first;
621  }
622  }
623 
624  PostInit();
625 }
626 
628 {
629  Clear();
630 
631  try
632  {
635  maFields.clear();
636  maFields.reserve(mnColumnCount);
637  for (SCCOL i = 0; i < mnColumnCount; ++i)
638  maFields.push_back(std::make_unique<Field>());
639 
640  // Get column titles and types.
641  maLabelNames = normalizeLabels(rDB, mnColumnCount);
642 
643  std::vector<Bucket> aBuckets;
644  ScDPItemData aData;
645  for (sal_Int32 nCol = 0; nCol < mnColumnCount; ++nCol)
646  {
647  if (!rDB.first())
648  continue;
649 
650  aBuckets.clear();
651  Field& rField = *maFields[nCol];
652  SCROW nRow = 0;
653  do
654  {
655  SvNumFormatType nFormatType = SvNumFormatType::UNDEFINED;
656  aData.SetEmpty();
657  rDB.getValue(nCol, aData, nFormatType);
658  aBuckets.emplace_back(aData, nRow);
659  if (!aData.IsEmpty())
660  {
661  maEmptyRows.insert_back(nRow, nRow+1, false);
662  SvNumberFormatter* pFormatter = mpDoc->GetFormatTable();
663  rField.mnNumFormat = pFormatter ? pFormatter->GetStandardFormat(nFormatType) : 0;
664  }
665 
666  ++nRow;
667  }
668  while (rDB.next());
669 
670  processBuckets(aBuckets, rField);
671  }
672 
673  rDB.finish();
674 
675  if (!maFields.empty())
676  mnRowCount = maFields[0]->maData.size();
677 
678  PostInit();
679  return true;
680  }
681  catch (const Exception&)
682  {
683  return false;
684  }
685 }
686 
687 bool ScDPCache::ValidQuery( SCROW nRow, const ScQueryParam &rParam) const
688 {
689  if (!rParam.GetEntryCount())
690  return true;
691 
692  if (!rParam.GetEntry(0).bDoQuery)
693  return true;
694 
695  bool bMatchWholeCell = mpDoc->GetDocOptions().IsMatchWholeCell();
696 
697  SCSIZE nEntryCount = rParam.GetEntryCount();
698  std::vector<bool> aPassed(nEntryCount, false);
699 
700  long nPos = -1;
701  CollatorWrapper* pCollator = (rParam.bCaseSens ? ScGlobal::GetCaseCollator() :
703  ::utl::TransliterationWrapper* pTransliteration = (rParam.bCaseSens ?
705 
706  for (size_t i = 0; i < nEntryCount && rParam.GetEntry(i).bDoQuery; ++i)
707  {
708  const ScQueryEntry& rEntry = rParam.GetEntry(i);
709  const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
710  // we can only handle one single direct query
711  // #i115431# nField in QueryParam is the sheet column, not the field within the source range
712  SCCOL nQueryCol = static_cast<SCCOL>(rEntry.nField);
713  if ( nQueryCol < rParam.nCol1 )
714  nQueryCol = rParam.nCol1;
715  if ( nQueryCol > rParam.nCol2 )
716  nQueryCol = rParam.nCol2;
717  SCCOL nSourceField = nQueryCol - rParam.nCol1;
718  SCROW nId = GetItemDataId( nSourceField, nRow, false );
719  const ScDPItemData* pCellData = GetItemDataById( nSourceField, nId );
720 
721  bool bOk = false;
722 
723  if (rEntry.GetQueryItem().meType == ScQueryEntry::ByEmpty)
724  {
725  if (rEntry.IsQueryByEmpty())
726  bOk = pCellData->IsEmpty();
727  else
728  {
729  assert(rEntry.IsQueryByNonEmpty());
730  bOk = !pCellData->IsEmpty();
731  }
732  }
733  else if (rEntry.GetQueryItem().meType != ScQueryEntry::ByString && pCellData->IsValue())
734  { // by Value
735  double nCellVal = pCellData->GetValue();
736 
737  switch (rEntry.eOp)
738  {
739  case SC_EQUAL :
740  bOk = ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
741  break;
742  case SC_LESS :
743  bOk = (nCellVal < rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
744  break;
745  case SC_GREATER :
746  bOk = (nCellVal > rItem.mfVal) && !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
747  break;
748  case SC_LESS_EQUAL :
749  bOk = (nCellVal < rItem.mfVal) || ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
750  break;
751  case SC_GREATER_EQUAL :
752  bOk = (nCellVal > rItem.mfVal) || ::rtl::math::approxEqual(nCellVal, rItem.mfVal);
753  break;
754  case SC_NOT_EQUAL :
755  bOk = !::rtl::math::approxEqual(nCellVal, rItem.mfVal);
756  break;
757  default:
758  bOk= false;
759  break;
760  }
761  }
762  else if ((rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL)
764  && pCellData->HasStringData() )
765  )
766  { // by String
767  OUString aCellStr = pCellData->GetString();
768 
769  bool bRealWildOrRegExp = (rParam.eSearchType != utl::SearchParam::SearchType::Normal &&
770  ((rEntry.eOp == SC_EQUAL) || (rEntry.eOp == SC_NOT_EQUAL)));
771  if (bRealWildOrRegExp)
772  {
773  sal_Int32 nStart = 0;
774  sal_Int32 nEnd = aCellStr.getLength();
775 
776  bool bMatch = rEntry.GetSearchTextPtr( rParam.eSearchType, rParam.bCaseSens, bMatchWholeCell )
777  ->SearchForward( aCellStr, &nStart, &nEnd );
778  // from 614 on, nEnd is behind the found text
779  if (bMatch && bMatchWholeCell
780  && (nStart != 0 || nEnd != aCellStr.getLength()))
781  bMatch = false; // RegExp must match entire cell string
782 
783  bOk = ((rEntry.eOp == SC_NOT_EQUAL) ? !bMatch : bMatch);
784  }
785  else
786  {
787  if (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL)
788  {
789  if (bMatchWholeCell)
790  {
791  // TODO: Use shared string for fast equality check.
792  OUString aStr = rEntry.GetQueryItem().maString.getString();
793  bOk = pTransliteration->isEqual(aCellStr, aStr);
794  bool bHasStar = false;
795  sal_Int32 nIndex;
796  if (( nIndex = aStr.indexOf('*') ) != -1)
797  bHasStar = true;
798  if (bHasStar && (nIndex>0))
799  {
800  for (sal_Int32 j=0;(j<nIndex) && (j< aCellStr.getLength()) ; j++)
801  {
802  if (aCellStr[j] == aStr[j])
803  {
804  bOk=true;
805  }
806  else
807  {
808  bOk=false;
809  break;
810  }
811  }
812  }
813  }
814  else
815  {
816  OUString aQueryStr = rEntry.GetQueryItem().maString.getString();
817  css::uno::Sequence< sal_Int32 > xOff;
818  const LanguageType nLang = ScGlobal::xSysLocale->GetLanguageTag().getLanguageType();
819  OUString aCell = pTransliteration->transliterate(
820  aCellStr, nLang, 0, aCellStr.getLength(), &xOff);
821  OUString aQuer = pTransliteration->transliterate(
822  aQueryStr, nLang, 0, aQueryStr.getLength(), &xOff);
823  bOk = (aCell.indexOf( aQuer ) != -1);
824  }
825  if (rEntry.eOp == SC_NOT_EQUAL)
826  bOk = !bOk;
827  }
828  else
829  { // use collator here because data was probably sorted
830  sal_Int32 nCompare = pCollator->compareString(
831  aCellStr, rEntry.GetQueryItem().maString.getString());
832  switch (rEntry.eOp)
833  {
834  case SC_LESS :
835  bOk = (nCompare < 0);
836  break;
837  case SC_GREATER :
838  bOk = (nCompare > 0);
839  break;
840  case SC_LESS_EQUAL :
841  bOk = (nCompare <= 0);
842  break;
843  case SC_GREATER_EQUAL :
844  bOk = (nCompare >= 0);
845  break;
846  case SC_NOT_EQUAL:
847  OSL_FAIL("SC_NOT_EQUAL");
848  break;
849  case SC_TOPVAL:
850  case SC_BOTVAL:
851  case SC_TOPPERC:
852  case SC_BOTPERC:
853  default:
854  break;
855  }
856  }
857  }
858  }
859 
860  if (nPos == -1)
861  {
862  nPos++;
863  aPassed[nPos] = bOk;
864  }
865  else
866  {
867  if (rEntry.eConnect == SC_AND)
868  {
869  aPassed[nPos] = aPassed[nPos] && bOk;
870  }
871  else
872  {
873  nPos++;
874  aPassed[nPos] = bOk;
875  }
876  }
877  }
878 
879  for (long j=1; j <= nPos; j++)
880  aPassed[0] = aPassed[0] || aPassed[j];
881 
882  bool bRet = aPassed[0];
883  return bRet;
884 }
885 
887 {
888  return mpDoc;
889 }
890 
892 {
893  return mnColumnCount;
894 }
895 
896 bool ScDPCache::IsRowEmpty(SCROW nRow) const
897 {
898  bool bEmpty = true;
899  maEmptyRows.search_tree(nRow, bEmpty);
900  return bEmpty;
901 }
902 
904 {
905  if (nDim < 0)
906  return nullptr;
907 
908  long nSourceCount = static_cast<long>(maFields.size());
909  if (nDim < nSourceCount)
910  return maFields[nDim]->mpGroup.get();
911 
912  nDim -= nSourceCount;
913  if (nDim < static_cast<long>(maGroupFields.size()))
914  return maGroupFields[nDim].get();
915 
916  return nullptr;
917 }
918 
919 OUString ScDPCache::GetDimensionName(std::vector<OUString>::size_type nDim) const
920 {
921  OSL_ENSURE(nDim < maLabelNames.size()-1 , "ScDPTableDataCache::GetDimensionName");
922  OSL_ENSURE(maLabelNames.size() == static_cast <sal_uInt16> (mnColumnCount+1), "ScDPTableDataCache::GetDimensionName");
923 
924  if ( nDim+1 < maLabelNames.size() )
925  {
926  return maLabelNames[nDim+1];
927  }
928  else
929  return OUString();
930 }
931 
933 {
934  OSL_ENSURE(!maFields.empty(), "Cache not initialized!");
935 
936  maEmptyRows.build_tree();
937  auto it = maEmptyRows.rbegin();
938  OSL_ENSURE(it != maEmptyRows.rend(), "corrupt flat_segment_tree instance!");
939  mnDataSize = maFields[0]->maData.size();
940  ++it; // Skip the first position.
941  OSL_ENSURE(it != maEmptyRows.rend(), "buggy version of flat_segment_tree is used.");
942  if (it->second)
943  {
944  SCROW nLastNonEmpty = it->first - 1;
945  if (nLastNonEmpty+1 < mnDataSize)
946  mnDataSize = nLastNonEmpty+1;
947  }
948 }
949 
951 {
952  mnColumnCount = 0;
953  mnRowCount = 0;
954  maFields.clear();
955  maLabelNames.clear();
956  maGroupFields.clear();
957  maEmptyRows.clear();
958  maStringPools.clear();
959 }
960 
961 SCROW ScDPCache::GetItemDataId(sal_uInt16 nDim, SCROW nRow, bool bRepeatIfEmpty) const
962 {
963  OSL_ENSURE(nDim < mnColumnCount, "ScDPTableDataCache::GetItemDataId ");
964 
965  const Field& rField = *maFields[nDim];
966  if (o3tl::make_unsigned(nRow) >= rField.maData.size())
967  {
968  // nRow is in the trailing empty rows area.
969  if (bRepeatIfEmpty)
970  nRow = rField.maData.size()-1; // Move to the last non-empty row.
971  else
972  // Return the last item, which should always be empty if the
973  // initialization has skipped trailing empty rows.
974  return rField.maItems.size()-1;
975 
976  }
977  else if (bRepeatIfEmpty)
978  {
979  while (nRow > 0 && rField.maItems[rField.maData[nRow]].IsEmpty())
980  --nRow;
981  }
982 
983  return rField.maData[nRow];
984 }
985 
986 const ScDPItemData* ScDPCache::GetItemDataById(long nDim, SCROW nId) const
987 {
988  if (nDim < 0 || nId < 0)
989  return nullptr;
990 
991  size_t nSourceCount = maFields.size();
992  size_t nDimPos = static_cast<size_t>(nDim);
993  size_t nItemId = static_cast<size_t>(nId);
994  if (nDimPos < nSourceCount)
995  {
996  // source field.
997  const Field& rField = *maFields[nDimPos];
998  if (nItemId < rField.maItems.size())
999  return &rField.maItems[nItemId];
1000 
1001  if (!rField.mpGroup)
1002  return nullptr;
1003 
1004  nItemId -= rField.maItems.size();
1005  const ScDPItemDataVec& rGI = rField.mpGroup->maItems;
1006  if (nItemId >= rGI.size())
1007  return nullptr;
1008 
1009  return &rGI[nItemId];
1010  }
1011 
1012  // Try group fields.
1013  nDimPos -= nSourceCount;
1014  if (nDimPos >= maGroupFields.size())
1015  return nullptr;
1016 
1017  const ScDPItemDataVec& rGI = maGroupFields[nDimPos]->maItems;
1018  if (nItemId >= rGI.size())
1019  return nullptr;
1020 
1021  return &rGI[nItemId];
1022 }
1023 
1025 {
1026  return maFields.size();
1027 }
1028 
1030 {
1031  return maGroupFields.size();
1032 }
1033 
1035 {
1036  return mnRowCount;
1037 }
1038 
1040 {
1041  OSL_ENSURE(mnDataSize <= GetRowCount(), "Data size should never be larger than the row count.");
1042  return mnDataSize >= 0 ? mnDataSize : 0;
1043 }
1044 
1046 {
1047  if (nDim >= maFields.size())
1048  return nullptr;
1049 
1050  return &maFields[nDim]->maData;
1051 }
1052 
1054 {
1055  OSL_ENSURE( nDim>=0 && nDim < mnColumnCount ," nDim < mnColumnCount ");
1056  return maFields.at(nDim)->maItems;
1057 }
1058 
1059 sal_uInt32 ScDPCache::GetNumberFormat( long nDim ) const
1060 {
1061  if ( nDim >= mnColumnCount )
1062  return 0;
1063 
1064  // TODO: Find a way to determine the dominant number format in presence of
1065  // multiple number formats in the same field.
1066  return maFields[nDim]->mnNumFormat;
1067 }
1068 
1069 bool ScDPCache::IsDateDimension( long nDim ) const
1070 {
1071  if (nDim >= mnColumnCount)
1072  return false;
1073 
1074  SvNumberFormatter* pFormatter = mpDoc->GetFormatTable();
1075  if (!pFormatter)
1076  return false;
1077 
1078  SvNumFormatType eType = pFormatter->GetType(maFields[nDim]->mnNumFormat);
1079  return (eType == SvNumFormatType::DATE) || (eType == SvNumFormatType::DATETIME);
1080 }
1081 
1082 long ScDPCache::GetDimMemberCount(long nDim) const
1083 {
1084  OSL_ENSURE( nDim>=0 && nDim < mnColumnCount ," ScDPTableDataCache::GetDimMemberCount : out of bound ");
1085  return maFields[nDim]->maItems.size();
1086 }
1087 
1088 SCCOL ScDPCache::GetDimensionIndex(const OUString& sName) const
1089 {
1090  for (size_t i = 1; i < maLabelNames.size(); ++i)
1091  {
1092  if (maLabelNames[i] == sName)
1093  return static_cast<SCCOL>(i-1);
1094  }
1095  return -1;
1096 }
1097 
1098 rtl_uString* ScDPCache::InternString( size_t nDim, const OUString& rStr )
1099 {
1100  assert(nDim < maStringPools.size());
1101  return internString(maStringPools[nDim], rStr);
1102 }
1103 
1105 {
1106  maRefObjects.insert(pObj);
1107 }
1108 
1110 {
1111  if (mbDisposing)
1112  // Object being deleted.
1113  return;
1114 
1115  maRefObjects.erase(pObj);
1116  if (maRefObjects.empty())
1117  mpDoc->GetDPCollection()->RemoveCache(this);
1118 }
1119 
1121 {
1122  return maRefObjects;
1123 }
1124 
1125 SCROW ScDPCache::GetIdByItemData(long nDim, const ScDPItemData& rItem) const
1126 {
1127  if (nDim < 0)
1128  return -1;
1129 
1130  if (nDim < mnColumnCount)
1131  {
1132  // source field.
1133  const ScDPItemDataVec& rItems = maFields[nDim]->maItems;
1134  for (size_t i = 0, n = rItems.size(); i < n; ++i)
1135  {
1136  if (rItems[i] == rItem)
1137  return i;
1138  }
1139 
1140  if (!maFields[nDim]->mpGroup)
1141  return -1;
1142 
1143  // grouped source field.
1144  const ScDPItemDataVec& rGI = maFields[nDim]->mpGroup->maItems;
1145  for (size_t i = 0, n = rGI.size(); i < n; ++i)
1146  {
1147  if (rGI[i] == rItem)
1148  return rItems.size() + i;
1149  }
1150  return -1;
1151  }
1152 
1153  // group field.
1154  nDim -= mnColumnCount;
1155  if (o3tl::make_unsigned(nDim) < maGroupFields.size())
1156  {
1157  const ScDPItemDataVec& rGI = maGroupFields[nDim]->maItems;
1158  for (size_t i = 0, n = rGI.size(); i < n; ++i)
1159  {
1160  if (rGI[i] == rItem)
1161  return i;
1162  }
1163  }
1164 
1165  return -1;
1166 }
1167 
1168 // static
1169 sal_uInt32 ScDPCache::GetLocaleIndependentFormat( SvNumberFormatter& rFormatter, sal_uInt32 nNumFormat )
1170 {
1171  // For a date or date+time format use ISO format so it works across locales
1172  // and can be matched against string based item queries. For time use 24h
1173  // format. All others use General format, no currency, percent, ...
1174  // Use en-US locale for all.
1175  switch (rFormatter.GetType( nNumFormat))
1176  {
1177  case SvNumFormatType::DATE:
1179  break;
1180  case SvNumFormatType::TIME:
1181  return rFormatter.GetFormatIndex( NF_TIME_HHMMSS, LANGUAGE_ENGLISH_US);
1182  break;
1183  case SvNumFormatType::DATETIME:
1185  break;
1186  default:
1188  }
1189 }
1190 
1191 // static
1193 {
1194  return rtl::math::doubleToUString( fValue, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true);
1195 }
1196 
1197 // static
1199  SvNumberFormatter& rFormatter, sal_uInt32 nNumFormat )
1200 {
1201  nNumFormat = GetLocaleIndependentFormat( rFormatter, nNumFormat);
1202  if ((nNumFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
1204 
1205  OUString aStr;
1206  Color* pColor = nullptr;
1207  rFormatter.GetOutputString( fValue, nNumFormat, aStr, &pColor);
1208  return aStr;
1209 }
1210 
1211 OUString ScDPCache::GetFormattedString(long nDim, const ScDPItemData& rItem, bool bLocaleIndependent) const
1212 {
1213  if (nDim < 0)
1214  return rItem.GetString();
1215 
1216  ScDPItemData::Type eType = rItem.GetType();
1217  if (eType == ScDPItemData::Value)
1218  {
1219  // Format value using the stored number format.
1220  SvNumberFormatter* pFormatter = mpDoc->GetFormatTable();
1221  if (pFormatter)
1222  {
1223  sal_uInt32 nNumFormat = GetNumberFormat(nDim);
1224  if (bLocaleIndependent)
1225  return GetLocaleIndependentFormattedString( rItem.GetValue(), *pFormatter, nNumFormat);
1226 
1227  OUString aStr;
1228  Color* pColor = nullptr;
1229  pFormatter->GetOutputString(rItem.GetValue(), nNumFormat, aStr, &pColor);
1230  return aStr;
1231  }
1232 
1233  // Last resort...
1235  }
1236 
1237  if (eType == ScDPItemData::GroupValue)
1238  {
1240  double fStart = 0.0, fEnd = 0.0;
1241  const GroupItems* p = GetGroupItems(nDim);
1242  if (p)
1243  {
1244  fStart = p->maInfo.mfStart;
1245  fEnd = p->maInfo.mfEnd;
1246  }
1248  aAttr.mnGroupType, aAttr.mnValue, mpDoc->GetFormatTable(), fStart, fEnd);
1249  }
1250 
1251  if (eType == ScDPItemData::RangeStart)
1252  {
1253  double fVal = rItem.GetValue();
1254  const GroupItems* p = GetGroupItems(nDim);
1255  if (!p)
1256  return rItem.GetString();
1257 
1259  return ScDPUtil::getNumGroupName(fVal, p->maInfo, cDecSep, mpDoc->GetFormatTable());
1260  }
1261 
1262  return rItem.GetString();
1263 }
1264 
1266 {
1267  return mpDoc->GetFormatTable();
1268 }
1269 
1271 {
1272  maGroupFields.push_back(std::make_unique<GroupItems>());
1273  return static_cast<long>(maFields.size() + maGroupFields.size() - 1);
1274 }
1275 
1276 void ScDPCache::ResetGroupItems(long nDim, const ScDPNumGroupInfo& rNumInfo, sal_Int32 nGroupType)
1277 {
1278  if (nDim < 0)
1279  return;
1280 
1281  long nSourceCount = static_cast<long>(maFields.size());
1282  if (nDim < nSourceCount)
1283  {
1284  maFields.at(nDim)->mpGroup.reset(new GroupItems(rNumInfo, nGroupType));
1285  return;
1286  }
1287 
1288  nDim -= nSourceCount;
1289  if (nDim < static_cast<long>(maGroupFields.size()))
1290  {
1291  GroupItems& rGI = *maGroupFields[nDim];
1292  rGI.maItems.clear();
1293  rGI.maInfo = rNumInfo;
1294  rGI.mnGroupType = nGroupType;
1295  }
1296 }
1297 
1299 {
1300  if (nDim < 0)
1301  return -1;
1302 
1303  long nSourceCount = static_cast<long>(maFields.size());
1304  if (nDim < nSourceCount)
1305  {
1306  GroupItems& rGI = *maFields.at(nDim)->mpGroup;
1307  rGI.maItems.push_back(rData);
1308  SCROW nId = maFields[nDim]->maItems.size() + rGI.maItems.size() - 1;
1309  return nId;
1310  }
1311 
1312  nDim -= nSourceCount;
1313  if (nDim < static_cast<long>(maGroupFields.size()))
1314  {
1315  ScDPItemDataVec& rItems = maGroupFields.at(nDim)->maItems;
1316  rItems.push_back(rData);
1317  return rItems.size()-1;
1318  }
1319 
1320  return -1;
1321 }
1322 
1323 void ScDPCache::GetGroupDimMemberIds(long nDim, std::vector<SCROW>& rIds) const
1324 {
1325  if (nDim < 0)
1326  return;
1327 
1328  long nSourceCount = static_cast<long>(maFields.size());
1329  if (nDim < nSourceCount)
1330  {
1331  if (!maFields.at(nDim)->mpGroup)
1332  return;
1333 
1334  size_t nOffset = maFields[nDim]->maItems.size();
1335  const ScDPItemDataVec& rGI = maFields[nDim]->mpGroup->maItems;
1336  for (size_t i = 0, n = rGI.size(); i < n; ++i)
1337  rIds.push_back(static_cast<SCROW>(i + nOffset));
1338 
1339  return;
1340  }
1341 
1342  nDim -= nSourceCount;
1343  if (nDim < static_cast<long>(maGroupFields.size()))
1344  {
1345  const ScDPItemDataVec& rGI = maGroupFields.at(nDim)->maItems;
1346  for (size_t i = 0, n = rGI.size(); i < n; ++i)
1347  rIds.push_back(static_cast<SCROW>(i));
1348  }
1349 }
1350 
1351 namespace {
1352 
1353 struct ClearGroupItems
1354 {
1355  void operator() (const std::unique_ptr<ScDPCache::Field>& r) const
1356  {
1357  r->mpGroup.reset();
1358  }
1359 };
1360 
1361 }
1362 
1364 {
1365  maGroupFields.clear();
1366 }
1367 
1369 {
1370  ClearGroupFields();
1371  std::for_each(maFields.begin(), maFields.end(), ClearGroupItems());
1372 }
1373 
1375 {
1376  if (nDim < 0)
1377  return nullptr;
1378 
1379  long nSourceCount = static_cast<long>(maFields.size());
1380  if (nDim < nSourceCount)
1381  {
1382  if (!maFields.at(nDim)->mpGroup)
1383  return nullptr;
1384 
1385  return &maFields[nDim]->mpGroup->maInfo;
1386  }
1387 
1388  nDim -= nSourceCount;
1389  if (nDim < static_cast<long>(maGroupFields.size()))
1390  return &maGroupFields.at(nDim)->maInfo;
1391 
1392  return nullptr;
1393 }
1394 
1395 sal_Int32 ScDPCache::GetGroupType(long nDim) const
1396 {
1397  if (nDim < 0)
1398  return 0;
1399 
1400  long nSourceCount = static_cast<long>(maFields.size());
1401  if (nDim < nSourceCount)
1402  {
1403  if (!maFields.at(nDim)->mpGroup)
1404  return 0;
1405 
1406  return maFields[nDim]->mpGroup->mnGroupType;
1407  }
1408 
1409  nDim -= nSourceCount;
1410  if (nDim < static_cast<long>(maGroupFields.size()))
1411  return maGroupFields.at(nDim)->mnGroupType;
1412 
1413  return 0;
1414 }
1415 
1416 #if DUMP_PIVOT_TABLE
1417 
1418 namespace {
1419 
1420 void dumpItems(const ScDPCache& rCache, long nDim, const ScDPCache::ScDPItemDataVec& rItems, size_t nOffset)
1421 {
1422  for (size_t i = 0; i < rItems.size(); ++i)
1423  cout << " " << (i+nOffset) << ": " << rCache.GetFormattedString(nDim, rItems[i], false) << endl;
1424 }
1425 
1426 void dumpSourceData(const ScDPCache& rCache, long nDim, const ScDPCache::ScDPItemDataVec& rItems, const ScDPCache::IndexArrayType& rArray)
1427 {
1428  for (const auto& rIndex : rArray)
1429  cout << " '" << rCache.GetFormattedString(nDim, rItems[rIndex], false) << "'" << endl;
1430 }
1431 
1432 const char* getGroupTypeName(sal_Int32 nType)
1433 {
1434  static const char* pNames[] = {
1435  "", "years", "quarters", "months", "days", "hours", "minutes", "seconds"
1436  };
1437 
1438  switch (nType)
1439  {
1440  case sheet::DataPilotFieldGroupBy::YEARS: return pNames[1];
1441  case sheet::DataPilotFieldGroupBy::QUARTERS: return pNames[2];
1442  case sheet::DataPilotFieldGroupBy::MONTHS: return pNames[3];
1443  case sheet::DataPilotFieldGroupBy::DAYS: return pNames[4];
1444  case sheet::DataPilotFieldGroupBy::HOURS: return pNames[5];
1445  case sheet::DataPilotFieldGroupBy::MINUTES: return pNames[6];
1446  case sheet::DataPilotFieldGroupBy::SECONDS: return pNames[7];
1447  default:
1448  ;
1449  }
1450 
1451  return pNames[0];
1452 }
1453 
1454 }
1455 
1456 void ScDPCache::Dump() const
1457 {
1458  // Change these flags to fit your debugging needs.
1459  bool bDumpItems = false;
1460  bool bDumpSourceData = false;
1461 
1462  cout << "--- pivot cache dump" << endl;
1463  {
1464  size_t i = 0;
1465  for (const auto& rxField : maFields)
1466  {
1467  const Field& fld = *rxField;
1468  cout << "* source dimension: " << GetDimensionName(i) << " (ID = " << i << ")" << endl;
1469  cout << " item count: " << fld.maItems.size() << endl;
1470  if (bDumpItems)
1471  dumpItems(*this, i, fld.maItems, 0);
1472  if (fld.mpGroup)
1473  {
1474  cout << " group item count: " << fld.mpGroup->maItems.size() << endl;
1475  cout << " group type: " << getGroupTypeName(fld.mpGroup->mnGroupType) << endl;
1476  if (bDumpItems)
1477  dumpItems(*this, i, fld.mpGroup->maItems, fld.maItems.size());
1478  }
1479 
1480  if (bDumpSourceData)
1481  {
1482  cout << " source data (re-constructed):" << endl;
1483  dumpSourceData(*this, i, fld.maItems, fld.maData);
1484  }
1485 
1486  ++i;
1487  }
1488  }
1489 
1490  {
1491  size_t i = maFields.size();
1492  for (const auto& rxGroupField : maGroupFields)
1493  {
1494  const GroupItems& gi = *rxGroupField;
1495  cout << "* group dimension: (unnamed) (ID = " << i << ")" << endl;
1496  cout << " item count: " << gi.maItems.size() << endl;
1497  cout << " group type: " << getGroupTypeName(gi.mnGroupType) << endl;
1498  if (bDumpItems)
1499  dumpItems(*this, i, gi.maItems, 0);
1500  ++i;
1501  }
1502  }
1503 
1504  {
1505  struct { SCROW start; SCROW end; bool empty; } aRange;
1506  cout << "* empty rows: " << endl;
1507  mdds::flat_segment_tree<SCROW, bool>::const_iterator it = maEmptyRows.begin(), itEnd = maEmptyRows.end();
1508  if (it != itEnd)
1509  {
1510  aRange.start = it->first;
1511  aRange.empty = it->second;
1512 
1513  for (++it; it != itEnd; ++it)
1514  {
1515  aRange.end = it->first-1;
1516  cout << " rows " << aRange.start << "-" << aRange.end << ": " << (aRange.empty ? "empty" : "not-empty") << endl;
1517  aRange.start = it->first;
1518  aRange.empty = it->second;
1519  }
1520  }
1521  }
1522 
1523  cout << "---" << endl;
1524 }
1525 
1526 #endif
1527 
1528 /* 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:1029
static sal_uInt32 GetLocaleIndependentFormat(SvNumberFormatter &rFormatter, sal_uInt32 nNumFormat)
Definition: dpcache.cxx:1169
sal_Int32 nIndex
OUString getString() const
virtual bool first()=0
ScDPItemDataVec maItems
Definition: dpcache.hxx:58
ScAddress aStart
Definition: address.hxx:500
bool hasError() const
Definition: cellvalue.cxx:627
EmptyRowsType maEmptyRows
Definition: dpcache.hxx:127
sal_Int32 compareString(const OUString &s1, const OUString &s2) const
long GetDimMemberCount(long nDim) const
Definition: dpcache.cxx:1082
sal_Int32 mnGroupType
Definition: dpcache.hxx:60
OUString GetDimensionName(std::vector< OUString >::size_type nDim) const
Definition: dpcache.cxx:919
SCROW Row() const
Definition: address.hxx:262
static OUString GetLocaleIndependentFormattedString(double fValue, SvNumberFormatter &rFormatter, sal_uInt32 nNumFormat)
Definition: dpcache.cxx:1198
#define LANGUAGE_ENGLISH_US
const char aData[]
std::vector< StringSetType > maStringPools
Definition: dpcache.hxx:124
static SC_DLLPUBLIC::utl::TransliterationWrapper * GetpTransliteration()
Definition: global.cxx:982
virtual bool next()=0
size_t GetFieldCount() const
Definition: dpcache.cxx:1024
sal_Int32 GetGroupType(long nDim) const
Return a group type identifier.
Definition: dpcache.cxx:1395
sal_uInt32 GetFormatIndex(NfIndexTableOffset, LanguageType eLnge=LANGUAGE_DONTKNOW)
::utl::TransliterationWrapper * GetCaseTransliteration()
Definition: global.cxx:994
bool isEmpty() const
Definition: cellvalue.cxx:670
void GetOutputString(const double &fOutNumber, sal_uInt32 nFIndex, OUString &sOutString, Color **ppColor, bool bUseStarFormat=false)
ScDPCache(const ScDPCache &)=delete
ScDocument * GetDoc() const
Definition: dpcache.cxx:886
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:346
static SC_DLLPUBLIC CollatorWrapper * GetCollator()
Definition: global.cxx:1035
sal_Int32 mnCol
aBuf
SvNumFormatType GetType(sal_uInt32 nFIndex) const
rtl_uString * InternString(size_t nDim, const OUString &rStr)
Definition: dpcache.cxx:1098
sal_Int16 nId
SC_DLLPUBLIC void GetNumberFormat(SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt32 &rFormat) const
Definition: document.cxx:3644
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:1053
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:47
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:961
const Item & GetQueryItem() const
Definition: queryentry.cxx:118
SCCOL mnColumnCount
Definition: dpcache.hxx:112
std::unordered_set< OUString > StringSetType
Definition: dpcache.hxx:50
NF_NUMBER_STANDARD
sal_uInt16 sal_Unicode
bool IsRowEmpty(SCROW nRow) const
Definition: dpcache.cxx:896
ObjectFormatterData & mrData
static SC_DLLPUBLIC OUString getDateGroupName(sal_Int32 nDatePart, sal_Int32 nValue, SvNumberFormatter *pFormatter, double fStart, double fEnd)
Definition: dputil.cxx:102
sal_uInt32 mnNumFormat
Definition: dpcache.hxx:87
size_t SCSIZE
size_t typedef to be able to find places where code was changed from USHORT to size_t and is used to ...
Definition: address.hxx:45
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
size_t pos
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:1034
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:3489
SC_DLLPUBLIC const ScQueryEntry & GetEntry(SCSIZE n) const
Definition: queryparam.cxx:124
virtual long getColumnCount() const =0
void AddReference(ScDPObject *pObj) const
Definition: dpcache.cxx:1104
SCTAB Tab() const
Definition: address.hxx:271
ScDocument * mpDoc
Definition: dpcache.hxx:111
NF_DATE_ISO_YYYYMMDD
NF_TIME_HHMMSS
void SetStringInterned(rtl_uString *pS)
Definition: dpitemdata.cxx:145
void Clear()
Definition: dpcache.cxx:950
const SCROW MAXROWCOUNT
Definition: address.hxx:63
long GetColumnCount() const
Definition: dpcache.cxx:891
~ScDPCache()
Definition: dpcache.cxx:93
ScDPObjectSet maRefObjects
All pivot table objects that references this cache.
Definition: dpcache.hxx:117
::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:73
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:126
SC_DLLPUBLIC const ScDocOptions & GetDocOptions() const
Definition: documen3.cxx:1904
OUString getRawString(const ScDocument *pDoc) const
Retrieve a string value without modifying the states of any objects in the referenced document store...
Definition: cellvalue.cxx:665
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:1363
SC_DLLPUBLIC SvNumberFormatter * GetFormatTable() const
Definition: documen2.cxx:438
SCROW mnDataSize
Definition: dpcache.hxx:128
Type GetType() const
Definition: dpitemdata.hxx:68
bool IsDateDimension(long nDim) const
Definition: dpcache.cxx:1069
OUString GetFormattedString(long nDim, const ScDPItemData &rItem, bool bLocaleIndependent) const
Definition: dpcache.cxx:1211
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
void GetGroupDimMemberIds(long nDim, std::vector< SCROW > &rIds) const
Definition: dpcache.cxx:1323
void InitFromDoc(ScDocument *pDoc, const ScRange &rRange)
Definition: dpcache.cxx:510
sal_Int16 SCCOL
Definition: types.hxx:22
SCROW GetDataSize() const
Data size is the number of records without any trailing empty rows for sheet source data...
Definition: dpcache.cxx:1039
SvNumberFormatter * GetNumberFormatter() const
Definition: dpcache.cxx:1265
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
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:1368
static CollatorWrapper * GetCaseCollator()
Definition: global.cxx:1045
bool hasNumeric() const
Definition: cellvalue.cxx:622
#define SV_COUNTRY_LANGUAGE_OFFSET
OUString lowercase(const OUString &rStr, sal_Int32 nPos, sal_Int32 nCount) const
sal_uInt32 GetNumberFormat(long nDim) const
Definition: dpcache.cxx:1059
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
virtual void getValue(long nCol, ScDPItemData &rData, SvNumFormatType &rNumType) const =0
GroupFieldsType maGroupFields
Definition: dpcache.hxx:123
void RemoveCache(const ScDPCache *pCache)
Only to be called from ScDPCache::RemoveReference().
Definition: dpobject.cxx:3832
SCROW GetIdByItemData(long nDim, const ScDPItemData &rItem) const
Definition: dpcache.cxx:1125
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:1045
void PostInit()
Definition: dpcache.cxx:932
static SC_DLLPUBLIC const LocaleDataWrapper * getLocaleDataPtr()
Definition: global.cxx:1007
static std::unique_ptr< SvtSysLocale > xSysLocale
Definition: global.hxx:538
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:131
bool InitFromDataBase(DBConnector &rDB)
Definition: dpcache.cxx:627
sal_Int32 SCROW
Definition: types.hxx:18
ScDPItemDataVec maItems
Unique values in the field, stored in ascending order.
Definition: dpcache.hxx:78
static OUString GetLocaleIndependentFormattedNumberString(double fValue)
Definition: dpcache.cxx:1192
void RemoveReference(ScDPObject *pObj) const
Definition: dpcache.cxx:1109
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:1120
void ResetGroupItems(long nDim, const ScDPNumGroupInfo &rNumInfo, sal_Int32 nGroupType)
Definition: dpcache.cxx:1276
bool HasStringData() const
Definition: dpitemdata.cxx:366
FieldsType maFields
Definition: dpcache.hxx:122
NF_DATETIME_ISO_YYYYMMDD_HHMMSS
static SC_DLLPUBLIC const CharClass * getCharClassPtr()
Definition: global.cxx:1016
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
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:99
const ScDPNumGroupInfo * GetNumGroupInfo(long nDim) const
Definition: dpcache.cxx:1374
void(* f)(TrueTypeTable *)
OUString GetString() const
Definition: dpitemdata.cxx:327
if(!pCandidateA->getEnd().equal(pCandidateB->getStart()))
long AppendGroupField()
Definition: dpcache.cxx:1270
const ScDPItemData * GetItemDataById(long nDim, SCROW nId) const
Definition: dpcache.cxx:986
bool IsCaseInsEqual(const ScDPItemData &r) const
Definition: dpitemdata.cxx:187
SCCOL GetDimensionIndex(const OUString &sName) const
Definition: dpcache.cxx:1088
ScQueryOp eOp
Definition: queryentry.hxx:52
void parallelSort(const RandItr aBegin, const RandItr aEnd, Compare aComp=Compare())
std::set< ScDPObject * > ScDPObjectSet
Definition: dpcache.hxx:53
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
SCROW SetGroupItem(long nDim, const ScDPItemData &rData)
Definition: dpcache.cxx:1298
void SetEmpty()
Definition: dpitemdata.cxx:131
utl::SearchParam::SearchType eSearchType
Definition: queryparam.hxx:44
const GroupItems * GetGroupItems(long nDim) const
Definition: dpcache.cxx:903
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:129
bool ValidQuery(SCROW nRow, const ScQueryParam &rQueryParam) const
Definition: dpcache.cxx:687
virtual OUString getColumnLabel(long nCol) const =0