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