1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: dpgroup.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
36 // INCLUDE ---------------------------------------------------------------
38 #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
40 #include <tools/debug.hxx>
41 #include <rtl/math.hxx>
42 #include <unotools/localedatawrapper.hxx>
43 #include <svtools/zforlist.hxx>
45 #include "dpgroup.hxx"
46 #include "collect.hxx"
48 #include "document.hxx"
49 #include "dpcachetable.hxx"
50 #include "dptabsrc.hxx"
51 #include "dptabres.hxx"
52 #include "dpobject.hxx"
54 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
55 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
61 using namespace ::com::sun::star
;
62 using ::com::sun::star::uno::Any
;
63 using ::com::sun::star::uno::Reference
;
64 using ::com::sun::star::uno::Sequence
;
65 using ::com::sun::star::uno::UNO_QUERY
;
66 using ::com::sun::star::uno::UNO_QUERY_THROW
;
67 using ::rtl::OUString
;
68 using ::rtl::OUStringHash
;
71 using ::std::hash_set
;
72 using ::std::hash_map
;
73 using ::boost::shared_ptr
;
75 #define D_TIMEFACTOR 86400.0
77 const USHORT SC_DP_LEAPYEAR
= 1648; // arbitrary leap year for date calculations
79 // part values for the extra "<" and ">" entries (same for all parts)
80 const sal_Int32 SC_DP_DATE_FIRST
= -1;
81 const sal_Int32 SC_DP_DATE_LAST
= 10000;
83 // ============================================================================
85 class ScDPGroupDateFilter
: public ScDPCacheTable::FilterBase
88 ScDPGroupDateFilter(double fMatchValue
, sal_Int32 nDatePart
,
89 const Date
* pNullDate
, const ScDPNumGroupInfo
* pNumInfo
);
91 virtual bool match(const ScDPCacheCell
&rCell
) const;
94 ScDPGroupDateFilter(); // disabled
96 const Date
* mpNullDate
;
97 const ScDPNumGroupInfo
* mpNumInfo
;
102 // ----------------------------------------------------------------------------
104 ScDPGroupDateFilter::ScDPGroupDateFilter(double fMatchValue
, sal_Int32 nDatePart
,
105 const Date
* pNullDate
, const ScDPNumGroupInfo
* pNumInfo
) :
106 mpNullDate(pNullDate
),
108 mfMatchValue(fMatchValue
),
109 mnDatePart(nDatePart
)
111 // fprintf(stdout, "ScDPCacheTable:DateGroupFilter::DateGroupFilter: match value = %g; date part = %ld\n",
112 // mfMatchValue, mnDatePart);
115 bool ScDPGroupDateFilter::match(const ScDPCacheCell
& rCell
) const
117 using namespace ::com::sun::star::sheet
;
118 using ::rtl::math::approxFloor
;
119 using ::rtl::math::approxEqual
;
121 if (!rCell
.mbNumeric
)
127 // Start and end dates are inclusive. (An end date without a time value
128 // is included, while an end date with a time value is not.)
130 if ( rCell
.mfValue
< mpNumInfo
->Start
&& !approxEqual(rCell
.mfValue
, mpNumInfo
->Start
) )
131 return static_cast<sal_Int32
>(mfMatchValue
) == SC_DP_DATE_FIRST
;
133 if ( rCell
.mfValue
> mpNumInfo
->End
&& !approxEqual(rCell
.mfValue
, mpNumInfo
->End
) )
134 return static_cast<sal_Int32
>(mfMatchValue
) == SC_DP_DATE_LAST
;
136 if (mnDatePart
== DataPilotFieldGroupBy::HOURS
|| mnDatePart
== DataPilotFieldGroupBy::MINUTES
||
137 mnDatePart
== DataPilotFieldGroupBy::SECONDS
)
140 // (as in the cell functions, ScInterpreter::ScGetHour etc.: seconds are rounded)
142 double time
= rCell
.mfValue
- approxFloor(rCell
.mfValue
);
143 long seconds
= static_cast<long>(approxFloor(time
*D_TIMEFACTOR
+ 0.5));
147 case DataPilotFieldGroupBy::HOURS
:
149 sal_Int32 hrs
= seconds
/ 3600;
150 sal_Int32 matchHrs
= static_cast<sal_Int32
>(mfMatchValue
);
151 return hrs
== matchHrs
;
153 case DataPilotFieldGroupBy::MINUTES
:
155 sal_Int32 minutes
= (seconds
% 3600) / 60;
156 sal_Int32 matchMinutes
= static_cast<sal_Int32
>(mfMatchValue
);
157 return minutes
== matchMinutes
;
159 case DataPilotFieldGroupBy::SECONDS
:
161 sal_Int32 sec
= seconds
% 60;
162 sal_Int32 matchSec
= static_cast<sal_Int32
>(mfMatchValue
);
163 return sec
== matchSec
;
166 DBG_ERROR("invalid time part");
171 Date date
= *mpNullDate
+ static_cast<long>(approxFloor(rCell
.mfValue
));
174 case DataPilotFieldGroupBy::YEARS
:
176 sal_Int32 year
= static_cast<sal_Int32
>(date
.GetYear());
177 sal_Int32 matchYear
= static_cast<sal_Int32
>(mfMatchValue
);
178 return year
== matchYear
;
180 case DataPilotFieldGroupBy::QUARTERS
:
182 sal_Int32 qtr
= 1 + (static_cast<sal_Int32
>(date
.GetMonth()) - 1) / 3;
183 sal_Int32 matchQtr
= static_cast<sal_Int32
>(mfMatchValue
);
184 return qtr
== matchQtr
;
186 case DataPilotFieldGroupBy::MONTHS
:
188 sal_Int32 month
= static_cast<sal_Int32
>(date
.GetMonth());
189 sal_Int32 matchMonth
= static_cast<sal_Int32
>(mfMatchValue
);
190 return month
== matchMonth
;
192 case DataPilotFieldGroupBy::DAYS
:
194 Date
yearStart(1, 1, date
.GetYear());
195 sal_Int32 days
= (date
- yearStart
) + 1; // Jan 01 has value 1
196 if (days
>= 60 && !date
.IsLeapYear())
198 // This is not a leap year. Adjust the value accordingly.
201 sal_Int32 matchDays
= static_cast<sal_Int32
>(mfMatchValue
);
202 return days
== matchDays
;
205 DBG_ERROR("invalid date part");
211 // ============================================================================
213 void lcl_AppendDateStr( rtl::OUStringBuffer
& rBuffer
, double fValue
, SvNumberFormatter
* pFormatter
)
215 ULONG nFormat
= pFormatter
->GetStandardFormat( NUMBERFORMAT_DATE
, ScGlobal::eLnge
);
217 pFormatter
->GetInputLineString( fValue
, nFormat
, aString
);
218 rBuffer
.append( aString
);
221 // -----------------------------------------------------------------------
223 ScDPDateGroupHelper::ScDPDateGroupHelper( const ScDPNumGroupInfo
& rInfo
, sal_Int32 nPart
) :
229 ScDPDateGroupHelper::~ScDPDateGroupHelper()
233 String
lcl_GetTwoDigitString( sal_Int32 nValue
)
235 String aRet
= String::CreateFromInt32( nValue
);
236 if ( aRet
.Len() < 2 )
237 aRet
.Insert( (sal_Unicode
)'0', 0 );
241 String
lcl_GetDateGroupName( sal_Int32 nDatePart
, sal_Int32 nValue
, SvNumberFormatter
* pFormatter
)
246 case com::sun::star::sheet::DataPilotFieldGroupBy::YEARS
:
247 aRet
= String::CreateFromInt32( nValue
);
249 case com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS
:
250 aRet
= ScGlobal::pLocaleData
->getQuarterAbbreviation( (sal_Int16
)(nValue
- 1) ); // nValue is 1-based
252 case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS
:
253 //! cache getMonths() result?
254 aRet
= ScGlobal::GetCalendar()->getDisplayName(
255 ::com::sun::star::i18n::CalendarDisplayIndex::MONTH
,
256 sal_Int16(nValue
-1), 0 ); // 0-based, get short name
258 case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS
:
260 Date
aDate( 1, 1, SC_DP_LEAPYEAR
);
261 aDate
+= ( nValue
- 1 ); // nValue is 1-based
262 Date aNullDate
= *(pFormatter
->GetNullDate());
263 long nDays
= aDate
- aNullDate
;
265 ULONG nFormat
= pFormatter
->GetFormatIndex( NF_DATE_SYS_DDMMM
, ScGlobal::eLnge
);
267 pFormatter
->GetOutputString( nDays
, nFormat
, aRet
, &pColor
);
270 case com::sun::star::sheet::DataPilotFieldGroupBy::HOURS
:
271 //! allow am/pm format?
272 aRet
= lcl_GetTwoDigitString( nValue
);
274 case com::sun::star::sheet::DataPilotFieldGroupBy::MINUTES
:
275 case com::sun::star::sheet::DataPilotFieldGroupBy::SECONDS
:
276 aRet
= ScGlobal::pLocaleData
->getTimeSep();
277 aRet
.Append( lcl_GetTwoDigitString( nValue
) );
280 DBG_ERROR("invalid date part");
285 sal_Int32
lcl_GetDatePartValue( double fValue
, sal_Int32 nDatePart
, SvNumberFormatter
* pFormatter
,
286 const ScDPNumGroupInfo
* pNumInfo
)
288 // Start and end are inclusive
289 // (End date without a time value is included, with a time value it's not)
293 if ( fValue
< pNumInfo
->Start
&& !rtl::math::approxEqual( fValue
, pNumInfo
->Start
) )
294 return SC_DP_DATE_FIRST
;
295 if ( fValue
> pNumInfo
->End
&& !rtl::math::approxEqual( fValue
, pNumInfo
->End
) )
296 return SC_DP_DATE_LAST
;
299 sal_Int32 nResult
= 0;
301 if ( nDatePart
== com::sun::star::sheet::DataPilotFieldGroupBy::HOURS
|| nDatePart
== com::sun::star::sheet::DataPilotFieldGroupBy::MINUTES
|| nDatePart
== com::sun::star::sheet::DataPilotFieldGroupBy::SECONDS
)
304 // (as in the cell functions, ScInterpreter::ScGetHour etc.: seconds are rounded)
306 double fTime
= fValue
- ::rtl::math::approxFloor(fValue
);
307 long nSeconds
= (long)::rtl::math::approxFloor(fTime
*D_TIMEFACTOR
+0.5);
311 case com::sun::star::sheet::DataPilotFieldGroupBy::HOURS
:
312 nResult
= nSeconds
/ 3600;
314 case com::sun::star::sheet::DataPilotFieldGroupBy::MINUTES
:
315 nResult
= ( nSeconds
% 3600 ) / 60;
317 case com::sun::star::sheet::DataPilotFieldGroupBy::SECONDS
:
318 nResult
= nSeconds
% 60;
324 Date aDate
= *(pFormatter
->GetNullDate());
325 aDate
+= (long)::rtl::math::approxFloor( fValue
);
329 case com::sun::star::sheet::DataPilotFieldGroupBy::YEARS
:
330 nResult
= aDate
.GetYear();
332 case com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS
:
333 nResult
= 1 + ( aDate
.GetMonth() - 1 ) / 3; // 1..4
335 case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS
:
336 nResult
= aDate
.GetMonth(); // 1..12
338 case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS
:
340 Date
aYearStart( 1, 1, aDate
.GetYear() );
341 nResult
= ( aDate
- aYearStart
) + 1; // Jan 01 has value 1
342 if ( nResult
>= 60 && !aDate
.IsLeapYear() )
344 // days are counted from 1 to 366 - if not from a leap year, adjust
350 DBG_ERROR("invalid date part");
357 BOOL
lcl_DateContained( sal_Int32 nGroupPart
, const ScDPItemData
& rGroupData
,
358 sal_Int32 nBasePart
, const ScDPItemData
& rBaseData
)
360 if ( !rGroupData
.bHasValue
|| !rBaseData
.bHasValue
)
362 // non-numeric entries involved: only match equal entries
363 return rGroupData
.IsCaseInsEqual( rBaseData
);
366 // no approxFloor needed, values were created from integers
367 sal_Int32 nGroupValue
= (sal_Int32
) rGroupData
.fValue
;
368 sal_Int32 nBaseValue
= (sal_Int32
) rBaseData
.fValue
;
369 if ( nBasePart
> nGroupPart
)
371 // switch, so the base part is the smaller (inner) part
373 ::std::swap( nGroupPart
, nBasePart
);
374 ::std::swap( nGroupValue
, nBaseValue
);
377 if ( nGroupValue
== SC_DP_DATE_FIRST
|| nGroupValue
== SC_DP_DATE_LAST
||
378 nBaseValue
== SC_DP_DATE_FIRST
|| nBaseValue
== SC_DP_DATE_LAST
)
380 // first/last entry matches only itself
381 return ( nGroupValue
== nBaseValue
);
384 BOOL bContained
= TRUE
;
385 switch ( nBasePart
) // inner part
387 case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS
:
388 // a month is only contained in its quarter
389 if ( nGroupPart
== com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS
)
391 // months and quarters are both 1-based
392 bContained
= ( nGroupValue
- 1 == ( nBaseValue
- 1 ) / 3 );
395 case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS
:
396 // a day is only contained in its quarter or month
397 if ( nGroupPart
== com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS
|| nGroupPart
== com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS
)
399 Date
aDate( 1, 1, SC_DP_LEAPYEAR
);
400 aDate
+= ( nBaseValue
- 1 ); // days are 1-based
401 sal_Int32 nCompare
= aDate
.GetMonth();
402 if ( nGroupPart
== com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS
)
403 nCompare
= ( ( nCompare
- 1 ) / 3 ) + 1; // get quarter from date
405 bContained
= ( nGroupValue
== nCompare
);
409 // other parts: everything is contained
415 String
lcl_GetSpecialDateName( double fValue
, bool bFirst
, SvNumberFormatter
* pFormatter
)
417 rtl::OUStringBuffer aBuffer
;
418 aBuffer
.append((sal_Unicode
)( bFirst
? '<' : '>' ));
419 lcl_AppendDateStr( aBuffer
, fValue
, pFormatter
);
420 return aBuffer
.makeStringAndClear();
423 void ScDPDateGroupHelper::FillColumnEntries( TypedScStrCollection
& rEntries
, const TypedScStrCollection
& rOriginal
,
424 SvNumberFormatter
* pFormatter
) const
426 // auto min/max is only used for "Years" part, but the loop is always needed
427 double fSourceMin
= 0.0;
428 double fSourceMax
= 0.0;
431 USHORT nOriginalCount
= rOriginal
.GetCount();
432 for (USHORT nOriginalPos
=0; nOriginalPos
<nOriginalCount
; nOriginalPos
++)
434 const TypedStrData
& rStrData
= *rOriginal
[nOriginalPos
];
435 if ( rStrData
.IsStrData() )
437 // string data: just copy
438 TypedStrData
* pNew
= new TypedStrData( rStrData
);
439 if ( !rEntries
.Insert( pNew
) )
444 double fSourceValue
= rStrData
.GetValue();
447 fSourceMin
= fSourceMax
= fSourceValue
;
452 if ( fSourceValue
< fSourceMin
)
453 fSourceMin
= fSourceValue
;
454 if ( fSourceValue
> fSourceMax
)
455 fSourceMax
= fSourceValue
;
460 // For the start/end values, use the same date rounding as in ScDPNumGroupDimension::GetNumEntries
461 // (but not for the list of available years):
462 if ( aNumInfo
.AutoStart
)
463 const_cast<ScDPDateGroupHelper
*>(this)->aNumInfo
.Start
= rtl::math::approxFloor( fSourceMin
);
464 if ( aNumInfo
.AutoEnd
)
465 const_cast<ScDPDateGroupHelper
*>(this)->aNumInfo
.End
= rtl::math::approxFloor( fSourceMax
) + 1;
467 //! if not automatic, limit fSourceMin/fSourceMax for list of year values?
470 long nEnd
= 0; // including
474 case com::sun::star::sheet::DataPilotFieldGroupBy::YEARS
:
475 nStart
= lcl_GetDatePartValue( fSourceMin
, com::sun::star::sheet::DataPilotFieldGroupBy::YEARS
, pFormatter
, NULL
);
476 nEnd
= lcl_GetDatePartValue( fSourceMax
, com::sun::star::sheet::DataPilotFieldGroupBy::YEARS
, pFormatter
, NULL
);
478 case com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS
: nStart
= 1; nEnd
= 4; break;
479 case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS
: nStart
= 1; nEnd
= 12; break;
480 case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS
: nStart
= 1; nEnd
= 366; break;
481 case com::sun::star::sheet::DataPilotFieldGroupBy::HOURS
: nStart
= 0; nEnd
= 23; break;
482 case com::sun::star::sheet::DataPilotFieldGroupBy::MINUTES
: nStart
= 0; nEnd
= 59; break;
483 case com::sun::star::sheet::DataPilotFieldGroupBy::SECONDS
: nStart
= 0; nEnd
= 59; break;
485 DBG_ERROR("invalid date part");
488 for ( sal_Int32 nValue
= nStart
; nValue
<= nEnd
; nValue
++ )
490 String aName
= lcl_GetDateGroupName( nDatePart
, nValue
, pFormatter
);
491 TypedStrData
* pNew
= new TypedStrData( aName
, nValue
, SC_STRTYPE_VALUE
);
492 if ( !rEntries
.Insert( pNew
) )
496 // add first/last entry (min/max)
498 String aFirstName
= lcl_GetSpecialDateName( aNumInfo
.Start
, true, pFormatter
);
499 TypedStrData
* pFirstEntry
= new TypedStrData( aFirstName
, SC_DP_DATE_FIRST
, SC_STRTYPE_VALUE
);
500 if ( !rEntries
.Insert( pFirstEntry
) )
503 String aLastName
= lcl_GetSpecialDateName( aNumInfo
.End
, false, pFormatter
);
504 TypedStrData
* pLastEntry
= new TypedStrData( aLastName
, SC_DP_DATE_LAST
, SC_STRTYPE_VALUE
);
505 if ( !rEntries
.Insert( pLastEntry
) )
509 // -----------------------------------------------------------------------
511 ScDPGroupItem::ScDPGroupItem( const ScDPItemData
& rName
) :
516 ScDPGroupItem::~ScDPGroupItem()
520 void ScDPGroupItem::AddElement( const ScDPItemData
& rName
)
522 aElements
.push_back( rName
);
525 bool ScDPGroupItem::HasElement( const ScDPItemData
& rData
) const
527 for ( ScDPItemDataVec::const_iterator
aIter(aElements
.begin()); aIter
!= aElements
.end(); aIter
++ )
528 if ( aIter
->IsCaseInsEqual( rData
) )
534 bool ScDPGroupItem::HasCommonElement( const ScDPGroupItem
& rOther
) const
536 for ( ScDPItemDataVec::const_iterator
aIter(aElements
.begin()); aIter
!= aElements
.end(); aIter
++ )
537 if ( rOther
.HasElement( *aIter
) )
543 void ScDPGroupItem::FillGroupFilter( ScDPCacheTable::GroupFilter
& rFilter
) const
545 ScDPItemDataVec::const_iterator itrEnd
= aElements
.end();
546 for (ScDPItemDataVec::const_iterator itr
= aElements
.begin(); itr
!= itrEnd
; ++itr
)
547 rFilter
.addMatchItem(itr
->aString
, itr
->fValue
, itr
->bHasValue
);
550 // -----------------------------------------------------------------------
552 ScDPGroupDimension::ScDPGroupDimension( long nSource
, const String
& rNewName
) :
553 nSourceDim( nSource
),
555 aGroupName( rNewName
),
561 ScDPGroupDimension::~ScDPGroupDimension()
567 ScDPGroupDimension::ScDPGroupDimension( const ScDPGroupDimension
& rOther
) :
568 nSourceDim( rOther
.nSourceDim
),
569 nGroupDim( rOther
.nGroupDim
),
570 aGroupName( rOther
.aGroupName
),
572 aItems( rOther
.aItems
),
573 pCollection( NULL
) // collection isn't copied - allocated on demand
575 if ( rOther
.pDateHelper
)
576 pDateHelper
= new ScDPDateGroupHelper( *rOther
.pDateHelper
);
579 ScDPGroupDimension
& ScDPGroupDimension::operator=( const ScDPGroupDimension
& rOther
)
581 nSourceDim
= rOther
.nSourceDim
;
582 nGroupDim
= rOther
.nGroupDim
;
583 aGroupName
= rOther
.aGroupName
;
584 aItems
= rOther
.aItems
;
587 if ( rOther
.pDateHelper
)
588 pDateHelper
= new ScDPDateGroupHelper( *rOther
.pDateHelper
);
592 delete pCollection
; // collection isn't copied - allocated on demand
597 void ScDPGroupDimension::MakeDateHelper( const ScDPNumGroupInfo
& rInfo
, sal_Int32 nPart
)
600 pDateHelper
= new ScDPDateGroupHelper( rInfo
, nPart
);
603 void ScDPGroupDimension::AddItem( const ScDPGroupItem
& rItem
)
605 aItems
.push_back( rItem
);
608 void ScDPGroupDimension::SetGroupDim( long nDim
)
613 const TypedScStrCollection
& ScDPGroupDimension::GetColumnEntries(
614 const TypedScStrCollection
& rOriginal
, ScDocument
* pDoc
) const
618 pCollection
= new TypedScStrCollection();
620 pDateHelper
->FillColumnEntries( *pCollection
, rOriginal
, pDoc
->GetFormatTable() );
623 long nCount
= aItems
.size();
624 for (long i
=0; i
<nCount
; i
++)
627 TypedStrData
* pNew
= new TypedStrData( aItems
[i
].GetName().aString
);
628 if ( !pCollection
->Insert( pNew
) )
632 USHORT nOriginalCount
= rOriginal
.GetCount();
633 for (USHORT nOriginalPos
=0; nOriginalPos
<nOriginalCount
; nOriginalPos
++)
635 const TypedStrData
& rStrData
= *rOriginal
[nOriginalPos
];
636 ScDPItemData
aItemData( rStrData
.GetString(), rStrData
.GetValue(), !rStrData
.IsStrData() );
637 if ( !GetGroupForData( aItemData
) )
639 // not in any group -> add as its own group
640 TypedStrData
* pNew
= new TypedStrData( rStrData
);
641 if ( !pCollection
->Insert( pNew
) )
650 const ScDPGroupItem
* ScDPGroupDimension::GetGroupForData( const ScDPItemData
& rData
) const
652 for ( ScDPGroupItemVec::const_iterator
aIter(aItems
.begin()); aIter
!= aItems
.end(); aIter
++ )
653 if ( aIter
->HasElement( rData
) )
659 const ScDPGroupItem
* ScDPGroupDimension::GetGroupForName( const ScDPItemData
& rName
) const
661 for ( ScDPGroupItemVec::const_iterator
aIter(aItems
.begin()); aIter
!= aItems
.end(); aIter
++ )
662 if ( aIter
->GetName().IsCaseInsEqual( rName
) )
668 const ScDPGroupItem
* ScDPGroupDimension::GetGroupByIndex( size_t nIndex
) const
670 if (nIndex
>= aItems
.size())
673 return &aItems
[nIndex
];
676 void ScDPGroupDimension::DisposeData()
682 // -----------------------------------------------------------------------
684 ScDPNumGroupDimension::ScDPNumGroupDimension() :
687 bHasNonInteger( false ),
692 ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupInfo
& rInfo
) :
696 bHasNonInteger( false ),
701 ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupDimension
& rOther
) :
702 aGroupInfo( rOther
.aGroupInfo
),
704 pCollection( NULL
), // collection isn't copied - allocated on demand
705 bHasNonInteger( false ),
708 if ( rOther
.pDateHelper
)
709 pDateHelper
= new ScDPDateGroupHelper( *rOther
.pDateHelper
);
712 ScDPNumGroupDimension
& ScDPNumGroupDimension::operator=( const ScDPNumGroupDimension
& rOther
)
714 aGroupInfo
= rOther
.aGroupInfo
;
717 if ( rOther
.pDateHelper
)
718 pDateHelper
= new ScDPDateGroupHelper( *rOther
.pDateHelper
);
722 delete pCollection
; // collection isn't copied - allocated on demand
724 bHasNonInteger
= false;
728 void ScDPNumGroupDimension::DisposeData()
732 bHasNonInteger
= false;
735 ScDPNumGroupDimension::~ScDPNumGroupDimension()
741 void ScDPNumGroupDimension::MakeDateHelper( const ScDPNumGroupInfo
& rInfo
, sal_Int32 nPart
)
744 pDateHelper
= new ScDPDateGroupHelper( rInfo
, nPart
);
746 aGroupInfo
.Enable
= sal_True
; //! or query both?
749 String
lcl_GetNumGroupName( double fStartValue
, const ScDPNumGroupInfo
& rInfo
,
750 bool bHasNonInteger
, sal_Unicode cDecSeparator
, SvNumberFormatter
* pFormatter
)
752 DBG_ASSERT( cDecSeparator
!= 0, "cDecSeparator not initialized" );
754 double fStep
= rInfo
.Step
;
755 double fEndValue
= fStartValue
+ fStep
;
756 if ( !bHasNonInteger
&& ( rInfo
.DateValues
|| !rtl::math::approxEqual( fEndValue
, rInfo
.End
) ) )
758 // The second number of the group label is
759 // (first number + size - 1) if there are only integer numbers,
760 // (first number + size) if any non-integer numbers are involved.
761 // Exception: The last group (containing the end value) is always
762 // shown as including the end value (but not for dates).
767 if ( fEndValue
> rInfo
.End
&& !rInfo
.AutoEnd
)
769 // limit the last group to the end value
771 fEndValue
= rInfo
.End
;
774 rtl::OUStringBuffer aBuffer
;
775 if ( rInfo
.DateValues
)
777 lcl_AppendDateStr( aBuffer
, fStartValue
, pFormatter
);
778 aBuffer
.appendAscii( " - " ); // with spaces
779 lcl_AppendDateStr( aBuffer
, fEndValue
, pFormatter
);
783 rtl::math::doubleToUStringBuffer( aBuffer
, fStartValue
, rtl_math_StringFormat_Automatic
,
784 rtl_math_DecimalPlaces_Max
, cDecSeparator
, true );
785 aBuffer
.append( (sal_Unicode
) '-' );
786 rtl::math::doubleToUStringBuffer( aBuffer
, fEndValue
, rtl_math_StringFormat_Automatic
,
787 rtl_math_DecimalPlaces_Max
, cDecSeparator
, true );
790 return aBuffer
.makeStringAndClear();
793 String
lcl_GetSpecialNumGroupName( double fValue
, bool bFirst
, sal_Unicode cDecSeparator
,
794 bool bDateValues
, SvNumberFormatter
* pFormatter
)
796 DBG_ASSERT( cDecSeparator
!= 0, "cDecSeparator not initialized" );
798 rtl::OUStringBuffer aBuffer
;
799 aBuffer
.append((sal_Unicode
)( bFirst
? '<' : '>' ));
801 lcl_AppendDateStr( aBuffer
, fValue
, pFormatter
);
803 rtl::math::doubleToUStringBuffer( aBuffer
, fValue
, rtl_math_StringFormat_Automatic
,
804 rtl_math_DecimalPlaces_Max
, cDecSeparator
, true );
805 return aBuffer
.makeStringAndClear();
808 inline bool IsInteger( double fValue
)
810 return rtl::math::approxEqual( fValue
, rtl::math::approxFloor(fValue
) );
813 const TypedScStrCollection
& ScDPNumGroupDimension::GetNumEntries(
814 const TypedScStrCollection
& rOriginal
, ScDocument
* pDoc
) const
818 SvNumberFormatter
* pFormatter
= pDoc
->GetFormatTable();
820 pCollection
= new TypedScStrCollection();
822 pDateHelper
->FillColumnEntries( *pCollection
, rOriginal
, pFormatter
);
825 // Copy textual entries.
826 // Also look through the source entries for non-integer numbers, minimum and maximum.
827 // GetNumEntries (GetColumnEntries) must be called before accessing the groups
828 // (this in ensured by calling ScDPLevel::GetMembersObject for all column/row/page
829 // dimensions before iterating over the values).
831 cDecSeparator
= ScGlobal::pLocaleData
->getNumDecimalSep().GetChar(0);
833 // non-integer GroupInfo values count, too
834 bHasNonInteger
= ( !aGroupInfo
.AutoStart
&& !IsInteger( aGroupInfo
.Start
) ) ||
835 ( !aGroupInfo
.AutoEnd
&& !IsInteger( aGroupInfo
.End
) ) ||
836 !IsInteger( aGroupInfo
.Step
);
837 double fSourceMin
= 0.0;
838 double fSourceMax
= 0.0;
841 USHORT nOriginalCount
= rOriginal
.GetCount();
842 for (USHORT nOriginalPos
=0; nOriginalPos
<nOriginalCount
; nOriginalPos
++)
844 const TypedStrData
& rStrData
= *rOriginal
[nOriginalPos
];
845 if ( rStrData
.IsStrData() )
847 // string data: just copy
848 TypedStrData
* pNew
= new TypedStrData( rStrData
);
849 if ( !pCollection
->Insert( pNew
) )
854 double fSourceValue
= rStrData
.GetValue();
857 fSourceMin
= fSourceMax
= fSourceValue
;
862 if ( fSourceValue
< fSourceMin
)
863 fSourceMin
= fSourceValue
;
864 if ( fSourceValue
> fSourceMax
)
865 fSourceMax
= fSourceValue
;
867 if ( !bHasNonInteger
&& !IsInteger( fSourceValue
) )
869 // if any non-integer numbers are involved, the group labels are
870 // shown including their upper limit
871 bHasNonInteger
= true;
876 if ( aGroupInfo
.DateValues
)
878 // special handling for dates: always integer, round down limits
879 bHasNonInteger
= false;
880 fSourceMin
= rtl::math::approxFloor( fSourceMin
);
881 fSourceMax
= rtl::math::approxFloor( fSourceMax
) + 1;
884 if ( aGroupInfo
.AutoStart
)
885 const_cast<ScDPNumGroupDimension
*>(this)->aGroupInfo
.Start
= fSourceMin
;
886 if ( aGroupInfo
.AutoEnd
)
887 const_cast<ScDPNumGroupDimension
*>(this)->aGroupInfo
.End
= fSourceMax
;
889 //! limit number of entries?
892 double fLoop
= aGroupInfo
.Start
;
894 // Use "less than" instead of "less or equal" for the loop - don't create a group
895 // that consists only of the end value. Instead, the end value is then included
896 // in the last group (last group is bigger than the others).
897 // The first group has to be created nonetheless. GetNumGroupForValue has corresponding logic.
899 bool bFirstGroup
= true;
900 while ( bFirstGroup
|| ( fLoop
< aGroupInfo
.End
&& !rtl::math::approxEqual( fLoop
, aGroupInfo
.End
) ) )
902 String aName
= lcl_GetNumGroupName( fLoop
, aGroupInfo
, bHasNonInteger
, cDecSeparator
, pFormatter
);
903 // create a numerical entry to ensure proper sorting
904 // (in FillMemberResults this needs special handling)
905 TypedStrData
* pNew
= new TypedStrData( aName
, fLoop
, SC_STRTYPE_VALUE
);
906 if ( !pCollection
->Insert( pNew
) )
910 fLoop
= aGroupInfo
.Start
+ nLoopCount
* aGroupInfo
.Step
;
913 // ScDPItemData values are compared with approxEqual
916 String aFirstName
= lcl_GetSpecialNumGroupName( aGroupInfo
.Start
, true, cDecSeparator
, aGroupInfo
.DateValues
, pFormatter
);
917 TypedStrData
* pFirstEntry
= new TypedStrData( aFirstName
, aGroupInfo
.Start
- aGroupInfo
.Step
, SC_STRTYPE_VALUE
);
918 if ( !pCollection
->Insert( pFirstEntry
) )
921 String aLastName
= lcl_GetSpecialNumGroupName( aGroupInfo
.End
, false, cDecSeparator
, aGroupInfo
.DateValues
, pFormatter
);
922 TypedStrData
* pLastEntry
= new TypedStrData( aLastName
, aGroupInfo
.End
+ aGroupInfo
.Step
, SC_STRTYPE_VALUE
);
923 if ( !pCollection
->Insert( pLastEntry
) )
930 // -----------------------------------------------------------------------
932 String
lcl_GetNumGroupForValue( double fValue
, const ScDPNumGroupInfo
& rInfo
, bool bHasNonInteger
,
933 sal_Unicode cDecSeparator
, double& rGroupValue
, ScDocument
* pDoc
)
935 SvNumberFormatter
* pFormatter
= pDoc
->GetFormatTable();
937 if ( fValue
< rInfo
.Start
&& !rtl::math::approxEqual( fValue
, rInfo
.Start
) )
939 rGroupValue
= rInfo
.Start
- rInfo
.Step
;
940 return lcl_GetSpecialNumGroupName( rInfo
.Start
, true, cDecSeparator
, rInfo
.DateValues
, pFormatter
);
943 if ( fValue
> rInfo
.End
&& !rtl::math::approxEqual( fValue
, rInfo
.End
) )
945 rGroupValue
= rInfo
.End
+ rInfo
.Step
;
946 return lcl_GetSpecialNumGroupName( rInfo
.End
, false, cDecSeparator
, rInfo
.DateValues
, pFormatter
);
949 double fDiff
= fValue
- rInfo
.Start
;
950 double fDiv
= rtl::math::approxFloor( fDiff
/ rInfo
.Step
);
951 double fGroupStart
= rInfo
.Start
+ fDiv
* rInfo
.Step
;
953 if ( rtl::math::approxEqual( fGroupStart
, rInfo
.End
) &&
954 !rtl::math::approxEqual( fGroupStart
, rInfo
.Start
) )
956 if ( !rInfo
.DateValues
)
958 // A group that would consist only of the end value is not created,
959 // instead the value is included in the last group before. So the
960 // previous group is used if the calculated group start value is the
961 // selected end value.
964 fGroupStart
= rInfo
.Start
+ fDiv
* rInfo
.Step
;
968 // For date values, the end value is instead treated as above the limit
969 // if it would be a group of its own.
971 rGroupValue
= rInfo
.End
+ rInfo
.Step
;
972 return lcl_GetSpecialNumGroupName( rInfo
.End
, false, cDecSeparator
, rInfo
.DateValues
, pFormatter
);
976 rGroupValue
= fGroupStart
;
978 return lcl_GetNumGroupName( fGroupStart
, rInfo
, bHasNonInteger
, cDecSeparator
, pFormatter
);
981 ScDPGroupTableData::ScDPGroupTableData( const shared_ptr
<ScDPTableData
>& pSource
, ScDocument
* pDocument
) :
982 ScDPTableData(pDocument
),
983 pSourceData( pSource
),
986 DBG_ASSERT( pSource
, "ScDPGroupTableData: pSource can't be NULL" );
989 nSourceCount
= pSource
->GetColumnCount(); // real columns, excluding data layout
990 pNumGroups
= new ScDPNumGroupDimension
[nSourceCount
];
993 ScDPGroupTableData::~ScDPGroupTableData()
998 void ScDPGroupTableData::AddGroupDimension( const ScDPGroupDimension
& rGroup
)
1000 ScDPGroupDimension
aNewGroup( rGroup
);
1001 aNewGroup
.SetGroupDim( GetColumnCount() ); // new dimension will be at the end
1002 aGroups
.push_back( aNewGroup
);
1003 aGroupNames
.insert( OUString(aNewGroup
.GetName()) );
1006 void ScDPGroupTableData::SetNumGroupDimension( long nIndex
, const ScDPNumGroupDimension
& rGroup
)
1008 if ( nIndex
< nSourceCount
)
1010 pNumGroups
[nIndex
] = rGroup
;
1012 // automatic minimum / maximum is handled in GetNumEntries
1016 long ScDPGroupTableData::GetDimensionIndex( const String
& rName
)
1018 for (long i
=0; i
<nSourceCount
; i
++) // nSourceCount excludes data layout
1019 if ( pSourceData
->getDimensionName(i
) == rName
) //! ignore case?
1025 long ScDPGroupTableData::GetColumnCount()
1027 return nSourceCount
+ aGroups
.size();
1030 bool ScDPGroupTableData::IsNumGroupDimension( long nDimension
) const
1032 return ( nDimension
< nSourceCount
&& pNumGroups
[nDimension
].GetInfo().Enable
);
1035 void ScDPGroupTableData::GetNumGroupInfo( long nDimension
, ScDPNumGroupInfo
& rInfo
,
1036 bool& rNonInteger
, sal_Unicode
& rDecimal
)
1038 if ( nDimension
< nSourceCount
)
1040 rInfo
= pNumGroups
[nDimension
].GetInfo();
1041 rNonInteger
= pNumGroups
[nDimension
].HasNonInteger();
1042 rDecimal
= pNumGroups
[nDimension
].GetDecSeparator();
1046 const TypedScStrCollection
& ScDPGroupTableData::GetColumnEntries(long nColumn
)
1048 // date handling is in ScDPGroupDimension::GetColumnEntries / ScDPNumGroupDimension::GetNumEntries
1049 // (to use the pCollection members)
1051 if ( nColumn
>= nSourceCount
)
1053 if ( nColumn
== sal::static_int_cast
<long>( nSourceCount
+ aGroups
.size() ) ) // data layout dimension?
1054 nColumn
= nSourceCount
; // index of data layout in source data
1057 const ScDPGroupDimension
& rGroupDim
= aGroups
[nColumn
- nSourceCount
];
1058 long nSourceDim
= rGroupDim
.GetSourceDim();
1059 // collection is cached at pSourceData, GetColumnEntries can be called every time
1060 const TypedScStrCollection
& rOriginal
= pSourceData
->GetColumnEntries( nSourceDim
);
1061 return rGroupDim
.GetColumnEntries( rOriginal
, pDoc
);
1065 if ( IsNumGroupDimension( nColumn
) )
1067 // dimension number is unchanged for numerical groups
1068 const TypedScStrCollection
& rOriginal
= pSourceData
->GetColumnEntries( nColumn
);
1069 return pNumGroups
[nColumn
].GetNumEntries( rOriginal
, pDoc
);
1072 return pSourceData
->GetColumnEntries( nColumn
);
1075 String
ScDPGroupTableData::getDimensionName(long nColumn
)
1077 if ( nColumn
>= nSourceCount
)
1079 if ( nColumn
== sal::static_int_cast
<long>( nSourceCount
+ aGroups
.size() ) ) // data layout dimension?
1080 nColumn
= nSourceCount
; // index of data layout in source data
1082 return aGroups
[nColumn
- nSourceCount
].GetName();
1085 return pSourceData
->getDimensionName( nColumn
);
1088 BOOL
ScDPGroupTableData::getIsDataLayoutDimension(long nColumn
)
1090 // position of data layout dimension is moved from source data
1091 return ( nColumn
== sal::static_int_cast
<long>( nSourceCount
+ aGroups
.size() ) ); // data layout dimension?
1094 BOOL
ScDPGroupTableData::IsDateDimension(long nDim
)
1096 if ( nDim
>= nSourceCount
)
1098 if ( nDim
== sal::static_int_cast
<long>( nSourceCount
+ aGroups
.size() ) ) // data layout dimension?
1099 nDim
= nSourceCount
; // index of data layout in source data
1101 nDim
= aGroups
[nDim
- nSourceCount
].GetSourceDim(); // look at original dimension
1104 return pSourceData
->IsDateDimension( nDim
);
1107 UINT32
ScDPGroupTableData::GetNumberFormat(long nDim
)
1109 if ( nDim
>= nSourceCount
)
1111 if ( nDim
== sal::static_int_cast
<long>( nSourceCount
+ aGroups
.size() ) ) // data layout dimension?
1112 nDim
= nSourceCount
; // index of data layout in source data
1114 nDim
= aGroups
[nDim
- nSourceCount
].GetSourceDim(); // look at original dimension
1117 return pSourceData
->GetNumberFormat( nDim
);
1120 void ScDPGroupTableData::DisposeData()
1122 for ( ScDPGroupDimensionVec::iterator
aIter(aGroups
.begin()); aIter
!= aGroups
.end(); aIter
++ )
1123 aIter
->DisposeData();
1125 for ( long i
=0; i
<nSourceCount
; i
++ )
1126 pNumGroups
[i
].DisposeData();
1128 pSourceData
->DisposeData();
1131 void ScDPGroupTableData::SetEmptyFlags( BOOL bIgnoreEmptyRows
, BOOL bRepeatIfEmpty
)
1133 pSourceData
->SetEmptyFlags( bIgnoreEmptyRows
, bRepeatIfEmpty
);
1136 bool ScDPGroupTableData::IsRepeatIfEmpty()
1138 return pSourceData
->IsRepeatIfEmpty();
1141 void ScDPGroupTableData::CreateCacheTable()
1143 pSourceData
->CreateCacheTable();
1146 void ScDPGroupTableData::ModifyFilterCriteria(vector
<ScDPCacheTable::Criterion
>& rCriteria
)
1148 typedef hash_map
<long, const ScDPGroupDimension
*> GroupFieldMapType
;
1149 GroupFieldMapType aGroupFieldIds
;
1151 ScDPGroupDimensionVec::const_iterator itr
= aGroups
.begin(), itrEnd
= aGroups
.end();
1152 for (; itr
!= itrEnd
; ++itr
)
1153 aGroupFieldIds
.insert( hash_map
<long, const ScDPGroupDimension
*>::value_type(itr
->GetGroupDim(), &(*itr
)) );
1156 vector
<ScDPCacheTable::Criterion
> aNewCriteria
;
1157 aNewCriteria
.reserve(rCriteria
.size() + aGroups
.size());
1159 // Go through all the filtered field names and process them appropriately.
1161 vector
<ScDPCacheTable::Criterion
>::const_iterator itrEnd
= rCriteria
.end();
1162 GroupFieldMapType::const_iterator itrGrpEnd
= aGroupFieldIds
.end();
1163 for (vector
<ScDPCacheTable::Criterion
>::const_iterator itr
= rCriteria
.begin(); itr
!= itrEnd
; ++itr
)
1165 ScDPCacheTable::SingleFilter
* pFilter
= dynamic_cast<ScDPCacheTable::SingleFilter
*>(itr
->mpFilter
.get());
1167 // We expect this to be a single filter.
1170 GroupFieldMapType::const_iterator itrGrp
= aGroupFieldIds
.find(itr
->mnFieldIndex
);
1171 if (itrGrp
== itrGrpEnd
)
1173 if (IsNumGroupDimension(itr
->mnFieldIndex
))
1175 // internal number group field
1176 const ScDPNumGroupDimension
& rNumGrpDim
= pNumGroups
[itr
->mnFieldIndex
];
1177 const ScDPDateGroupHelper
* pDateHelper
= rNumGrpDim
.GetDateHelper();
1180 // What do we do here !?
1184 ScDPCacheTable::Criterion aCri
;
1185 aCri
.mnFieldIndex
= itr
->mnFieldIndex
;
1186 aCri
.mpFilter
.reset(new ScDPGroupDateFilter(
1187 pFilter
->getMatchValue(), pDateHelper
->GetDatePart(),
1188 pDoc
->GetFormatTable()->GetNullDate(), &pDateHelper
->GetNumInfo()));
1190 aNewCriteria
.push_back(aCri
);
1194 // This is a regular source field.
1195 aNewCriteria
.push_back(*itr
);
1200 // This is an ordinary group field or external number group field.
1202 const ScDPGroupDimension
* pGrpDim
= itrGrp
->second
;
1203 long nSrcDim
= pGrpDim
->GetSourceDim();
1204 const ScDPDateGroupHelper
* pDateHelper
= pGrpDim
->GetDateHelper();
1208 // external number group
1209 ScDPCacheTable::Criterion aCri
;
1210 aCri
.mnFieldIndex
= nSrcDim
; // use the source dimension, not the group dimension.
1211 aCri
.mpFilter
.reset(new ScDPGroupDateFilter(
1212 pFilter
->getMatchValue(), pDateHelper
->GetDatePart(),
1213 pDoc
->GetFormatTable()->GetNullDate(), &pDateHelper
->GetNumInfo()));
1215 aNewCriteria
.push_back(aCri
);
1221 // Note that each group dimension may have multiple group names!
1222 size_t nGroupItemCount
= pGrpDim
->GetItemCount();
1223 for (size_t i
= 0; i
< nGroupItemCount
; ++i
)
1225 const ScDPGroupItem
* pGrpItem
= pGrpDim
->GetGroupByIndex(i
);
1227 aName
.aString
= pFilter
->getMatchString();
1228 aName
.fValue
= pFilter
->getMatchValue();
1229 aName
.bHasValue
= pFilter
->hasValue();
1230 if (!pGrpItem
|| !pGrpItem
->GetName().IsCaseInsEqual(aName
))
1233 ScDPCacheTable::Criterion aCri
;
1234 aCri
.mnFieldIndex
= nSrcDim
;
1235 aCri
.mpFilter
.reset(new ScDPCacheTable::GroupFilter(GetSharedString()));
1236 ScDPCacheTable::GroupFilter
* pGrpFilter
=
1237 static_cast<ScDPCacheTable::GroupFilter
*>(aCri
.mpFilter
.get());
1239 pGrpItem
->FillGroupFilter(*pGrpFilter
);
1240 aNewCriteria
.push_back(aCri
);
1245 rCriteria
.swap(aNewCriteria
);
1248 void ScDPGroupTableData::FilterCacheTable(const vector
<ScDPCacheTable::Criterion
>& rCriteria
, const hash_set
<sal_Int32
>& rCatDims
)
1250 vector
<ScDPCacheTable::Criterion
> aNewCriteria(rCriteria
);
1251 ModifyFilterCriteria(aNewCriteria
);
1252 pSourceData
->FilterCacheTable(aNewCriteria
, rCatDims
);
1255 void ScDPGroupTableData::GetDrillDownData(const vector
<ScDPCacheTable::Criterion
>& rCriteria
, const hash_set
<sal_Int32
>& rCatDims
, Sequence
< Sequence
<Any
> >& rData
)
1257 vector
<ScDPCacheTable::Criterion
> aNewCriteria(rCriteria
);
1258 ModifyFilterCriteria(aNewCriteria
);
1259 pSourceData
->GetDrillDownData(aNewCriteria
, rCatDims
, rData
);
1262 void ScDPGroupTableData::CalcResults(CalcInfo
& rInfo
, bool bAutoShow
)
1264 // This CalcInfo instance is used only to retrive data from the original
1266 CalcInfo aInfoSrc
= rInfo
;
1267 CopyFields(rInfo
.aColLevelDims
, aInfoSrc
.aColLevelDims
);
1268 CopyFields(rInfo
.aRowLevelDims
, aInfoSrc
.aRowLevelDims
);
1269 CopyFields(rInfo
.aPageDims
, aInfoSrc
.aPageDims
);
1270 CopyFields(rInfo
.aDataSrcCols
, aInfoSrc
.aDataSrcCols
);
1272 const ScDPCacheTable
& rCacheTable
= pSourceData
->GetCacheTable();
1273 sal_Int32 nRowSize
= rCacheTable
.getRowSize();
1274 for (sal_Int32 nRow
= 0; nRow
< nRowSize
; ++nRow
)
1276 if (!rCacheTable
.isRowActive(nRow
))
1280 FillRowDataFromCacheTable(nRow
, rCacheTable
, aInfoSrc
, aData
);
1282 if ( !rInfo
.aColLevelDims
.empty() )
1283 FillGroupValues(&aData
.aColData
[0], rInfo
.aColLevelDims
.size(), &rInfo
.aColLevelDims
[0]);
1284 if ( !rInfo
.aRowLevelDims
.empty() )
1285 FillGroupValues(&aData
.aRowData
[0], rInfo
.aRowLevelDims
.size(), &rInfo
.aRowLevelDims
[0]);
1286 if ( !rInfo
.aPageDims
.empty() )
1287 FillGroupValues(&aData
.aPageData
[0], rInfo
.aPageDims
.size(), &rInfo
.aPageDims
[0]);
1289 ProcessRowData(rInfo
, aData
, bAutoShow
);
1293 const ScDPCacheTable
& ScDPGroupTableData::GetCacheTable() const
1295 return pSourceData
->GetCacheTable();
1298 void ScDPGroupTableData::CopyFields(const vector
<long>& rFieldDims
, vector
<long>& rNewFieldDims
)
1300 size_t nCount
= rFieldDims
.size();
1304 long nGroupedColumns
= aGroups
.size();
1306 rNewFieldDims
.clear();
1307 rNewFieldDims
.reserve(nCount
);
1308 for (size_t i
= 0; i
< nCount
; ++i
)
1310 if ( rFieldDims
[i
] >= nSourceCount
)
1312 if ( rFieldDims
[i
] == nSourceCount
+ nGroupedColumns
)
1313 // data layout in source
1314 rNewFieldDims
.push_back(nSourceCount
);
1317 // original dimension
1318 long n
= rFieldDims
[i
] - nSourceCount
;
1319 rNewFieldDims
.push_back(aGroups
[n
].GetSourceDim());
1323 rNewFieldDims
.push_back(rFieldDims
[i
]);
1327 void ScDPGroupTableData::FillGroupValues( ScDPItemData
* pItemData
, long nCount
, const long* pDims
)
1329 long nGroupedColumns
= aGroups
.size();
1331 for (long nDim
=0; nDim
<nCount
; nDim
++)
1333 const ScDPDateGroupHelper
* pDateHelper
= NULL
;
1335 long nColumn
= pDims
[nDim
];
1336 if ( nColumn
>= nSourceCount
&& nColumn
< nSourceCount
+ nGroupedColumns
)
1338 const ScDPGroupDimension
& rGroupDim
= aGroups
[nColumn
- nSourceCount
];
1339 pDateHelper
= rGroupDim
.GetDateHelper();
1340 if ( !pDateHelper
) // date is handled below
1342 const ScDPGroupItem
* pGroupItem
= rGroupDim
.GetGroupForData( pItemData
[nDim
] );
1344 pItemData
[nDim
] = pGroupItem
->GetName();
1345 // if no group is found, keep the original name
1348 else if ( IsNumGroupDimension( nColumn
) )
1350 pDateHelper
= pNumGroups
[nColumn
].GetDateHelper();
1351 if ( !pDateHelper
) // date is handled below
1353 if ( pItemData
[nDim
].bHasValue
)
1355 ScDPNumGroupInfo aNumInfo
;
1356 bool bHasNonInteger
= false;
1357 sal_Unicode cDecSeparator
= 0;
1358 GetNumGroupInfo( nColumn
, aNumInfo
, bHasNonInteger
, cDecSeparator
);
1360 String aGroupName
= lcl_GetNumGroupForValue( pItemData
[nDim
].fValue
,
1361 aNumInfo
, bHasNonInteger
, cDecSeparator
, fGroupValue
, pDoc
);
1363 // consistent with TypedStrData in GetNumEntries
1364 pItemData
[nDim
] = ScDPItemData( aGroupName
, fGroupValue
, TRUE
);
1366 // else (textual) keep original value
1372 if ( pItemData
[nDim
].bHasValue
)
1374 sal_Int32 nPartValue
= lcl_GetDatePartValue(
1375 pItemData
[nDim
].fValue
, pDateHelper
->GetDatePart(), pDoc
->GetFormatTable(),
1376 &pDateHelper
->GetNumInfo() );
1377 pItemData
[nDim
] = ScDPItemData( String(), nPartValue
, TRUE
);
1383 BOOL
ScDPGroupTableData::IsBaseForGroup(long nDim
) const
1385 for ( ScDPGroupDimensionVec::const_iterator
aIter(aGroups
.begin()); aIter
!= aGroups
.end(); aIter
++ )
1387 const ScDPGroupDimension
& rDim
= *aIter
;
1388 if ( rDim
.GetSourceDim() == nDim
)
1395 long ScDPGroupTableData::GetGroupBase(long nGroupDim
) const
1397 for ( ScDPGroupDimensionVec::const_iterator
aIter(aGroups
.begin()); aIter
!= aGroups
.end(); aIter
++ )
1399 const ScDPGroupDimension
& rDim
= *aIter
;
1400 if ( rDim
.GetGroupDim() == nGroupDim
)
1401 return rDim
.GetSourceDim();
1407 BOOL
ScDPGroupTableData::IsNumOrDateGroup(long nDimension
) const
1409 // Virtual method from ScDPTableData, used in result data to force text labels.
1411 if ( nDimension
< nSourceCount
)
1413 return pNumGroups
[nDimension
].GetInfo().Enable
||
1414 pNumGroups
[nDimension
].GetDateHelper();
1417 for ( ScDPGroupDimensionVec::const_iterator
aIter(aGroups
.begin()); aIter
!= aGroups
.end(); aIter
++ )
1419 const ScDPGroupDimension
& rDim
= *aIter
;
1420 if ( rDim
.GetGroupDim() == nDimension
)
1421 return ( rDim
.GetDateHelper() != NULL
);
1427 BOOL
ScDPGroupTableData::IsInGroup( const ScDPItemData
& rGroupData
, long nGroupIndex
,
1428 const ScDPItemData
& rBaseData
, long nBaseIndex
) const
1430 for ( ScDPGroupDimensionVec::const_iterator
aIter(aGroups
.begin()); aIter
!= aGroups
.end(); aIter
++ )
1432 const ScDPGroupDimension
& rDim
= *aIter
;
1433 if ( rDim
.GetGroupDim() == nGroupIndex
&& rDim
.GetSourceDim() == nBaseIndex
)
1435 const ScDPDateGroupHelper
* pGroupDateHelper
= rDim
.GetDateHelper();
1436 if ( pGroupDateHelper
)
1438 //! transform rBaseData (innermost date part)
1439 //! -> always do "HasCommonElement" style comparison
1440 //! (only Quarter, Month, Day affected)
1442 const ScDPDateGroupHelper
* pBaseDateHelper
= NULL
;
1443 if ( nBaseIndex
< nSourceCount
)
1444 pBaseDateHelper
= pNumGroups
[nBaseIndex
].GetDateHelper();
1446 // If there's a date group dimension, the base dimension must have
1447 // date group information, too.
1448 if ( !pBaseDateHelper
)
1450 DBG_ERROR( "mix of date and non-date groups" );
1454 sal_Int32 nGroupPart
= pGroupDateHelper
->GetDatePart();
1455 sal_Int32 nBasePart
= pBaseDateHelper
->GetDatePart();
1456 return lcl_DateContained( nGroupPart
, rGroupData
, nBasePart
, rBaseData
);
1460 // If the item is in a group, only that group is valid.
1461 // If the item is not in any group, its own name is valid.
1463 const ScDPGroupItem
* pGroup
= rDim
.GetGroupForData( rBaseData
);
1464 return pGroup
? pGroup
->GetName().IsCaseInsEqual( rGroupData
) :
1465 rGroupData
.IsCaseInsEqual( rBaseData
);
1470 DBG_ERROR("IsInGroup: no group dimension found");
1474 BOOL
ScDPGroupTableData::HasCommonElement( const ScDPItemData
& rFirstData
, long nFirstIndex
,
1475 const ScDPItemData
& rSecondData
, long nSecondIndex
) const
1477 const ScDPGroupDimension
* pFirstDim
= NULL
;
1478 const ScDPGroupDimension
* pSecondDim
= NULL
;
1479 for ( ScDPGroupDimensionVec::const_iterator
aIter(aGroups
.begin()); aIter
!= aGroups
.end(); aIter
++ )
1481 const ScDPGroupDimension
* pDim
= &(*aIter
);
1482 if ( pDim
->GetGroupDim() == nFirstIndex
)
1484 else if ( pDim
->GetGroupDim() == nSecondIndex
)
1487 if ( pFirstDim
&& pSecondDim
)
1489 const ScDPDateGroupHelper
* pFirstDateHelper
= pFirstDim
->GetDateHelper();
1490 const ScDPDateGroupHelper
* pSecondDateHelper
= pSecondDim
->GetDateHelper();
1491 if ( pFirstDateHelper
|| pSecondDateHelper
)
1493 // If one is a date group dimension, the other one must be, too.
1494 if ( !pFirstDateHelper
|| !pSecondDateHelper
)
1496 DBG_ERROR( "mix of date and non-date groups" );
1500 sal_Int32 nFirstPart
= pFirstDateHelper
->GetDatePart();
1501 sal_Int32 nSecondPart
= pSecondDateHelper
->GetDatePart();
1502 return lcl_DateContained( nFirstPart
, rFirstData
, nSecondPart
, rSecondData
);
1505 const ScDPGroupItem
* pFirstItem
= pFirstDim
->GetGroupForName( rFirstData
);
1506 const ScDPGroupItem
* pSecondItem
= pSecondDim
->GetGroupForName( rSecondData
);
1507 if ( pFirstItem
&& pSecondItem
)
1509 // two existing groups -> TRUE if they have a common element
1510 return pFirstItem
->HasCommonElement( *pSecondItem
);
1512 else if ( pFirstItem
)
1514 // "automatic" group contains only its own name
1515 return pFirstItem
->HasElement( rSecondData
);
1517 else if ( pSecondItem
)
1519 // "automatic" group contains only its own name
1520 return pSecondItem
->HasElement( rFirstData
);
1524 // no groups -> TRUE if equal
1525 return rFirstData
.IsCaseInsEqual( rSecondData
);
1529 DBG_ERROR("HasCommonElement: no group dimension found");
1533 // -----------------------------------------------------------------------