Line data Source code
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 "dpfilteredcache.hxx"
21 : #include "document.hxx"
22 : #include "address.hxx"
23 : #include "cell.hxx"
24 : #include "dptabdat.hxx"
25 : #include "dptabsrc.hxx"
26 : #include "dpobject.hxx"
27 : #include "queryparam.hxx"
28 : #include "queryentry.hxx"
29 : #include "dpitemdata.hxx"
30 :
31 : #include <com/sun/star/i18n/LocaleDataItem.hpp>
32 : #include <com/sun/star/sdbc/DataType.hpp>
33 : #include <com/sun/star/sdbc/XRow.hpp>
34 : #include <com/sun/star/sdbc/XRowSet.hpp>
35 : #include <com/sun/star/sdbc/XResultSetMetaData.hpp>
36 : #include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
37 : #include <com/sun/star/util/Date.hpp>
38 : #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
39 : #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
40 :
41 : #include <memory>
42 :
43 : using namespace ::com::sun::star;
44 :
45 : using ::rtl::OUString;
46 : using ::std::vector;
47 : using ::std::pair;
48 : using ::std::auto_ptr;
49 : using ::com::sun::star::i18n::LocaleDataItem;
50 : using ::com::sun::star::uno::Exception;
51 : using ::com::sun::star::uno::Reference;
52 : using ::com::sun::star::uno::Sequence;
53 : using ::com::sun::star::uno::Any;
54 : using ::com::sun::star::uno::UNO_QUERY;
55 : using ::com::sun::star::uno::UNO_QUERY_THROW;
56 : using ::com::sun::star::sheet::DataPilotFieldFilter;
57 :
58 0 : ScDPFilteredCache::SingleFilter::SingleFilter(const ScDPItemData& rItem) :
59 0 : maItem(rItem) {}
60 :
61 0 : bool ScDPFilteredCache::SingleFilter::match(const ScDPItemData& rCellData) const
62 : {
63 0 : return maItem == rCellData;
64 : }
65 :
66 0 : const ScDPItemData& ScDPFilteredCache::SingleFilter::getMatchValue() const
67 : {
68 0 : return maItem;
69 : }
70 :
71 4 : ScDPFilteredCache::GroupFilter::GroupFilter()
72 : {
73 4 : }
74 :
75 16 : bool ScDPFilteredCache::GroupFilter::match(const ScDPItemData& rCellData) const
76 : {
77 16 : vector<ScDPItemData>::const_iterator it = maItems.begin(), itEnd = maItems.end();
78 24 : for (; it != itEnd; ++it)
79 : {
80 16 : bool bMatch = *it == rCellData;
81 16 : if (bMatch)
82 8 : return true;
83 : }
84 8 : return false;
85 : }
86 :
87 6 : void ScDPFilteredCache::GroupFilter::addMatchItem(const ScDPItemData& rItem)
88 : {
89 6 : maItems.push_back(rItem);
90 6 : }
91 :
92 4 : size_t ScDPFilteredCache::GroupFilter::getMatchItemCount() const
93 : {
94 4 : return maItems.size();
95 : }
96 :
97 : // ----------------------------------------------------------------------------
98 :
99 4 : ScDPFilteredCache::Criterion::Criterion() :
100 : mnFieldIndex(-1),
101 4 : mpFilter(static_cast<FilterBase*>(NULL))
102 : {
103 4 : }
104 :
105 : // ----------------------------------------------------------------------------
106 :
107 19 : ScDPFilteredCache::ScDPFilteredCache(const ScDPCache& rCache) :
108 19 : maShowByFilter(0, MAXROW+1, false), maShowByPage(0, MAXROW+1, true), mrCache(rCache)
109 : {
110 19 : }
111 :
112 19 : ScDPFilteredCache::~ScDPFilteredCache()
113 : {
114 19 : }
115 :
116 56 : sal_Int32 ScDPFilteredCache::getRowSize() const
117 : {
118 56 : return mrCache.GetRowCount();
119 : }
120 :
121 10506 : sal_Int32 ScDPFilteredCache::getColSize() const
122 : {
123 10506 : return mrCache.GetColumnCount();
124 : }
125 :
126 27 : void ScDPFilteredCache::fillTable(
127 : const ScQueryParam& rQuery, bool bIgnoreEmptyRows, bool bRepeatIfEmpty)
128 : {
129 27 : SCROW nRowCount = getRowSize();
130 27 : SCROW nDataSize = mrCache.GetDataSize();
131 27 : SCCOL nColCount = getColSize();
132 27 : if (nRowCount <= 0 || nColCount <= 0)
133 27 : return;
134 :
135 27 : maShowByFilter.clear();
136 27 : maShowByPage.clear();
137 27 : maShowByPage.build_tree();
138 :
139 : // Process the non-empty data rows.
140 239 : for (SCROW nRow = 0; nRow < nDataSize; ++nRow)
141 : {
142 212 : if (!getCache()->ValidQuery(nRow, rQuery))
143 8 : continue;
144 :
145 204 : if (bIgnoreEmptyRows && getCache()->IsRowEmpty(nRow))
146 0 : continue;
147 :
148 204 : maShowByFilter.insert_back(nRow, nRow+1, true);
149 : }
150 :
151 : // Process the trailing empty rows.
152 27 : if (!bIgnoreEmptyRows)
153 25 : maShowByFilter.insert_back(nDataSize, nRowCount, true);
154 :
155 27 : maShowByFilter.build_tree();
156 :
157 : // Initialize field entries container.
158 27 : maFieldEntries.clear();
159 27 : maFieldEntries.reserve(nColCount);
160 :
161 : // Build unique field entries.
162 101 : for (SCCOL nCol = 0; nCol < nColCount; ++nCol)
163 : {
164 74 : maFieldEntries.push_back( vector<SCROW>() );
165 74 : SCROW nMemCount = getCache()->GetDimMemberCount( nCol );
166 74 : if (!nMemCount)
167 0 : continue;
168 :
169 74 : std::vector<SCROW> aAdded(nMemCount, -1);
170 74 : bool bShow = false;
171 74 : SCROW nEndSegment = -1;
172 648 : for (SCROW nRow = 0; nRow < nRowCount; ++nRow)
173 : {
174 574 : if (nRow > nEndSegment)
175 : {
176 88 : if (!maShowByFilter.search_tree(nRow, bShow, NULL, &nEndSegment))
177 : {
178 : OSL_FAIL("Tree search failed!");
179 0 : continue;
180 : }
181 88 : --nEndSegment; // End position is not inclusive. Move back one.
182 : }
183 :
184 574 : if (!bShow)
185 : {
186 14 : nRow = nEndSegment;
187 14 : continue;
188 : }
189 :
190 560 : SCROW nIndex = getCache()->GetItemDataId(nCol, nRow, bRepeatIfEmpty);
191 560 : SCROW nOrder = getOrder(nCol, nIndex);
192 560 : aAdded[nOrder] = nIndex;
193 : }
194 500 : for (SCROW nRow = 0; nRow < nMemCount; ++nRow)
195 : {
196 426 : if (aAdded[nRow] != -1)
197 412 : maFieldEntries.back().push_back(aAdded[nRow]);
198 : }
199 74 : }
200 : }
201 :
202 1 : void ScDPFilteredCache::fillTable()
203 : {
204 1 : SCROW nRowCount = getRowSize();
205 1 : SCCOL nColCount = getColSize();
206 1 : if (nRowCount <= 0 || nColCount <= 0)
207 1 : return;
208 :
209 1 : maShowByPage.clear();
210 1 : maShowByPage.build_tree();
211 :
212 1 : maShowByFilter.clear();
213 1 : maShowByFilter.insert_front(0, nRowCount, true);
214 1 : maShowByFilter.build_tree();
215 :
216 : // Initialize field entries container.
217 1 : maFieldEntries.clear();
218 1 : maFieldEntries.reserve(nColCount);
219 :
220 : // Data rows
221 4 : for (SCCOL nCol = 0; nCol < nColCount; ++nCol)
222 : {
223 3 : maFieldEntries.push_back( vector<SCROW>() );
224 3 : SCROW nMemCount = getCache()->GetDimMemberCount( nCol );
225 3 : if (!nMemCount)
226 0 : continue;
227 :
228 3 : std::vector<SCROW> aAdded(nMemCount, -1);
229 :
230 21 : for (SCROW nRow = 0; nRow < nRowCount; ++nRow)
231 : {
232 18 : SCROW nIndex = getCache()->GetItemDataId(nCol, nRow, false);
233 18 : SCROW nOrder = getOrder(nCol, nIndex);
234 18 : aAdded[nOrder] = nIndex;
235 : }
236 18 : for (SCROW nRow = 0; nRow < nMemCount; ++nRow)
237 : {
238 15 : if (aAdded[nRow] != -1)
239 15 : maFieldEntries.back().push_back(aAdded[nRow]);
240 : }
241 3 : }
242 : }
243 :
244 188 : bool ScDPFilteredCache::isRowActive(sal_Int32 nRow, sal_Int32* pLastRow) const
245 : {
246 188 : bool bFilter = false, bPage = true;
247 188 : SCROW nLastRowFilter = MAXROW, nLastRowPage = MAXROW;
248 188 : maShowByFilter.search_tree(nRow, bFilter, NULL, &nLastRowFilter);
249 188 : maShowByPage.search_tree(nRow, bPage, NULL, &nLastRowPage);
250 188 : if (pLastRow)
251 : {
252 : // Return the last row of current segment.
253 182 : *pLastRow = nLastRowFilter < nLastRowPage ? nLastRowFilter : nLastRowPage;
254 182 : *pLastRow -= 1; // End position is not inclusive. Move back one.
255 : }
256 :
257 188 : return bFilter && bPage;
258 : }
259 :
260 2 : void ScDPFilteredCache::filterByPageDimension(const vector<Criterion>& rCriteria, const boost::unordered_set<sal_Int32>& rRepeatIfEmptyDims)
261 : {
262 2 : SCROW nRowSize = getRowSize();
263 :
264 2 : maShowByPage.clear();
265 :
266 18 : for (SCROW nRow = 0; nRow < nRowSize; ++nRow)
267 : {
268 16 : bool bShow = isRowQualified(nRow, rCriteria, rRepeatIfEmptyDims);
269 16 : maShowByPage.insert_back(nRow, nRow+1, bShow);
270 : }
271 :
272 2 : maShowByPage.build_tree();
273 2 : }
274 :
275 228 : const ScDPItemData* ScDPFilteredCache::getCell(SCCOL nCol, SCROW nRow, bool bRepeatIfEmpty) const
276 : {
277 228 : SCROW nId= mrCache.GetItemDataId(nCol, nRow, bRepeatIfEmpty);
278 228 : return mrCache.GetItemDataById( nCol, nId );
279 : }
280 :
281 212 : void ScDPFilteredCache::getValue( ScDPValueData& rVal, SCCOL nCol, SCROW nRow, bool bRepeatIfEmpty) const
282 : {
283 212 : const ScDPItemData* pData = getCell( nCol, nRow, bRepeatIfEmpty );
284 :
285 212 : if (pData)
286 : {
287 212 : rVal.fValue = pData->IsValue() ? pData->GetValue() : 0.0;
288 212 : rVal.nType = pData->GetCellType();
289 : }
290 : else
291 0 : rVal.Set(0.0, SC_VALTYPE_EMPTY);
292 212 : }
293 :
294 2120 : rtl::OUString ScDPFilteredCache::getFieldName(SCCOL nIndex) const
295 : {
296 2120 : return mrCache.GetDimensionName(nIndex);
297 : }
298 :
299 189 : const ::std::vector<SCROW>& ScDPFilteredCache::getFieldEntries( sal_Int32 nColumn ) const
300 : {
301 189 : if (nColumn < 0 || static_cast<size_t>(nColumn) >= maFieldEntries.size())
302 : {
303 : // index out of bound. Hopefully this code will never be reached.
304 0 : static const ::std::vector<SCROW> emptyEntries;
305 0 : return emptyEntries;
306 : }
307 189 : return maFieldEntries[nColumn];
308 : }
309 :
310 0 : void ScDPFilteredCache::filterTable(const vector<Criterion>& rCriteria, Sequence< Sequence<Any> >& rTabData,
311 : const boost::unordered_set<sal_Int32>& rRepeatIfEmptyDims)
312 : {
313 0 : sal_Int32 nRowSize = getRowSize();
314 0 : sal_Int32 nColSize = getColSize();
315 :
316 0 : if (!nRowSize)
317 : // no data to filter.
318 0 : return;
319 :
320 : // Row first, then column.
321 0 : vector< Sequence<Any> > tableData;
322 0 : tableData.reserve(nRowSize+1);
323 :
324 : // Header first.
325 0 : Sequence<Any> headerRow(nColSize);
326 0 : for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
327 : {
328 0 : OUString str;
329 0 : str = getFieldName( nCol);
330 0 : Any any;
331 0 : any <<= str;
332 0 : headerRow[nCol] = any;
333 0 : }
334 0 : tableData.push_back(headerRow);
335 :
336 0 : for (sal_Int32 nRow = 0; nRow < nRowSize; ++nRow)
337 : {
338 : sal_Int32 nLastRow;
339 0 : if (!isRowActive(nRow, &nLastRow))
340 : {
341 : // This row is filtered out.
342 0 : nRow = nLastRow;
343 0 : continue;
344 : }
345 :
346 0 : if (!isRowQualified(nRow, rCriteria, rRepeatIfEmptyDims))
347 0 : continue;
348 :
349 : // Insert this row into table.
350 :
351 0 : Sequence<Any> row(nColSize);
352 0 : for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
353 : {
354 0 : Any any;
355 0 : bool bRepeatIfEmpty = rRepeatIfEmptyDims.count(nCol) > 0;
356 0 : const ScDPItemData* pData= getCell(nCol, nRow, bRepeatIfEmpty);
357 0 : if ( pData->IsValue() )
358 0 : any <<= pData->GetValue();
359 : else
360 : {
361 0 : OUString string (pData->GetString() );
362 0 : any <<= string;
363 : }
364 0 : row[nCol] = any;
365 0 : }
366 0 : tableData.push_back(row);
367 0 : }
368 :
369 : // convert vector to Seqeunce
370 0 : sal_Int32 nTabSize = static_cast<sal_Int32>(tableData.size());
371 0 : rTabData.realloc(nTabSize);
372 0 : for (sal_Int32 i = 0; i < nTabSize; ++i)
373 0 : rTabData[i] = tableData[i];
374 : }
375 :
376 922 : SCROW ScDPFilteredCache::getOrder(long nDim, SCROW nIndex) const
377 : {
378 922 : return mrCache.GetOrder(nDim, nIndex);
379 : }
380 :
381 25 : void ScDPFilteredCache::clear()
382 : {
383 25 : maFieldEntries.clear();
384 25 : maShowByFilter.clear();
385 25 : maShowByPage.clear();
386 25 : }
387 :
388 10838 : bool ScDPFilteredCache::empty() const
389 : {
390 10838 : return maFieldEntries.empty();
391 : }
392 :
393 16 : bool ScDPFilteredCache::isRowQualified(sal_Int32 nRow, const vector<Criterion>& rCriteria,
394 : const boost::unordered_set<sal_Int32>& rRepeatIfEmptyDims) const
395 : {
396 16 : sal_Int32 nColSize = getColSize();
397 16 : vector<Criterion>::const_iterator itrEnd = rCriteria.end();
398 24 : for (vector<Criterion>::const_iterator itr = rCriteria.begin(); itr != itrEnd; ++itr)
399 : {
400 16 : if (itr->mnFieldIndex >= nColSize)
401 : // specified field is outside the source data columns. Don't
402 : // use this criterion.
403 0 : continue;
404 :
405 : // Check if the 'repeat if empty' flag is set for this field.
406 16 : bool bRepeatIfEmpty = rRepeatIfEmptyDims.count(itr->mnFieldIndex) > 0;
407 16 : const ScDPItemData* pCellData = getCell(static_cast<SCCOL>(itr->mnFieldIndex), nRow, bRepeatIfEmpty);
408 16 : if (!itr->mpFilter->match(*pCellData))
409 8 : return false;
410 : }
411 8 : return true;
412 : }
413 :
414 3016 : const ScDPCache* ScDPFilteredCache::getCache() const
415 : {
416 3016 : return &mrCache;
417 15 : }
418 :
419 : #if DEBUG_PIVOT_TABLE
420 : #include <iostream>
421 : using std::cout;
422 : using std::endl;
423 :
424 : void ScDPFilteredCache::dumpRowFlag(const RowFlagType& rFlag) const
425 : {
426 : RowFlagType::const_iterator it = rFlag.begin(), itEnd = rFlag.end();
427 : bool bShow = it->second;
428 : SCROW nRow1 = it->first;
429 : for (++it; it != itEnd; ++it)
430 : {
431 : SCROW nRow2 = it->first;
432 : cout << " * range " << nRow1 << "-" << nRow2 << ": " << (bShow ? "on" : "off") << endl;
433 : bShow = it->second;
434 : nRow1 = nRow2;
435 : }
436 : }
437 :
438 : void ScDPFilteredCache::dump() const
439 : {
440 : cout << "--- pivot cache filter dump" << endl;
441 :
442 : cout << endl;
443 : cout << "* show by filter" << endl;
444 : dumpRowFlag(maShowByFilter);
445 :
446 : cout << endl;
447 : cout << "* show by page dimensions" << endl;
448 : dumpRowFlag(maShowByPage);
449 :
450 : cout << "---" << endl;
451 : }
452 :
453 : #endif
454 :
455 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|