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  return { maItem };
45 }
46 
48 {
49 }
50 
52 {
53  return std::find(maItems.begin(), maItems.end(), rCellData) != maItems.end();
54 }
55 
56 std::vector<ScDPItemData> ScDPFilteredCache::GroupFilter::getMatchValues() const
57 {
58  return maItems;
59 }
60 
62 {
63  maItems.push_back(rItem);
64 }
65 
67 {
68  return maItems.size();
69 }
70 
72  mnFieldIndex(-1)
73 {
74 }
75 
77  maShowByFilter(0, MAXROW+1, false), maShowByPage(0, MAXROW+1, true), mrCache(rCache)
78 {
79 }
80 
82 {
83 }
84 
86 {
87  return mrCache.GetRowCount();
88 }
89 
91 {
92  return mrCache.GetColumnCount();
93 }
94 
96  const ScQueryParam& rQuery, bool bIgnoreEmptyRows, bool bRepeatIfEmpty)
97 {
98  SCROW nRowCount = getRowSize();
99  SCROW nDataSize = mrCache.GetDataSize();
100  SCCOL nColCount = getColSize();
101  if (nRowCount <= 0 || nColCount <= 0)
102  return;
103 
104  maShowByFilter.clear();
105  maShowByPage.clear();
106  maShowByPage.build_tree();
107 
108  // Process the non-empty data rows.
109  for (SCROW nRow = 0; nRow < nDataSize; ++nRow)
110  {
111  if (!getCache().ValidQuery(nRow, rQuery))
112  continue;
113 
114  if (bIgnoreEmptyRows && getCache().IsRowEmpty(nRow))
115  continue;
116 
117  maShowByFilter.insert_back(nRow, nRow+1, true);
118  }
119 
120  // Process the trailing empty rows.
121  if (!bIgnoreEmptyRows)
122  maShowByFilter.insert_back(nDataSize, nRowCount, true);
123 
124  maShowByFilter.build_tree();
125 
126  // Initialize field entries container.
127  maFieldEntries.clear();
128  maFieldEntries.reserve(nColCount);
129 
130  // Build unique field entries.
131  for (SCCOL nCol = 0; nCol < nColCount; ++nCol)
132  {
133  maFieldEntries.emplace_back( );
134  SCROW nMemCount = getCache().GetDimMemberCount( nCol );
135  if (!nMemCount)
136  continue;
137 
138  std::vector<SCROW> aAdded(nMemCount, -1);
139  bool bShow = false;
140  SCROW nEndSegment = -1;
141  for (SCROW nRow = 0; nRow < nRowCount; ++nRow)
142  {
143  if (nRow > nEndSegment)
144  {
145  if (!maShowByFilter.search_tree(nRow, bShow, nullptr, &nEndSegment).second)
146  {
147  OSL_FAIL("Tree search failed!");
148  continue;
149  }
150  --nEndSegment; // End position is not inclusive. Move back one.
151  }
152 
153  if (!bShow)
154  {
155  nRow = nEndSegment;
156  continue;
157  }
158 
159  SCROW nIndex = getCache().GetItemDataId(nCol, nRow, bRepeatIfEmpty);
160  aAdded[nIndex] = nIndex;
161 
162  // tdf#96588 - large numbers of trailing identical empty
163  // rows generate the same nIndex & nOrder.
164  if (nRow == nDataSize)
165  break;
166  }
167  for (SCROW nRow = 0; nRow < nMemCount; ++nRow)
168  {
169  if (aAdded[nRow] != -1)
170  maFieldEntries.back().push_back(aAdded[nRow]);
171  }
172  }
173 }
174 
176 {
177  SCROW nRowCount = getRowSize();
178  SCCOL nColCount = getColSize();
179  if (nRowCount <= 0 || nColCount <= 0)
180  return;
181 
182  maShowByPage.clear();
183  maShowByPage.build_tree();
184 
185  maShowByFilter.clear();
186  maShowByFilter.insert_front(0, nRowCount, true);
187  maShowByFilter.build_tree();
188 
189  // Initialize field entries container.
190  maFieldEntries.clear();
191  maFieldEntries.reserve(nColCount);
192 
193  // Data rows
194  for (SCCOL nCol = 0; nCol < nColCount; ++nCol)
195  {
196  maFieldEntries.emplace_back( );
197  SCROW nMemCount = getCache().GetDimMemberCount( nCol );
198  if (!nMemCount)
199  continue;
200 
201  std::vector<SCROW> aAdded(nMemCount, -1);
202 
203  for (SCROW nRow = 0; nRow < nRowCount; ++nRow)
204  {
205  SCROW nIndex = getCache().GetItemDataId(nCol, nRow, false);
206  aAdded[nIndex] = nIndex;
207  }
208  for (SCROW nRow = 0; nRow < nMemCount; ++nRow)
209  {
210  if (aAdded[nRow] != -1)
211  maFieldEntries.back().push_back(aAdded[nRow]);
212  }
213  }
214 }
215 
216 bool ScDPFilteredCache::isRowActive(sal_Int32 nRow, sal_Int32* pLastRow) const
217 {
218  bool bFilter = false, bPage = true;
219  SCROW nLastRowFilter = MAXROW, nLastRowPage = MAXROW;
220  maShowByFilter.search_tree(nRow, bFilter, nullptr, &nLastRowFilter);
221  maShowByPage.search_tree(nRow, bPage, nullptr, &nLastRowPage);
222  if (pLastRow)
223  {
224  // Return the last row of current segment.
225  *pLastRow = std::min(nLastRowFilter, nLastRowPage);
226  *pLastRow -= 1; // End position is not inclusive. Move back one.
227  }
228 
229  return bFilter && bPage;
230 }
231 
232 void ScDPFilteredCache::filterByPageDimension(const vector<Criterion>& rCriteria, const std::unordered_set<sal_Int32>& rRepeatIfEmptyDims)
233 {
234  SCROW nRowSize = getRowSize();
235  SCROW nDataSize = mrCache.GetDataSize();
236 
237  maShowByPage.clear();
238 
239  for (SCROW nRow = 0; nRow < nDataSize; ++nRow)
240  {
241  bool bShow = isRowQualified(nRow, rCriteria, rRepeatIfEmptyDims);
242  maShowByPage.insert_back(nRow, nRow+1, bShow);
243  }
244 
245  // tdf#96588 - rapidly extend for blank rows with identical data
246  if (nDataSize < nRowSize)
247  {
248  bool bBlankShow = isRowQualified(nDataSize, rCriteria, rRepeatIfEmptyDims);
249  maShowByPage.insert_back(nDataSize, nRowSize, bBlankShow);
250  }
251 
252  maShowByPage.build_tree();
253 }
254 
255 const ScDPItemData* ScDPFilteredCache::getCell(SCCOL nCol, SCROW nRow, bool bRepeatIfEmpty) const
256 {
257  SCROW nId= mrCache.GetItemDataId(nCol, nRow, bRepeatIfEmpty);
258  return mrCache.GetItemDataById( nCol, nId );
259 }
260 
261 void ScDPFilteredCache::getValue( ScDPValue& rVal, SCCOL nCol, SCROW nRow) const
262 {
263  const ScDPItemData* pData = getCell( nCol, nRow, false/*bRepeatIfEmpty*/ );
264 
265  if (pData)
266  {
267  rVal.mfValue = pData->IsValue() ? pData->GetValue() : 0.0;
268  rVal.meType = pData->GetCellType();
269  }
270  else
271  rVal.Set(0.0, ScDPValue::Empty);
272 }
273 
275 {
276  return mrCache.GetDimensionName(nIndex);
277 }
278 
279 const ::std::vector<SCROW>& ScDPFilteredCache::getFieldEntries( sal_Int32 nColumn ) const
280 {
281  if (nColumn < 0 || o3tl::make_unsigned(nColumn) >= maFieldEntries.size())
282  {
283  // index out of bound. Hopefully this code will never be reached.
284  static const ::std::vector<SCROW> emptyEntries{};
285  return emptyEntries;
286  }
287  return maFieldEntries[nColumn];
288 }
289 
290 void ScDPFilteredCache::filterTable(const vector<Criterion>& rCriteria, Sequence< Sequence<Any> >& rTabData,
291  const std::unordered_set<sal_Int32>& rRepeatIfEmptyDims)
292 {
293  sal_Int32 nRowSize = getRowSize();
294  SCCOL nColSize = getColSize();
295 
296  if (!nRowSize)
297  // no data to filter.
298  return;
299 
300  // Row first, then column.
301  vector< Sequence<Any> > tableData;
302  tableData.reserve(nRowSize+1);
303 
304  // Header first.
305  Sequence<Any> headerRow(nColSize);
306  auto pRow = headerRow.getArray();
307  for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
308  {
309  OUString str = getFieldName( nCol);
310  Any any;
311  any <<= str;
312  pRow[nCol] = any;
313  }
314  tableData.push_back(headerRow);
315 
316  for (sal_Int32 nRow = 0; nRow < nRowSize; ++nRow)
317  {
318  sal_Int32 nLastRow;
319  if (!isRowActive(nRow, &nLastRow))
320  {
321  // This row is filtered out.
322  nRow = nLastRow;
323  continue;
324  }
325 
326  if (!isRowQualified(nRow, rCriteria, rRepeatIfEmptyDims))
327  continue;
328 
329  // Insert this row into table.
330 
331  Sequence<Any> row(nColSize);
332  pRow = row.getArray();
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  pRow[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  auto pTabData = rTabData.getArray();
354  for (sal_Int32 i = 0; i < nTabSize; ++i)
355  pTabData[i] = tableData[i];
356 }
357 
359 {
360  maFieldEntries.clear();
361  maShowByFilter.clear();
362  maShowByPage.clear();
363 }
364 
366 {
367  return maFieldEntries.empty();
368 }
369 
370 bool ScDPFilteredCache::isRowQualified(sal_Int32 nRow, const vector<Criterion>& rCriteria,
371  const std::unordered_set<sal_Int32>& rRepeatIfEmptyDims) const
372 {
373  sal_Int32 nColSize = getColSize();
374  for (const auto& rCriterion : rCriteria)
375  {
376  if (rCriterion.mnFieldIndex >= nColSize)
377  // specified field is outside the source data columns. Don't
378  // use this criterion.
379  continue;
380 
381  // Check if the 'repeat if empty' flag is set for this field.
382  bool bRepeatIfEmpty = rRepeatIfEmptyDims.count(rCriterion.mnFieldIndex) > 0;
383  const ScDPItemData* pCellData = getCell(static_cast<SCCOL>(rCriterion.mnFieldIndex), nRow, bRepeatIfEmpty);
384  if (!rCriterion.mpFilter->match(*pCellData))
385  return false;
386  }
387  return true;
388 }
389 
390 #if DUMP_PIVOT_TABLE
391 
392 void ScDPFilteredCache::dumpRowFlag( const RowFlagType& rFlag )
393 {
394  RowFlagType::const_iterator it = rFlag.begin(), itEnd = rFlag.end();
395  bool bShow = it->second;
396  SCROW nRow1 = it->first;
397  for (++it; it != itEnd; ++it)
398  {
399  SCROW nRow2 = it->first;
400  cout << " * range " << nRow1 << "-" << nRow2 << ": " << (bShow ? "on" : "off") << endl;
401  bShow = it->second;
402  nRow1 = nRow2;
403  }
404 }
405 
406 void ScDPFilteredCache::dump() const
407 {
408  cout << "--- pivot filtered cache dump" << endl;
409 
410  cout << endl;
411  cout << "* show by filter" << endl;
413 
414  cout << endl;
415  cout << "* show by page dimensions" << endl;
417 
418  cout << endl;
419  cout << "* field entries" << endl;
420  size_t nFieldCount = maFieldEntries.size();
421  for (size_t i = 0; i < nFieldCount; ++i)
422  {
423  const vector<SCROW>& rField = maFieldEntries[i];
424  cout << " * field " << i << endl;
425  for (size_t j = 0, n = rField.size(); j < n; ++j)
426  cout << " ID: " << rField[j] << endl;
427  }
428  cout << "---" << endl;
429 }
430 
431 #endif
432 
433 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OUString getFieldName(SCCOL nIndex) const
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:1079
OUString GetDimensionName(std::vector< OUString >::size_type nDim) const
Definition: dpcache.cxx:916
sal_Int32 getRowSize() const
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.
std::unique_ptr< sal_Int32[]> pData
sal_Int64 n
sal_Int16 nId
This class represents the cached data part of the datapilot cache table implementation.
Definition: dpcache.hxx:47
SCROW GetItemDataId(sal_uInt16 nDim, SCROW nRow, bool bRepeatIfEmpty) const
Definition: dpcache.cxx:958
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:1031
const SCROW MAXROW
Definition: address.hxx:68
const ScDPItemData * GetItemDataById(tools::Long nDim, SCROW nId) const
Definition: dpcache.cxx:983
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:28
int i
ScDPFilteredCache(const ScDPCache &rCache)
sal_Int16 SCCOL
Definition: types.hxx:21
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:1036
::std::vector<::std::vector< SCROW > > maFieldEntries
unique field entries for each field (column).
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:888
double mfValue
Definition: dpglobal.hxx:48
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:17
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:49
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
bool m_bDetectedRangeSegmentation false