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