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