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