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 { 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 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<> 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<> 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> 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> 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 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 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 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 220 inline bool IsInteger( double fValue ) 221 { 222 return rtl::math::approxEqual( fValue, rtl::math::approxFloor(fValue) ); 223 } 224 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 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 } 307 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 404 ScDPDateGroupHelper::ScDPDateGroupHelper( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart ) : 405 aNumInfo( rInfo ), 406 nDatePart( nPart ) 407 { 408 } 409 410 ScDPDateGroupHelper::~ScDPDateGroupHelper() 411 { 412 } 413 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 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 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 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 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 607 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 686 ScDPGroupItem::ScDPGroupItem( const ScDPItemData& rName ) : 687 aGroupName( rName ) 688 { 689 } 690 691 ScDPGroupItem::~ScDPGroupItem() 692 { 693 } 694 695 void ScDPGroupItem::AddElement( const ScDPItemData& rName ) 696 { 697 aElements.push_back( rName ); 698 } 699 700 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 709 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 718 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 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 739 ScDPGroupDimension::~ScDPGroupDimension() 740 { 741 delete pDateHelper; 742 maMemberEntries.clear(); 743 } 744 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 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 772 void ScDPGroupDimension::MakeDateHelper( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart ) 773 { 774 delete pDateHelper; 775 pDateHelper = new ScDPDateGroupHelper( rInfo, nPart ); 776 } 777 778 void ScDPGroupDimension::AddItem( const ScDPGroupItem& rItem ) 779 { 780 aItems.push_back( rItem ); 781 } 782 783 void ScDPGroupDimension::SetGroupDim( long nDim ) 784 { 785 nGroupDim = nDim; 786 } 787 // Wang Xu Ming -- 2009-9-2 788 // DataPilot Migration - Cache&&Performance 789 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 823 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 832 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 841 const ScDPGroupItem* ScDPGroupDimension::GetGroupByIndex( size_t nIndex ) const 842 { 843 if (nIndex >= aItems.size()) 844 return NULL; 845 846 return &aItems[nIndex]; 847 } 848 849 void ScDPGroupDimension::DisposeData() 850 { 851 maMemberEntries.clear(); 852 } 853 854 // ----------------------------------------------------------------------- 855 856 ScDPNumGroupDimension::ScDPNumGroupDimension() : 857 pDateHelper( NULL ), 858 bHasNonInteger( false ), 859 cDecSeparator( 0 ) 860 { 861 } 862 863 ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupInfo& rInfo ) : 864 aGroupInfo( rInfo ), 865 pDateHelper( NULL ), 866 bHasNonInteger( false ), 867 cDecSeparator( 0 ) 868 { 869 } 870 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 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 895 void ScDPNumGroupDimension::DisposeData() 896 { 897 bHasNonInteger = false; 898 maMemberEntries.clear(); 899 } 900 901 ScDPNumGroupDimension::~ScDPNumGroupDimension() 902 { 903 delete pDateHelper; 904 } 905 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 914 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 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 1033 ScDPGroupTableData::~ScDPGroupTableData() 1034 { 1035 delete[] pNumGroups; 1036 } 1037 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 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 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 1064 long ScDPGroupTableData::GetColumnCount() 1065 { 1066 return nSourceCount + aGroups.size(); 1067 } 1068 1069 bool ScDPGroupTableData::IsNumGroupDimension( long nDimension ) const 1070 { 1071 return ( nDimension < nSourceCount && pNumGroups[nDimension].GetInfo().Enable ); 1072 } 1073 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 1085 long ScDPGroupTableData::GetMembersCount( long nDim ) 1086 { 1087 const std::vector< SCROW >& members = GetColumnEntries( nDim ); 1088 return members.size(); 1089 } 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 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 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 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 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 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 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 1187 void ScDPGroupTableData::SetEmptyFlags( sal_Bool bIgnoreEmptyRows, sal_Bool bRepeatIfEmpty ) 1188 { 1189 pSourceData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty ); 1190 } 1191 1192 bool ScDPGroupTableData::IsRepeatIfEmpty() 1193 { 1194 return pSourceData->IsRepeatIfEmpty(); 1195 } 1196 1197 void ScDPGroupTableData::CreateCacheTable() 1198 { 1199 pSourceData->CreateCacheTable(); 1200 } 1201 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 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 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 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 1348 const ScDPCacheTable& ScDPGroupTableData::GetCacheTable() const 1349 { 1350 return pSourceData->GetCacheTable(); 1351 } 1352 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 1417 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 1429 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 1441 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 1461 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 1508 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 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 } 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