LibreOffice Module sc (master)  1
dpfilteredcache.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 #include <dpfilteredcache.hxx>
22 #include <address.hxx>
23 #include <queryparam.hxx>
24 #include <dpitemdata.hxx>
25 #include <com/sun/star/uno/Sequence.hxx>
26 #include <o3tl/safeint.hxx>
27 #include <osl/diagnose.h>
28 #include <algorithm>
29 
30 using ::std::vector;
31 using ::com::sun::star::uno::Sequence;
32 using ::com::sun::star::uno::Any;
33 
35  maItem(rItem) {}
36 
38 {
39  return maItem == rCellData;
40 }
41 
42 std::vector<ScDPItemData> ScDPFilteredCache::SingleFilter::getMatchValues() const
43 {
44  std::vector<ScDPItemData> aValues;
45  aValues.push_back(maItem);
46  return aValues;
47 }
48 
50 {
51 }
52 
54 {
55  return std::find(maItems.begin(), maItems.end(), rCellData) != maItems.end();
56 }
57 
58 std::vector<ScDPItemData> ScDPFilteredCache::GroupFilter::getMatchValues() const
59 {
60  return maItems;
61 }
62 
64 {
65  maItems.push_back(rItem);
66 }
67 
69 {
70  return maItems.size();
71 }
72 
74  mnFieldIndex(-1)
75 {
76 }
77 
79  maShowByFilter(0, MAXROW+1, false), maShowByPage(0, MAXROW+1, true), mrCache(rCache)
80 {
81 }
82 
84 {
85 }
86 
88 {
89  return mrCache.GetRowCount();
90 }
91 
93 {
94  return mrCache.GetColumnCount();
95 }
96 
98  const ScQueryParam& rQuery, bool bIgnoreEmptyRows, bool bRepeatIfEmpty)
99 {
100  SCROW nRowCount = getRowSize();
101  SCROW nDataSize = mrCache.GetDataSize();
103  if (nRowCount <= 0 || nColCount <= 0)
104  return;
105 
106  maShowByFilter.clear();
107  maShowByPage.clear();
108  maShowByPage.build_tree();
109 
110  // Process the non-empty data rows.
111  for (SCROW nRow = 0; nRow < nDataSize; ++nRow)
112  {
113  if (!getCache().ValidQuery(nRow, rQuery))
114  continue;
115 
116  if (bIgnoreEmptyRows && getCache().IsRowEmpty(nRow))
117  continue;
118 
119  maShowByFilter.insert_back(nRow, nRow+1, true);
120  }
121 
122  // Process the trailing empty rows.
123  if (!bIgnoreEmptyRows)
124  maShowByFilter.insert_back(nDataSize, nRowCount, true);
125 
126  maShowByFilter.build_tree();
127 
128  // Initialize field entries container.
129  maFieldEntries.clear();
130  maFieldEntries.reserve(nColCount);
131 
132  // Build unique field entries.
133  for (SCCOL nCol = 0; nCol < nColCount; ++nCol)
134  {
135  maFieldEntries.emplace_back( );
136  SCROW nMemCount = getCache().GetDimMemberCount( nCol );
137  if (!nMemCount)
138  continue;
139 
140  std::vector<SCROW> aAdded(nMemCount, -1);
141  bool bShow = false;
142  SCROW nEndSegment = -1;
143  for (SCROW nRow = 0; nRow < nRowCount; ++nRow)
144  {
145  if (nRow > nEndSegment)
146  {
147  if (!maShowByFilter.search_tree(nRow, bShow, nullptr, &nEndSegment).second)
148  {
149  OSL_FAIL("Tree search failed!");
150  continue;
151  }
152  --nEndSegment; // End position is not inclusive. Move back one.
153  }
154 
155  if (!bShow)
156  {
157  nRow = nEndSegment;
158  continue;
159  }
160 
161  SCROW nIndex = getCache().GetItemDataId(nCol, nRow, bRepeatIfEmpty);
162  aAdded[nIndex] = nIndex;
163 
164  // tdf#96588 - large numbers of trailing identical empty
165  // rows generate the same nIndex & nOrder.
166  if (nRow == nDataSize)
167  break;
168  }
169  for (SCROW nRow = 0; nRow < nMemCount; ++nRow)
170  {
171  if (aAdded[nRow] != -1)
172  maFieldEntries.back().push_back(aAdded[nRow]);
173  }
174  }
175 }
176 
178 {
179  SCROW nRowCount = getRowSize();
181  if (nRowCount <= 0 || nColCount <= 0)
182  return;
183 
184  maShowByPage.clear();
185  maShowByPage.build_tree();
186 
187  maShowByFilter.clear();
188  maShowByFilter.insert_front(0, nRowCount, true);
189  maShowByFilter.build_tree();
190 
191  // Initialize field entries container.
192  maFieldEntries.clear();
193  maFieldEntries.reserve(nColCount);
194 
195  // Data rows
196  for (SCCOL nCol = 0; nCol < nColCount; ++nCol)
197  {
198  maFieldEntries.emplace_back( );
199  SCROW nMemCount = getCache().GetDimMemberCount( nCol );
200  if (!nMemCount)
201  continue;
202 
203  std::vector<SCROW> aAdded(nMemCount, -1);
204 
205  for (SCROW nRow = 0; nRow < nRowCount; ++nRow)
206  {
207  SCROW nIndex = getCache().GetItemDataId(nCol, nRow, false);
208  aAdded[nIndex] = nIndex;
209  }
210  for (SCROW nRow = 0; nRow < nMemCount; ++nRow)
211  {
212  if (aAdded[nRow] != -1)
213  maFieldEntries.back().push_back(aAdded[nRow]);
214  }
215  }
216 }
217 
218 bool ScDPFilteredCache::isRowActive(sal_Int32 nRow, sal_Int32* pLastRow) const
219 {
220  bool bFilter = false, bPage = true;
221  SCROW nLastRowFilter = MAXROW, nLastRowPage = MAXROW;
222  maShowByFilter.search_tree(nRow, bFilter, nullptr, &nLastRowFilter);
223  maShowByPage.search_tree(nRow, bPage, nullptr, &nLastRowPage);
224  if (pLastRow)
225  {
226  // Return the last row of current segment.
227  *pLastRow = std::min(nLastRowFilter, nLastRowPage);
228  *pLastRow -= 1; // End position is not inclusive. Move back one.
229  }
230 
231  return bFilter && bPage;
232 }
233 
234 void ScDPFilteredCache::filterByPageDimension(const vector<Criterion>& rCriteria, const std::unordered_set<sal_Int32>& rRepeatIfEmptyDims)
235 {
236  SCROW nRowSize = getRowSize();
237  SCROW nDataSize = mrCache.GetDataSize();
238 
239  maShowByPage.clear();
240 
241  for (SCROW nRow = 0; nRow < nDataSize; ++nRow)
242  {
243  bool bShow = isRowQualified(nRow, rCriteria, rRepeatIfEmptyDims);
244  maShowByPage.insert_back(nRow, nRow+1, bShow);
245  }
246 
247  // tdf#96588 - rapidly extend for blank rows with identical data
248  if (nDataSize < nRowSize)
249  {
250  bool bBlankShow = isRowQualified(nDataSize, rCriteria, rRepeatIfEmptyDims);
251  maShowByPage.insert_back(nDataSize, nRowSize, bBlankShow);
252  }
253 
254  maShowByPage.build_tree();
255 }
256 
257 const ScDPItemData* ScDPFilteredCache::getCell(SCCOL nCol, SCROW nRow, bool bRepeatIfEmpty) const
258 {
259  SCROW nId= mrCache.GetItemDataId(nCol, nRow, bRepeatIfEmpty);
260  return mrCache.GetItemDataById( nCol, nId );
261 }
262 
263 void ScDPFilteredCache::getValue( ScDPValue& rVal, SCCOL nCol, SCROW nRow) const
264 {
265  const ScDPItemData* pData = getCell( nCol, nRow, false/*bRepeatIfEmpty*/ );
266 
267  if (pData)
268  {
269  rVal.mfValue = pData->IsValue() ? pData->GetValue() : 0.0;
270  rVal.meType = pData->GetCellType();
271  }
272  else
273  rVal.Set(0.0, ScDPValue::Empty);
274 }
275 
277 {
278  return mrCache.GetDimensionName(nIndex);
279 }
280 
281 const ::std::vector<SCROW>& ScDPFilteredCache::getFieldEntries( sal_Int32 nColumn ) const
282 {
283  if (nColumn < 0 || o3tl::make_unsigned(nColumn) >= maFieldEntries.size())
284  {
285  // index out of bound. Hopefully this code will never be reached.
286  static const ::std::vector<SCROW> emptyEntries{};
287  return emptyEntries;
288  }
289  return maFieldEntries[nColumn];
290 }
291 
292 void ScDPFilteredCache::filterTable(const vector<Criterion>& rCriteria, Sequence< Sequence<Any> >& rTabData,
293  const std::unordered_set<sal_Int32>& rRepeatIfEmptyDims)
294 {
295  sal_Int32 nRowSize = getRowSize();
296  SCCOL nColSize = getColSize();
297 
298  if (!nRowSize)
299  // no data to filter.
300  return;
301 
302  // Row first, then column.
303  vector< Sequence<Any> > tableData;
304  tableData.reserve(nRowSize+1);
305 
306  // Header first.
307  Sequence<Any> headerRow(nColSize);
308  for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
309  {
310  OUString str = getFieldName( nCol);
311  Any any;
312  any <<= str;
313  headerRow[nCol] = any;
314  }
315  tableData.push_back(headerRow);
316 
317  for (sal_Int32 nRow = 0; nRow < nRowSize; ++nRow)
318  {
319  sal_Int32 nLastRow;
320  if (!isRowActive(nRow, &nLastRow))
321  {
322  // This row is filtered out.
323  nRow = nLastRow;
324  continue;
325  }
326 
327  if (!isRowQualified(nRow, rCriteria, rRepeatIfEmptyDims))
328  continue;
329 
330  // Insert this row into table.
331 
332  Sequence<Any> row(nColSize);
333  for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
334  {
335  Any any;
336  bool bRepeatIfEmpty = rRepeatIfEmptyDims.count(nCol) > 0;
337  const ScDPItemData* pData= getCell(nCol, nRow, bRepeatIfEmpty);
338  if ( pData->IsValue() )
339  any <<= pData->GetValue();
340  else
341  {
342  OUString string (pData->GetString() );
343  any <<= string;
344  }
345  row[nCol] = any;
346  }
347  tableData.push_back(row);
348  }
349 
350  // convert vector to Sequence
351  sal_Int32 nTabSize = static_cast<sal_Int32>(tableData.size());
352  rTabData.realloc(nTabSize);
353  for (sal_Int32 i = 0; i < nTabSize; ++i)
354  rTabData[i] = tableData[i];
355 }
356 
358 {
359  maFieldEntries.clear();
360  maShowByFilter.clear();
361  maShowByPage.clear();
362 }
363 
365 {
366  return maFieldEntries.empty();
367 }
368 
369 bool ScDPFilteredCache::isRowQualified(sal_Int32 nRow, const vector<Criterion>& rCriteria,
370  const std::unordered_set<sal_Int32>& rRepeatIfEmptyDims) const
371 {
372  sal_Int32 nColSize = getColSize();
373  for (const auto& rCriterion : rCriteria)
374  {
375  if (rCriterion.mnFieldIndex >= nColSize)
376  // specified field is outside the source data columns. Don't
377  // use this criterion.
378  continue;
379 
380  // Check if the 'repeat if empty' flag is set for this field.
381  bool bRepeatIfEmpty = rRepeatIfEmptyDims.count(rCriterion.mnFieldIndex) > 0;
382  const ScDPItemData* pCellData = getCell(static_cast<SCCOL>(rCriterion.mnFieldIndex), nRow, bRepeatIfEmpty);
383  if (!rCriterion.mpFilter->match(*pCellData))
384  return false;
385  }
386  return true;
387 }
388 
389 #if DUMP_PIVOT_TABLE
390 
391 void ScDPFilteredCache::dumpRowFlag( const RowFlagType& rFlag )
392 {
393  RowFlagType::const_iterator it = rFlag.begin(), itEnd = rFlag.end();
394  bool bShow = it->second;
395  SCROW nRow1 = it->first;
396  for (++it; it != itEnd; ++it)
397  {
398  SCROW nRow2 = it->first;
399  cout << " * range " << nRow1 << "-" << nRow2 << ": " << (bShow ? "on" : "off") << endl;
400  bShow = it->second;
401  nRow1 = nRow2;
402  }
403 }
404 
405 void ScDPFilteredCache::dump() const
406 {
407  cout << "--- pivot filtered cache dump" << endl;
408 
409  cout << endl;
410  cout << "* show by filter" << endl;
412 
413  cout << endl;
414  cout << "* show by page dimensions" << endl;
416 
417  cout << endl;
418  cout << "* field entries" << endl;
419  size_t nFieldCount = maFieldEntries.size();
420  for (size_t i = 0; i < nFieldCount; ++i)
421  {
422  const vector<SCROW>& rField = maFieldEntries[i];
423  cout << " * field " << i << endl;
424  for (size_t j = 0, n = rField.size(); j < n; ++j)
425  cout << " ID: " << rField[j] << endl;
426  }
427  cout << "---" << endl;
428 }
429 
430 #endif
431 
432 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OUString getFieldName(SCCOL nIndex) const
const int nColCount
virtual std::vector< ScDPItemData > getMatchValues() const override
sal_Int32 nIndex
virtual bool match(const ScDPItemData &rCellData) const override
returns true if the matching condition is met for a single cell value, or false otherwise.
tools::Long GetDimMemberCount(tools::Long nDim) const
Definition: dpcache.cxx:1081
OUString GetDimensionName(std::vector< OUString >::size_type nDim) const
Definition: dpcache.cxx:918
sal_Int32 getRowSize() const
std::unique_ptr< ContentProperties > pData
sal_Int64 n
sal_Int16 nId
This class represents the cached data part of the datapilot cache table implementation.
Definition: dpcache.hxx:48
::std::vector< ::std::vector< SCROW > > maFieldEntries
unique field entries for each field (column).
SCROW GetItemDataId(sal_uInt16 nDim, SCROW nRow, bool bRepeatIfEmpty) const
Definition: dpcache.cxx:960
static void dumpRowFlag(const RowFlagType &rFlag)
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
const SCROW MAXROW
Definition: address.hxx:69
const ScDPItemData * GetItemDataById(tools::Long nDim, SCROW nId) const
Definition: dpcache.cxx:985
void getValue(ScDPValue &rVal, SCCOL nCol, SCROW nRow) const
bool isRowActive(sal_Int32 nRow, sal_Int32 *pLastRow=nullptr) const
Check whether a specified row is active or not.
virtual bool match(const ScDPItemData &rCellData) const override
returns true if the matching condition is met for a single cell value, or false otherwise.
When assigning a string value, you can also assign an interned string whose life-cycle is managed by ...
Definition: dpitemdata.hxx:29
int i
ScDPFilteredCache(const ScDPCache &rCache)
sal_Int16 SCCOL
Definition: types.hxx:22
const ScDPItemData * getCell(SCCOL nCol, SCROW nRow, bool bRepeatIfEmpty) const
Get the cell instance at specified location within the data grid.
SCROW GetDataSize() const
Data size is the number of records without any trailing empty rows for sheet source data...
Definition: dpcache.cxx:1038
const Any & any
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
tools::Long GetColumnCount() const
Definition: dpcache.cxx:890
double mfValue
Definition: dpglobal.hxx:49
void addMatchItem(const ScDPItemData &rItem)
RowFlagType maShowByPage
Rows visible by page dimension filtering.
RowFlagType maShowByFilter
Rows visible by standard filter query.
sal_Int32 SCROW
Definition: types.hxx:18
sal_Int32 getColSize() const
const size_t nTabSize
bool isRowQualified(sal_Int32 nRow, const ::std::vector< Criterion > &rCriteria, const std::unordered_set< sal_Int32 > &rRepeatIfEmptyDims) const
Check if a given row meets all specified criteria.
SvStream & endl(SvStream &rStr)
virtual std::vector< ScDPItemData > getMatchValues() const override
double GetValue() const
Definition: dpitemdata.cxx:347
const ::std::vector< SCROW > & getFieldEntries(sal_Int32 nColumn) const
Get the unique entries for a field specified by index.
SingleFilter(const ScDPItemData &rItem)
bool IsValue() const
Definition: dpitemdata.cxx:322
Type meType
Definition: dpglobal.hxx:50
void filterTable(const std::vector< Criterion > &rCriteria, css::uno::Sequence< css::uno::Sequence< css::uno::Any > > &rTabData, const std::unordered_set< sal_Int32 > &rRepeatIfEmptyDims)
Filter the table based on the specified criteria, and copy the result to rTabData.
OUString GetString() const
Definition: dpitemdata.cxx:327
ScDPValue::Type GetCellType() const
Definition: dpitemdata.cxx:265
const ScDPCache & mrCache
void filterByPageDimension(const std::vector< Criterion > &rCriteria, const std::unordered_set< sal_Int32 > &rRepeatIfEmptyDims)
Set filter on/off flag to each row to control visibility.
void Set(double fV, Type eT)
Definition: dpglobal.cxx:14
void dump() const
const ScDPCache & getCache() const