LCOV - code coverage report
Current view: top level - usr/local/src/libreoffice/sc/source/core/data - dputil.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 89 144 61.8 %
Date: 2013-07-09 Functions: 11 13 84.6 %
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             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  */
      12             : 
      13             : #include "dputil.hxx"
      14             : #include "global.hxx"
      15             : #include "dpitemdata.hxx"
      16             : #include "dpnumgroupinfo.hxx"
      17             : #include "globalnames.hxx"
      18             : 
      19             : #include "comphelper/string.hxx"
      20             : #include "unotools/localedatawrapper.hxx"
      21             : #include "unotools/calendarwrapper.hxx"
      22             : #include "svl/zforlist.hxx"
      23             : #include "rtl/math.hxx"
      24             : 
      25             : #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
      26             : #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
      27             : 
      28             : using namespace com::sun::star;
      29             : 
      30             : namespace {
      31             : 
      32             : const sal_uInt16 SC_DP_LEAPYEAR = 1648;     // arbitrary leap year for date calculations
      33             : 
      34           0 : OUString getTwoDigitString(sal_Int32 nValue)
      35             : {
      36           0 :     OUString aRet = OUString::number( nValue );
      37           0 :     if ( aRet.getLength() < 2 )
      38           0 :         aRet = "0" + aRet;
      39           0 :     return aRet;
      40             : }
      41             : 
      42           2 : void appendDateStr(OUStringBuffer& rBuffer, double fValue, SvNumberFormatter* pFormatter)
      43             : {
      44           2 :     sal_uLong nFormat = pFormatter->GetStandardFormat( NUMBERFORMAT_DATE, ScGlobal::eLnge );
      45           2 :     OUString aString;
      46           2 :     pFormatter->GetInputLineString(fValue, nFormat, aString);
      47           2 :     rBuffer.append(aString);
      48           2 : }
      49             : 
      50           2 : OUString getSpecialDateName(double fValue, bool bFirst, SvNumberFormatter* pFormatter)
      51             : {
      52           2 :     OUStringBuffer aBuffer;
      53           2 :     aBuffer.append(sal_Unicode(bFirst ? '<' : '>'));
      54           2 :     appendDateStr(aBuffer, fValue, pFormatter);
      55           2 :     return aBuffer.makeStringAndClear();
      56             : }
      57             : 
      58             : }
      59             : 
      60         117 : bool ScDPUtil::isDuplicateDimension(const OUString& rName)
      61             : {
      62         117 :     if (rName.isEmpty())
      63          18 :         return false;
      64             : 
      65          99 :     sal_Unicode cLast = rName[rName.getLength()-1];
      66          99 :     return cLast == sal_Unicode('*');
      67             : }
      68             : 
      69         701 : OUString ScDPUtil::getSourceDimensionName(const OUString& rName)
      70             : {
      71         701 :     return comphelper::string::stripEnd(rName, '*');
      72             : }
      73             : 
      74           1 : OUString ScDPUtil::createDuplicateDimensionName(const OUString& rOriginal, size_t nDupCount)
      75             : {
      76           1 :     if (!nDupCount)
      77           0 :         return rOriginal;
      78             : 
      79           1 :     OUStringBuffer aBuf(rOriginal);
      80           2 :     for (size_t i = 0; i < nDupCount; ++i)
      81           1 :         aBuf.append(sal_Unicode('*'));
      82             : 
      83           1 :     return aBuf.makeStringAndClear();
      84             : }
      85             : 
      86          23 : OUString ScDPUtil::getDateGroupName(
      87             :         sal_Int32 nDatePart, sal_Int32 nValue, SvNumberFormatter* pFormatter,
      88             :         double fStart, double fEnd)
      89             : {
      90          23 :     if (nValue == ScDPItemData::DateFirst)
      91           1 :         return getSpecialDateName(fStart, true, pFormatter);
      92          22 :     if (nValue == ScDPItemData::DateLast)
      93           1 :         return getSpecialDateName(fEnd, false, pFormatter);
      94             : 
      95          21 :     switch ( nDatePart )
      96             :     {
      97             :         case sheet::DataPilotFieldGroupBy::YEARS:
      98           5 :             return OUString::valueOf(nValue);
      99             :         case sheet::DataPilotFieldGroupBy::QUARTERS:
     100           6 :             return ScGlobal::pLocaleData->getQuarterAbbreviation(sal_Int16(nValue-1));    // nValue is 1-based
     101             :         case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS:
     102             :             return ScGlobal::GetCalendar()->getDisplayName(
     103          10 :                         i18n::CalendarDisplayIndex::MONTH, sal_Int16(nValue-1), 0);    // 0-based, get short name
     104             :         case sheet::DataPilotFieldGroupBy::DAYS:
     105             :         {
     106           0 :             Date aDate(1, 1, SC_DP_LEAPYEAR);
     107           0 :             aDate += (nValue - 1);            // nValue is 1-based
     108           0 :             Date aNullDate = *pFormatter->GetNullDate();
     109           0 :             long nDays = aDate - aNullDate;
     110             : 
     111           0 :             sal_uLong nFormat = pFormatter->GetFormatIndex(NF_DATE_SYS_DDMMM, ScGlobal::eLnge);
     112             :             Color* pColor;
     113           0 :             OUString aStr;
     114           0 :             pFormatter->GetOutputString(nDays, nFormat, aStr, &pColor);
     115           0 :             return aStr;
     116             :         }
     117             :         case sheet::DataPilotFieldGroupBy::HOURS:
     118             :         {
     119             :             //! allow am/pm format?
     120           0 :             return getTwoDigitString(nValue);
     121             :         }
     122             :         break;
     123             :         case sheet::DataPilotFieldGroupBy::MINUTES:
     124             :         case sheet::DataPilotFieldGroupBy::SECONDS:
     125             :         {
     126           0 :             OUStringBuffer aBuf(ScGlobal::pLocaleData->getTimeSep());
     127           0 :             aBuf.append(getTwoDigitString(nValue));
     128           0 :             return aBuf.makeStringAndClear();
     129             :         }
     130             :         break;
     131             :         default:
     132             :             OSL_FAIL("invalid date part");
     133             :     }
     134             : 
     135           0 :     return OUString("FIXME: unhandled value");
     136             : }
     137             : 
     138          18 : double ScDPUtil::getNumGroupStartValue(double fValue, const ScDPNumGroupInfo& rInfo)
     139             : {
     140          18 :     if (fValue < rInfo.mfStart && !rtl::math::approxEqual(fValue, rInfo.mfStart))
     141             :     {
     142           6 :         rtl::math::setInf(&fValue, true);
     143           6 :         return fValue;
     144             :     }
     145             : 
     146          12 :     if (fValue > rInfo.mfEnd && !rtl::math::approxEqual(fValue, rInfo.mfEnd))
     147             :     {
     148           6 :         rtl::math::setInf(&fValue, false);
     149           6 :         return fValue;
     150             :     }
     151             : 
     152           6 :     double fDiff = fValue - rInfo.mfStart;
     153           6 :     double fDiv = rtl::math::approxFloor( fDiff / rInfo.mfStep );
     154           6 :     double fGroupStart = rInfo.mfStart + fDiv * rInfo.mfStep;
     155             : 
     156           6 :     if (rtl::math::approxEqual(fGroupStart, rInfo.mfEnd) &&
     157           0 :         !rtl::math::approxEqual(fGroupStart, rInfo.mfStart))
     158             :     {
     159           0 :         if (!rInfo.mbDateValues)
     160             :         {
     161             :             // A group that would consist only of the end value is not
     162             :             // created, instead the value is included in the last group
     163             :             // before. So the previous group is used if the calculated group
     164             :             // start value is the selected end value.
     165             : 
     166           0 :             fDiv -= 1.0;
     167           0 :             return rInfo.mfStart + fDiv * rInfo.mfStep;
     168             :         }
     169             : 
     170             :         // For date values, the end value is instead treated as above the
     171             :         // limit if it would be a group of its own.
     172             : 
     173           0 :         return rInfo.mfEnd + rInfo.mfStep;
     174             :     }
     175             : 
     176           6 :     return fGroupStart;
     177             : }
     178             : 
     179             : namespace {
     180             : 
     181           0 : void lcl_AppendDateStr( OUStringBuffer& rBuffer, double fValue, SvNumberFormatter* pFormatter )
     182             : {
     183           0 :     sal_uLong nFormat = pFormatter->GetStandardFormat( NUMBERFORMAT_DATE, ScGlobal::eLnge );
     184           0 :     OUString aString;
     185           0 :     pFormatter->GetInputLineString( fValue, nFormat, aString );
     186           0 :     rBuffer.append( aString );
     187           0 : }
     188             : 
     189           2 : OUString lcl_GetSpecialNumGroupName( double fValue, bool bFirst, sal_Unicode cDecSeparator,
     190             :     bool bDateValues, SvNumberFormatter* pFormatter )
     191             : {
     192             :     OSL_ENSURE( cDecSeparator != 0, "cDecSeparator not initialized" );
     193             : 
     194           2 :     OUStringBuffer aBuffer;
     195           2 :     aBuffer.append((sal_Unicode)( bFirst ? '<' : '>' ));
     196           2 :     if ( bDateValues )
     197           0 :         lcl_AppendDateStr( aBuffer, fValue, pFormatter );
     198             :     else
     199             :         rtl::math::doubleToUStringBuffer( aBuffer, fValue, rtl_math_StringFormat_Automatic,
     200           2 :         rtl_math_DecimalPlaces_Max, cDecSeparator, true );
     201           2 :     return aBuffer.makeStringAndClear();
     202             : }
     203             : 
     204           3 : OUString lcl_GetNumGroupName(
     205             :     double fStartValue, const ScDPNumGroupInfo& rInfo, sal_Unicode cDecSep,
     206             :     SvNumberFormatter* pFormatter)
     207             : {
     208             :     OSL_ENSURE( cDecSep != 0, "cDecSeparator not initialized" );
     209             : 
     210           3 :     double fStep = rInfo.mfStep;
     211           3 :     double fEndValue = fStartValue + fStep;
     212           3 :     if (rInfo.mbIntegerOnly && (rInfo.mbDateValues || !rtl::math::approxEqual(fEndValue, rInfo.mfEnd)))
     213             :     {
     214             :         //  The second number of the group label is
     215             :         //  (first number + size - 1) if there are only integer numbers,
     216             :         //  (first number + size) if any non-integer numbers are involved.
     217             :         //  Exception: The last group (containing the end value) is always
     218             :         //  shown as including the end value (but not for dates).
     219             : 
     220           2 :         fEndValue -= 1.0;
     221             :     }
     222             : 
     223           3 :     if ( fEndValue > rInfo.mfEnd && !rInfo.mbAutoEnd )
     224             :     {
     225             :         // limit the last group to the end value
     226             : 
     227           0 :         fEndValue = rInfo.mfEnd;
     228             :     }
     229             : 
     230           3 :     OUStringBuffer aBuffer;
     231           3 :     if ( rInfo.mbDateValues )
     232             :     {
     233           0 :         lcl_AppendDateStr( aBuffer, fStartValue, pFormatter );
     234           0 :         aBuffer.appendAscii( " - " );   // with spaces
     235           0 :         lcl_AppendDateStr( aBuffer, fEndValue, pFormatter );
     236             :     }
     237             :     else
     238             :     {
     239             :         rtl::math::doubleToUStringBuffer( aBuffer, fStartValue, rtl_math_StringFormat_Automatic,
     240           3 :             rtl_math_DecimalPlaces_Max, cDecSep, true );
     241           3 :         aBuffer.append( (sal_Unicode) '-' );
     242             :         rtl::math::doubleToUStringBuffer( aBuffer, fEndValue, rtl_math_StringFormat_Automatic,
     243           3 :             rtl_math_DecimalPlaces_Max, cDecSep, true );
     244             :     }
     245             : 
     246           3 :     return aBuffer.makeStringAndClear();
     247             : }
     248             : 
     249             : }
     250             : 
     251           5 : OUString ScDPUtil::getNumGroupName(
     252             :     double fValue, const ScDPNumGroupInfo& rInfo, sal_Unicode cDecSep, SvNumberFormatter* pFormatter)
     253             : {
     254           5 :     if ( fValue < rInfo.mfStart && !rtl::math::approxEqual( fValue, rInfo.mfStart ) )
     255           1 :         return lcl_GetSpecialNumGroupName( rInfo.mfStart, true, cDecSep, rInfo.mbDateValues, pFormatter );
     256             : 
     257           4 :     if ( fValue > rInfo.mfEnd && !rtl::math::approxEqual( fValue, rInfo.mfEnd ) )
     258           1 :         return lcl_GetSpecialNumGroupName( rInfo.mfEnd, false, cDecSep, rInfo.mbDateValues, pFormatter );
     259             : 
     260           3 :     double fDiff = fValue - rInfo.mfStart;
     261           3 :     double fDiv = rtl::math::approxFloor( fDiff / rInfo.mfStep );
     262           3 :     double fGroupStart = rInfo.mfStart + fDiv * rInfo.mfStep;
     263             : 
     264           3 :     if ( rtl::math::approxEqual( fGroupStart, rInfo.mfEnd ) &&
     265           0 :         !rtl::math::approxEqual( fGroupStart, rInfo.mfStart ) )
     266             :     {
     267           0 :         if (rInfo.mbDateValues)
     268             :         {
     269             :             //  For date values, the end value is instead treated as above the limit
     270             :             //  if it would be a group of its own.
     271           0 :             return lcl_GetSpecialNumGroupName( rInfo.mfEnd, false, cDecSep, rInfo.mbDateValues, pFormatter );
     272             :         }
     273             :     }
     274             : 
     275           3 :     return lcl_GetNumGroupName(fGroupStart, rInfo, cDecSep, pFormatter);
     276             : }
     277             : 
     278          50 : sal_Int32 ScDPUtil::getDatePartValue(
     279             :     double fValue, const ScDPNumGroupInfo& rInfo, sal_Int32 nDatePart,
     280             :     SvNumberFormatter* pFormatter)
     281             : {
     282             :     // Start and end are inclusive
     283             :     // (End date without a time value is included, with a time value it's not)
     284             : 
     285          50 :     if (fValue < rInfo.mfStart && !rtl::math::approxEqual(fValue, rInfo.mfStart))
     286           0 :         return ScDPItemData::DateFirst;
     287          50 :     if (fValue > rInfo.mfEnd && !rtl::math::approxEqual(fValue, rInfo.mfEnd))
     288           0 :         return ScDPItemData::DateLast;
     289             : 
     290          50 :     sal_Int32 nResult = 0;
     291             : 
     292          50 :     if (nDatePart == sheet::DataPilotFieldGroupBy::HOURS ||
     293          50 :         nDatePart == sheet::DataPilotFieldGroupBy::MINUTES ||
     294             :         nDatePart == sheet::DataPilotFieldGroupBy::SECONDS)
     295             :     {
     296             :         // handle time
     297             :         // (as in the cell functions, ScInterpreter::ScGetHour etc.: seconds are rounded)
     298             : 
     299           0 :         double fTime = fValue - rtl::math::approxFloor(fValue);
     300           0 :         long nSeconds = (long)rtl::math::approxFloor(fTime*DATE_TIME_FACTOR+0.5);
     301             : 
     302           0 :         switch (nDatePart)
     303             :         {
     304             :             case sheet::DataPilotFieldGroupBy::HOURS:
     305           0 :                 nResult = nSeconds / 3600;
     306           0 :                 break;
     307             :             case sheet::DataPilotFieldGroupBy::MINUTES:
     308           0 :                 nResult = ( nSeconds % 3600 ) / 60;
     309           0 :                 break;
     310             :             case sheet::DataPilotFieldGroupBy::SECONDS:
     311           0 :                 nResult = nSeconds % 60;
     312           0 :                 break;
     313           0 :         }
     314             :     }
     315             :     else
     316             :     {
     317          50 :         Date aDate = *(pFormatter->GetNullDate());
     318          50 :         aDate += (long)::rtl::math::approxFloor(fValue);
     319             : 
     320          50 :         switch ( nDatePart )
     321             :         {
     322             :             case com::sun::star::sheet::DataPilotFieldGroupBy::YEARS:
     323          18 :                 nResult = aDate.GetYear();
     324          18 :                 break;
     325             :             case com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS:
     326          16 :                 nResult = 1 + (aDate.GetMonth() - 1) / 3;     // 1..4
     327          16 :                 break;
     328             :             case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS:
     329          16 :                 nResult = aDate.GetMonth();     // 1..12
     330          16 :                 break;
     331             :             case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS:
     332             :                 {
     333           0 :                     Date aYearStart(1, 1, aDate.GetYear());
     334           0 :                     nResult = (aDate - aYearStart) + 1;       // Jan 01 has value 1
     335           0 :                     if (nResult >= 60 && !aDate.IsLeapYear())
     336             :                     {
     337             :                         // days are counted from 1 to 366 - if not from a leap year, adjust
     338           0 :                         ++nResult;
     339             :                     }
     340             :                 }
     341           0 :                 break;
     342             :             default:
     343             :                 OSL_FAIL("invalid date part");
     344             :         }
     345             :     }
     346             : 
     347          50 :     return nResult;
     348             : }
     349             : 
     350             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10