LCOV - code coverage report
Current view: top level - libreoffice/sc/source/core/data - dputil.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 88 143 61.5 %
Date: 2012-12-27 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             :  * Version: MPL 1.1 / GPLv3+ / LGPLv3+
       4             :  *
       5             :  * The contents of this file are subject to the Mozilla Public License Version
       6             :  * 1.1 (the "License"); you may not use this file except in compliance with
       7             :  * the License or as specified alternatively below. You may obtain a copy of
       8             :  * the License at http://www.mozilla.org/MPL/
       9             :  *
      10             :  * Software distributed under the License is distributed on an "AS IS" basis,
      11             :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12             :  * for the specific language governing rights and limitations under the
      13             :  * License.
      14             :  *
      15             :  * Major Contributor(s):
      16             :  *   Copyright (C) 2012 Kohei Yoshida <kohei.yoshida@suse.com>
      17             :  *
      18             :  * All Rights Reserved.
      19             :  *
      20             :  * For minor contributions see the git repository.
      21             :  *
      22             :  * Alternatively, the contents of this file may be used under the terms of
      23             :  * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
      24             :  * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
      25             :  * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
      26             :  * instead of those above.
      27             :  */
      28             : 
      29             : #include "dputil.hxx"
      30             : #include "global.hxx"
      31             : #include "dpitemdata.hxx"
      32             : #include "dpnumgroupinfo.hxx"
      33             : 
      34             : #include "comphelper/string.hxx"
      35             : #include "unotools/localedatawrapper.hxx"
      36             : #include "unotools/calendarwrapper.hxx"
      37             : #include "svl/zforlist.hxx"
      38             : #include "rtl/math.hxx"
      39             : 
      40             : #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
      41             : #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
      42             : 
      43             : #define D_TIMEFACTOR 86400.0
      44             : 
      45             : using namespace com::sun::star;
      46             : 
      47             : namespace {
      48             : 
      49             : const sal_uInt16 SC_DP_LEAPYEAR = 1648;     // arbitrary leap year for date calculations
      50             : 
      51           0 : rtl::OUString getTwoDigitString(sal_Int32 nValue)
      52             : {
      53           0 :     String aRet = String::CreateFromInt32( nValue );
      54           0 :     if ( aRet.Len() < 2 )
      55           0 :         aRet.Insert( (sal_Unicode)'0', 0 );
      56           0 :     return aRet;
      57             : }
      58             : 
      59           2 : void appendDateStr(rtl::OUStringBuffer& rBuffer, double fValue, SvNumberFormatter* pFormatter)
      60             : {
      61           2 :     sal_uLong nFormat = pFormatter->GetStandardFormat( NUMBERFORMAT_DATE, ScGlobal::eLnge );
      62           2 :     rtl::OUString aString;
      63           2 :     pFormatter->GetInputLineString(fValue, nFormat, aString);
      64           2 :     rBuffer.append(aString);
      65           2 : }
      66             : 
      67           2 : rtl::OUString getSpecialDateName(double fValue, bool bFirst, SvNumberFormatter* pFormatter)
      68             : {
      69           2 :     rtl::OUStringBuffer aBuffer;
      70           2 :     aBuffer.append(sal_Unicode(bFirst ? '<' : '>'));
      71           2 :     appendDateStr(aBuffer, fValue, pFormatter);
      72           2 :     return aBuffer.makeStringAndClear();
      73             : }
      74             : 
      75             : }
      76             : 
      77          55 : bool ScDPUtil::isDuplicateDimension(const rtl::OUString& rName)
      78             : {
      79          55 :     if (rName.isEmpty())
      80          14 :         return false;
      81             : 
      82          41 :     sal_Unicode cLast = rName[rName.getLength()-1];
      83          41 :     return cLast == sal_Unicode('*');
      84             : }
      85             : 
      86         145 : rtl::OUString ScDPUtil::getSourceDimensionName(const rtl::OUString& rName)
      87             : {
      88         145 :     return comphelper::string::stripEnd(rName, '*');
      89             : }
      90             : 
      91           1 : rtl::OUString ScDPUtil::createDuplicateDimensionName(const rtl::OUString& rOriginal, size_t nDupCount)
      92             : {
      93           1 :     if (!nDupCount)
      94           0 :         return rOriginal;
      95             : 
      96           1 :     rtl::OUStringBuffer aBuf(rOriginal);
      97           2 :     for (size_t i = 0; i < nDupCount; ++i)
      98           1 :         aBuf.append(sal_Unicode('*'));
      99             : 
     100           1 :     return aBuf.makeStringAndClear();
     101             : }
     102             : 
     103          71 : rtl::OUString ScDPUtil::getDateGroupName(
     104             :         sal_Int32 nDatePart, sal_Int32 nValue, SvNumberFormatter* pFormatter,
     105             :         double fStart, double fEnd)
     106             : {
     107          71 :     if (nValue == ScDPItemData::DateFirst)
     108           1 :         return getSpecialDateName(fStart, true, pFormatter);
     109          70 :     if (nValue == ScDPItemData::DateLast)
     110           1 :         return getSpecialDateName(fEnd, false, pFormatter);
     111             : 
     112          69 :     switch ( nDatePart )
     113             :     {
     114             :         case sheet::DataPilotFieldGroupBy::YEARS:
     115          21 :             return rtl::OUString::valueOf(nValue);
     116             :         case sheet::DataPilotFieldGroupBy::QUARTERS:
     117          22 :             return ScGlobal::pLocaleData->getQuarterAbbreviation(sal_Int16(nValue-1));    // nValue is 1-based
     118             :         case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS:
     119             :             return ScGlobal::GetCalendar()->getDisplayName(
     120          26 :                         i18n::CalendarDisplayIndex::MONTH, sal_Int16(nValue-1), 0);    // 0-based, get short name
     121             :         case sheet::DataPilotFieldGroupBy::DAYS:
     122             :         {
     123           0 :             Date aDate(1, 1, SC_DP_LEAPYEAR);
     124           0 :             aDate += (nValue - 1);            // nValue is 1-based
     125           0 :             Date aNullDate = *pFormatter->GetNullDate();
     126           0 :             long nDays = aDate - aNullDate;
     127             : 
     128           0 :             sal_uLong nFormat = pFormatter->GetFormatIndex(NF_DATE_SYS_DDMMM, ScGlobal::eLnge);
     129             :             Color* pColor;
     130           0 :             String aStr;
     131           0 :             pFormatter->GetOutputString(nDays, nFormat, aStr, &pColor);
     132           0 :             return aStr;
     133             :         }
     134             :         case sheet::DataPilotFieldGroupBy::HOURS:
     135             :         {
     136             :             //! allow am/pm format?
     137           0 :             return getTwoDigitString(nValue);
     138             :         }
     139             :         break;
     140             :         case sheet::DataPilotFieldGroupBy::MINUTES:
     141             :         case sheet::DataPilotFieldGroupBy::SECONDS:
     142             :         {
     143           0 :             rtl::OUStringBuffer aBuf(ScGlobal::pLocaleData->getTimeSep());
     144           0 :             aBuf.append(getTwoDigitString(nValue));
     145           0 :             return aBuf.makeStringAndClear();
     146             :         }
     147             :         break;
     148             :         default:
     149             :             OSL_FAIL("invalid date part");
     150             :     }
     151             : 
     152           0 :     return rtl::OUString::createFromAscii("FIXME: unhandled value");
     153             : }
     154             : 
     155          18 : double ScDPUtil::getNumGroupStartValue(double fValue, const ScDPNumGroupInfo& rInfo)
     156             : {
     157          18 :     if (fValue < rInfo.mfStart && !rtl::math::approxEqual(fValue, rInfo.mfStart))
     158             :     {
     159           6 :         rtl::math::setInf(&fValue, true);
     160           6 :         return fValue;
     161             :     }
     162             : 
     163          12 :     if (fValue > rInfo.mfEnd && !rtl::math::approxEqual(fValue, rInfo.mfEnd))
     164             :     {
     165           6 :         rtl::math::setInf(&fValue, false);
     166           6 :         return fValue;
     167             :     }
     168             : 
     169           6 :     double fDiff = fValue - rInfo.mfStart;
     170           6 :     double fDiv = rtl::math::approxFloor( fDiff / rInfo.mfStep );
     171           6 :     double fGroupStart = rInfo.mfStart + fDiv * rInfo.mfStep;
     172             : 
     173           6 :     if (rtl::math::approxEqual(fGroupStart, rInfo.mfEnd) &&
     174           0 :         !rtl::math::approxEqual(fGroupStart, rInfo.mfStart))
     175             :     {
     176           0 :         if (!rInfo.mbDateValues)
     177             :         {
     178             :             // A group that would consist only of the end value is not
     179             :             // created, instead the value is included in the last group
     180             :             // before. So the previous group is used if the calculated group
     181             :             // start value is the selected end value.
     182             : 
     183           0 :             fDiv -= 1.0;
     184           0 :             return rInfo.mfStart + fDiv * rInfo.mfStep;
     185             :         }
     186             : 
     187             :         // For date values, the end value is instead treated as above the
     188             :         // limit if it would be a group of its own.
     189             : 
     190           0 :         return rInfo.mfEnd + rInfo.mfStep;
     191             :     }
     192             : 
     193           6 :     return fGroupStart;
     194             : }
     195             : 
     196             : namespace {
     197             : 
     198           0 : void lcl_AppendDateStr( rtl::OUStringBuffer& rBuffer, double fValue, SvNumberFormatter* pFormatter )
     199             : {
     200           0 :     sal_uLong nFormat = pFormatter->GetStandardFormat( NUMBERFORMAT_DATE, ScGlobal::eLnge );
     201           0 :     rtl::OUString aString;
     202           0 :     pFormatter->GetInputLineString( fValue, nFormat, aString );
     203           0 :     rBuffer.append( aString );
     204           0 : }
     205             : 
     206           2 : rtl::OUString lcl_GetSpecialNumGroupName( double fValue, bool bFirst, sal_Unicode cDecSeparator,
     207             :     bool bDateValues, SvNumberFormatter* pFormatter )
     208             : {
     209             :     OSL_ENSURE( cDecSeparator != 0, "cDecSeparator not initialized" );
     210             : 
     211           2 :     rtl::OUStringBuffer aBuffer;
     212           2 :     aBuffer.append((sal_Unicode)( bFirst ? '<' : '>' ));
     213           2 :     if ( bDateValues )
     214           0 :         lcl_AppendDateStr( aBuffer, fValue, pFormatter );
     215             :     else
     216             :         rtl::math::doubleToUStringBuffer( aBuffer, fValue, rtl_math_StringFormat_Automatic,
     217           2 :         rtl_math_DecimalPlaces_Max, cDecSeparator, true );
     218           2 :     return aBuffer.makeStringAndClear();
     219             : }
     220             : 
     221           3 : rtl::OUString lcl_GetNumGroupName(
     222             :     double fStartValue, const ScDPNumGroupInfo& rInfo, sal_Unicode cDecSep,
     223             :     SvNumberFormatter* pFormatter)
     224             : {
     225             :     OSL_ENSURE( cDecSep != 0, "cDecSeparator not initialized" );
     226             : 
     227           3 :     double fStep = rInfo.mfStep;
     228           3 :     double fEndValue = fStartValue + fStep;
     229           3 :     if (rInfo.mbIntegerOnly && (rInfo.mbDateValues || !rtl::math::approxEqual(fEndValue, rInfo.mfEnd)))
     230             :     {
     231             :         //  The second number of the group label is
     232             :         //  (first number + size - 1) if there are only integer numbers,
     233             :         //  (first number + size) if any non-integer numbers are involved.
     234             :         //  Exception: The last group (containing the end value) is always
     235             :         //  shown as including the end value (but not for dates).
     236             : 
     237           2 :         fEndValue -= 1.0;
     238             :     }
     239             : 
     240           3 :     if ( fEndValue > rInfo.mfEnd && !rInfo.mbAutoEnd )
     241             :     {
     242             :         // limit the last group to the end value
     243             : 
     244           0 :         fEndValue = rInfo.mfEnd;
     245             :     }
     246             : 
     247           3 :     rtl::OUStringBuffer aBuffer;
     248           3 :     if ( rInfo.mbDateValues )
     249             :     {
     250           0 :         lcl_AppendDateStr( aBuffer, fStartValue, pFormatter );
     251           0 :         aBuffer.appendAscii( " - " );   // with spaces
     252           0 :         lcl_AppendDateStr( aBuffer, fEndValue, pFormatter );
     253             :     }
     254             :     else
     255             :     {
     256             :         rtl::math::doubleToUStringBuffer( aBuffer, fStartValue, rtl_math_StringFormat_Automatic,
     257           3 :             rtl_math_DecimalPlaces_Max, cDecSep, true );
     258           3 :         aBuffer.append( (sal_Unicode) '-' );
     259             :         rtl::math::doubleToUStringBuffer( aBuffer, fEndValue, rtl_math_StringFormat_Automatic,
     260           3 :             rtl_math_DecimalPlaces_Max, cDecSep, true );
     261             :     }
     262             : 
     263           3 :     return aBuffer.makeStringAndClear();
     264             : }
     265             : 
     266             : }
     267             : 
     268           5 : rtl::OUString ScDPUtil::getNumGroupName(
     269             :     double fValue, const ScDPNumGroupInfo& rInfo, sal_Unicode cDecSep, SvNumberFormatter* pFormatter)
     270             : {
     271           5 :     if ( fValue < rInfo.mfStart && !rtl::math::approxEqual( fValue, rInfo.mfStart ) )
     272           1 :         return lcl_GetSpecialNumGroupName( rInfo.mfStart, true, cDecSep, rInfo.mbDateValues, pFormatter );
     273             : 
     274           4 :     if ( fValue > rInfo.mfEnd && !rtl::math::approxEqual( fValue, rInfo.mfEnd ) )
     275           1 :         return lcl_GetSpecialNumGroupName( rInfo.mfEnd, false, cDecSep, rInfo.mbDateValues, pFormatter );
     276             : 
     277           3 :     double fDiff = fValue - rInfo.mfStart;
     278           3 :     double fDiv = rtl::math::approxFloor( fDiff / rInfo.mfStep );
     279           3 :     double fGroupStart = rInfo.mfStart + fDiv * rInfo.mfStep;
     280             : 
     281           3 :     if ( rtl::math::approxEqual( fGroupStart, rInfo.mfEnd ) &&
     282           0 :         !rtl::math::approxEqual( fGroupStart, rInfo.mfStart ) )
     283             :     {
     284           0 :         if (rInfo.mbDateValues)
     285             :         {
     286             :             //  For date values, the end value is instead treated as above the limit
     287             :             //  if it would be a group of its own.
     288           0 :             return lcl_GetSpecialNumGroupName( rInfo.mfEnd, false, cDecSep, rInfo.mbDateValues, pFormatter );
     289             :         }
     290             :     }
     291             : 
     292           3 :     return lcl_GetNumGroupName(fGroupStart, rInfo, cDecSep, pFormatter);
     293             : }
     294             : 
     295          50 : sal_Int32 ScDPUtil::getDatePartValue(
     296             :     double fValue, const ScDPNumGroupInfo& rInfo, sal_Int32 nDatePart,
     297             :     SvNumberFormatter* pFormatter)
     298             : {
     299             :     // Start and end are inclusive
     300             :     // (End date without a time value is included, with a time value it's not)
     301             : 
     302          50 :     if (fValue < rInfo.mfStart && !rtl::math::approxEqual(fValue, rInfo.mfStart))
     303           0 :         return ScDPItemData::DateFirst;
     304          50 :     if (fValue > rInfo.mfEnd && !rtl::math::approxEqual(fValue, rInfo.mfEnd))
     305           0 :         return ScDPItemData::DateLast;
     306             : 
     307          50 :     sal_Int32 nResult = 0;
     308             : 
     309          50 :     if (nDatePart == sheet::DataPilotFieldGroupBy::HOURS ||
     310             :         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*D_TIMEFACTOR+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          50 :         Date aDate = *(pFormatter->GetNullDate());
     335          50 :         aDate += (long)::rtl::math::approxFloor(fValue);
     336             : 
     337          50 :         switch ( nDatePart )
     338             :         {
     339             :             case com::sun::star::sheet::DataPilotFieldGroupBy::YEARS:
     340          18 :                 nResult = aDate.GetYear();
     341          18 :                 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          50 :     return nResult;
     365             : }
     366             : 
     367             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10