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