LCOV - code coverage report
Current view: top level - sc/source/core/data - dputil.cxx (source / functions) Hit Total Coverage
Test: commit 0e63ca4fde4e446f346e35849c756a30ca294aab Lines: 120 178 67.4 %
Date: 2014-04-11 Functions: 14 16 87.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  */
       9             : 
      10             : #include "dputil.hxx"
      11             : #include "dpitemdata.hxx"
      12             : #include "dpnumgroupinfo.hxx"
      13             : #include "globalnames.hxx"
      14             : #include "globstr.hrc"
      15             : 
      16             : #include "comphelper/string.hxx"
      17             : #include "unotools/localedatawrapper.hxx"
      18             : #include "unotools/calendarwrapper.hxx"
      19             : #include "svl/zforlist.hxx"
      20             : #include "rtl/math.hxx"
      21             : 
      22             : #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
      23             : #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
      24             : 
      25             : using namespace com::sun::star;
      26             : 
      27             : namespace {
      28             : 
      29             : const sal_uInt16 SC_DP_LEAPYEAR = 1648;     // arbitrary leap year for date calculations
      30             : 
      31           0 : OUString getTwoDigitString(sal_Int32 nValue)
      32             : {
      33           0 :     OUString aRet = OUString::number( nValue );
      34           0 :     if ( aRet.getLength() < 2 )
      35           0 :         aRet = "0" + aRet;
      36           0 :     return aRet;
      37             : }
      38             : 
      39          10 : void appendDateStr(OUStringBuffer& rBuffer, double fValue, SvNumberFormatter* pFormatter)
      40             : {
      41          10 :     sal_uLong nFormat = pFormatter->GetStandardFormat( NUMBERFORMAT_DATE, ScGlobal::eLnge );
      42          10 :     OUString aString;
      43          10 :     pFormatter->GetInputLineString(fValue, nFormat, aString);
      44          10 :     rBuffer.append(aString);
      45          10 : }
      46             : 
      47          10 : OUString getSpecialDateName(double fValue, bool bFirst, SvNumberFormatter* pFormatter)
      48             : {
      49          10 :     OUStringBuffer aBuffer;
      50          10 :     aBuffer.append( bFirst ? '<' : '>' );
      51          10 :     appendDateStr(aBuffer, fValue, pFormatter);
      52          10 :     return aBuffer.makeStringAndClear();
      53             : }
      54             : 
      55             : }
      56             : 
      57         131 : bool ScDPUtil::isDuplicateDimension(const OUString& rName)
      58             : {
      59         131 :     return rName.endsWith("*");
      60             : }
      61             : 
      62         792 : OUString ScDPUtil::getSourceDimensionName(const OUString& rName)
      63             : {
      64         792 :     return comphelper::string::stripEnd(rName, '*');
      65             : }
      66             : 
      67           4 : sal_uInt8 ScDPUtil::getDuplicateIndex(const OUString& rName)
      68             : {
      69             :     // Count all trailing '*'s.
      70             : 
      71           4 :     sal_Int32 n = rName.getLength();
      72           4 :     if (!n)
      73           0 :         return 0;
      74             : 
      75           4 :     sal_uInt8 nDupCount = 0;
      76           4 :     const sal_Unicode* p = rName.getStr();
      77           4 :     const sal_Unicode* pStart = p;
      78           4 :     p += n-1; // Set it to the last char.
      79           5 :     for (; p != pStart; --p, ++nDupCount)
      80             :     {
      81           5 :         if (*p != '*')
      82           4 :             break;
      83             :     }
      84             : 
      85           4 :     return nDupCount;
      86             : }
      87             : 
      88           2 : OUString ScDPUtil::createDuplicateDimensionName(const OUString& rOriginal, size_t nDupCount)
      89             : {
      90           2 :     if (!nDupCount)
      91           0 :         return rOriginal;
      92             : 
      93           2 :     OUStringBuffer aBuf(rOriginal);
      94           4 :     for (size_t i = 0; i < nDupCount; ++i)
      95           2 :         aBuf.append('*');
      96             : 
      97           2 :     return aBuf.makeStringAndClear();
      98             : }
      99             : 
     100         796 : OUString ScDPUtil::getDateGroupName(
     101             :         sal_Int32 nDatePart, sal_Int32 nValue, SvNumberFormatter* pFormatter,
     102             :         double fStart, double fEnd)
     103             : {
     104         796 :     if (nValue == ScDPItemData::DateFirst)
     105           5 :         return getSpecialDateName(fStart, true, pFormatter);
     106         791 :     if (nValue == ScDPItemData::DateLast)
     107           5 :         return getSpecialDateName(fEnd, false, pFormatter);
     108             : 
     109         786 :     switch ( nDatePart )
     110             :     {
     111             :         case sheet::DataPilotFieldGroupBy::YEARS:
     112          10 :             return OUString::number(nValue);
     113             :         case sheet::DataPilotFieldGroupBy::QUARTERS:
     114          12 :             return ScGlobal::pLocaleData->getQuarterAbbreviation(sal_Int16(nValue-1));    // nValue is 1-based
     115             :         case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS:
     116             :             return ScGlobal::GetCalendar()->getDisplayName(
     117          32 :                         i18n::CalendarDisplayIndex::MONTH, sal_Int16(nValue-1), 0);    // 0-based, get short name
     118             :         case sheet::DataPilotFieldGroupBy::DAYS:
     119             :         {
     120         732 :             Date aDate(1, 1, SC_DP_LEAPYEAR);
     121         732 :             aDate += (nValue - 1);            // nValue is 1-based
     122         732 :             Date aNullDate = *pFormatter->GetNullDate();
     123         732 :             long nDays = aDate - aNullDate;
     124             : 
     125         732 :             sal_uLong nFormat = pFormatter->GetFormatIndex(NF_DATE_SYS_DDMMM, ScGlobal::eLnge);
     126             :             Color* pColor;
     127         732 :             OUString aStr;
     128         732 :             pFormatter->GetOutputString(nDays, nFormat, aStr, &pColor);
     129         732 :             return aStr;
     130             :         }
     131             :         case sheet::DataPilotFieldGroupBy::HOURS:
     132             :         {
     133             :             //! allow am/pm format?
     134           0 :             return getTwoDigitString(nValue);
     135             :         }
     136             :         break;
     137             :         case sheet::DataPilotFieldGroupBy::MINUTES:
     138             :         case sheet::DataPilotFieldGroupBy::SECONDS:
     139             :         {
     140           0 :             OUStringBuffer aBuf(ScGlobal::pLocaleData->getTimeSep());
     141           0 :             aBuf.append(getTwoDigitString(nValue));
     142           0 :             return aBuf.makeStringAndClear();
     143             :         }
     144             :         break;
     145             :         default:
     146             :             OSL_FAIL("invalid date part");
     147             :     }
     148             : 
     149           0 :     return OUString("FIXME: unhandled value");
     150             : }
     151             : 
     152          18 : double ScDPUtil::getNumGroupStartValue(double fValue, const ScDPNumGroupInfo& rInfo)
     153             : {
     154          18 :     if (fValue < rInfo.mfStart && !rtl::math::approxEqual(fValue, rInfo.mfStart))
     155             :     {
     156           6 :         rtl::math::setInf(&fValue, true);
     157           6 :         return fValue;
     158             :     }
     159             : 
     160          12 :     if (fValue > rInfo.mfEnd && !rtl::math::approxEqual(fValue, rInfo.mfEnd))
     161             :     {
     162           6 :         rtl::math::setInf(&fValue, false);
     163           6 :         return fValue;
     164             :     }
     165             : 
     166           6 :     double fDiff = fValue - rInfo.mfStart;
     167           6 :     double fDiv = rtl::math::approxFloor( fDiff / rInfo.mfStep );
     168           6 :     double fGroupStart = rInfo.mfStart + fDiv * rInfo.mfStep;
     169             : 
     170           6 :     if (rtl::math::approxEqual(fGroupStart, rInfo.mfEnd) &&
     171           0 :         !rtl::math::approxEqual(fGroupStart, rInfo.mfStart))
     172             :     {
     173           0 :         if (!rInfo.mbDateValues)
     174             :         {
     175             :             // A group that would consist only of the end value is not
     176             :             // created, instead the value is included in the last group
     177             :             // before. So the previous group is used if the calculated group
     178             :             // start value is the selected end value.
     179             : 
     180           0 :             fDiv -= 1.0;
     181           0 :             return rInfo.mfStart + fDiv * rInfo.mfStep;
     182             :         }
     183             : 
     184             :         // For date values, the end value is instead treated as above the
     185             :         // limit if it would be a group of its own.
     186             : 
     187           0 :         return rInfo.mfEnd + rInfo.mfStep;
     188             :     }
     189             : 
     190           6 :     return fGroupStart;
     191             : }
     192             : 
     193             : namespace {
     194             : 
     195           0 : void lcl_AppendDateStr( OUStringBuffer& rBuffer, double fValue, SvNumberFormatter* pFormatter )
     196             : {
     197           0 :     sal_uLong nFormat = pFormatter->GetStandardFormat( NUMBERFORMAT_DATE, ScGlobal::eLnge );
     198           0 :     OUString aString;
     199           0 :     pFormatter->GetInputLineString( fValue, nFormat, aString );
     200           0 :     rBuffer.append( aString );
     201           0 : }
     202             : 
     203           4 : OUString lcl_GetSpecialNumGroupName( double fValue, bool bFirst, sal_Unicode cDecSeparator,
     204             :     bool bDateValues, SvNumberFormatter* pFormatter )
     205             : {
     206             :     OSL_ENSURE( cDecSeparator != 0, "cDecSeparator not initialized" );
     207             : 
     208           4 :     OUStringBuffer aBuffer;
     209           4 :     aBuffer.append( bFirst ? '<' : '>' );
     210           4 :     if ( bDateValues )
     211           0 :         lcl_AppendDateStr( aBuffer, fValue, pFormatter );
     212             :     else
     213             :         rtl::math::doubleToUStringBuffer( aBuffer, fValue, rtl_math_StringFormat_Automatic,
     214           4 :         rtl_math_DecimalPlaces_Max, cDecSeparator, true );
     215           4 :     return aBuffer.makeStringAndClear();
     216             : }
     217             : 
     218           6 : OUString lcl_GetNumGroupName(
     219             :     double fStartValue, const ScDPNumGroupInfo& rInfo, sal_Unicode cDecSep,
     220             :     SvNumberFormatter* pFormatter)
     221             : {
     222             :     OSL_ENSURE( cDecSep != 0, "cDecSeparator not initialized" );
     223             : 
     224           6 :     double fStep = rInfo.mfStep;
     225           6 :     double fEndValue = fStartValue + fStep;
     226           6 :     if (rInfo.mbIntegerOnly && (rInfo.mbDateValues || !rtl::math::approxEqual(fEndValue, rInfo.mfEnd)))
     227             :     {
     228             :         //  The second number of the group label is
     229             :         //  (first number + size - 1) if there are only integer numbers,
     230             :         //  (first number + size) if any non-integer numbers are involved.
     231             :         //  Exception: The last group (containing the end value) is always
     232             :         //  shown as including the end value (but not for dates).
     233             : 
     234           4 :         fEndValue -= 1.0;
     235             :     }
     236             : 
     237           6 :     if ( fEndValue > rInfo.mfEnd && !rInfo.mbAutoEnd )
     238             :     {
     239             :         // limit the last group to the end value
     240             : 
     241           0 :         fEndValue = rInfo.mfEnd;
     242             :     }
     243             : 
     244           6 :     OUStringBuffer aBuffer;
     245           6 :     if ( rInfo.mbDateValues )
     246             :     {
     247           0 :         lcl_AppendDateStr( aBuffer, fStartValue, pFormatter );
     248           0 :         aBuffer.appendAscii( " - " );   // with spaces
     249           0 :         lcl_AppendDateStr( aBuffer, fEndValue, pFormatter );
     250             :     }
     251             :     else
     252             :     {
     253             :         rtl::math::doubleToUStringBuffer( aBuffer, fStartValue, rtl_math_StringFormat_Automatic,
     254           6 :             rtl_math_DecimalPlaces_Max, cDecSep, true );
     255           6 :         aBuffer.append( '-' );
     256             :         rtl::math::doubleToUStringBuffer( aBuffer, fEndValue, rtl_math_StringFormat_Automatic,
     257           6 :             rtl_math_DecimalPlaces_Max, cDecSep, true );
     258             :     }
     259             : 
     260           6 :     return aBuffer.makeStringAndClear();
     261             : }
     262             : 
     263             : }
     264             : 
     265          10 : OUString ScDPUtil::getNumGroupName(
     266             :     double fValue, const ScDPNumGroupInfo& rInfo, sal_Unicode cDecSep, SvNumberFormatter* pFormatter)
     267             : {
     268          10 :     if ( fValue < rInfo.mfStart && !rtl::math::approxEqual( fValue, rInfo.mfStart ) )
     269           2 :         return lcl_GetSpecialNumGroupName( rInfo.mfStart, true, cDecSep, rInfo.mbDateValues, pFormatter );
     270             : 
     271           8 :     if ( fValue > rInfo.mfEnd && !rtl::math::approxEqual( fValue, rInfo.mfEnd ) )
     272           2 :         return lcl_GetSpecialNumGroupName( rInfo.mfEnd, false, cDecSep, rInfo.mbDateValues, pFormatter );
     273             : 
     274           6 :     double fDiff = fValue - rInfo.mfStart;
     275           6 :     double fDiv = rtl::math::approxFloor( fDiff / rInfo.mfStep );
     276           6 :     double fGroupStart = rInfo.mfStart + fDiv * rInfo.mfStep;
     277             : 
     278           6 :     if ( rtl::math::approxEqual( fGroupStart, rInfo.mfEnd ) &&
     279           0 :         !rtl::math::approxEqual( fGroupStart, rInfo.mfStart ) )
     280             :     {
     281           0 :         if (rInfo.mbDateValues)
     282             :         {
     283             :             //  For date values, the end value is instead treated as above the limit
     284             :             //  if it would be a group of its own.
     285           0 :             return lcl_GetSpecialNumGroupName( rInfo.mfEnd, false, cDecSep, rInfo.mbDateValues, pFormatter );
     286             :         }
     287             :     }
     288             : 
     289           6 :     return lcl_GetNumGroupName(fGroupStart, rInfo, cDecSep, pFormatter);
     290             : }
     291             : 
     292          54 : sal_Int32 ScDPUtil::getDatePartValue(
     293             :     double fValue, const ScDPNumGroupInfo* pInfo, sal_Int32 nDatePart,
     294             :     SvNumberFormatter* pFormatter)
     295             : {
     296             :     // Start and end are inclusive
     297             :     // (End date without a time value is included, with a time value it's not)
     298             : 
     299          54 :     if (pInfo)
     300             :     {
     301          48 :         if (fValue < pInfo->mfStart && !rtl::math::approxEqual(fValue, pInfo->mfStart))
     302           0 :             return ScDPItemData::DateFirst;
     303          48 :         if (fValue > pInfo->mfEnd && !rtl::math::approxEqual(fValue, pInfo->mfEnd))
     304           0 :             return ScDPItemData::DateLast;
     305             :     }
     306             : 
     307          54 :     sal_Int32 nResult = 0;
     308             : 
     309          54 :     if (nDatePart == sheet::DataPilotFieldGroupBy::HOURS ||
     310          54 :         nDatePart == sheet::DataPilotFieldGroupBy::MINUTES ||
     311             :         nDatePart == sheet::DataPilotFieldGroupBy::SECONDS)
     312             :     {
     313             :         // handle time
     314             :         // (as in the cell functions, ScInterpreter::ScGetHour etc.: seconds are rounded)
     315             : 
     316           0 :         double fTime = fValue - rtl::math::approxFloor(fValue);
     317           0 :         long nSeconds = (long)rtl::math::approxFloor(fTime*DATE_TIME_FACTOR+0.5);
     318             : 
     319           0 :         switch (nDatePart)
     320             :         {
     321             :             case sheet::DataPilotFieldGroupBy::HOURS:
     322           0 :                 nResult = nSeconds / 3600;
     323           0 :                 break;
     324             :             case sheet::DataPilotFieldGroupBy::MINUTES:
     325           0 :                 nResult = ( nSeconds % 3600 ) / 60;
     326           0 :                 break;
     327             :             case sheet::DataPilotFieldGroupBy::SECONDS:
     328           0 :                 nResult = nSeconds % 60;
     329           0 :                 break;
     330           0 :         }
     331             :     }
     332             :     else
     333             :     {
     334          54 :         Date aDate = *(pFormatter->GetNullDate());
     335          54 :         aDate += (long)::rtl::math::approxFloor(fValue);
     336             : 
     337          54 :         switch ( nDatePart )
     338             :         {
     339             :             case com::sun::star::sheet::DataPilotFieldGroupBy::YEARS:
     340          22 :                 nResult = aDate.GetYear();
     341          22 :                 break;
     342             :             case com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS:
     343          16 :                 nResult = 1 + (aDate.GetMonth() - 1) / 3;     // 1..4
     344          16 :                 break;
     345             :             case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS:
     346          16 :                 nResult = aDate.GetMonth();     // 1..12
     347          16 :                 break;
     348             :             case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS:
     349             :                 {
     350           0 :                     Date aYearStart(1, 1, aDate.GetYear());
     351           0 :                     nResult = (aDate - aYearStart) + 1;       // Jan 01 has value 1
     352           0 :                     if (nResult >= 60 && !aDate.IsLeapYear())
     353             :                     {
     354             :                         // days are counted from 1 to 366 - if not from a leap year, adjust
     355           0 :                         ++nResult;
     356             :                     }
     357             :                 }
     358           0 :                 break;
     359             :             default:
     360             :                 OSL_FAIL("invalid date part");
     361             :         }
     362             :     }
     363             : 
     364          54 :     return nResult;
     365             : }
     366             : 
     367             : namespace {
     368             : 
     369             : sal_uInt16 nFuncStrIds[12] = {
     370             :     0,                              // SUBTOTAL_FUNC_NONE
     371             :     STR_FUN_TEXT_AVG,               // SUBTOTAL_FUNC_AVE
     372             :     STR_FUN_TEXT_COUNT,             // SUBTOTAL_FUNC_CNT
     373             :     STR_FUN_TEXT_COUNT,             // SUBTOTAL_FUNC_CNT2
     374             :     STR_FUN_TEXT_MAX,               // SUBTOTAL_FUNC_MAX
     375             :     STR_FUN_TEXT_MIN,               // SUBTOTAL_FUNC_MIN
     376             :     STR_FUN_TEXT_PRODUCT,           // SUBTOTAL_FUNC_PROD
     377             :     STR_FUN_TEXT_STDDEV,            // SUBTOTAL_FUNC_STD
     378             :     STR_FUN_TEXT_STDDEV,            // SUBTOTAL_FUNC_STDP
     379             :     STR_FUN_TEXT_SUM,               // SUBTOTAL_FUNC_SUM
     380             :     STR_FUN_TEXT_VAR,               // SUBTOTAL_FUNC_VAR
     381             :     STR_FUN_TEXT_VAR                // SUBTOTAL_FUNC_VARP
     382             : };
     383             : 
     384             : }
     385             : 
     386         130 : OUString ScDPUtil::getDisplayedMeasureName(const OUString& rName, ScSubTotalFunc eFunc)
     387             : {
     388         130 :     OUStringBuffer aRet;
     389         130 :     sal_uInt16 nId = nFuncStrIds[eFunc];
     390         130 :     if (nId)
     391             :     {
     392         119 :         aRet.append(ScGlobal::GetRscString(nId));        // function name
     393         119 :         aRet.append(" - ");
     394             :     }
     395         130 :     aRet.append(rName);                   // field name
     396             : 
     397         130 :     return aRet.makeStringAndClear();
     398             : }
     399             : 
     400         127 : ScSubTotalFunc ScDPUtil::toSubTotalFunc(com::sun::star::sheet::GeneralFunction eGenFunc)
     401             : {
     402             :     ScSubTotalFunc eSubTotal;
     403         127 :     switch (eGenFunc)
     404             :     {
     405           0 :         case sheet::GeneralFunction_NONE:       eSubTotal = SUBTOTAL_FUNC_NONE; break;
     406         117 :         case sheet::GeneralFunction_SUM:        eSubTotal = SUBTOTAL_FUNC_SUM;  break;
     407           9 :         case sheet::GeneralFunction_COUNT:      eSubTotal = SUBTOTAL_FUNC_CNT2; break;
     408           1 :         case sheet::GeneralFunction_AVERAGE:    eSubTotal = SUBTOTAL_FUNC_AVE;  break;
     409           0 :         case sheet::GeneralFunction_MAX:        eSubTotal = SUBTOTAL_FUNC_MAX;  break;
     410           0 :         case sheet::GeneralFunction_MIN:        eSubTotal = SUBTOTAL_FUNC_MIN;  break;
     411           0 :         case sheet::GeneralFunction_PRODUCT:    eSubTotal = SUBTOTAL_FUNC_PROD; break;
     412           0 :         case sheet::GeneralFunction_COUNTNUMS:  eSubTotal = SUBTOTAL_FUNC_CNT;  break;
     413           0 :         case sheet::GeneralFunction_STDEV:      eSubTotal = SUBTOTAL_FUNC_STD;  break;
     414           0 :         case sheet::GeneralFunction_STDEVP:     eSubTotal = SUBTOTAL_FUNC_STDP; break;
     415           0 :         case sheet::GeneralFunction_VAR:        eSubTotal = SUBTOTAL_FUNC_VAR;  break;
     416           0 :         case sheet::GeneralFunction_VARP:       eSubTotal = SUBTOTAL_FUNC_VARP; break;
     417             :         case sheet::GeneralFunction_AUTO:
     418             :         default:
     419           0 :             eSubTotal = SUBTOTAL_FUNC_NONE;
     420             :     }
     421         127 :     return eSubTotal;
     422             : }
     423             : 
     424             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10