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
30using ::std::vector;
31using ::com::sun::star::uno::Sequence;
32using ::com::sun::star::uno::Any;
33
35 maItem(rItem) {}
36
38{
39 return maItem == rCellData;
40}
41
42std::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
56std::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
216bool 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
232void 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
255const 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
261void 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{
277}
278
279const ::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
290void 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
370bool 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
392void 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
406void 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: */
const Any & any
const SCROW MAXROW
Definition: address.hxx:68
This class represents the cached data part of the datapilot cache table implementation.
Definition: dpcache.hxx:48
SCROW GetDataSize() const
Data size is the number of records without any trailing empty rows for sheet source data.
Definition: dpcache.cxx:1036
const ScDPItemData * GetItemDataById(tools::Long nDim, SCROW nId) const
Definition: dpcache.cxx:983
tools::Long GetDimMemberCount(tools::Long nDim) const
Definition: dpcache.cxx:1079
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
SCROW GetItemDataId(sal_uInt16 nDim, SCROW nRow, bool bRepeatIfEmpty) const
Definition: dpcache.cxx:958
OUString GetDimensionName(std::vector< OUString >::size_type nDim) const
Definition: dpcache.cxx:916
tools::Long GetColumnCount() const
Definition: dpcache.cxx:888
virtual std::vector< ScDPItemData > getMatchValues() const override
void addMatchItem(const ScDPItemData &rItem)
virtual bool match(const ScDPItemData &rCellData) const override
returns true if the matching condition is met for a single cell value, or false otherwise.
virtual bool match(const ScDPItemData &rCellData) const override
returns true if the matching condition is met for a single cell value, or false otherwise.
virtual std::vector< ScDPItemData > getMatchValues() const override
SingleFilter(const ScDPItemData &rItem)
sal_Int32 getColSize() 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.
RowFlagType maShowByFilter
Rows visible by standard filter query.
const ScDPItemData * getCell(SCCOL nCol, SCROW nRow, bool bRepeatIfEmpty) const
Get the cell instance at specified location within the data grid.
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.
void dump() const
RowFlagType maShowByPage
Rows visible by page dimension filtering.
ScDPFilteredCache(const ScDPCache &rCache)
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.
sal_Int32 getRowSize() const
OUString getFieldName(SCCOL nIndex) const
static void dumpRowFlag(const RowFlagType &rFlag)
const ScDPCache & mrCache
const ::std::vector< SCROW > & getFieldEntries(sal_Int32 nColumn) const
Get the unique entries for a field specified by index.
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.
::std::vector<::std::vector< SCROW > > maFieldEntries
unique field entries for each field (column).
const ScDPCache & getCache() const
When assigning a string value, you can also assign an interned string whose life-cycle is managed by ...
Definition: dpitemdata.hxx:29
sal_Int32 nIndex
std::unique_ptr< sal_Int32[]> pData
int i
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
sal_Int16 nId
const size_t nTabSize
TOOLS_DLLPUBLIC SvStream & endl(SvStream &rStr)
void Set(double fV, Type eT)
Definition: dpglobal.cxx:14
double mfValue
Definition: dpglobal.hxx:48
Type meType
Definition: dpglobal.hxx:49
sal_Int16 SCCOL
Definition: types.hxx:21
sal_Int32 SCROW
Definition: types.hxx:17