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