xref: /aoo41x/main/sc/source/core/data/dpgroup.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sc.hxx"
30 
31 
32 
33 // INCLUDE ---------------------------------------------------------------
34 
35 #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
36 
37 #include <tools/debug.hxx>
38 #include <rtl/math.hxx>
39 #include <unotools/localedatawrapper.hxx>
40 #include <svl/zforlist.hxx>
41 
42 #include "dpgroup.hxx"
43 #include "collect.hxx"
44 #include "global.hxx"
45 #include "document.hxx"
46 #include "dpcachetable.hxx"
47 #include "dptabsrc.hxx"
48 #include "dptabres.hxx"
49 #include "dpobject.hxx"
50 #include "dpglobal.hxx"
51 
52 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
53 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
54 
55 #include <vector>
56 #include <hash_set>
57 #include <hash_map>
58 
59 using namespace ::com::sun::star;
60 using ::com::sun::star::uno::Any;
61 using ::com::sun::star::uno::Reference;
62 using ::com::sun::star::uno::Sequence;
63 using ::com::sun::star::uno::UNO_QUERY;
64 using ::com::sun::star::uno::UNO_QUERY_THROW;
65 using ::rtl::OUString;
66 using ::rtl::OUStringHash;
67 
68 using ::std::vector;
69 using ::std::hash_set;
70 using ::std::hash_map;
71 using ::boost::shared_ptr;
72 
73 #define D_TIMEFACTOR              86400.0
74 
75 const sal_uInt16 SC_DP_LEAPYEAR = 1648;     // arbitrary leap year for date calculations
76 
77 // part values for the extra "<" and ">" entries (same for all parts)
78 const sal_Int32 SC_DP_DATE_FIRST = -1;
79 const sal_Int32 SC_DP_DATE_LAST = 10000;
80 
81 // ============================================================================
82 namespace
83 {
84     sal_Bool lcl_Search( SCCOL nSourceDim, ScDPTableDataCache* pCache , const std::vector< SCROW >& vIdx, SCROW nNew , SCROW& rIndex)
85     {
86 	    rIndex = vIdx.size();
87 	    sal_Bool bFound = sal_False;
88 	    SCROW nLo = 0;
89 	    SCROW nHi = vIdx.size() - 1;
90 	    SCROW nIndex;
91 	    long nCompare;
92 	    while (nLo <= nHi)
93 	    {
94 		    nIndex = (nLo + nHi) / 2;
95 
96             const ScDPItemData* pData  = pCache->GetItemDataById( nSourceDim, vIdx[nIndex] );
97             const ScDPItemData* pDataInsert = pCache->GetItemDataById( nSourceDim, nNew );
98 
99 		    nCompare = ScDPItemData::Compare( *pData, *pDataInsert );
100 		    if (nCompare < 0)
101 			    nLo = nIndex + 1;
102 		    else
103 		    {
104 			    nHi = nIndex - 1;
105 			    if (nCompare == 0)
106 			    {
107 				    bFound = sal_True;
108 				    nLo = nIndex;
109 			    }
110 		    }
111 	    }
112 	    rIndex = nLo;
113 	    return bFound;
114     }
115 
116     void  lcl_Insert( SCCOL nSourceDim, ScDPTableDataCache* pCache ,  std::vector< SCROW >& vIdx, SCROW nNew )
117     {
118         SCROW nIndex = 0;
119         if ( !lcl_Search( nSourceDim, pCache, vIdx, nNew ,nIndex ) )
120             vIdx.insert( vIdx.begin()+nIndex, nNew  );
121     }
122 
123 	template<bool bUpdateData>
124 	SCROW lcl_InsertValue( SCCOL nSourceDim, ScDPTableDataCache* pCache ,  std::vector< SCROW >& vIdx, const ScDPItemData & rData );
125 
126 	template<>
127 	SCROW lcl_InsertValue<false>( SCCOL nSourceDim, ScDPTableDataCache* pCache ,  std::vector< SCROW >& vIdx, const ScDPItemData & rData )
128 	{
129 		SCROW nNewID = pCache->GetAdditionalItemID( rData );
130 		lcl_Insert( nSourceDim, pCache, vIdx, nNewID );
131 		return nNewID;
132 	}
133 
134 	template<>
135 	SCROW lcl_InsertValue<true>( SCCOL nSourceDim, ScDPTableDataCache* pCache ,  std::vector< SCROW >& vIdx, const ScDPItemData & rData )
136 	{
137 		SCROW nItemId = lcl_InsertValue<false>( nSourceDim, pCache, vIdx, rData );
138 
139 		if( const ScDPItemData *pData = pCache->GetItemDataById( nSourceDim, nItemId ) )
140 			const_cast<ScDPItemData&>(*pData) = rData;
141 
142 		return nItemId;
143 	}
144 
145 	template<bool bUpdateData>
146     void lcl_InsertValue ( SCCOL nSourceDim, ScDPTableDataCache* pCache ,  std::vector< SCROW >& vIdx, const String&  rString, const double& fValue )
147     {
148         lcl_InsertValue<bUpdateData>( nSourceDim, pCache, vIdx, ScDPItemData( rString, fValue, sal_True ) );
149     }
150 
151 	template<bool bUpdateData>
152 	void lcl_InsertValue ( SCCOL nSourceDim, ScDPTableDataCache* pCache ,  std::vector< SCROW >& vIdx, const String&  rString, const double& fValue, sal_Int32 nDatePart )
153 	{
154 		lcl_InsertValue<bUpdateData>( nSourceDim, pCache, vIdx, ScDPItemData( nDatePart, rString, fValue, ScDPItemData::MK_DATA|ScDPItemData::MK_VAL|ScDPItemData::MK_DATEPART ) );
155 	}
156 
157     void lcl_AppendDateStr( rtl::OUStringBuffer& rBuffer, double fValue, SvNumberFormatter* pFormatter )
158     {
159         sal_uLong nFormat = pFormatter->GetStandardFormat( NUMBERFORMAT_DATE, ScGlobal::eLnge );
160         String aString;
161         pFormatter->GetInputLineString( fValue, nFormat, aString );
162         rBuffer.append( aString );
163     }
164 
165     String lcl_GetNumGroupName( double fStartValue, const ScDPNumGroupInfo& rInfo,
166         bool bHasNonInteger, sal_Unicode cDecSeparator, SvNumberFormatter* pFormatter )
167     {
168         DBG_ASSERT( cDecSeparator != 0, "cDecSeparator not initialized" );
169 
170         double fStep = rInfo.Step;
171         double fEndValue = fStartValue + fStep;
172         if ( !bHasNonInteger && ( rInfo.DateValues || !rtl::math::approxEqual( fEndValue, rInfo.End ) ) )
173         {
174             //  The second number of the group label is
175             //  (first number + size - 1) if there are only integer numbers,
176             //  (first number + size) if any non-integer numbers are involved.
177             //  Exception: The last group (containing the end value) is always
178             //  shown as including the end value (but not for dates).
179 
180             fEndValue -= 1.0;
181         }
182 
183         if ( fEndValue > rInfo.End && !rInfo.AutoEnd )
184         {
185             // limit the last group to the end value
186 
187             fEndValue = rInfo.End;
188         }
189 
190         rtl::OUStringBuffer aBuffer;
191         if ( rInfo.DateValues )
192         {
193             lcl_AppendDateStr( aBuffer, fStartValue, pFormatter );
194             aBuffer.appendAscii( " - " );   // with spaces
195             lcl_AppendDateStr( aBuffer, fEndValue, pFormatter );
196         }
197         else
198         {
199             rtl::math::doubleToUStringBuffer( aBuffer, fStartValue, rtl_math_StringFormat_Automatic,
200                 rtl_math_DecimalPlaces_Max, cDecSeparator, true );
201             aBuffer.append( (sal_Unicode) '-' );
202             rtl::math::doubleToUStringBuffer( aBuffer, fEndValue, rtl_math_StringFormat_Automatic,
203                 rtl_math_DecimalPlaces_Max, cDecSeparator, true );
204         }
205 
206         return aBuffer.makeStringAndClear();
207     }
208 
209     String lcl_GetSpecialNumGroupName( double fValue, bool bFirst, sal_Unicode cDecSeparator,
210         bool bDateValues, SvNumberFormatter* pFormatter )
211     {
212         DBG_ASSERT( cDecSeparator != 0, "cDecSeparator not initialized" );
213 
214         rtl::OUStringBuffer aBuffer;
215         aBuffer.append((sal_Unicode)( bFirst ? '<' : '>' ));
216         if ( bDateValues )
217             lcl_AppendDateStr( aBuffer, fValue, pFormatter );
218         else
219             rtl::math::doubleToUStringBuffer( aBuffer, fValue, rtl_math_StringFormat_Automatic,
220             rtl_math_DecimalPlaces_Max, cDecSeparator, true );
221         return aBuffer.makeStringAndClear();
222     }
223 
224     inline bool IsInteger( double fValue )
225     {
226         return rtl::math::approxEqual( fValue, rtl::math::approxFloor(fValue) );
227     }
228 
229     String lcl_GetNumGroupForValue( double fValue, const ScDPNumGroupInfo& rInfo, bool bHasNonInteger,
230         sal_Unicode cDecSeparator, double& rGroupValue, ScDocument* pDoc )
231     {
232         SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
233 
234         if ( fValue < rInfo.Start && !rtl::math::approxEqual( fValue, rInfo.Start ) )
235         {
236             rGroupValue = rInfo.Start - rInfo.Step;
237             return lcl_GetSpecialNumGroupName( rInfo.Start, true, cDecSeparator, rInfo.DateValues, pFormatter );
238         }
239 
240         if ( fValue > rInfo.End && !rtl::math::approxEqual( fValue, rInfo.End ) )
241         {
242             rGroupValue = rInfo.End + rInfo.Step;
243             return lcl_GetSpecialNumGroupName( rInfo.End, false, cDecSeparator, rInfo.DateValues, pFormatter );
244         }
245 
246         double fDiff = fValue - rInfo.Start;
247         double fDiv = rtl::math::approxFloor( fDiff / rInfo.Step );
248         double fGroupStart = rInfo.Start + fDiv * rInfo.Step;
249 
250         if ( rtl::math::approxEqual( fGroupStart, rInfo.End ) &&
251             !rtl::math::approxEqual( fGroupStart, rInfo.Start ) )
252         {
253             if ( !rInfo.DateValues )
254             {
255                 //  A group that would consist only of the end value is not created,
256                 //  instead the value is included in the last group before. So the
257                 //  previous group is used if the calculated group start value is the
258                 //  selected end value.
259 
260                 fDiv -= 1.0;
261                 fGroupStart = rInfo.Start + fDiv * rInfo.Step;
262             }
263             else
264             {
265                 //  For date values, the end value is instead treated as above the limit
266                 //  if it would be a group of its own.
267 
268                 rGroupValue = rInfo.End + rInfo.Step;
269                 return lcl_GetSpecialNumGroupName( rInfo.End, false, cDecSeparator, rInfo.DateValues, pFormatter );
270             }
271         }
272 
273         rGroupValue = fGroupStart;
274 
275         return lcl_GetNumGroupName( fGroupStart, rInfo, bHasNonInteger, cDecSeparator, pFormatter );
276     }
277 }
278 
279 class ScDPGroupDateFilter : public ScDPCacheTable::FilterBase
280 {
281 public:
282     ScDPGroupDateFilter(double fMatchValue, sal_Int32 nDatePart,
283                         const Date* pNullDate, const ScDPNumGroupInfo* pNumInfo);
284 
285     // Wang Xu Ming -- 2009-8-17
286     // DataPilot Migration - Cache&&Performance
287     virtual bool match(const ScDPItemData & rCellData) const;
288     // End Comments
289 
290 private:
291     ScDPGroupDateFilter(); // disabled
292 
293     const Date*             mpNullDate;
294     const ScDPNumGroupInfo* mpNumInfo;
295     double                  mfMatchValue;
296     sal_Int32               mnDatePart;
297 };
298 
299 // ----------------------------------------------------------------------------
300 
301 ScDPGroupDateFilter::ScDPGroupDateFilter(double fMatchValue, sal_Int32 nDatePart,
302                                  const Date* pNullDate, const ScDPNumGroupInfo* pNumInfo) :
303     mpNullDate(pNullDate),
304     mpNumInfo(pNumInfo),
305     mfMatchValue(fMatchValue),
306     mnDatePart(nDatePart)
307 {
308 //  fprintf(stdout, "ScDPCacheTable:DateGroupFilter::DateGroupFilter: match value = %g; date part = %ld\n",
309 //          mfMatchValue, mnDatePart);
310 }
311 bool ScDPGroupDateFilter::match( const ScDPItemData & rCellData ) const
312 {
313     using namespace ::com::sun::star::sheet;
314     using ::rtl::math::approxFloor;
315     using ::rtl::math::approxEqual;
316 
317 	if ( !rCellData.IsValue() )
318 		return false;
319 //	ScDPCacheCell rCell( rCellData.fValue );
320     if (!mpNumInfo)
321         return false;
322 
323     // Start and end dates are inclusive.  (An end date without a time value
324     // is included, while an end date with a time value is not.)
325 
326     if ( rCellData.GetValue() < mpNumInfo->Start && !approxEqual(rCellData.GetValue(), mpNumInfo->Start) )
327         return static_cast<sal_Int32>(mfMatchValue) == SC_DP_DATE_FIRST;
328 
329     if ( rCellData.GetValue() > mpNumInfo->End && !approxEqual(rCellData.GetValue(), mpNumInfo->End) )
330         return static_cast<sal_Int32>(mfMatchValue) == SC_DP_DATE_LAST;
331 
332     if (mnDatePart == DataPilotFieldGroupBy::HOURS || mnDatePart == DataPilotFieldGroupBy::MINUTES ||
333         mnDatePart == DataPilotFieldGroupBy::SECONDS)
334     {
335         // handle time
336         // (as in the cell functions, ScInterpreter::ScGetHour etc.: seconds are rounded)
337 
338         double time = rCellData.GetValue() - approxFloor(rCellData.GetValue());
339         long seconds = static_cast<long>(approxFloor(time*D_TIMEFACTOR + 0.5));
340 
341         switch (mnDatePart)
342         {
343             case DataPilotFieldGroupBy::HOURS:
344             {
345                 sal_Int32 hrs = seconds / 3600;
346                 sal_Int32 matchHrs = static_cast<sal_Int32>(mfMatchValue);
347                 return hrs == matchHrs;
348             }
349             case DataPilotFieldGroupBy::MINUTES:
350             {
351                 sal_Int32 minutes = (seconds % 3600) / 60;
352                 sal_Int32 matchMinutes = static_cast<sal_Int32>(mfMatchValue);
353                 return minutes == matchMinutes;
354             }
355             case DataPilotFieldGroupBy::SECONDS:
356             {
357                 sal_Int32 sec = seconds % 60;
358                 sal_Int32 matchSec = static_cast<sal_Int32>(mfMatchValue);
359                 return sec == matchSec;
360             }
361             default:
362                 DBG_ERROR("invalid time part");
363         }
364         return false;
365     }
366 
367     Date date = *mpNullDate + static_cast<long>(approxFloor(rCellData.GetValue()));
368     switch (mnDatePart)
369     {
370         case DataPilotFieldGroupBy::YEARS:
371         {
372             sal_Int32 year = static_cast<sal_Int32>(date.GetYear());
373             sal_Int32 matchYear = static_cast<sal_Int32>(mfMatchValue);
374             return year == matchYear;
375         }
376         case DataPilotFieldGroupBy::QUARTERS:
377         {
378             sal_Int32 qtr =  1 + (static_cast<sal_Int32>(date.GetMonth()) - 1) / 3;
379             sal_Int32 matchQtr = static_cast<sal_Int32>(mfMatchValue);
380             return qtr == matchQtr;
381         }
382         case DataPilotFieldGroupBy::MONTHS:
383         {
384             sal_Int32 month = static_cast<sal_Int32>(date.GetMonth());
385             sal_Int32 matchMonth = static_cast<sal_Int32>(mfMatchValue);
386             return month == matchMonth;
387         }
388         case DataPilotFieldGroupBy::DAYS:
389         {
390             Date yearStart(1, 1, date.GetYear());
391             sal_Int32 days = (date - yearStart) + 1;       // Jan 01 has value 1
392             if (days >= 60 && !date.IsLeapYear())
393             {
394                 // This is not a leap year.  Adjust the value accordingly.
395                 ++days;
396             }
397             sal_Int32 matchDays = static_cast<sal_Int32>(mfMatchValue);
398             return days == matchDays;
399         }
400         default:
401             DBG_ERROR("invalid date part");
402     }
403 
404     return false;
405 }
406 // -----------------------------------------------------------------------
407 
408 ScDPDateGroupHelper::ScDPDateGroupHelper( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart ) :
409     aNumInfo( rInfo ),
410     nDatePart( nPart )
411 {
412 }
413 
414 ScDPDateGroupHelper::~ScDPDateGroupHelper()
415 {
416 }
417 
418 String lcl_GetTwoDigitString( sal_Int32 nValue )
419 {
420     String aRet = String::CreateFromInt32( nValue );
421     if ( aRet.Len() < 2 )
422         aRet.Insert( (sal_Unicode)'0', 0 );
423     return aRet;
424 }
425 
426 String lcl_GetDateGroupName( sal_Int32 nDatePart, sal_Int32 nValue, SvNumberFormatter* pFormatter )
427 {
428     String aRet;
429     switch ( nDatePart )
430     {
431         case com::sun::star::sheet::DataPilotFieldGroupBy::YEARS:
432             aRet = String::CreateFromInt32( nValue );
433             break;
434         case com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS:
435             aRet = ScGlobal::pLocaleData->getQuarterAbbreviation( (sal_Int16)(nValue - 1) );    // nValue is 1-based
436             break;
437         case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS:
438             //! cache getMonths() result?
439             aRet = ScGlobal::GetCalendar()->getDisplayName(
440 						::com::sun::star::i18n::CalendarDisplayIndex::MONTH,
441 						sal_Int16(nValue-1), 0 );    // 0-based, get short name
442             break;
443         case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS:
444             {
445                 Date aDate( 1, 1, SC_DP_LEAPYEAR );
446                 aDate += ( nValue - 1 );            // nValue is 1-based
447                 Date aNullDate = *(pFormatter->GetNullDate());
448                 long nDays = aDate - aNullDate;
449 
450                 sal_uLong nFormat = pFormatter->GetFormatIndex( NF_DATE_SYS_DDMMM, ScGlobal::eLnge );
451                 Color* pColor;
452                 pFormatter->GetOutputString( nDays, nFormat, aRet, &pColor );
453             }
454             break;
455         case com::sun::star::sheet::DataPilotFieldGroupBy::HOURS:
456             //! allow am/pm format?
457             aRet = lcl_GetTwoDigitString( nValue );
458             break;
459         case com::sun::star::sheet::DataPilotFieldGroupBy::MINUTES:
460         case com::sun::star::sheet::DataPilotFieldGroupBy::SECONDS:
461             aRet = ScGlobal::pLocaleData->getTimeSep();
462             aRet.Append( lcl_GetTwoDigitString( nValue ) );
463             break;
464         default:
465             DBG_ERROR("invalid date part");
466     }
467     return aRet;
468 }
469 
470 sal_Int32 lcl_GetDatePartValue( double fValue, sal_Int32 nDatePart, SvNumberFormatter* pFormatter,
471                                 const ScDPNumGroupInfo* pNumInfo )
472 {
473     // Start and end are inclusive
474     // (End date without a time value is included, with a time value it's not)
475 
476     if ( pNumInfo )
477     {
478         if ( fValue < pNumInfo->Start && !rtl::math::approxEqual( fValue, pNumInfo->Start ) )
479             return SC_DP_DATE_FIRST;
480         if ( fValue > pNumInfo->End && !rtl::math::approxEqual( fValue, pNumInfo->End ) )
481             return SC_DP_DATE_LAST;
482     }
483 
484     sal_Int32 nResult = 0;
485 
486     if ( nDatePart == com::sun::star::sheet::DataPilotFieldGroupBy::HOURS || nDatePart == com::sun::star::sheet::DataPilotFieldGroupBy::MINUTES || nDatePart == com::sun::star::sheet::DataPilotFieldGroupBy::SECONDS )
487     {
488         // handle time
489         // (as in the cell functions, ScInterpreter::ScGetHour etc.: seconds are rounded)
490 
491         double fTime = fValue - ::rtl::math::approxFloor(fValue);
492         long nSeconds = (long)::rtl::math::approxFloor(fTime*D_TIMEFACTOR+0.5);
493 
494         switch ( nDatePart )
495         {
496             case com::sun::star::sheet::DataPilotFieldGroupBy::HOURS:
497                 nResult = nSeconds / 3600;
498                 break;
499             case com::sun::star::sheet::DataPilotFieldGroupBy::MINUTES:
500                 nResult = ( nSeconds % 3600 ) / 60;
501                 break;
502             case com::sun::star::sheet::DataPilotFieldGroupBy::SECONDS:
503                 nResult = nSeconds % 60;
504                 break;
505         }
506     }
507     else
508     {
509         Date aDate = *(pFormatter->GetNullDate());
510         aDate += (long)::rtl::math::approxFloor( fValue );
511 
512         switch ( nDatePart )
513         {
514             case com::sun::star::sheet::DataPilotFieldGroupBy::YEARS:
515                 nResult = aDate.GetYear();
516                 break;
517             case com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS:
518                 nResult = 1 + ( aDate.GetMonth() - 1 ) / 3;     // 1..4
519                 break;
520             case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS:
521                 nResult = aDate.GetMonth();     // 1..12
522                 break;
523             case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS:
524                 {
525                     Date aYearStart( 1, 1, aDate.GetYear() );
526                     nResult = ( aDate - aYearStart ) + 1;       // Jan 01 has value 1
527                     if ( nResult >= 60 && !aDate.IsLeapYear() )
528                     {
529                         // days are counted from 1 to 366 - if not from a leap year, adjust
530                         ++nResult;
531                     }
532                 }
533                 break;
534             default:
535                 DBG_ERROR("invalid date part");
536         }
537     }
538 
539     return nResult;
540 }
541 
542 sal_Bool lcl_DateContained( sal_Int32 nGroupPart, const ScDPItemData& rGroupData,
543                         sal_Int32 nBasePart, const ScDPItemData& rBaseData )
544 {
545     if ( !rGroupData.IsValue() || !rBaseData.IsValue() )
546     {
547         // non-numeric entries involved: only match equal entries
548         return rGroupData.IsCaseInsEqual( rBaseData );
549     }
550 
551     // no approxFloor needed, values were created from integers
552 // Wang Xu Ming -- 2009-8-17
553 // DataPilot Migration - Cache&&Performance
554     sal_Int32 nGroupValue = (sal_Int32) rGroupData.GetValue();
555     sal_Int32 nBaseValue = (sal_Int32) rBaseData.GetValue();
556 // End Comments
557     if ( nBasePart > nGroupPart )
558     {
559         // switch, so the base part is the smaller (inner) part
560 
561         ::std::swap( nGroupPart, nBasePart );
562         ::std::swap( nGroupValue, nBaseValue );
563     }
564 
565     if ( nGroupValue == SC_DP_DATE_FIRST || nGroupValue == SC_DP_DATE_LAST ||
566          nBaseValue == SC_DP_DATE_FIRST || nBaseValue == SC_DP_DATE_LAST )
567     {
568         // first/last entry matches only itself
569         return ( nGroupValue == nBaseValue );
570     }
571 
572     sal_Bool bContained = sal_True;
573     switch ( nBasePart )        // inner part
574     {
575         case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS:
576             // a month is only contained in its quarter
577             if ( nGroupPart == com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS )
578             {
579                 // months and quarters are both 1-based
580                 bContained = ( nGroupValue - 1 == ( nBaseValue - 1 ) / 3 );
581             }
582             break;
583         case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS:
584             // a day is only contained in its quarter or month
585             if ( nGroupPart == com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS || nGroupPart == com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS )
586             {
587                 Date aDate( 1, 1, SC_DP_LEAPYEAR );
588                 aDate += ( nBaseValue - 1 );            // days are 1-based
589                 sal_Int32 nCompare = aDate.GetMonth();
590                 if ( nGroupPart == com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS )
591                     nCompare = ( ( nCompare - 1 ) / 3 ) + 1;    // get quarter from date
592 
593                 bContained = ( nGroupValue == nCompare );
594             }
595             break;
596 
597         // other parts: everything is contained
598     }
599 
600     return bContained;
601 }
602 
603 String lcl_GetSpecialDateName( double fValue, bool bFirst, SvNumberFormatter* pFormatter )
604 {
605     rtl::OUStringBuffer aBuffer;
606     aBuffer.append((sal_Unicode)( bFirst ? '<' : '>' ));
607     lcl_AppendDateStr( aBuffer, fValue, pFormatter );
608     return aBuffer.makeStringAndClear();
609 }
610 
611 void ScDPDateGroupHelper::FillColumnEntries( SCCOL nSourceDim, ScDPTableDataCache* pCache, std::vector< SCROW >& rEntries, const std::vector< SCROW >& rOriginal  ) const
612 {
613     // auto min/max is only used for "Years" part, but the loop is always needed
614     double fSourceMin = 0.0;
615     double fSourceMax = 0.0;
616     bool bFirst = true;
617 
618     size_t  nOriginalCount = rOriginal.size();
619     for (size_t nOriginalPos=0; nOriginalPos<nOriginalCount; nOriginalPos++)
620     {
621         const  ScDPItemData* pItemData = pCache->GetItemDataById( nSourceDim, rOriginal[nOriginalPos] );
622         if ( pItemData->HasStringData() )
623         {
624             // string data: just copy
625             lcl_Insert( nSourceDim, pCache , rEntries,  rOriginal[nOriginalPos] );
626         }
627         else
628         {
629             double fSourceValue = pItemData->GetValue();
630             if ( bFirst )
631             {
632                 fSourceMin = fSourceMax = fSourceValue;
633                 bFirst = false;
634             }
635             else
636             {
637                 if ( fSourceValue < fSourceMin )
638                     fSourceMin = fSourceValue;
639                 if ( fSourceValue > fSourceMax )
640                     fSourceMax = fSourceValue;
641             }
642         }
643     }
644 
645     // For the start/end values, use the same date rounding as in ScDPNumGroupDimension::GetNumEntries
646     // (but not for the list of available years):
647     if ( aNumInfo.AutoStart )
648         const_cast<ScDPDateGroupHelper*>(this)->aNumInfo.Start = rtl::math::approxFloor( fSourceMin );
649     if ( aNumInfo.AutoEnd )
650         const_cast<ScDPDateGroupHelper*>(this)->aNumInfo.End = rtl::math::approxFloor( fSourceMax ) + 1;
651 
652     //! if not automatic, limit fSourceMin/fSourceMax for list of year values?
653     SvNumberFormatter* pFormatter = pCache->GetDoc()->GetFormatTable();
654 
655     long nStart = 0;
656     long nEnd = 0;          // including
657 
658     switch ( nDatePart )
659     {
660         case com::sun::star::sheet::DataPilotFieldGroupBy::YEARS:
661             nStart = lcl_GetDatePartValue( fSourceMin, com::sun::star::sheet::DataPilotFieldGroupBy::YEARS, pFormatter, NULL );
662             nEnd = lcl_GetDatePartValue( fSourceMax, com::sun::star::sheet::DataPilotFieldGroupBy::YEARS, pFormatter, NULL );
663             break;
664         case com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS: nStart = 1; nEnd = 4;   break;
665         case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS:   nStart = 1; nEnd = 12;  break;
666         case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS:     nStart = 1; nEnd = 366; break;
667         case com::sun::star::sheet::DataPilotFieldGroupBy::HOURS:    nStart = 0; nEnd = 23;  break;
668         case com::sun::star::sheet::DataPilotFieldGroupBy::MINUTES:  nStart = 0; nEnd = 59;  break;
669         case com::sun::star::sheet::DataPilotFieldGroupBy::SECONDS:  nStart = 0; nEnd = 59;  break;
670         default:
671             DBG_ERROR("invalid date part");
672     }
673 
674     for ( sal_Int32 nValue = nStart; nValue <= nEnd; nValue++ )
675     {
676         String aName = lcl_GetDateGroupName( nDatePart, nValue, pFormatter );
677 	  lcl_InsertValue<false>( nSourceDim, pCache, rEntries, aName, nValue, nDatePart );
678     }
679 
680     // add first/last entry (min/max)
681 	String aFirstName = lcl_GetSpecialDateName( aNumInfo.Start, true, pFormatter );
682 	lcl_InsertValue<true>( nSourceDim, pCache, rEntries, aFirstName, SC_DP_DATE_FIRST, nDatePart );
683 
684 	String aLastName = lcl_GetSpecialDateName( aNumInfo.End, false, pFormatter );
685 	lcl_InsertValue<true>( nSourceDim, pCache, rEntries, aLastName, SC_DP_DATE_LAST, nDatePart );
686 }
687 
688 // -----------------------------------------------------------------------
689 
690 ScDPGroupItem::ScDPGroupItem( const ScDPItemData& rName ) :
691     aGroupName( rName )
692 {
693 }
694 
695 ScDPGroupItem::~ScDPGroupItem()
696 {
697 }
698 
699 void ScDPGroupItem::AddElement( const ScDPItemData& rName )
700 {
701     aElements.push_back( rName );
702 }
703 
704 bool ScDPGroupItem::HasElement( const ScDPItemData& rData ) const
705 {
706     for ( ScDPItemDataVec::const_iterator aIter(aElements.begin()); aIter != aElements.end(); aIter++ )
707         if ( aIter->IsCaseInsEqual( rData ) )
708             return true;
709 
710     return false;
711 }
712 
713 bool ScDPGroupItem::HasCommonElement( const ScDPGroupItem& rOther ) const
714 {
715     for ( ScDPItemDataVec::const_iterator aIter(aElements.begin()); aIter != aElements.end(); aIter++ )
716         if ( rOther.HasElement( *aIter ) )
717             return true;
718 
719     return false;
720 }
721 
722 void ScDPGroupItem::FillGroupFilter( ScDPCacheTable::GroupFilter& rFilter ) const
723 {
724     ScDPItemDataVec::const_iterator itrEnd = aElements.end();
725     for (ScDPItemDataVec::const_iterator itr = aElements.begin(); itr != itrEnd; ++itr)
726 // Wang Xu Ming -- 2009-8-17
727 // DataPilot Migration - Cache&&Performance
728         rFilter.addMatchItem(itr->GetString(), itr->GetValue(), itr->IsValue());
729 // End Comments
730 }
731 
732 // -----------------------------------------------------------------------
733 
734 ScDPGroupDimension::ScDPGroupDimension( long nSource, const String& rNewName ) :
735     nSourceDim( nSource ),
736     nGroupDim( -1 ),
737     aGroupName( rNewName ),
738     pDateHelper( NULL )/*,
739     pCollection( NULL )*/
740 {
741 }
742 
743 ScDPGroupDimension::~ScDPGroupDimension()
744 {
745     delete pDateHelper;
746     maMemberEntries.clear();
747 }
748 
749 ScDPGroupDimension::ScDPGroupDimension( const ScDPGroupDimension& rOther ) :
750     nSourceDim( rOther.nSourceDim ),
751     nGroupDim( rOther.nGroupDim ),
752     aGroupName( rOther.aGroupName ),
753     pDateHelper( NULL ),
754    aItems( rOther.aItems )
755 {
756     if ( rOther.pDateHelper )
757         pDateHelper = new ScDPDateGroupHelper( *rOther.pDateHelper );
758 }
759 
760 ScDPGroupDimension&	ScDPGroupDimension::operator=( const ScDPGroupDimension& rOther )
761 {
762     nSourceDim = rOther.nSourceDim;
763     nGroupDim  = rOther.nGroupDim;
764     aGroupName = rOther.aGroupName;
765     aItems     = rOther.aItems;
766 
767     delete pDateHelper;
768     if ( rOther.pDateHelper )
769         pDateHelper = new ScDPDateGroupHelper( *rOther.pDateHelper );
770     else
771         pDateHelper = NULL;
772 
773     return *this;
774 }
775 
776 void ScDPGroupDimension::MakeDateHelper( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart )
777 {
778     delete pDateHelper;
779     pDateHelper = new ScDPDateGroupHelper( rInfo, nPart );
780 }
781 
782 void ScDPGroupDimension::AddItem( const ScDPGroupItem& rItem )
783 {
784     aItems.push_back( rItem );
785 }
786 
787 void ScDPGroupDimension::SetGroupDim( long nDim )
788 {
789     nGroupDim = nDim;
790 }
791 // Wang Xu Ming -- 2009-9-2
792 // DataPilot Migration - Cache&&Performance
793 const std::vector< SCROW >&  ScDPGroupDimension::GetColumnEntries( const ScDPCacheTable&  rCacheTable, const std::vector< SCROW >& rOriginal  )  const
794 {
795     if ( maMemberEntries.empty() )
796     {
797         if ( pDateHelper )
798         {
799             pDateHelper->FillColumnEntries(  (SCCOL)GetSourceDim(), rCacheTable.GetCache(), maMemberEntries,  rOriginal  );
800         }
801         else
802         {
803             for (size_t  i =0; i < rOriginal.size( );  i ++)
804             {
805                 const  ScDPItemData* pItemData = rCacheTable.GetCache()->GetItemDataById( (SCCOL)GetSourceDim(), rOriginal[i] );
806                 if ( !pItemData || !GetGroupForData( *pItemData ) )
807                 {
808                     // not in any group -> add as its own group
809                     maMemberEntries.push_back( rOriginal[i] );
810                 }
811             }
812 
813             long nCount = aItems.size();
814             for (long i=0; i<nCount; i++)
815             {
816                 SCROW nNew = rCacheTable.GetCache()->GetAdditionalItemID(  aItems[i].GetName() );
817                 lcl_Insert ( (SCCOL)GetSourceDim(), rCacheTable.GetCache(), maMemberEntries, nNew  );
818             }
819         }
820     }
821     return maMemberEntries;
822 }
823 
824 // End Comments
825 
826 
827 const ScDPGroupItem* ScDPGroupDimension::GetGroupForData( const ScDPItemData& rData ) const
828 {
829     for ( ScDPGroupItemVec::const_iterator aIter(aItems.begin()); aIter != aItems.end(); aIter++ )
830         if ( aIter->HasElement( rData ) )
831             return &*aIter;
832 
833     return NULL;
834 }
835 
836 const ScDPGroupItem* ScDPGroupDimension::GetGroupForName( const ScDPItemData& rName ) const
837 {
838     for ( ScDPGroupItemVec::const_iterator aIter(aItems.begin()); aIter != aItems.end(); aIter++ )
839         if ( aIter->GetName().IsCaseInsEqual( rName ) )
840             return &*aIter;
841 
842     return NULL;
843 }
844 
845 const ScDPGroupItem* ScDPGroupDimension::GetGroupByIndex( size_t nIndex ) const
846 {
847     if (nIndex >= aItems.size())
848         return NULL;
849 
850     return &aItems[nIndex];
851 }
852 
853 void ScDPGroupDimension::DisposeData()
854 {
855     maMemberEntries.clear();
856 }
857 
858 // -----------------------------------------------------------------------
859 
860 ScDPNumGroupDimension::ScDPNumGroupDimension() :
861     pDateHelper( NULL ),
862     bHasNonInteger( false ),
863     cDecSeparator( 0 )
864 {
865 }
866 
867 ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupInfo& rInfo ) :
868     aGroupInfo( rInfo ),
869     pDateHelper( NULL ),
870     bHasNonInteger( false ),
871     cDecSeparator( 0 )
872 {
873 }
874 
875 ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupDimension& rOther ) :
876     aGroupInfo( rOther.aGroupInfo ),
877     pDateHelper( NULL ),
878     bHasNonInteger( false ),
879     cDecSeparator( 0 )
880 {
881     if ( rOther.pDateHelper )
882         pDateHelper = new ScDPDateGroupHelper( *rOther.pDateHelper );
883 }
884 
885 ScDPNumGroupDimension& ScDPNumGroupDimension::operator=( const ScDPNumGroupDimension& rOther )
886 {
887     aGroupInfo = rOther.aGroupInfo;
888 
889     delete pDateHelper;
890     if ( rOther.pDateHelper )
891         pDateHelper = new ScDPDateGroupHelper( *rOther.pDateHelper );
892     else
893         pDateHelper = NULL;
894 
895     bHasNonInteger = false;
896     return *this;
897 }
898 
899 void ScDPNumGroupDimension::DisposeData()
900 {
901     bHasNonInteger = false;
902     maMemberEntries.clear();
903 }
904 
905 ScDPNumGroupDimension::~ScDPNumGroupDimension()
906 {
907     delete pDateHelper;
908 }
909 
910 void ScDPNumGroupDimension::MakeDateHelper( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart )
911 {
912     delete pDateHelper;
913     pDateHelper = new ScDPDateGroupHelper( rInfo, nPart );
914 
915     aGroupInfo.Enable = sal_True;   //! or query both?
916 }
917 
918 const std::vector< SCROW >& ScDPNumGroupDimension::GetNumEntries( SCCOL nSourceDim, ScDPTableDataCache* pCache,
919                     const std::vector< SCROW >& rOriginal  ) const
920 {
921     if ( maMemberEntries.empty() )
922     {
923         SvNumberFormatter* pFormatter = pCache->GetDoc()->GetFormatTable();
924 
925         if ( pDateHelper )
926             pDateHelper->FillColumnEntries( nSourceDim, pCache, maMemberEntries,rOriginal );
927         else
928         {
929             // Copy textual entries.
930             // Also look through the source entries for non-integer numbers, minimum and maximum.
931             // GetNumEntries (GetColumnEntries) must be called before accessing the groups
932             // (this in ensured by calling ScDPLevel::GetMembersObject for all column/row/page
933             // dimensions before iterating over the values).
934 
935             cDecSeparator = ScGlobal::pLocaleData->getNumDecimalSep().GetChar(0);
936 
937             // non-integer GroupInfo values count, too
938             bHasNonInteger = ( !aGroupInfo.AutoStart && !IsInteger( aGroupInfo.Start ) ) ||
939                              ( !aGroupInfo.AutoEnd   && !IsInteger( aGroupInfo.End   ) ) ||
940                              !IsInteger( aGroupInfo.Step );
941             double fSourceMin = 0.0;
942             double fSourceMax = 0.0;
943             bool bFirst = true;
944 
945             size_t  nOriginalCount = rOriginal.size();
946             for (size_t  nOriginalPos=0; nOriginalPos<nOriginalCount; nOriginalPos++)
947             {
948                const  ScDPItemData* pItemData = pCache->GetItemDataById( nSourceDim , rOriginal[nOriginalPos] );
949 
950                if ( pItemData && pItemData ->HasStringData() )
951                {
952                    lcl_Insert( nSourceDim, pCache, maMemberEntries,  rOriginal[nOriginalPos] );
953                }
954                else
955                {
956                    double fSourceValue = pItemData->GetValue();
957                    if ( bFirst )
958                    {
959                        fSourceMin = fSourceMax = fSourceValue;
960                        bFirst = false;
961                    }
962                    else
963                    {
964                        if ( fSourceValue < fSourceMin )
965                            fSourceMin = fSourceValue;
966                        if ( fSourceValue > fSourceMax )
967                            fSourceMax = fSourceValue;
968                    }
969                    if ( !bHasNonInteger && !IsInteger( fSourceValue ) )
970                    {
971                        // if any non-integer numbers are involved, the group labels are
972                        // shown including their upper limit
973                        bHasNonInteger = true;
974                    }
975                }
976             }
977 
978             if ( aGroupInfo.DateValues )
979             {
980                 // special handling for dates: always integer, round down limits
981                 bHasNonInteger = false;
982                 fSourceMin = rtl::math::approxFloor( fSourceMin );
983                 fSourceMax = rtl::math::approxFloor( fSourceMax ) + 1;
984             }
985 
986             if ( aGroupInfo.AutoStart )
987                 const_cast<ScDPNumGroupDimension*>(this)->aGroupInfo.Start = fSourceMin;
988             if ( aGroupInfo.AutoEnd )
989                 const_cast<ScDPNumGroupDimension*>(this)->aGroupInfo.End = fSourceMax;
990 
991             //! limit number of entries?
992 
993             long nLoopCount = 0;
994             double fLoop = aGroupInfo.Start;
995 
996             // Use "less than" instead of "less or equal" for the loop - don't create a group
997             // that consists only of the end value. Instead, the end value is then included
998             // in the last group (last group is bigger than the others).
999             // The first group has to be created nonetheless. GetNumGroupForValue has corresponding logic.
1000 
1001             bool bFirstGroup = true;
1002             while ( bFirstGroup || ( fLoop < aGroupInfo.End && !rtl::math::approxEqual( fLoop, aGroupInfo.End ) ) )
1003             {
1004                 String aName = lcl_GetNumGroupName( fLoop, aGroupInfo, bHasNonInteger, cDecSeparator, pFormatter );
1005                 // create a numerical entry to ensure proper sorting
1006                 // (in FillMemberResults this needs special handling)
1007 		        lcl_InsertValue<true>( nSourceDim,  pCache,  maMemberEntries, aName, fLoop );
1008                 ++nLoopCount;
1009                 fLoop = aGroupInfo.Start + nLoopCount * aGroupInfo.Step;
1010                 bFirstGroup = false;
1011 
1012                 // ScDPItemData values are compared with approxEqual
1013             }
1014 
1015             String aFirstName = lcl_GetSpecialNumGroupName( aGroupInfo.Start, true, cDecSeparator, aGroupInfo.DateValues, pFormatter );
1016             lcl_InsertValue<true>( nSourceDim,  pCache,  maMemberEntries, aFirstName,  aGroupInfo.Start - aGroupInfo.Step );
1017 
1018             String aLastName = lcl_GetSpecialNumGroupName( aGroupInfo.End, false, cDecSeparator, aGroupInfo.DateValues, pFormatter );
1019             lcl_InsertValue<true>( nSourceDim,  pCache,  maMemberEntries, aLastName,  aGroupInfo.End + aGroupInfo.Step );
1020         }
1021     }
1022     return maMemberEntries;
1023 }
1024 
1025 ScDPGroupTableData::ScDPGroupTableData( const shared_ptr<ScDPTableData>& pSource, ScDocument* pDocument ) :
1026     ScDPTableData(pDocument, pSource->GetCacheId() ),
1027     pSourceData( pSource ),
1028     pDoc( pDocument )
1029 {
1030     DBG_ASSERT( pSource, "ScDPGroupTableData: pSource can't be NULL" );
1031 
1032     CreateCacheTable();
1033     nSourceCount = pSource->GetColumnCount();               // real columns, excluding data layout
1034     pNumGroups = new ScDPNumGroupDimension[nSourceCount];
1035 }
1036 
1037 ScDPGroupTableData::~ScDPGroupTableData()
1038 {
1039     delete[] pNumGroups;
1040 }
1041 
1042 void ScDPGroupTableData::AddGroupDimension( const ScDPGroupDimension& rGroup )
1043 {
1044     ScDPGroupDimension aNewGroup( rGroup );
1045     aNewGroup.SetGroupDim( GetColumnCount() );      // new dimension will be at the end
1046     aGroups.push_back( aNewGroup );
1047     aGroupNames.insert( OUString(aNewGroup.GetName()) );
1048 }
1049 
1050 void ScDPGroupTableData::SetNumGroupDimension( long nIndex, const ScDPNumGroupDimension& rGroup )
1051 {
1052     if ( nIndex < nSourceCount )
1053     {
1054         pNumGroups[nIndex] = rGroup;
1055 
1056         // automatic minimum / maximum is handled in GetNumEntries
1057     }
1058 }
1059 
1060 long ScDPGroupTableData::GetDimensionIndex( const String& rName )
1061 {
1062     for (long i=0; i<nSourceCount; i++)                         // nSourceCount excludes data layout
1063         if ( pSourceData->getDimensionName(i) == rName )        //! ignore case?
1064             return i;
1065     return -1;  // none
1066 }
1067 
1068 long ScDPGroupTableData::GetColumnCount()
1069 {
1070     return nSourceCount + aGroups.size();
1071 }
1072 
1073 bool ScDPGroupTableData::IsNumGroupDimension( long nDimension ) const
1074 {
1075     return ( nDimension < nSourceCount && pNumGroups[nDimension].GetInfo().Enable );
1076 }
1077 
1078 void ScDPGroupTableData::GetNumGroupInfo( long nDimension, ScDPNumGroupInfo& rInfo,
1079                                         bool& rNonInteger, sal_Unicode& rDecimal )
1080 {
1081     if ( nDimension < nSourceCount )
1082     {
1083         rInfo       = pNumGroups[nDimension].GetInfo();
1084         rNonInteger = pNumGroups[nDimension].HasNonInteger();
1085         rDecimal    = pNumGroups[nDimension].GetDecSeparator();
1086     }
1087 }
1088 // Wang Xu Ming - DataPilot migration
1089 long  ScDPGroupTableData::GetMembersCount( long nDim )
1090 {
1091     const std::vector< SCROW >&  members = GetColumnEntries( nDim );
1092     return members.size();
1093 }
1094 const std::vector< SCROW >& ScDPGroupTableData::GetColumnEntries( long  nColumn )
1095 {
1096     if ( nColumn >= nSourceCount )
1097     {
1098         if ( getIsDataLayoutDimension( nColumn) )     // data layout dimension?
1099             nColumn = nSourceCount;                         // index of data layout in source data
1100         else
1101         {
1102             const ScDPGroupDimension& rGroupDim = aGroups[nColumn - nSourceCount];
1103             long nSourceDim = rGroupDim.GetSourceDim();
1104             // collection is cached at pSourceData, GetColumnEntries can be called every time
1105             const  std::vector< SCROW >& rOriginal = pSourceData->GetColumnEntries( nSourceDim );
1106             return rGroupDim.GetColumnEntries( GetCacheTable(), rOriginal );
1107         }
1108     }
1109 
1110     if ( IsNumGroupDimension( nColumn ) )
1111     {
1112         // dimension number is unchanged for numerical groups
1113         const  std::vector< SCROW >& rOriginal = pSourceData->GetColumnEntries( nColumn );
1114         return pNumGroups[nColumn].GetNumEntries( (SCCOL)nColumn,  GetCacheTable().GetCache(), rOriginal );
1115     }
1116 
1117     return pSourceData->GetColumnEntries( nColumn );
1118 }
1119 
1120 const ScDPItemData* ScDPGroupTableData::GetMemberById( long nDim, long nId )
1121 {
1122     if ( nDim >= nSourceCount )
1123     {
1124 	    if ( getIsDataLayoutDimension( nDim) )
1125 		    nDim 	= nSourceCount;
1126 	    else
1127 	    {
1128 		    const ScDPGroupDimension& rGroupDim = aGroups[nDim - nSourceCount];
1129 		    nDim = rGroupDim.GetSourceDim();
1130 	    }
1131     }
1132     return pSourceData->GetMemberById( nDim, nId );
1133 }
1134 
1135 String ScDPGroupTableData::getDimensionName(long nColumn)
1136 {
1137     if ( nColumn >= nSourceCount )
1138     {
1139         if ( nColumn == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) )     // data layout dimension?
1140             nColumn = nSourceCount;                         // index of data layout in source data
1141         else
1142             return aGroups[nColumn - nSourceCount].GetName();
1143     }
1144 
1145     return pSourceData->getDimensionName( nColumn );
1146 }
1147 
1148 sal_Bool ScDPGroupTableData::getIsDataLayoutDimension(long nColumn)
1149 {
1150     // position of data layout dimension is moved from source data
1151     return ( nColumn == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) );    // data layout dimension?
1152 }
1153 
1154 sal_Bool ScDPGroupTableData::IsDateDimension(long nDim)
1155 {
1156     if ( nDim >= nSourceCount )
1157     {
1158         if ( nDim == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) )        // data layout dimension?
1159             nDim = nSourceCount;                            // index of data layout in source data
1160         else
1161             nDim = aGroups[nDim - nSourceCount].GetSourceDim();  // look at original dimension
1162     }
1163 
1164     return pSourceData->IsDateDimension( nDim );
1165 }
1166 
1167 sal_uLong ScDPGroupTableData::GetNumberFormat(long nDim)
1168 {
1169     if ( nDim >= nSourceCount )
1170     {
1171         if ( nDim == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) )        // data layout dimension?
1172             nDim = nSourceCount;                            // index of data layout in source data
1173         else
1174             nDim = aGroups[nDim - nSourceCount].GetSourceDim();  // look at original dimension
1175     }
1176 
1177     return pSourceData->GetNumberFormat( nDim );
1178 }
1179 
1180 void ScDPGroupTableData::DisposeData()
1181 {
1182     for ( ScDPGroupDimensionVec::iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ )
1183         aIter->DisposeData();
1184 
1185     for ( long i=0; i<nSourceCount; i++ )
1186         pNumGroups[i].DisposeData();
1187 
1188     pSourceData->DisposeData();
1189 }
1190 
1191 void ScDPGroupTableData::SetEmptyFlags( sal_Bool bIgnoreEmptyRows, sal_Bool bRepeatIfEmpty )
1192 {
1193     pSourceData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
1194 }
1195 
1196 bool ScDPGroupTableData::IsRepeatIfEmpty()
1197 {
1198     return pSourceData->IsRepeatIfEmpty();
1199 }
1200 
1201 void ScDPGroupTableData::CreateCacheTable()
1202 {
1203     pSourceData->CreateCacheTable();
1204 }
1205 
1206 void ScDPGroupTableData::ModifyFilterCriteria(vector<ScDPCacheTable::Criterion>& rCriteria)
1207 {
1208     typedef hash_map<long, const ScDPGroupDimension*> GroupFieldMapType;
1209     GroupFieldMapType aGroupFieldIds;
1210     {
1211         ScDPGroupDimensionVec::const_iterator itr = aGroups.begin(), itrEnd = aGroups.end();
1212         for (; itr != itrEnd; ++itr)
1213             aGroupFieldIds.insert( hash_map<long, const ScDPGroupDimension*>::value_type(itr->GetGroupDim(), &(*itr)) );
1214     }
1215 
1216     vector<ScDPCacheTable::Criterion> aNewCriteria;
1217     aNewCriteria.reserve(rCriteria.size() + aGroups.size());
1218 
1219     // Go through all the filtered field names and process them appropriately.
1220 
1221     vector<ScDPCacheTable::Criterion>::const_iterator itrEnd = rCriteria.end();
1222     GroupFieldMapType::const_iterator itrGrpEnd = aGroupFieldIds.end();
1223     for (vector<ScDPCacheTable::Criterion>::const_iterator itr = rCriteria.begin(); itr != itrEnd; ++itr)
1224     {
1225         ScDPCacheTable::SingleFilter* pFilter = dynamic_cast<ScDPCacheTable::SingleFilter*>(itr->mpFilter.get());
1226         if (!pFilter)
1227             // We expect this to be a single filter.
1228             continue;
1229 
1230         GroupFieldMapType::const_iterator itrGrp = aGroupFieldIds.find(itr->mnFieldIndex);
1231         if (itrGrp == itrGrpEnd)
1232         {
1233             if (IsNumGroupDimension(itr->mnFieldIndex))
1234             {
1235                 // internal number group field
1236                 const ScDPNumGroupDimension& rNumGrpDim = pNumGroups[itr->mnFieldIndex];
1237                 const ScDPDateGroupHelper* pDateHelper = rNumGrpDim.GetDateHelper();
1238                 if (!pDateHelper)
1239                 {
1240                     // What do we do here !?
1241                     continue;
1242                 }
1243 
1244                 ScDPCacheTable::Criterion aCri;
1245                 aCri.mnFieldIndex = itr->mnFieldIndex;
1246                 aCri.mpFilter.reset(new ScDPGroupDateFilter(
1247                     pFilter->getMatchValue(), pDateHelper->GetDatePart(),
1248                     pDoc->GetFormatTable()->GetNullDate(), &pDateHelper->GetNumInfo()));
1249 
1250                 aNewCriteria.push_back(aCri);
1251             }
1252             else
1253             {
1254                 // This is a regular source field.
1255                 aNewCriteria.push_back(*itr);
1256             }
1257         }
1258         else
1259         {
1260             // This is an ordinary group field or external number group field.
1261 
1262             const ScDPGroupDimension* pGrpDim = itrGrp->second;
1263             long nSrcDim = pGrpDim->GetSourceDim();
1264             const ScDPDateGroupHelper* pDateHelper = pGrpDim->GetDateHelper();
1265 
1266             if (pDateHelper)
1267             {
1268                 // external number group
1269                 ScDPCacheTable::Criterion aCri;
1270                 aCri.mnFieldIndex = nSrcDim;  // use the source dimension, not the group dimension.
1271                 aCri.mpFilter.reset(new ScDPGroupDateFilter(
1272                     pFilter->getMatchValue(), pDateHelper->GetDatePart(),
1273                     pDoc->GetFormatTable()->GetNullDate(), &pDateHelper->GetNumInfo()));
1274 
1275                 aNewCriteria.push_back(aCri);
1276             }
1277             else
1278             {
1279                 // normal group
1280 
1281                 // Note that each group dimension may have multiple group names!
1282                 size_t nGroupItemCount = pGrpDim->GetItemCount();
1283                 for (size_t i = 0; i < nGroupItemCount; ++i)
1284                 {
1285                     const ScDPGroupItem* pGrpItem = pGrpDim->GetGroupByIndex(i);
1286 					// Wang Xu Ming -- 2009-6-9
1287 					// DataPilot Migration
1288 					ScDPItemData aName( pFilter->getMatchString(),pFilter->getMatchValue(),pFilter->hasValue()) ;
1289 					/*aName.aString   = pFilter->getMatchString();
1290 					aName.fValue    = pFilter->getMatchValue();
1291 					aName.bHasValue = pFilter->hasValue();*/
1292 					// End Comments
1293 									   if (!pGrpItem || !pGrpItem->GetName().IsCaseInsEqual(aName))
1294                         continue;
1295 
1296                     ScDPCacheTable::Criterion aCri;
1297                     aCri.mnFieldIndex = nSrcDim;
1298                     aCri.mpFilter.reset(new ScDPCacheTable::GroupFilter());
1299                     ScDPCacheTable::GroupFilter* pGrpFilter =
1300                         static_cast<ScDPCacheTable::GroupFilter*>(aCri.mpFilter.get());
1301 
1302                     pGrpItem->FillGroupFilter(*pGrpFilter);
1303                     aNewCriteria.push_back(aCri);
1304                 }
1305             }
1306         }
1307     }
1308     rCriteria.swap(aNewCriteria);
1309 }
1310 
1311 void ScDPGroupTableData::FilterCacheTable(const vector<ScDPCacheTable::Criterion>& rCriteria, const hash_set<sal_Int32>& rCatDims)
1312 {
1313     vector<ScDPCacheTable::Criterion> aNewCriteria(rCriteria);
1314     ModifyFilterCriteria(aNewCriteria);
1315     pSourceData->FilterCacheTable(aNewCriteria, rCatDims);
1316 }
1317 
1318 void ScDPGroupTableData::GetDrillDownData(const vector<ScDPCacheTable::Criterion>& rCriteria, const hash_set<sal_Int32>& rCatDims, Sequence< Sequence<Any> >& rData)
1319 {
1320     vector<ScDPCacheTable::Criterion> aNewCriteria(rCriteria);
1321     ModifyFilterCriteria(aNewCriteria);
1322     pSourceData->GetDrillDownData(aNewCriteria, rCatDims, rData);
1323 }
1324 
1325 void ScDPGroupTableData::CalcResults(CalcInfo& rInfo, bool bAutoShow)
1326 {
1327     // #i111435# Inside FillRowDataFromCacheTable/GetItemData, virtual methods
1328     // getIsDataLayoutDimension and GetSourceDim are used, so it has to be called
1329     // with original rInfo, containing dimension indexes of the grouped data.
1330 
1331     const ScDPCacheTable& rCacheTable = pSourceData->GetCacheTable();
1332     sal_Int32 nRowSize = rCacheTable.getRowSize();
1333     for (sal_Int32 nRow = 0; nRow < nRowSize; ++nRow)
1334     {
1335         if (!rCacheTable.isRowActive(nRow))
1336             continue;
1337 
1338         CalcRowData aData;
1339         FillRowDataFromCacheTable(nRow, rCacheTable, rInfo, aData);
1340 
1341         if ( !rInfo.aColLevelDims.empty() )
1342             FillGroupValues(&aData.aColData[0], rInfo.aColLevelDims.size(), &rInfo.aColLevelDims[0]);
1343         if ( !rInfo.aRowLevelDims.empty() )
1344             FillGroupValues(&aData.aRowData[0], rInfo.aRowLevelDims.size(), &rInfo.aRowLevelDims[0]);
1345         if ( !rInfo.aPageDims.empty() )
1346             FillGroupValues(&aData.aPageData[0], rInfo.aPageDims.size(), &rInfo.aPageDims[0]);
1347 
1348         ProcessRowData(rInfo, aData, bAutoShow);
1349     }
1350 }
1351 
1352 const ScDPCacheTable& ScDPGroupTableData::GetCacheTable() const
1353 {
1354     return pSourceData->GetCacheTable();
1355 }
1356 
1357 void ScDPGroupTableData::FillGroupValues( /*ScDPItemData* pItemData*/ SCROW* pItemDataIndex, long nCount, const long* pDims )
1358 {
1359     long nGroupedColumns = aGroups.size();
1360 
1361     ScDPTableDataCache* pCache = GetCacheTable().GetCache();
1362     for (long nDim=0; nDim<nCount; nDim++)
1363     {
1364         const ScDPDateGroupHelper* pDateHelper = NULL;
1365 
1366         long nColumn = pDims[nDim];
1367         long nSourceDim = nColumn;
1368         if ( nColumn >= nSourceCount && nColumn < nSourceCount + nGroupedColumns )
1369         {
1370             const ScDPGroupDimension& rGroupDim = aGroups[nColumn - nSourceCount];
1371             nSourceDim= rGroupDim.GetSourceDim();
1372             pDateHelper = rGroupDim.GetDateHelper();
1373             if ( !pDateHelper )                         // date is handled below
1374             {
1375        		  const ScDPGroupItem* pGroupItem = rGroupDim.GetGroupForData( *GetMemberById( nSourceDim, pItemDataIndex[nDim] ));
1376               if ( pGroupItem )
1377 		        	  pItemDataIndex[nDim] = pCache->GetAdditionalItemID(  pGroupItem->GetName() );
1378             }
1379         }
1380         else if ( IsNumGroupDimension( nColumn ) )
1381         {
1382             pDateHelper = pNumGroups[nColumn].GetDateHelper();
1383             if ( !pDateHelper )                         // date is handled below
1384             {
1385             	 const ScDPItemData* pData = pCache->GetItemDataById( (SCCOL)nSourceDim, pItemDataIndex[nDim]);
1386                 if ( pData ->IsValue() )
1387                 {
1388                     ScDPNumGroupInfo aNumInfo;
1389                     bool bHasNonInteger = false;
1390                     sal_Unicode cDecSeparator = 0;
1391                     GetNumGroupInfo( nColumn, aNumInfo, bHasNonInteger, cDecSeparator );
1392                     double fGroupValue;
1393                     String aGroupName = lcl_GetNumGroupForValue( pData->GetValue(),
1394                              aNumInfo, bHasNonInteger, cDecSeparator, fGroupValue, pDoc );
1395                     ScDPItemData  aItemData ( aGroupName, fGroupValue, sal_True ) ;
1396                     pItemDataIndex[nDim] = pCache->GetAdditionalItemID( aItemData  );
1397                 }
1398                 // else (textual) keep original value
1399             }
1400         }
1401 
1402         if ( pDateHelper )
1403         {
1404             const ScDPItemData* pData  = GetCacheTable().GetCache()->GetItemDataById( (SCCOL)nSourceDim, pItemDataIndex[nDim]);
1405               if ( pData ->IsValue() )
1406             {
1407                 sal_Int32 nPartValue = lcl_GetDatePartValue(
1408                     pData->GetValue(), pDateHelper->GetDatePart(), pDoc->GetFormatTable(),
1409                     &pDateHelper->GetNumInfo() );
1410 // Wang Xu Ming -- 2009-9-7
1411 // DataPilot Migration - Cache&&Performance
1412 				//String aName = lcl_GetDateGroupName( pDateHelper, nPartValue, pDoc->GetFormatTable() );
1413 				ScDPItemData  aItemData( pDateHelper->GetDatePart(), String(), nPartValue, ScDPItemData::MK_DATA|ScDPItemData::MK_VAL|ScDPItemData::MK_DATEPART );
1414                 pItemDataIndex[nDim] = GetCacheTable().GetCache()->GetAdditionalItemID( aItemData );
1415 // End Comments
1416             }
1417         }
1418 	}
1419 }
1420 
1421 sal_Bool ScDPGroupTableData::IsBaseForGroup(long nDim) const
1422 {
1423     for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ )
1424     {
1425         const ScDPGroupDimension& rDim = *aIter;
1426         if ( rDim.GetSourceDim() == nDim )
1427             return sal_True;
1428     }
1429 
1430     return sal_False;
1431 }
1432 
1433 long ScDPGroupTableData::GetGroupBase(long nGroupDim) const
1434 {
1435     for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ )
1436     {
1437         const ScDPGroupDimension& rDim = *aIter;
1438         if ( rDim.GetGroupDim() == nGroupDim )
1439             return rDim.GetSourceDim();
1440     }
1441 
1442     return -1;      // none
1443 }
1444 
1445 sal_Bool ScDPGroupTableData::IsNumOrDateGroup(long nDimension) const
1446 {
1447     // Virtual method from ScDPTableData, used in result data to force text labels.
1448 
1449     if ( nDimension < nSourceCount )
1450     {
1451         return pNumGroups[nDimension].GetInfo().Enable ||
1452                pNumGroups[nDimension].GetDateHelper();
1453     }
1454 
1455     for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ )
1456     {
1457         const ScDPGroupDimension& rDim = *aIter;
1458         if ( rDim.GetGroupDim() == nDimension )
1459             return ( rDim.GetDateHelper() != NULL );
1460     }
1461 
1462     return sal_False;
1463 }
1464 
1465 sal_Bool ScDPGroupTableData::IsInGroup( const ScDPItemData& rGroupData, long nGroupIndex,
1466                                     const ScDPItemData& rBaseData, long nBaseIndex ) const
1467 {
1468     for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ )
1469     {
1470         const ScDPGroupDimension& rDim = *aIter;
1471         if ( rDim.GetGroupDim() == nGroupIndex && rDim.GetSourceDim() == nBaseIndex )
1472         {
1473             const ScDPDateGroupHelper* pGroupDateHelper = rDim.GetDateHelper();
1474             if ( pGroupDateHelper )
1475             {
1476                 //! transform rBaseData (innermost date part)
1477                 //! -> always do "HasCommonElement" style comparison
1478                 //! (only Quarter, Month, Day affected)
1479 
1480                 const ScDPDateGroupHelper* pBaseDateHelper = NULL;
1481                 if ( nBaseIndex < nSourceCount )
1482                     pBaseDateHelper = pNumGroups[nBaseIndex].GetDateHelper();
1483 
1484                 // If there's a date group dimension, the base dimension must have
1485                 // date group information, too.
1486                 if ( !pBaseDateHelper )
1487                 {
1488                     DBG_ERROR( "mix of date and non-date groups" );
1489                     return sal_True;
1490                 }
1491 
1492                 sal_Int32 nGroupPart = pGroupDateHelper->GetDatePart();
1493                 sal_Int32 nBasePart = pBaseDateHelper->GetDatePart();
1494                 return lcl_DateContained( nGroupPart, rGroupData, nBasePart, rBaseData );
1495             }
1496             else
1497             {
1498                 // If the item is in a group, only that group is valid.
1499                 // If the item is not in any group, its own name is valid.
1500 
1501                 const ScDPGroupItem* pGroup = rDim.GetGroupForData( rBaseData );
1502                 return pGroup ? pGroup->GetName().IsCaseInsEqual( rGroupData ) :
1503                                 rGroupData.IsCaseInsEqual( rBaseData );
1504             }
1505         }
1506     }
1507 
1508     DBG_ERROR("IsInGroup: no group dimension found");
1509     return sal_True;
1510 }
1511 
1512 sal_Bool ScDPGroupTableData::HasCommonElement( const ScDPItemData& rFirstData, long nFirstIndex,
1513                                          const ScDPItemData& rSecondData, long nSecondIndex ) const
1514 {
1515     const ScDPGroupDimension* pFirstDim = NULL;
1516     const ScDPGroupDimension* pSecondDim = NULL;
1517     for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ )
1518     {
1519         const ScDPGroupDimension* pDim = &(*aIter);
1520         if ( pDim->GetGroupDim() == nFirstIndex )
1521             pFirstDim = pDim;
1522         else if ( pDim->GetGroupDim() == nSecondIndex )
1523             pSecondDim = pDim;
1524     }
1525     if ( pFirstDim && pSecondDim )
1526     {
1527         const ScDPDateGroupHelper* pFirstDateHelper = pFirstDim->GetDateHelper();
1528         const ScDPDateGroupHelper* pSecondDateHelper = pSecondDim->GetDateHelper();
1529         if ( pFirstDateHelper || pSecondDateHelper )
1530         {
1531             // If one is a date group dimension, the other one must be, too.
1532             if ( !pFirstDateHelper || !pSecondDateHelper )
1533             {
1534                 DBG_ERROR( "mix of date and non-date groups" );
1535                 return sal_True;
1536             }
1537 
1538             sal_Int32 nFirstPart = pFirstDateHelper->GetDatePart();
1539             sal_Int32 nSecondPart = pSecondDateHelper->GetDatePart();
1540             return lcl_DateContained( nFirstPart, rFirstData, nSecondPart, rSecondData );
1541         }
1542 
1543         const ScDPGroupItem* pFirstItem = pFirstDim->GetGroupForName( rFirstData );
1544         const ScDPGroupItem* pSecondItem = pSecondDim->GetGroupForName( rSecondData );
1545         if ( pFirstItem && pSecondItem )
1546         {
1547             // two existing groups -> sal_True if they have a common element
1548             return pFirstItem->HasCommonElement( *pSecondItem );
1549         }
1550         else if ( pFirstItem )
1551         {
1552             // "automatic" group contains only its own name
1553             return pFirstItem->HasElement( rSecondData );
1554         }
1555         else if ( pSecondItem )
1556         {
1557             // "automatic" group contains only its own name
1558             return pSecondItem->HasElement( rFirstData );
1559         }
1560         else
1561         {
1562             // no groups -> sal_True if equal
1563             return rFirstData.IsCaseInsEqual( rSecondData );
1564         }
1565     }
1566 
1567     DBG_ERROR("HasCommonElement: no group dimension found");
1568     return sal_True;
1569 }
1570 
1571 long ScDPGroupTableData::GetSourceDim( long nDim )
1572 {
1573 	if ( getIsDataLayoutDimension( nDim ) )
1574 		return nSourceCount;
1575 	if (  nDim >= nSourceCount && nDim < nSourceCount +(long) aGroups.size()  )
1576 	{
1577 	     const ScDPGroupDimension& rGroupDim = aGroups[nDim - nSourceCount];
1578             return  rGroupDim.GetSourceDim();
1579 	}
1580 	return nDim;
1581 }
1582  long ScDPGroupTableData::Compare( long nDim, long nDataId1, long nDataId2)
1583 {
1584 	if ( getIsDataLayoutDimension(nDim) )
1585 		return 0;
1586 	return ScDPItemData::Compare( *GetMemberById(nDim,  nDataId1),*GetMemberById(nDim,  nDataId2) );
1587 }
1588 // -----------------------------------------------------------------------
1589 
1590