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