LCOV - code coverage report
Current view: top level - basic/source/sbx - sbxscan.cxx (source / functions) Hit Total Coverage
Test: commit 10e77ab3ff6f4314137acd6e2702a6e5c1ce1fae Lines: 243 407 59.7 %
Date: 2014-11-03 Functions: 15 19 78.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             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include <config_features.h>
      21             : 
      22             : #include <tools/errcode.hxx>
      23             : #include <basic/sbx.hxx>
      24             : #include "sbxconv.hxx"
      25             : 
      26             : #include <unotools/syslocale.hxx>
      27             : 
      28             : #include <stdlib.h>
      29             : 
      30             : #include <vcl/svapp.hxx>
      31             : #include <vcl/settings.hxx>
      32             : 
      33             : #include <math.h>
      34             : #include <string.h>
      35             : #include <ctype.h>
      36             : 
      37             : #include "sbxres.hxx"
      38             : #include "sbxbase.hxx"
      39             : #include <basic/sbxfac.hxx>
      40             : #include <basic/sbxform.hxx>
      41             : #include <svtools/svtools.hrc>
      42             : 
      43             : #include "basrid.hxx"
      44             : #include "date.hxx"
      45             : #include "runtime.hxx"
      46             : 
      47             : #include <rtl/strbuf.hxx>
      48             : #include <svl/zforlist.hxx>
      49             : #include <comphelper/processfactory.hxx>
      50             : 
      51             : 
      52        1576 : void ImpGetIntntlSep( sal_Unicode& rcDecimalSep, sal_Unicode& rcThousandSep )
      53             : {
      54        1576 :     SvtSysLocale aSysLocale;
      55        1576 :     const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
      56        1576 :     rcDecimalSep = rData.getNumDecimalSep()[0];
      57        1576 :     rcThousandSep = rData.getNumThousandSep()[0];
      58        1576 : }
      59             : 
      60          52 : inline bool ImpIsDigit( sal_Unicode c )
      61             : {
      62          52 :     return '0' <= c && c <= '9';
      63             : }
      64             : 
      65             : /** NOTE: slightly differs from strchr() in that it does not consider the
      66             :     terminating NULL character to be part of the string and returns bool
      67             :     instead of pointer, if character is 0 returns false.
      68             :  */
      69         252 : bool ImpStrChr( const sal_Unicode* p, sal_Unicode c )
      70             : {
      71         252 :     if (!c)
      72          64 :         return false;
      73         894 :     while (*p)
      74             :     {
      75         614 :         if (*p++ == c)
      76          96 :             return true;
      77             :     }
      78          92 :     return false;
      79             : }
      80             : 
      81           0 : bool ImpIsAlNum( sal_Unicode c )
      82             : {
      83           0 :     return c < 128 && isalnum( static_cast<char>(c) );
      84             : }
      85             : 
      86             : // scanning a string according to BASIC-conventions
      87             : // but exponent may also be a D, so data type is SbxDOUBLE
      88             : // conversion error if data type is fixed and it doesn't fit
      89             : 
      90          52 : SbxError ImpScan( const OUString& rWSrc, double& nVal, SbxDataType& rType,
      91             :                   sal_uInt16* pLen, bool bAllowIntntl, bool bOnlyIntntl )
      92             : {
      93             :     sal_Unicode cIntntlDecSep, cIntntlGrpSep;
      94          52 :     sal_Unicode cNonIntntlDecSep = '.';
      95          52 :     if( bAllowIntntl || bOnlyIntntl )
      96             :     {
      97           4 :         ImpGetIntntlSep( cIntntlDecSep, cIntntlGrpSep );
      98           8 :         if( bOnlyIntntl )
      99           4 :             cNonIntntlDecSep = cIntntlDecSep;
     100             :     }
     101             :     else
     102             :     {
     103          48 :         cIntntlDecSep = cNonIntntlDecSep;
     104          48 :         cIntntlGrpSep = 0;  // no group separator accepted in non-i18n
     105             :     }
     106             : 
     107          52 :     const sal_Unicode* const pStart = rWSrc.getStr();
     108          52 :     const sal_Unicode* p = pStart;
     109          52 :     OUStringBuffer aBuf( rWSrc.getLength());
     110          52 :     bool bRes = true;
     111          52 :     bool bMinus = false;
     112          52 :     nVal = 0;
     113          52 :     SbxDataType eScanType = SbxSINGLE;
     114         104 :     while( *p == ' ' || *p == '\t' )
     115           0 :         p++;
     116          52 :     if( *p == '-' )
     117             :     {
     118           0 :         p++;
     119           0 :         bMinus = true;
     120             :     }
     121          84 :     if( ImpIsDigit( *p ) || ((*p == cNonIntntlDecSep || *p == cIntntlDecSep ||
     122          20 :                     (cIntntlDecSep && *p == cIntntlGrpSep)) && ImpIsDigit( *(p+1) )))
     123             :     {
     124          32 :         short exp = 0;
     125          32 :         short decsep = 0;
     126          32 :         short ndig = 0;
     127          32 :         short ncdig = 0;    // number of digits after decimal point
     128          32 :         OUStringBuffer aSearchStr("0123456789DEde");
     129          32 :         aSearchStr.append(cNonIntntlDecSep);
     130          32 :         if( cIntntlDecSep != cNonIntntlDecSep )
     131           0 :             aSearchStr.append(cIntntlDecSep);
     132          32 :         if( bOnlyIntntl )
     133           4 :             aSearchStr.append(cIntntlGrpSep);
     134          32 :         const sal_Unicode* const pSearchStr = aSearchStr.getStr();
     135          32 :         const sal_Unicode pDdEe[] = { 'D', 'd', 'E', 'e', 0 };
     136         160 :         while( ImpStrChr( pSearchStr, *p ) )
     137             :         {
     138          96 :             aBuf.append( *p );
     139          96 :             if( bOnlyIntntl && *p == cIntntlGrpSep )
     140             :             {
     141           2 :                 p++;
     142           2 :                 continue;
     143             :             }
     144          94 :             if( *p == cNonIntntlDecSep || *p == cIntntlDecSep )
     145             :             {
     146             :                 // Use the separator that is passed to stringToDouble()
     147           2 :                 aBuf[ p - pStart ] = cIntntlDecSep;
     148           2 :                 p++;
     149           4 :                 if( ++decsep > 1 )
     150           0 :                     continue;
     151             :             }
     152          92 :             else if( ImpStrChr( pDdEe, *p ) )
     153             :             {
     154           0 :                 if( ++exp > 1 )
     155             :                 {
     156           0 :                     p++;
     157           0 :                     continue;
     158             :                 }
     159           0 :                 if( *p == 'D' || *p == 'd' )
     160           0 :                     eScanType = SbxDOUBLE;
     161           0 :                 aBuf[ p - pStart ] = 'E';
     162           0 :                 p++;
     163             :             }
     164             :             else
     165             :             {
     166          92 :                 p++;
     167          92 :                 if( decsep && !exp )
     168           6 :                     ncdig++;
     169             :             }
     170          94 :             if( !exp )
     171          94 :                 ndig++;
     172             :         }
     173             : 
     174          32 :         if( decsep > 1 || exp > 1 )
     175           0 :             bRes = false;
     176             : 
     177          64 :         OUString aBufStr( aBuf.makeStringAndClear());
     178          32 :         rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
     179          32 :         sal_Int32 nParseEnd = 0;
     180          32 :         nVal = rtl::math::stringToDouble( aBufStr, cIntntlDecSep, cIntntlGrpSep, &eStatus, &nParseEnd );
     181          32 :         if( eStatus != rtl_math_ConversionStatus_Ok || nParseEnd != aBufStr.getLength() )
     182           0 :             bRes = false;
     183             : 
     184          32 :         if( !decsep && !exp )
     185             :         {
     186          30 :             if( nVal >= SbxMININT && nVal <= SbxMAXINT )
     187          28 :                 eScanType = SbxINTEGER;
     188           2 :             else if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
     189           2 :                 eScanType = SbxLONG;
     190             :         }
     191             : 
     192          32 :         ndig = ndig - decsep;
     193             :         // too many numbers for SINGLE?
     194          32 :         if( ndig > 15 || ncdig > 6 )
     195           0 :             eScanType = SbxDOUBLE;
     196             : 
     197             :         // type detection?
     198          32 :         const sal_Unicode pTypes[] = { '%', '!', '&', '#', 0 };
     199          32 :         if( ImpStrChr( pTypes, *p ) )
     200          32 :             p++;
     201             :     }
     202             :     // hex/octal number? read in and convert:
     203          20 :     else if( *p == '&' )
     204             :     {
     205           0 :         p++;
     206           0 :         eScanType = SbxLONG;
     207           0 :         OUString aCmp( "0123456789ABCDEFabcdef" );
     208           0 :         char base = 16;
     209           0 :         char ndig = 8;
     210           0 :         switch( *p++ )
     211             :         {
     212             :             case 'O':
     213             :             case 'o':
     214           0 :                 aCmp = "01234567";
     215           0 :                 base = 8;
     216           0 :                 ndig = 11;
     217           0 :                 break;
     218             :             case 'H':
     219             :             case 'h':
     220           0 :                 break;
     221             :             default :
     222           0 :                 bRes = false;
     223             :         }
     224           0 :         const sal_Unicode* const pCmp = aCmp.getStr();
     225           0 :         while( ImpIsAlNum( *p ) )    /* XXX: really munge all alnum also when error? */
     226             :         {
     227           0 :             sal_Unicode ch = *p;
     228           0 :             if( ImpStrChr( pCmp, ch ) )
     229             :             {
     230           0 :                 if (ch > 0x60)
     231           0 :                     ch -= 0x20;     // convert ASCII lower to upper case
     232           0 :                 aBuf.append( ch );
     233             :             }
     234             :             else
     235           0 :                 bRes = false;
     236           0 :             p++;
     237             :         }
     238           0 :         OUString aBufStr( aBuf.makeStringAndClear());
     239           0 :         long l = 0;
     240           0 :         for( const sal_Unicode* q = aBufStr.getStr(); bRes && *q; q++ )
     241             :         {
     242           0 :             int i = *q - '0';
     243           0 :             if( i > 9 )
     244           0 :                 i -= 7;     // 'A'-'0' = 17 => 10, ...
     245           0 :             l = ( l * base ) + i;
     246           0 :             if( !ndig-- )
     247           0 :                 bRes = false;
     248             :         }
     249           0 :         if( *p == '&' )
     250           0 :             p++;
     251           0 :         nVal = (double) l;
     252           0 :         if( l >= SbxMININT && l <= SbxMAXINT )
     253           0 :             eScanType = SbxINTEGER;
     254             :     }
     255             : #if HAVE_FEATURE_SCRIPTING
     256          20 :     else if ( SbiRuntime::isVBAEnabled() )
     257             :     {
     258             :         OSL_TRACE("Reporting error converting");
     259          20 :         return SbxERR_CONVERSION;
     260             :     }
     261             : #endif
     262          32 :     if( pLen )
     263           4 :         *pLen = (sal_uInt16) ( p - pStart );
     264          32 :     if( !bRes )
     265           0 :         return SbxERR_CONVERSION;
     266          32 :     if( bMinus )
     267           0 :         nVal = -nVal;
     268          32 :     rType = eScanType;
     269          32 :     return SbxERR_OK;
     270             : }
     271             : 
     272             : // port for CDbl in the Basic
     273           4 : SbxError SbxValue::ScanNumIntnl( const OUString& rSrc, double& nVal, bool bSingle )
     274             : {
     275             :     SbxDataType t;
     276           4 :     sal_uInt16 nLen = 0;
     277             :     SbxError nRetError = ImpScan( rSrc, nVal, t, &nLen,
     278           4 :         /*bAllowIntntl*/false, /*bOnlyIntntl*/true );
     279             :     // read completely?
     280           4 :     if( nRetError == SbxERR_OK && nLen != rSrc.getLength() )
     281             :     {
     282           0 :         nRetError = SbxERR_CONVERSION;
     283             :     }
     284           4 :     if( bSingle )
     285             :     {
     286           0 :         SbxValues aValues( nVal );
     287           0 :         nVal = (double)ImpGetSingle( &aValues );    // here error at overflow
     288             :     }
     289           4 :     return nRetError;
     290             : }
     291             : 
     292             : 
     293             : static const double roundArray[] = {
     294             :     5.0e+0, 0.5e+0, 0.5e-1, 0.5e-2, 0.5e-3, 0.5e-4, 0.5e-5, 0.5e-6, 0.5e-7,
     295             :     0.5e-8, 0.5e-9, 0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14,0.5e-15 };
     296             : 
     297             : /***************************************************************************
     298             : |*
     299             : |*  void myftoa( double, char *, short, short, bool, bool )
     300             : |*
     301             : |*  description:        conversion double --> ASCII
     302             : |*  parameters:         double              the number
     303             : |*                      char *              target buffer
     304             : |*                      short               number of positions after decimal point
     305             : |*                      short               range of the exponent ( 0=no E )
     306             : |*                      bool                true: with 1000-separators
     307             : |*                      bool                true: output without formatting
     308             : |*
     309             : ***************************************************************************/
     310             : 
     311         786 : static void myftoa( double nNum, char * pBuf, short nPrec, short nExpWidth,
     312             :                     bool bPt, bool bFix, sal_Unicode cForceThousandSep = 0 )
     313             : {
     314             : 
     315         786 :     short nExp = 0;
     316         786 :     short nDig = nPrec + 1;
     317             :     short nDec;                         // number of positions before decimal point
     318             :     int i;
     319             : 
     320             :     sal_Unicode cDecimalSep, cThousandSep;
     321         786 :     ImpGetIntntlSep( cDecimalSep, cThousandSep );
     322         786 :     if( cForceThousandSep )
     323         786 :         cThousandSep = cForceThousandSep;
     324             : 
     325             :     // compute exponent
     326         786 :     nExp = 0;
     327         786 :     if( nNum > 0.0 )
     328             :     {
     329         654 :         while( nNum <   1.0 ) nNum *= 10.0, nExp--;
     330         654 :         while( nNum >= 10.0 ) nNum /= 10.0, nExp++;
     331             :     }
     332         786 :     if( !bFix && !nExpWidth )
     333           0 :         nDig = nDig + nExp;
     334         786 :     else if( bFix && !nPrec )
     335         412 :         nDig = nExp + 1;
     336             : 
     337             :     // round number
     338         786 :     if( (nNum += roundArray [( nDig > 16 ) ? 16 : nDig] ) >= 10.0 )
     339             :     {
     340           0 :         nNum = 1.0;
     341           0 :         ++nExp;
     342           0 :         if( !nExpWidth ) ++nDig;
     343             :     }
     344             : 
     345             :     // determine positions before decimal point
     346         786 :     if( !nExpWidth )
     347             :     {
     348         776 :         if( nExp < 0 )
     349             :         {
     350             :             // #41691: also a 0 at bFix
     351          56 :             *pBuf++ = '0';
     352          56 :             if( nPrec ) *pBuf++ = (char)cDecimalSep;
     353          56 :             i = -nExp - 1;
     354          56 :             if( nDig <= 0 ) i = nPrec;
     355          56 :             while( i-- )    *pBuf++ = '0';
     356          56 :             nDec = 0;
     357             :         }
     358             :         else
     359         720 :             nDec = nExp+1;
     360             :     }
     361             :     else
     362          10 :         nDec = 1;
     363             : 
     364             :     // output number
     365         786 :     if( nDig > 0 )
     366             :     {
     367             :         int digit;
     368        6194 :         for( i = 0 ; ; ++i )
     369             :         {
     370        6194 :             if( i < 16 )
     371             :             {
     372        6194 :                 digit = (int) nNum;
     373        6194 :                 *pBuf++ = sal::static_int_cast< char >(digit + '0');
     374        6194 :                 nNum =( nNum - digit ) * 10.0;
     375             :             } else
     376           0 :                 *pBuf++ = '0';
     377        6194 :             if( --nDig == 0 ) break;
     378        5408 :             if( nDec )
     379             :             {
     380         830 :                 nDec--;
     381         830 :                 if( !nDec )
     382         318 :                     *pBuf++ = (char)cDecimalSep;
     383         512 :                 else if( !(nDec % 3 ) && bPt )
     384           0 :                     *pBuf++ = (char)cThousandSep;
     385             :             }
     386        5408 :         }
     387             :     }
     388             : 
     389             :     // output exponent
     390         786 :     if( nExpWidth )
     391             :     {
     392          10 :         if( nExpWidth < 3 ) nExpWidth = 3;
     393          10 :         nExpWidth -= 2;
     394          10 :         *pBuf++ = 'E';
     395          10 :         *pBuf++ =( nExp < 0 ) ?( (nExp = -nExp ), '-' ) : '+';
     396          10 :         while( nExpWidth > 3 ) *pBuf++ = '0', nExpWidth--;
     397          10 :         if( nExp >= 100 || nExpWidth == 3 )
     398             :         {
     399           0 :             *pBuf++ = sal::static_int_cast< char >(nExp/100 + '0');
     400           0 :             nExp %= 100;
     401             :         }
     402          10 :         if( nExp/10 || nExpWidth >= 2 )
     403          10 :             *pBuf++ = sal::static_int_cast< char >(nExp/10 + '0');
     404          10 :         *pBuf++ = sal::static_int_cast< char >(nExp%10 + '0');
     405             :     }
     406         786 :     *pBuf = 0;
     407         786 : }
     408             : 
     409             : // The number is prepared unformattedly with the given number of
     410             : // NK-positions. A leading minus is added if applicable.
     411             : // This routine is public because it's also used by the Put-functions
     412             : // in the class SbxImpSTRING.
     413             : 
     414         786 : void ImpCvtNum( double nNum, short nPrec, OUString& rRes, bool bCoreString )
     415             : {
     416             :     char *q;
     417         786 :     char cBuf[ 40 ], *p = cBuf;
     418             : 
     419             :     sal_Unicode cDecimalSep, cThousandSep;
     420         786 :     ImpGetIntntlSep( cDecimalSep, cThousandSep );
     421         786 :     if( bCoreString )
     422           0 :         cDecimalSep = '.';
     423             : 
     424         786 :     if( nNum < 0.0 ) {
     425          50 :         nNum = -nNum;
     426          50 :         *p++ = '-';
     427             :     }
     428         786 :     double dMaxNumWithoutExp = (nPrec == 6) ? 1E6 : 1E14;
     429         654 :     myftoa( nNum, p, nPrec,( nNum &&( nNum < 1E-1 || nNum >= dMaxNumWithoutExp ) ) ? 4:0,
     430         796 :         false, true, cDecimalSep );
     431             :     // remove trailing zeros
     432         786 :     for( p = cBuf; *p &&( *p != 'E' ); p++ ) {}
     433         786 :     q = p; p--;
     434         786 :     while( nPrec && *p == '0' ) nPrec--, p--;
     435         786 :     if( *p == cDecimalSep ) p--;
     436         786 :     while( *q ) *++p = *q++;
     437         786 :     *++p = 0;
     438         786 :     rRes = OUString::createFromAscii( cBuf );
     439         786 : }
     440             : 
     441           0 : bool ImpConvStringExt( OUString& rSrc, SbxDataType eTargetType )
     442             : {
     443           0 :     bool bChanged = false;
     444           0 :     OUString aNewString;
     445             : 
     446             :     // only special cases are handled, nothing on default
     447           0 :     switch( eTargetType )
     448             :     {
     449             :         // consider international for floating point
     450             :         case SbxSINGLE:
     451             :         case SbxDOUBLE:
     452             :         case SbxCURRENCY:
     453             :         {
     454             :             sal_Unicode cDecimalSep, cThousandSep;
     455           0 :             ImpGetIntntlSep( cDecimalSep, cThousandSep );
     456           0 :             aNewString = rSrc;
     457             : 
     458           0 :             if( cDecimalSep != (sal_Unicode)'.' )
     459             :             {
     460           0 :                 sal_Int32 nPos = aNewString.indexOf( cDecimalSep );
     461           0 :                 if( nPos != -1 )
     462             :                 {
     463           0 :                     sal_Unicode* pStr = (sal_Unicode*)aNewString.getStr();
     464           0 :                     pStr[nPos] = (sal_Unicode)'.';
     465           0 :                     bChanged = true;
     466             :                 }
     467             :             }
     468           0 :             break;
     469             :         }
     470             : 
     471             :         // check as string in case of sal_Bool sal_True and sal_False
     472             :         case SbxBOOL:
     473             :         {
     474           0 :             if( rSrc.equalsIgnoreAsciiCase("true") )
     475             :             {
     476           0 :                 aNewString = OUString::number( SbxTRUE );
     477           0 :                 bChanged = true;
     478             :             }
     479           0 :             else if( rSrc.equalsIgnoreAsciiCase("false") )
     480             :             {
     481           0 :                 aNewString = OUString::number( SbxFALSE );
     482           0 :                 bChanged = true;
     483             :             }
     484           0 :             break;
     485             :         }
     486           0 :         default: break;
     487             :     }
     488             : 
     489           0 :     if( bChanged )
     490           0 :         rSrc = aNewString;
     491           0 :     return bChanged;
     492             : }
     493             : 
     494             : 
     495             : // formatted number output
     496             : // the return value is the number of characters used
     497             : // from the format
     498             : 
     499           0 : static sal_uInt16 printfmtstr( const OUString& rStr, OUString& rRes, const OUString& rFmt )
     500             : {
     501           0 :     OUStringBuffer aTemp;
     502           0 :     const sal_Unicode* pStr = rStr.getStr();
     503           0 :     const sal_Unicode* pFmtStart = rFmt.getStr();
     504           0 :     const sal_Unicode* pFmt = pFmtStart;
     505             : 
     506           0 :     switch( *pFmt )
     507             :     {
     508             :     case '!':
     509           0 :         aTemp.append(*pStr++);
     510           0 :         pFmt++;
     511           0 :         break;
     512             :     case '\\':
     513           0 :         do
     514             :         {
     515           0 :             aTemp.append( *pStr ? *pStr++ : static_cast< sal_Unicode >(' '));
     516           0 :             pFmt++;
     517             :         }
     518           0 :         while( *pFmt != '\\' );
     519           0 :         aTemp.append(*pStr ? *pStr++ : static_cast< sal_Unicode >(' '));
     520           0 :         pFmt++; break;
     521             :     case '&':
     522           0 :         aTemp = rStr;
     523           0 :         pFmt++; break;
     524             :     default:
     525           0 :         aTemp = rStr;
     526           0 :         break;
     527             :     }
     528           0 :     rRes = aTemp.makeStringAndClear();
     529           0 :     return (sal_uInt16) ( pFmt - pFmtStart );
     530             : }
     531             : 
     532             : 
     533           0 : bool SbxValue::Scan( const OUString& rSrc, sal_uInt16* pLen )
     534             : {
     535           0 :     SbxError eRes = SbxERR_OK;
     536           0 :     if( !CanWrite() )
     537             :     {
     538           0 :         eRes = SbxERR_PROP_READONLY;
     539             :     }
     540             :     else
     541             :     {
     542             :         double n;
     543             :         SbxDataType t;
     544           0 :         eRes = ImpScan( rSrc, n, t, pLen );
     545           0 :         if( eRes == SbxERR_OK )
     546             :         {
     547           0 :             if( !IsFixed() )
     548             :             {
     549           0 :                 SetType( t );
     550             :             }
     551           0 :             PutDouble( n );
     552             :         }
     553             :     }
     554           0 :     if( eRes )
     555             :     {
     556           0 :         SetError( eRes );
     557           0 :         return false;
     558             :     }
     559             :     else
     560             :     {
     561           0 :         return true;
     562             :     }
     563             : }
     564             : 
     565             : 
     566          14 : ResMgr* implGetResMgr( void )
     567             : {
     568             :     static ResMgr* pResMgr = NULL;
     569          14 :     if( !pResMgr )
     570             :     {
     571           2 :         pResMgr = ResMgr::CreateResMgr("sb", Application::GetSettings().GetUILanguageTag() );
     572             :     }
     573          14 :     return pResMgr;
     574             : }
     575             : 
     576             : class SbxValueFormatResId : public ResId
     577             : {
     578             : public:
     579          14 :     SbxValueFormatResId( sal_uInt16 nId )
     580          14 :         : ResId( nId, *implGetResMgr() )
     581          14 :     {}
     582             : };
     583             : 
     584             : 
     585             : enum VbaFormatType
     586             : {
     587             :     VBA_FORMAT_TYPE_OFFSET, // standard number format
     588             :     VBA_FORMAT_TYPE_USERDEFINED, // user defined number format
     589             :     VBA_FORMAT_TYPE_NULL
     590             : };
     591             : 
     592        4411 : struct VbaFormatInfo
     593             : {
     594             :     VbaFormatType meType;
     595             :     OUString mpVbaFormat; // Format string in vba
     596             :     NfIndexTableOffset meOffset; // SvNumberFormatter format index, if meType = VBA_FORMAT_TYPE_OFFSET
     597             :     const char* mpOOoFormat; // if meType = VBA_FORMAT_TYPE_USERDEFINED
     598             : };
     599             : 
     600             : #define VBA_FORMAT_OFFSET( pcUtf8, eOffset ) \
     601             :     { VBA_FORMAT_TYPE_OFFSET, OUString(pcUtf8), eOffset, 0 }
     602             : 
     603             : #define VBA_FORMAT_USERDEFINED( pcUtf8, pcDefinedUtf8 ) \
     604             :     { VBA_FORMAT_TYPE_USERDEFINED, OUString(pcUtf8), NF_NUMBER_STANDARD, pcDefinedUtf8 }
     605             : 
     606         802 : static VbaFormatInfo pFormatInfoTable[] =
     607             : {
     608             :     VBA_FORMAT_OFFSET( "Long Date", NF_DATE_SYSTEM_LONG ),
     609             :     VBA_FORMAT_USERDEFINED( "Medium Date", "DD-MMM-YY" ),
     610             :     VBA_FORMAT_OFFSET( "Short Date", NF_DATE_SYSTEM_SHORT ),
     611             :     VBA_FORMAT_USERDEFINED( "Long Time", "H:MM:SS AM/PM" ),
     612             :     VBA_FORMAT_OFFSET( "Medium Time", NF_TIME_HHMMAMPM ),
     613             :     VBA_FORMAT_OFFSET( "Short Time", NF_TIME_HHMM ),
     614             :     VBA_FORMAT_OFFSET( "ddddd", NF_DATE_SYSTEM_SHORT ),
     615             :     VBA_FORMAT_OFFSET( "dddddd", NF_DATE_SYSTEM_LONG ),
     616             :     VBA_FORMAT_USERDEFINED( "ttttt", "H:MM:SS AM/PM" ),
     617             :     VBA_FORMAT_OFFSET( "ww", NF_DATE_WW ),
     618             :     { VBA_FORMAT_TYPE_NULL, OUString(""), NF_INDEX_TABLE_ENTRIES, 0 }
     619         401 : };
     620             : 
     621          24 : VbaFormatInfo* getFormatInfo( const OUString& rFmt )
     622             : {
     623          24 :     VbaFormatInfo* pInfo = NULL;
     624          24 :     sal_Int16 i = 0;
     625         288 :     while( (pInfo = pFormatInfoTable + i )->meType != VBA_FORMAT_TYPE_NULL )
     626             :     {
     627         240 :         if( rFmt.equalsIgnoreAsciiCase( pInfo->mpVbaFormat ) )
     628           0 :             break;
     629         240 :         i++;
     630             :     }
     631          24 :     return pInfo;
     632             : }
     633             : 
     634             : #define VBAFORMAT_GENERALDATE       "General Date"
     635             : #define VBAFORMAT_C                 "c"
     636             : #define VBAFORMAT_N                 "n"
     637             : #define VBAFORMAT_NN                "nn"
     638             : #define VBAFORMAT_W                 "w"
     639             : #define VBAFORMAT_Y                 "y"
     640             : #define VBAFORMAT_LOWERCASE         "<"
     641             : #define VBAFORMAT_UPPERCASE         ">"
     642             : 
     643          60 : void SbxValue::Format( OUString& rRes, const OUString* pFmt ) const
     644             : {
     645          60 :     short nComma = 0;
     646          60 :     double d = 0;
     647             : 
     648             :     // pflin, It is better to use SvNumberFormatter to handle the date/time/number format.
     649             :     // the SvNumberFormatter output is mostly compatible with
     650             :     // VBA output besides the OOo-basic output
     651          60 :     if( pFmt && !SbxBasicFormater::isBasicFormat( *pFmt ) )
     652             :     {
     653          28 :         OUString aStr = GetOUString();
     654             : 
     655          28 :         SvtSysLocale aSysLocale;
     656          28 :         const CharClass& rCharClass = aSysLocale.GetCharClass();
     657             : 
     658          28 :         if( pFmt->equalsIgnoreAsciiCase( VBAFORMAT_LOWERCASE ) )
     659             :         {
     660           2 :             rRes = rCharClass.lowercase( aStr );
     661           2 :             return;
     662             :         }
     663          26 :         if( pFmt->equalsIgnoreAsciiCase( VBAFORMAT_UPPERCASE ) )
     664             :         {
     665           2 :             rRes = rCharClass.uppercase( aStr );
     666           2 :             return;
     667             :         }
     668             : 
     669          24 :         LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
     670          24 :         SvNumberFormatter aFormatter( comphelper::getProcessComponentContext(), eLangType );
     671             : 
     672          24 :         sal_uInt32 nIndex = 0;
     673             :         double nNumber;
     674             :         Color* pCol;
     675             : 
     676          24 :         bool bSuccess = aFormatter.IsNumberFormat( aStr, nIndex, nNumber );
     677             : 
     678             :         // number format, use SvNumberFormatter to handle it.
     679          24 :         if( bSuccess )
     680             :         {
     681          24 :             sal_Int32 nCheckPos = 0;
     682             :             short nType;
     683          24 :             OUString aFmtStr = *pFmt;
     684          24 :             VbaFormatInfo* pInfo = getFormatInfo( aFmtStr );
     685          24 :             if( pInfo && pInfo->meType != VBA_FORMAT_TYPE_NULL )
     686             :             {
     687           0 :                 if( pInfo->meType == VBA_FORMAT_TYPE_OFFSET )
     688             :                 {
     689           0 :                     nIndex = aFormatter.GetFormatIndex( pInfo->meOffset, eLangType );
     690             :                 }
     691             :                 else
     692             :                 {
     693           0 :                     aFmtStr = OUString::createFromAscii(pInfo->mpOOoFormat);
     694           0 :                     aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
     695             :                 }
     696           0 :                 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
     697             :             }
     698          48 :             else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_GENERALDATE )
     699          24 :                     || aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_C ))
     700             :             {
     701           0 :                 if( nNumber <=-1.0 || nNumber >= 1.0 )
     702             :                 {
     703             :                     // short date
     704           0 :                     nIndex = aFormatter.GetFormatIndex( NF_DATE_SYSTEM_SHORT, eLangType );
     705           0 :                     aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
     706             : 
     707             :                     // long time
     708           0 :                     if( floor( nNumber ) != nNumber )
     709             :                     {
     710           0 :                         aFmtStr = "H:MM:SS AM/PM";
     711           0 :                         aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
     712           0 :                         OUString aTime;
     713           0 :                         aFormatter.GetOutputString( nNumber, nIndex, aTime, &pCol );
     714           0 :                         rRes += " " + aTime;
     715           0 :                     }
     716             :                 }
     717             :                 else
     718             :                 {
     719             :                     // long time only
     720           0 :                     aFmtStr = "H:MM:SS AM/PM";
     721           0 :                     aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
     722           0 :                     aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
     723             :                 }
     724             :             }
     725          48 :             else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_N ) ||
     726          24 :                      aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_NN ))
     727             :             {
     728           0 :                 sal_Int32 nMin = implGetMinute( nNumber );
     729           0 :                 if( nMin < 10 && aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_NN ))
     730             :                 {
     731             :                     // Minute in two digits
     732             :                      sal_Unicode aBuf[2];
     733           0 :                      aBuf[0] = '0';
     734           0 :                      aBuf[1] = '0' + nMin;
     735           0 :                      rRes = OUString(aBuf, SAL_N_ELEMENTS(aBuf));
     736             :                 }
     737             :                 else
     738             :                 {
     739           0 :                     rRes = OUString::number(nMin);
     740             :                 }
     741             :             }
     742          24 :             else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_W ))
     743             :             {
     744           0 :                 sal_Int32 nWeekDay = implGetWeekDay( nNumber );
     745           0 :                 rRes = OUString::number(nWeekDay);
     746             :             }
     747          24 :             else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_Y ))
     748             :             {
     749           0 :                 sal_Int16 nYear = implGetDateYear( nNumber );
     750             :                 double dBaseDate;
     751           0 :                 implDateSerial( nYear, 1, 1, dBaseDate );
     752           0 :                 sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate );
     753           0 :                 rRes = OUString::number(nYear32);
     754             :             }
     755             :             else
     756             :             {
     757          24 :                 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
     758          24 :                 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
     759             :             }
     760             : 
     761          24 :             return;
     762           0 :         }
     763             :     }
     764             : 
     765          32 :     SbxDataType eType = GetType();
     766          32 :     switch( eType )
     767             :     {
     768             :     case SbxCHAR:
     769             :     case SbxBYTE:
     770             :     case SbxINTEGER:
     771             :     case SbxUSHORT:
     772             :     case SbxLONG:
     773             :     case SbxULONG:
     774             :     case SbxINT:
     775             :     case SbxUINT:
     776             :     case SbxNULL:       // #45929 NULL with a little cheating
     777          12 :         nComma = 0;     goto cvt;
     778             :     case SbxSINGLE:
     779           0 :         nComma = 6;     goto cvt;
     780             :     case SbxDOUBLE:
     781          16 :         nComma = 14;
     782             : 
     783             :     cvt:
     784          28 :         if( eType != SbxNULL )
     785             :         {
     786          28 :             d = GetDouble();
     787             :         }
     788             :         // #45355 another point to jump in for isnumeric-String
     789             :     cvt2:
     790          28 :         if( pFmt )
     791             :         {
     792          24 :             SbxAppData& rAppData = GetSbxData_Impl();
     793             : 
     794          24 :             LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
     795          24 :             if( rAppData.pBasicFormater )
     796             :             {
     797          22 :                 if( rAppData.eBasicFormaterLangType != eLangType )
     798             :                 {
     799           0 :                     delete rAppData.pBasicFormater;
     800           0 :                     rAppData.pBasicFormater = NULL;
     801             :                 }
     802             :             }
     803          24 :             rAppData.eBasicFormaterLangType = eLangType;
     804             : 
     805             : 
     806          24 :             if( !rAppData.pBasicFormater )
     807             :             {
     808           2 :                 SvtSysLocale aSysLocale;
     809           2 :                 const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
     810           2 :                 sal_Unicode cComma = rData.getNumDecimalSep()[0];
     811           2 :                 sal_Unicode c1000  = rData.getNumThousandSep()[0];
     812           4 :                 OUString aCurrencyStrg = rData.getCurrSymbol();
     813             : 
     814             :                 // initialize the Basic-formater help object:
     815             :                 // get resources for predefined output
     816             :                 // of the Format()-command, e. g. for "On/Off"
     817           4 :                 OUString aOnStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_ON).toString();
     818           4 :                 OUString aOffStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_OFF).toString();
     819           4 :                 OUString aYesStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_YES).toString();
     820           4 :                 OUString aNoStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_NO).toString();
     821           4 :                 OUString aTrueStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_TRUE).toString();
     822           4 :                 OUString aFalseStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_FALSE).toString();
     823           4 :                 OUString aCurrencyFormatStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_CURRENCY).toString();
     824             : 
     825             :                 rAppData.pBasicFormater = new SbxBasicFormater( cComma,c1000,aOnStrg,aOffStrg,
     826             :                                                                 aYesStrg,aNoStrg,aTrueStrg,aFalseStrg,
     827           4 :                                                                 aCurrencyStrg,aCurrencyFormatStrg );
     828             :             }
     829             :             // Remark: For performance reasons there's only ONE BasicFormater-
     830             :             //    object created and 'stored', so that the expensive resource-
     831             :             //    loading is saved (for country-specific predefined outputs,
     832             :             //    e. g. "On/Off") and the continuous string-creation
     833             :             //    operations, too.
     834             :             // BUT: therefore this code is NOT multithreading capable!
     835             : 
     836             :             // here are problems with ;;;Null because this method is only
     837             :             // called, if SbxValue is a number!!!
     838             :             // in addition rAppData.pBasicFormater->BasicFormatNull( *pFmt ); could be called!
     839          24 :             if( eType != SbxNULL )
     840             :             {
     841          24 :                 rRes = rAppData.pBasicFormater->BasicFormat( d ,*pFmt );
     842             :             }
     843             :             else
     844             :             {
     845           0 :                 rRes = rAppData.pBasicFormater->BasicFormatNull( *pFmt );
     846             :             }
     847             : 
     848             :         }
     849             :         else
     850             :         {
     851           4 :             OUString aTmpString( rRes );
     852           4 :             ImpCvtNum( GetDouble(), nComma, aTmpString );
     853           4 :             rRes = aTmpString;
     854             :         }
     855          28 :         break;
     856             :     case SbxSTRING:
     857           2 :         if( pFmt )
     858             :         {
     859             :             // #45355 converting if numeric
     860           0 :             if( IsNumericRTL() )
     861             :             {
     862           0 :                 ScanNumIntnl( GetOUString(), d, /*bSingle*/false );
     863           0 :                 goto cvt2;
     864             :             }
     865             :             else
     866             :             {
     867           0 :                 printfmtstr( GetOUString(), rRes, *pFmt );
     868             :             }
     869             :         }
     870             :         else
     871             :         {
     872           2 :             rRes = GetOUString();
     873             :         }
     874           2 :         break;
     875             :     default:
     876           2 :         rRes = GetOUString();
     877             :     }
     878        1203 : }
     879             : 
     880             : 
     881             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10