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

Generated by: LCOV version 1.11