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 "dpgroup.hxx"
21 :
22 : #include "global.hxx"
23 : #include "document.hxx"
24 : #include "dpfilteredcache.hxx"
25 : #include "dptabsrc.hxx"
26 : #include "dptabres.hxx"
27 : #include "dpobject.hxx"
28 : #include "dpglobal.hxx"
29 : #include "dputil.hxx"
30 : #include "globalnames.hxx"
31 :
32 : #include <osl/diagnose.h>
33 : #include <rtl/math.hxx>
34 :
35 : #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
36 : #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
37 : #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
38 :
39 : #include <unordered_map>
40 : #include <unordered_set>
41 : #include <vector>
42 : #include <algorithm>
43 :
44 : using namespace ::com::sun::star;
45 : using ::com::sun::star::uno::Any;
46 : using ::com::sun::star::uno::Reference;
47 : using ::com::sun::star::uno::Sequence;
48 : using ::com::sun::star::uno::UNO_QUERY;
49 : using ::com::sun::star::uno::UNO_QUERY_THROW;
50 :
51 : using ::std::vector;
52 : using ::boost::shared_ptr;
53 :
54 : const sal_uInt16 SC_DP_LEAPYEAR = 1648; // arbitrary leap year for date calculations
55 :
56 : class ScDPGroupNumFilter : public ScDPFilteredCache::FilterBase
57 : {
58 : public:
59 : ScDPGroupNumFilter(const std::vector<ScDPItemData>& rValues, const ScDPNumGroupInfo& rInfo);
60 0 : virtual ~ScDPGroupNumFilter() {}
61 : virtual bool match(const ScDPItemData &rCellData) const SAL_OVERRIDE;
62 : virtual std::vector<ScDPItemData> getMatchValues() const SAL_OVERRIDE;
63 : private:
64 : std::vector<ScDPItemData> maValues;
65 : ScDPNumGroupInfo maNumInfo;
66 : };
67 :
68 0 : ScDPGroupNumFilter::ScDPGroupNumFilter(const std::vector<ScDPItemData>& rValues, const ScDPNumGroupInfo& rInfo) :
69 0 : maValues(rValues), maNumInfo(rInfo) {}
70 :
71 0 : bool ScDPGroupNumFilter::match(const ScDPItemData& rCellData) const
72 : {
73 0 : if (rCellData.GetType() != ScDPItemData::Value)
74 0 : return false;
75 :
76 0 : std::vector<ScDPItemData>::const_iterator it = maValues.begin(), itEnd = maValues.end();
77 0 : for (; it != itEnd; ++it)
78 : {
79 0 : double fVal = it->GetValue();
80 0 : if (rtl::math::isInf(fVal))
81 : {
82 0 : if (rtl::math::isSignBitSet(fVal))
83 : {
84 : // Less than the min value.
85 0 : if (rCellData.GetValue() < maNumInfo.mfStart)
86 0 : return true;
87 : }
88 :
89 : // Greater than the max value.
90 0 : if (maNumInfo.mfEnd < rCellData.GetValue())
91 0 : return true;
92 :
93 0 : continue;
94 : }
95 :
96 0 : double low = fVal;
97 0 : double high = low + maNumInfo.mfStep;
98 0 : if (maNumInfo.mbIntegerOnly)
99 0 : high += 1.0;
100 :
101 0 : if (low <= rCellData.GetValue() && rCellData.GetValue() < high)
102 0 : return true;
103 : }
104 :
105 0 : return false;
106 : }
107 :
108 0 : std::vector<ScDPItemData> ScDPGroupNumFilter::getMatchValues() const
109 : {
110 0 : return std::vector<ScDPItemData>();
111 : }
112 :
113 : class ScDPGroupDateFilter : public ScDPFilteredCache::FilterBase
114 : {
115 : public:
116 0 : virtual ~ScDPGroupDateFilter() {}
117 : ScDPGroupDateFilter(
118 : const std::vector<ScDPItemData>& rValues, const Date& rNullDate, const ScDPNumGroupInfo& rNumInfo);
119 :
120 : virtual bool match(const ScDPItemData & rCellData) const SAL_OVERRIDE;
121 : virtual std::vector<ScDPItemData> getMatchValues() const SAL_OVERRIDE;
122 :
123 : private:
124 : std::vector<ScDPItemData> maValues;
125 : Date maNullDate;
126 : ScDPNumGroupInfo maNumInfo;
127 : };
128 :
129 0 : ScDPGroupDateFilter::ScDPGroupDateFilter(
130 : const std::vector<ScDPItemData>& rValues, const Date& rNullDate, const ScDPNumGroupInfo& rNumInfo) :
131 : maValues(rValues),
132 : maNullDate(rNullDate),
133 0 : maNumInfo(rNumInfo)
134 : {
135 0 : }
136 :
137 0 : bool ScDPGroupDateFilter::match( const ScDPItemData & rCellData ) const
138 : {
139 : using namespace ::com::sun::star::sheet;
140 : using ::rtl::math::approxFloor;
141 : using ::rtl::math::approxEqual;
142 :
143 0 : if ( !rCellData.IsValue() )
144 0 : return false;
145 :
146 0 : std::vector<ScDPItemData>::const_iterator it = maValues.begin(), itEnd = maValues.end();
147 0 : for (; it != itEnd; ++it)
148 : {
149 0 : const ScDPItemData& rValue = *it;
150 0 : if (rValue.GetType() != ScDPItemData::GroupValue)
151 0 : continue;
152 :
153 0 : sal_Int32 nGroupType = rValue.GetGroupValue().mnGroupType;
154 0 : sal_Int32 nValue = rValue.GetGroupValue().mnValue;
155 :
156 : // Start and end dates are inclusive. (An end date without a time value
157 : // is included, while an end date with a time value is not.)
158 :
159 0 : if (rCellData.GetValue() < maNumInfo.mfStart && !approxEqual(rCellData.GetValue(), maNumInfo.mfStart))
160 : {
161 0 : if (nValue == ScDPItemData::DateFirst)
162 0 : return true;
163 0 : continue;
164 : }
165 :
166 0 : if (rCellData.GetValue() > maNumInfo.mfEnd && !approxEqual(rCellData.GetValue(), maNumInfo.mfEnd))
167 : {
168 0 : if (nValue == ScDPItemData::DateLast)
169 0 : return true;
170 0 : continue;
171 : }
172 :
173 0 : if (nGroupType == DataPilotFieldGroupBy::HOURS || nGroupType == DataPilotFieldGroupBy::MINUTES ||
174 : nGroupType == DataPilotFieldGroupBy::SECONDS)
175 : {
176 : // handle time
177 : // (as in the cell functions, ScInterpreter::ScGetHour etc.: seconds are rounded)
178 :
179 0 : double time = rCellData.GetValue() - approxFloor(rCellData.GetValue());
180 0 : long seconds = static_cast<long>(approxFloor(time*DATE_TIME_FACTOR + 0.5));
181 :
182 0 : switch (nGroupType)
183 : {
184 : case DataPilotFieldGroupBy::HOURS:
185 : {
186 0 : sal_Int32 hrs = seconds / 3600;
187 0 : if (hrs == nValue)
188 0 : return true;
189 : }
190 0 : break;
191 : case DataPilotFieldGroupBy::MINUTES:
192 : {
193 0 : sal_Int32 minutes = (seconds % 3600) / 60;
194 0 : if (minutes == nValue)
195 0 : return true;
196 : }
197 0 : break;
198 : case DataPilotFieldGroupBy::SECONDS:
199 : {
200 0 : sal_Int32 sec = seconds % 60;
201 0 : if (sec == nValue)
202 0 : return true;
203 : }
204 0 : break;
205 : default:
206 : OSL_FAIL("invalid time part");
207 : }
208 :
209 0 : continue;
210 : }
211 :
212 0 : Date date = maNullDate + static_cast<long>(approxFloor(rCellData.GetValue()));
213 0 : switch (nGroupType)
214 : {
215 : case DataPilotFieldGroupBy::YEARS:
216 : {
217 0 : sal_Int32 year = static_cast<sal_Int32>(date.GetYear());
218 0 : if (year == nValue)
219 0 : return true;
220 : }
221 0 : break;
222 : case DataPilotFieldGroupBy::QUARTERS:
223 : {
224 0 : sal_Int32 qtr = 1 + (static_cast<sal_Int32>(date.GetMonth()) - 1) / 3;
225 0 : if (qtr == nValue)
226 0 : return true;
227 : }
228 0 : break;
229 : case DataPilotFieldGroupBy::MONTHS:
230 : {
231 0 : sal_Int32 month = static_cast<sal_Int32>(date.GetMonth());
232 0 : if (month == nValue)
233 0 : return true;
234 : }
235 0 : break;
236 : case DataPilotFieldGroupBy::DAYS:
237 : {
238 0 : Date yearStart(1, 1, date.GetYear());
239 0 : sal_Int32 days = (date - yearStart) + 1; // Jan 01 has value 1
240 0 : if (days >= 60 && !date.IsLeapYear())
241 : {
242 : // This is not a leap year. Adjust the value accordingly.
243 0 : ++days;
244 : }
245 0 : if (days == nValue)
246 0 : return true;
247 : }
248 0 : break;
249 : default:
250 : OSL_FAIL("invalid date part");
251 : }
252 : }
253 :
254 0 : return false;
255 : }
256 :
257 0 : std::vector<ScDPItemData> ScDPGroupDateFilter::getMatchValues() const
258 : {
259 0 : return std::vector<ScDPItemData>();
260 : }
261 :
262 : namespace {
263 :
264 36 : bool isDateInGroup(const ScDPItemData& rGroupItem, const ScDPItemData& rChildItem)
265 : {
266 36 : if (rGroupItem.GetType() != ScDPItemData::GroupValue || rChildItem.GetType() != ScDPItemData::GroupValue)
267 0 : return false;
268 :
269 36 : sal_Int32 nGroupPart = rGroupItem.GetGroupValue().mnGroupType;
270 36 : sal_Int32 nGroupValue = rGroupItem.GetGroupValue().mnValue;
271 36 : sal_Int32 nChildPart = rChildItem.GetGroupValue().mnGroupType;
272 36 : sal_Int32 nChildValue = rChildItem.GetGroupValue().mnValue;
273 :
274 72 : if (nGroupValue == ScDPItemData::DateFirst || nGroupValue == ScDPItemData::DateLast ||
275 72 : nChildValue == ScDPItemData::DateFirst || nChildValue == ScDPItemData::DateLast)
276 : {
277 : // first/last entry matches only itself
278 0 : return nGroupValue == nChildValue;
279 : }
280 :
281 36 : switch (nChildPart) // inner part
282 : {
283 : case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS:
284 : // a month is only contained in its quarter
285 28 : if (nGroupPart == com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS)
286 : // months and quarters are both 1-based
287 14 : return (nGroupValue - 1 == (nChildValue - 1) / 3);
288 14 : break;
289 : case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS:
290 : // a day is only contained in its quarter or month
291 0 : if (nGroupPart == com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS ||
292 : nGroupPart == com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS)
293 : {
294 0 : Date aDate(1, 1, SC_DP_LEAPYEAR);
295 0 : aDate += (nChildValue - 1); // days are 1-based
296 0 : sal_Int32 nCompare = aDate.GetMonth();
297 0 : if (nGroupPart == com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS)
298 0 : nCompare = ( ( nCompare - 1 ) / 3 ) + 1; // get quarter from date
299 :
300 0 : return nGroupValue == nCompare;
301 : }
302 0 : break;
303 : default:
304 : ;
305 : }
306 :
307 22 : return true;
308 : }
309 :
310 : }
311 :
312 7 : ScDPGroupItem::ScDPGroupItem( const ScDPItemData& rName ) :
313 7 : aGroupName( rName )
314 : {
315 7 : }
316 :
317 29 : ScDPGroupItem::~ScDPGroupItem()
318 : {
319 29 : }
320 :
321 23 : void ScDPGroupItem::AddElement( const ScDPItemData& rName )
322 : {
323 23 : aElements.push_back( rName );
324 23 : }
325 :
326 73 : bool ScDPGroupItem::HasElement( const ScDPItemData& rData ) const
327 : {
328 222 : for ( ScDPItemDataVec::const_iterator aIter(aElements.begin()); aIter != aElements.end(); ++aIter )
329 192 : if ( aIter->IsCaseInsEqual( rData ) )
330 43 : return true;
331 :
332 30 : return false;
333 : }
334 :
335 0 : bool ScDPGroupItem::HasCommonElement( const ScDPGroupItem& rOther ) const
336 : {
337 0 : for ( ScDPItemDataVec::const_iterator aIter(aElements.begin()); aIter != aElements.end(); ++aIter )
338 0 : if ( rOther.HasElement( *aIter ) )
339 0 : return true;
340 :
341 0 : return false;
342 : }
343 :
344 0 : void ScDPGroupItem::FillGroupFilter( ScDPFilteredCache::GroupFilter& rFilter ) const
345 : {
346 0 : ScDPItemDataVec::const_iterator itrEnd = aElements.end();
347 0 : for (ScDPItemDataVec::const_iterator itr = aElements.begin(); itr != itrEnd; ++itr)
348 0 : rFilter.addMatchItem(*itr);
349 0 : }
350 :
351 12 : ScDPGroupDimension::ScDPGroupDimension( long nSource, const OUString& rNewName ) :
352 : nSourceDim( nSource ),
353 : nGroupDim( -1 ),
354 : aGroupName( rNewName ),
355 12 : mbDateDimension(false)
356 : {
357 12 : }
358 :
359 78 : ScDPGroupDimension::~ScDPGroupDimension()
360 : {
361 39 : maMemberEntries.clear();
362 39 : }
363 :
364 27 : ScDPGroupDimension::ScDPGroupDimension( const ScDPGroupDimension& rOther ) :
365 : nSourceDim( rOther.nSourceDim ),
366 : nGroupDim( rOther.nGroupDim ),
367 : aGroupName( rOther.aGroupName ),
368 : aItems( rOther.aItems ),
369 27 : mbDateDimension(rOther.mbDateDimension)
370 : {
371 27 : }
372 :
373 0 : ScDPGroupDimension& ScDPGroupDimension::operator=( const ScDPGroupDimension& rOther )
374 : {
375 0 : nSourceDim = rOther.nSourceDim;
376 0 : nGroupDim = rOther.nGroupDim;
377 0 : aGroupName = rOther.aGroupName;
378 0 : aItems = rOther.aItems;
379 0 : mbDateDimension = rOther.mbDateDimension;
380 0 : return *this;
381 : }
382 :
383 7 : void ScDPGroupDimension::AddItem( const ScDPGroupItem& rItem )
384 : {
385 7 : aItems.push_back( rItem );
386 7 : }
387 :
388 12 : void ScDPGroupDimension::SetGroupDim( long nDim )
389 : {
390 12 : nGroupDim = nDim;
391 12 : }
392 :
393 54 : const std::vector<SCROW>& ScDPGroupDimension::GetColumnEntries(
394 : const ScDPFilteredCache& rCacheTable) const
395 : {
396 54 : if (!maMemberEntries.empty())
397 42 : return maMemberEntries;
398 :
399 12 : rCacheTable.getCache().GetGroupDimMemberIds(nGroupDim, maMemberEntries);
400 12 : return maMemberEntries;
401 : }
402 :
403 65 : const ScDPGroupItem* ScDPGroupDimension::GetGroupForData( const ScDPItemData& rData ) const
404 : {
405 95 : for (ScDPGroupItemVec::const_iterator aIter = aItems.begin(); aIter != aItems.end(); ++aIter)
406 73 : if (aIter->HasElement(rData))
407 43 : return &*aIter;
408 :
409 22 : return NULL;
410 : }
411 :
412 0 : const ScDPGroupItem* ScDPGroupDimension::GetGroupForName( const ScDPItemData& rName ) const
413 : {
414 0 : for ( ScDPGroupItemVec::const_iterator aIter(aItems.begin()); aIter != aItems.end(); ++aIter )
415 0 : if ( aIter->GetName().IsCaseInsEqual( rName ) )
416 0 : return &*aIter;
417 :
418 0 : return NULL;
419 : }
420 :
421 0 : const ScDPGroupItem* ScDPGroupDimension::GetGroupByIndex( size_t nIndex ) const
422 : {
423 0 : if (nIndex >= aItems.size())
424 0 : return NULL;
425 :
426 0 : return &aItems[nIndex];
427 : }
428 :
429 0 : void ScDPGroupDimension::DisposeData()
430 : {
431 0 : maMemberEntries.clear();
432 0 : }
433 :
434 6 : void ScDPGroupDimension::SetDateDimension()
435 : {
436 6 : mbDateDimension = true;
437 6 : }
438 :
439 48 : ScDPNumGroupDimension::ScDPNumGroupDimension() : mbDateDimension(false) {}
440 :
441 5 : ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupInfo& rInfo ) :
442 5 : aGroupInfo(rInfo), mbDateDimension(false) {}
443 :
444 0 : ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupDimension& rOther ) :
445 0 : aGroupInfo(rOther.aGroupInfo), mbDateDimension(rOther.mbDateDimension) {}
446 :
447 5 : ScDPNumGroupDimension& ScDPNumGroupDimension::operator=( const ScDPNumGroupDimension& rOther )
448 : {
449 5 : aGroupInfo = rOther.aGroupInfo;
450 5 : mbDateDimension = rOther.mbDateDimension;
451 5 : return *this;
452 : }
453 :
454 0 : void ScDPNumGroupDimension::DisposeData()
455 : {
456 0 : aGroupInfo = ScDPNumGroupInfo();
457 0 : maMemberEntries.clear();
458 0 : }
459 :
460 53 : ScDPNumGroupDimension::~ScDPNumGroupDimension()
461 : {
462 53 : }
463 :
464 4 : void ScDPNumGroupDimension::SetDateDimension()
465 : {
466 4 : aGroupInfo.mbEnable = true; //TODO: or query both?
467 4 : mbDateDimension = true;
468 4 : }
469 :
470 38 : const std::vector<SCROW>& ScDPNumGroupDimension::GetNumEntries(
471 : SCCOL nSourceDim, const ScDPCache* pCache) const
472 : {
473 38 : if (!maMemberEntries.empty())
474 33 : return maMemberEntries;
475 :
476 5 : pCache->GetGroupDimMemberIds(nSourceDim, maMemberEntries);
477 5 : return maMemberEntries;
478 : }
479 :
480 11 : ScDPGroupTableData::ScDPGroupTableData( const shared_ptr<ScDPTableData>& pSource, ScDocument* pDocument ) :
481 : ScDPTableData(pDocument),
482 : pSourceData( pSource ),
483 11 : pDoc( pDocument )
484 : {
485 : OSL_ENSURE( pSource, "ScDPGroupTableData: pSource can't be NULL" );
486 :
487 11 : CreateCacheTable();
488 11 : nSourceCount = pSource->GetColumnCount(); // real columns, excluding data layout
489 11 : pNumGroups = new ScDPNumGroupDimension[nSourceCount];
490 11 : }
491 :
492 33 : ScDPGroupTableData::~ScDPGroupTableData()
493 : {
494 11 : delete[] pNumGroups;
495 22 : }
496 :
497 12 : void ScDPGroupTableData::AddGroupDimension( const ScDPGroupDimension& rGroup )
498 : {
499 12 : ScDPGroupDimension aNewGroup( rGroup );
500 12 : aNewGroup.SetGroupDim( GetColumnCount() ); // new dimension will be at the end
501 12 : aGroups.push_back( aNewGroup );
502 12 : aGroupNames.insert(aNewGroup.GetName());
503 12 : }
504 :
505 5 : void ScDPGroupTableData::SetNumGroupDimension( long nIndex, const ScDPNumGroupDimension& rGroup )
506 : {
507 5 : if ( nIndex < nSourceCount )
508 : {
509 5 : pNumGroups[nIndex] = rGroup;
510 :
511 : // automatic minimum / maximum is handled in GetNumEntries
512 : }
513 5 : }
514 :
515 17 : long ScDPGroupTableData::GetDimensionIndex( const OUString& rName )
516 : {
517 33 : for (long i = 0; i < nSourceCount; ++i) // nSourceCount excludes data layout
518 33 : if (pSourceData->getDimensionName(i).equals(rName)) //TODO: ignore case?
519 17 : return i;
520 0 : return -1; // none
521 : }
522 :
523 4206 : long ScDPGroupTableData::GetColumnCount()
524 : {
525 4206 : return nSourceCount + aGroups.size();
526 : }
527 :
528 227 : bool ScDPGroupTableData::IsNumGroupDimension( long nDimension ) const
529 : {
530 227 : return ( nDimension < nSourceCount && pNumGroups[nDimension].GetInfo().mbEnable );
531 : }
532 :
533 18 : void ScDPGroupTableData::GetNumGroupInfo(long nDimension, ScDPNumGroupInfo& rInfo)
534 : {
535 18 : if ( nDimension < nSourceCount )
536 18 : rInfo = pNumGroups[nDimension].GetInfo();
537 18 : }
538 25 : long ScDPGroupTableData::GetMembersCount( long nDim )
539 : {
540 25 : const std::vector< SCROW >& members = GetColumnEntries( nDim );
541 25 : return members.size();
542 : }
543 188 : const std::vector< SCROW >& ScDPGroupTableData::GetColumnEntries( long nColumn )
544 : {
545 188 : if ( nColumn >= nSourceCount )
546 : {
547 54 : if ( getIsDataLayoutDimension( nColumn) ) // data layout dimension?
548 0 : nColumn = nSourceCount; // index of data layout in source data
549 : else
550 : {
551 54 : const ScDPGroupDimension& rGroupDim = aGroups[nColumn - nSourceCount];
552 54 : return rGroupDim.GetColumnEntries( GetCacheTable() );
553 : }
554 : }
555 :
556 134 : if ( IsNumGroupDimension( nColumn ) )
557 : {
558 : // dimension number is unchanged for numerical groups
559 : return pNumGroups[nColumn].GetNumEntries(
560 38 : static_cast<SCCOL>(nColumn), &GetCacheTable().getCache());
561 : }
562 :
563 96 : return pSourceData->GetColumnEntries( nColumn );
564 : }
565 :
566 1806 : const ScDPItemData* ScDPGroupTableData::GetMemberById( long nDim, long nId )
567 : {
568 1806 : return pSourceData->GetMemberById( nDim, nId );
569 : }
570 :
571 2047 : OUString ScDPGroupTableData::getDimensionName(long nColumn)
572 : {
573 2047 : if ( nColumn >= nSourceCount )
574 : {
575 403 : if ( nColumn == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ) // data layout dimension?
576 127 : nColumn = nSourceCount; // index of data layout in source data
577 : else
578 276 : return aGroups[nColumn - nSourceCount].GetName();
579 : }
580 :
581 1771 : return pSourceData->getDimensionName( nColumn );
582 : }
583 :
584 748 : bool ScDPGroupTableData::getIsDataLayoutDimension(long nColumn)
585 : {
586 : // position of data layout dimension is moved from source data
587 748 : return ( nColumn == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ); // data layout dimension?
588 : }
589 :
590 234 : bool ScDPGroupTableData::IsDateDimension(long nDim)
591 : {
592 234 : if ( nDim >= nSourceCount )
593 : {
594 96 : if ( nDim == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ) // data layout dimension?
595 36 : nDim = nSourceCount; // index of data layout in source data
596 : else
597 60 : nDim = aGroups[nDim - nSourceCount].GetSourceDim(); // look at original dimension
598 : }
599 :
600 234 : return pSourceData->IsDateDimension( nDim );
601 : }
602 :
603 58 : sal_uLong ScDPGroupTableData::GetNumberFormat(long nDim)
604 : {
605 58 : if ( nDim >= nSourceCount )
606 : {
607 19 : if ( nDim == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ) // data layout dimension?
608 9 : nDim = nSourceCount; // index of data layout in source data
609 : else
610 10 : nDim = aGroups[nDim - nSourceCount].GetSourceDim(); // look at original dimension
611 : }
612 :
613 58 : return pSourceData->GetNumberFormat( nDim );
614 : }
615 :
616 0 : void ScDPGroupTableData::DisposeData()
617 : {
618 0 : for ( ScDPGroupDimensionVec::iterator aIter(aGroups.begin()); aIter != aGroups.end(); ++aIter )
619 0 : aIter->DisposeData();
620 :
621 0 : for ( long i=0; i<nSourceCount; i++ )
622 0 : pNumGroups[i].DisposeData();
623 :
624 0 : pSourceData->DisposeData();
625 0 : }
626 :
627 40 : void ScDPGroupTableData::SetEmptyFlags( bool bIgnoreEmptyRows, bool bRepeatIfEmpty )
628 : {
629 40 : pSourceData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
630 40 : }
631 :
632 159 : bool ScDPGroupTableData::IsRepeatIfEmpty()
633 : {
634 159 : return pSourceData->IsRepeatIfEmpty();
635 : }
636 :
637 11 : void ScDPGroupTableData::CreateCacheTable()
638 : {
639 11 : pSourceData->CreateCacheTable();
640 11 : }
641 :
642 : namespace {
643 :
644 0 : class FindCaseInsensitive : std::unary_function<ScDPItemData, bool>
645 : {
646 : ScDPItemData maValue;
647 : public:
648 0 : FindCaseInsensitive(const ScDPItemData& rVal) : maValue(rVal) {}
649 :
650 0 : bool operator() (const ScDPItemData& rItem) const
651 : {
652 0 : return maValue.IsCaseInsEqual(rItem);
653 : }
654 : };
655 :
656 : }
657 :
658 0 : void ScDPGroupTableData::ModifyFilterCriteria(vector<ScDPFilteredCache::Criterion>& rCriteria)
659 : {
660 : // Build dimension ID to object map for group dimensions.
661 : typedef std::unordered_map<long, const ScDPGroupDimension*> GroupFieldMapType;
662 0 : GroupFieldMapType aGroupFieldIds;
663 : {
664 0 : ScDPGroupDimensionVec::const_iterator itr = aGroups.begin(), itrEnd = aGroups.end();
665 0 : for (; itr != itrEnd; ++itr)
666 : {
667 : aGroupFieldIds.insert(
668 0 : GroupFieldMapType::value_type(itr->GetGroupDim(), &(*itr)));
669 : }
670 : }
671 :
672 0 : vector<ScDPFilteredCache::Criterion> aNewCriteria;
673 0 : aNewCriteria.reserve(rCriteria.size() + aGroups.size());
674 :
675 : // Go through all the filtered field names and process them appropriately.
676 :
677 0 : const ScDPCache& rCache = GetCacheTable().getCache();
678 0 : vector<ScDPFilteredCache::Criterion>::const_iterator itrEnd = rCriteria.end();
679 0 : GroupFieldMapType::const_iterator itrGrpEnd = aGroupFieldIds.end();
680 0 : for (vector<ScDPFilteredCache::Criterion>::const_iterator itr = rCriteria.begin(); itr != itrEnd; ++itr)
681 : {
682 0 : std::vector<ScDPItemData> aMatchValues = itr->mpFilter->getMatchValues();
683 :
684 0 : GroupFieldMapType::const_iterator itrGrp = aGroupFieldIds.find(itr->mnFieldIndex);
685 0 : if (itrGrp == itrGrpEnd)
686 : {
687 0 : if (IsNumGroupDimension(itr->mnFieldIndex))
688 : {
689 : // internal number group field
690 0 : const ScDPNumGroupInfo* pNumInfo = rCache.GetNumGroupInfo(itr->mnFieldIndex);
691 0 : if (!pNumInfo)
692 : // Number group dimension without num info? Something is wrong...
693 0 : continue;
694 :
695 0 : ScDPFilteredCache::Criterion aCri;
696 0 : aCri.mnFieldIndex = itr->mnFieldIndex;
697 0 : const ScDPNumGroupDimension& rNumGrpDim = pNumGroups[itr->mnFieldIndex];
698 :
699 0 : if (rNumGrpDim.IsDateDimension())
700 : {
701 : // grouped by dates.
702 : aCri.mpFilter.reset(
703 : new ScDPGroupDateFilter(
704 0 : aMatchValues, *pDoc->GetFormatTable()->GetNullDate(), *pNumInfo));
705 : }
706 : else
707 : {
708 : // This dimension is grouped by numeric ranges.
709 : aCri.mpFilter.reset(
710 0 : new ScDPGroupNumFilter(aMatchValues, *pNumInfo));
711 : }
712 :
713 0 : aNewCriteria.push_back(aCri);
714 : }
715 : else
716 : {
717 : // This is a regular source field.
718 0 : aNewCriteria.push_back(*itr);
719 : }
720 : }
721 : else
722 : {
723 : // This is an ordinary group field or external number group field.
724 :
725 0 : const ScDPGroupDimension* pGrpDim = itrGrp->second;
726 0 : long nSrcDim = pGrpDim->GetSourceDim();
727 0 : long nGrpDim = pGrpDim->GetGroupDim();
728 0 : const ScDPNumGroupInfo* pNumInfo = rCache.GetNumGroupInfo(nGrpDim);
729 :
730 0 : if (pGrpDim->IsDateDimension() && pNumInfo)
731 : {
732 : // external number group
733 0 : ScDPFilteredCache::Criterion aCri;
734 0 : aCri.mnFieldIndex = nSrcDim; // use the source dimension, not the group dimension.
735 : aCri.mpFilter.reset(
736 : new ScDPGroupDateFilter(
737 0 : aMatchValues, *pDoc->GetFormatTable()->GetNullDate(), *pNumInfo));
738 :
739 0 : aNewCriteria.push_back(aCri);
740 : }
741 : else
742 : {
743 : // normal group
744 :
745 0 : ScDPFilteredCache::Criterion aCri;
746 0 : aCri.mnFieldIndex = nSrcDim;
747 0 : aCri.mpFilter.reset(new ScDPFilteredCache::GroupFilter());
748 : ScDPFilteredCache::GroupFilter* pGrpFilter =
749 0 : static_cast<ScDPFilteredCache::GroupFilter*>(aCri.mpFilter.get());
750 :
751 0 : size_t nGroupItemCount = pGrpDim->GetItemCount();
752 0 : for (size_t i = 0; i < nGroupItemCount; ++i)
753 : {
754 0 : const ScDPGroupItem* pGrpItem = pGrpDim->GetGroupByIndex(i);
755 0 : if (!pGrpItem)
756 0 : continue;
757 :
758 : // Make sure this group name equals one of the match values.
759 0 : if (std::none_of(aMatchValues.begin(), aMatchValues.end(), FindCaseInsensitive(pGrpItem->GetName())))
760 0 : continue;
761 :
762 0 : pGrpItem->FillGroupFilter(*pGrpFilter);
763 : }
764 :
765 0 : aNewCriteria.push_back(aCri);
766 : }
767 : }
768 0 : }
769 0 : rCriteria.swap(aNewCriteria);
770 0 : }
771 :
772 0 : void ScDPGroupTableData::FilterCacheTable(const vector<ScDPFilteredCache::Criterion>& rCriteria, const std::unordered_set<sal_Int32>& rCatDims)
773 : {
774 0 : vector<ScDPFilteredCache::Criterion> aNewCriteria(rCriteria);
775 0 : ModifyFilterCriteria(aNewCriteria);
776 0 : pSourceData->FilterCacheTable(aNewCriteria, rCatDims);
777 0 : }
778 :
779 0 : void ScDPGroupTableData::GetDrillDownData(const vector<ScDPFilteredCache::Criterion>& rCriteria, const std::unordered_set<sal_Int32>& rCatDims, Sequence< Sequence<Any> >& rData)
780 : {
781 0 : vector<ScDPFilteredCache::Criterion> aNewCriteria(rCriteria);
782 0 : ModifyFilterCriteria(aNewCriteria);
783 0 : pSourceData->GetDrillDownData(aNewCriteria, rCatDims, rData);
784 0 : }
785 :
786 9 : void ScDPGroupTableData::CalcResults(CalcInfo& rInfo, bool bAutoShow)
787 : {
788 : // #i111435# Inside FillRowDataFromCacheTable/GetItemData, virtual methods
789 : // getIsDataLayoutDimension and GetSourceDim are used, so it has to be called
790 : // with original rInfo, containing dimension indexes of the grouped data.
791 :
792 9 : const ScDPFilteredCache& rCacheTable = pSourceData->GetCacheTable();
793 9 : sal_Int32 nRowSize = rCacheTable.getRowSize();
794 77 : for (sal_Int32 nRow = 0; nRow < nRowSize; ++nRow)
795 : {
796 : sal_Int32 nLastRow;
797 68 : if (!rCacheTable.isRowActive(nRow, &nLastRow))
798 : {
799 0 : nRow = nLastRow;
800 0 : continue;
801 : }
802 :
803 68 : CalcRowData aData;
804 68 : FillRowDataFromCacheTable(nRow, rCacheTable, rInfo, aData);
805 :
806 68 : if ( !rInfo.aColLevelDims.empty() )
807 20 : FillGroupValues(aData.aColData, rInfo.aColLevelDims);
808 68 : if ( !rInfo.aRowLevelDims.empty() )
809 68 : FillGroupValues(aData.aRowData, rInfo.aRowLevelDims);
810 68 : if ( !rInfo.aPageDims.empty() )
811 5 : FillGroupValues(aData.aPageData, rInfo.aPageDims);
812 :
813 68 : ProcessRowData(rInfo, aData, bAutoShow);
814 68 : }
815 9 : }
816 :
817 1449 : const ScDPFilteredCache& ScDPGroupTableData::GetCacheTable() const
818 : {
819 1449 : return pSourceData->GetCacheTable();
820 : }
821 :
822 13 : void ScDPGroupTableData::ReloadCacheTable()
823 : {
824 13 : pSourceData->ReloadCacheTable();
825 13 : }
826 :
827 93 : void ScDPGroupTableData::FillGroupValues(vector<SCROW>& rItems, const vector<long>& rDims)
828 : {
829 93 : long nGroupedColumns = aGroups.size();
830 :
831 93 : const ScDPCache& rCache = GetCacheTable().getCache();
832 93 : vector<long>::const_iterator it = rDims.begin(), itEnd = rDims.end();
833 252 : for (size_t i = 0; it != itEnd; ++it, ++i)
834 : {
835 159 : long nColumn = *it;
836 159 : bool bDateDim = false;
837 :
838 159 : long nSourceDim = nColumn;
839 159 : if ( nColumn >= nSourceCount && nColumn < nSourceCount + nGroupedColumns )
840 : {
841 66 : const ScDPGroupDimension& rGroupDim = aGroups[nColumn - nSourceCount];
842 66 : nSourceDim= rGroupDim.GetSourceDim();
843 66 : bDateDim = rGroupDim.IsDateDimension();
844 66 : if (!bDateDim) // date is handled below
845 : {
846 34 : const ScDPItemData& rItem = *GetMemberById(nSourceDim, rItems[i]);
847 34 : const ScDPGroupItem* pGroupItem = rGroupDim.GetGroupForData(rItem);
848 34 : if (pGroupItem)
849 : {
850 23 : rItems[i] =
851 23 : rCache.GetIdByItemData(nColumn, pGroupItem->GetName());
852 : }
853 : else
854 11 : rItems[i] = rCache.GetIdByItemData(nColumn, rItem);
855 66 : }
856 : }
857 93 : else if ( IsNumGroupDimension( nColumn ) )
858 : {
859 34 : bDateDim = pNumGroups[nColumn].IsDateDimension();
860 34 : if (!bDateDim) // date is handled below
861 : {
862 18 : const ScDPItemData* pData = rCache.GetItemDataById(nSourceDim, rItems[i]);
863 18 : if (pData->GetType() == ScDPItemData::Value)
864 : {
865 18 : ScDPNumGroupInfo aNumInfo;
866 18 : GetNumGroupInfo(nColumn, aNumInfo);
867 18 : double fGroupValue = ScDPUtil::getNumGroupStartValue(pData->GetValue(), aNumInfo);
868 18 : ScDPItemData aItemData;
869 18 : aItemData.SetRangeStart(fGroupValue);
870 18 : rItems[i] = rCache.GetIdByItemData(nSourceDim, aItemData);
871 : }
872 : // else (textual) keep original value
873 : }
874 : }
875 :
876 159 : const ScDPNumGroupInfo* pNumInfo = rCache.GetNumGroupInfo(nColumn);
877 :
878 159 : if (bDateDim && pNumInfo)
879 : {
880 : // This is a date group dimension.
881 48 : sal_Int32 nDatePart = rCache.GetGroupType(nColumn);
882 48 : const ScDPItemData* pData = rCache.GetItemDataById(nSourceDim, rItems[i]);
883 48 : if (pData->GetType() == ScDPItemData::Value)
884 : {
885 48 : SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
886 : sal_Int32 nPartValue = ScDPUtil::getDatePartValue(
887 48 : pData->GetValue(), pNumInfo, nDatePart, pFormatter);
888 :
889 48 : ScDPItemData aItem(nDatePart, nPartValue);
890 48 : rItems[i] = rCache.GetIdByItemData(nColumn, aItem);
891 : }
892 : }
893 : }
894 93 : }
895 :
896 75 : bool ScDPGroupTableData::IsBaseForGroup(long nDim) const
897 : {
898 121 : for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); ++aIter )
899 : {
900 92 : const ScDPGroupDimension& rDim = *aIter;
901 92 : if ( rDim.GetSourceDim() == nDim )
902 46 : return true;
903 : }
904 :
905 29 : return false;
906 : }
907 :
908 142 : long ScDPGroupTableData::GetGroupBase(long nGroupDim) const
909 : {
910 232 : for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); ++aIter )
911 : {
912 181 : const ScDPGroupDimension& rDim = *aIter;
913 181 : if ( rDim.GetGroupDim() == nGroupDim )
914 91 : return rDim.GetSourceDim();
915 : }
916 :
917 51 : return -1; // none
918 : }
919 :
920 44 : bool ScDPGroupTableData::IsNumOrDateGroup(long nDimension) const
921 : {
922 : // Virtual method from ScDPTableData, used in result data to force text labels.
923 :
924 44 : if ( nDimension < nSourceCount )
925 : {
926 60 : return pNumGroups[nDimension].GetInfo().mbEnable ||
927 60 : pNumGroups[nDimension].IsDateDimension();
928 : }
929 :
930 12 : for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); ++aIter )
931 : {
932 12 : const ScDPGroupDimension& rDim = *aIter;
933 12 : if ( rDim.GetGroupDim() == nDimension )
934 9 : return rDim.IsDateDimension();
935 : }
936 :
937 0 : return false;
938 : }
939 :
940 59 : bool ScDPGroupTableData::IsInGroup( const ScDPItemData& rGroupData, long nGroupIndex,
941 : const ScDPItemData& rBaseData, long nBaseIndex ) const
942 : {
943 73 : for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); ++aIter )
944 : {
945 73 : const ScDPGroupDimension& rDim = *aIter;
946 73 : if ( rDim.GetGroupDim() == nGroupIndex && rDim.GetSourceDim() == nBaseIndex )
947 : {
948 59 : if (rDim.IsDateDimension())
949 : {
950 87 : return isDateInGroup(rGroupData, rBaseData);
951 : }
952 : else
953 : {
954 : // If the item is in a group, only that group is valid.
955 : // If the item is not in any group, its own name is valid.
956 :
957 31 : const ScDPGroupItem* pGroup = rDim.GetGroupForData( rBaseData );
958 20 : return pGroup ? pGroup->GetName().IsCaseInsEqual( rGroupData ) :
959 51 : rGroupData.IsCaseInsEqual( rBaseData );
960 : }
961 : }
962 : }
963 :
964 : OSL_FAIL("IsInGroup: no group dimension found");
965 0 : return true;
966 : }
967 :
968 8 : bool ScDPGroupTableData::HasCommonElement( const ScDPItemData& rFirstData, long nFirstIndex,
969 : const ScDPItemData& rSecondData, long nSecondIndex ) const
970 : {
971 8 : const ScDPGroupDimension* pFirstDim = NULL;
972 8 : const ScDPGroupDimension* pSecondDim = NULL;
973 24 : for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); ++aIter )
974 : {
975 16 : const ScDPGroupDimension* pDim = &(*aIter);
976 16 : if ( pDim->GetGroupDim() == nFirstIndex )
977 8 : pFirstDim = pDim;
978 8 : else if ( pDim->GetGroupDim() == nSecondIndex )
979 8 : pSecondDim = pDim;
980 : }
981 8 : if ( pFirstDim && pSecondDim )
982 : {
983 8 : bool bFirstDate = pFirstDim->IsDateDimension();
984 8 : bool bSecondDate = pSecondDim->IsDateDimension();
985 8 : if (bFirstDate || bSecondDate)
986 : {
987 : // If one is a date group dimension, the other one must be, too.
988 8 : if (!bFirstDate || !bSecondDate)
989 : {
990 : OSL_FAIL( "mix of date and non-date groups" );
991 0 : return true;
992 : }
993 :
994 8 : return isDateInGroup(rFirstData, rSecondData);
995 : }
996 :
997 0 : const ScDPGroupItem* pFirstItem = pFirstDim->GetGroupForName( rFirstData );
998 0 : const ScDPGroupItem* pSecondItem = pSecondDim->GetGroupForName( rSecondData );
999 0 : if ( pFirstItem && pSecondItem )
1000 : {
1001 : // two existing groups -> sal_True if they have a common element
1002 0 : return pFirstItem->HasCommonElement( *pSecondItem );
1003 : }
1004 0 : else if ( pFirstItem )
1005 : {
1006 : // "automatic" group contains only its own name
1007 0 : return pFirstItem->HasElement( rSecondData );
1008 : }
1009 0 : else if ( pSecondItem )
1010 : {
1011 : // "automatic" group contains only its own name
1012 0 : return pSecondItem->HasElement( rFirstData );
1013 : }
1014 : else
1015 : {
1016 : // no groups -> sal_True if equal
1017 0 : return rFirstData.IsCaseInsEqual( rSecondData );
1018 : }
1019 : }
1020 :
1021 : OSL_FAIL("HasCommonElement: no group dimension found");
1022 0 : return true;
1023 : }
1024 :
1025 159 : long ScDPGroupTableData::GetSourceDim( long nDim )
1026 : {
1027 159 : if ( getIsDataLayoutDimension( nDim ) )
1028 0 : return nSourceCount;
1029 159 : if ( nDim >= nSourceCount && nDim < nSourceCount +(long) aGroups.size() )
1030 : {
1031 66 : const ScDPGroupDimension& rGroupDim = aGroups[nDim - nSourceCount];
1032 66 : return rGroupDim.GetSourceDim();
1033 : }
1034 93 : return nDim;
1035 : }
1036 :
1037 189 : long ScDPGroupTableData::Compare(long nDim, long nDataId1, long nDataId2)
1038 : {
1039 189 : if ( getIsDataLayoutDimension(nDim) )
1040 0 : return 0;
1041 189 : return ScDPItemData::Compare( *GetMemberById(nDim, nDataId1),*GetMemberById(nDim, nDataId2) );
1042 156 : }
1043 :
1044 : #if DEBUG_PIVOT_TABLE
1045 : using std::cout;
1046 : using std::endl;
1047 :
1048 : void ScDPGroupTableData::Dump() const
1049 : {
1050 : cout << "--- ScDPGroupTableData" << endl;
1051 : for (long i = 0; i < nSourceCount; ++i)
1052 : {
1053 : cout << "* dimension: " << i << endl;
1054 : const ScDPNumGroupDimension& rGrp = pNumGroups[i];
1055 : rGrp.GetInfo().Dump();
1056 : }
1057 : cout << "---" << endl;
1058 : }
1059 : #endif
1060 :
1061 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|