bump product version to 5.0.4.1
[LibreOffice.git] / basic / source / sbx / sbxscan.cxx
blob6ba986d97bab27fee4f764cf7ac41d743904f8f3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <config_features.h>
22 #include <tools/errcode.hxx>
23 #include <basic/sbx.hxx>
24 #include "sbxconv.hxx"
26 #include <unotools/syslocale.hxx>
28 #include <stdlib.h>
30 #include <vcl/svapp.hxx>
31 #include <vcl/settings.hxx>
33 #include <math.h>
34 #include <string.h>
35 #include <ctype.h>
37 #include "sbxres.hxx"
38 #include "sbxbase.hxx"
39 #include <basic/sbxfac.hxx>
40 #include <basic/sbxform.hxx>
41 #include <svtools/svtools.hrc>
43 #include "basrid.hxx"
44 #include "date.hxx"
45 #include "runtime.hxx"
47 #include <rtl/strbuf.hxx>
48 #include <svl/zforlist.hxx>
49 #include <comphelper/processfactory.hxx>
52 void ImpGetIntntlSep( sal_Unicode& rcDecimalSep, sal_Unicode& rcThousandSep )
54 SvtSysLocale aSysLocale;
55 const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
56 rcDecimalSep = rData.getNumDecimalSep()[0];
57 rcThousandSep = rData.getNumThousandSep()[0];
60 inline bool ImpIsDigit( sal_Unicode c )
62 return '0' <= c && c <= '9';
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.
69 bool ImpStrChr( const sal_Unicode* p, sal_Unicode c )
71 if (!c)
72 return false;
73 while (*p)
75 if (*p++ == c)
76 return true;
78 return false;
81 bool ImpIsAlNum( sal_Unicode c )
83 return c < 128 && isalnum( static_cast<char>(c) );
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
90 SbxError ImpScan( const OUString& rWSrc, double& nVal, SbxDataType& rType,
91 sal_uInt16* pLen, bool bAllowIntntl, bool bOnlyIntntl )
93 sal_Unicode cIntntlDecSep, cIntntlGrpSep;
94 sal_Unicode cNonIntntlDecSep = '.';
95 if( bAllowIntntl || bOnlyIntntl )
97 ImpGetIntntlSep( cIntntlDecSep, cIntntlGrpSep );
98 if( bOnlyIntntl )
99 cNonIntntlDecSep = cIntntlDecSep;
101 else
103 cIntntlDecSep = cNonIntntlDecSep;
104 cIntntlGrpSep = 0; // no group separator accepted in non-i18n
107 const sal_Unicode* const pStart = rWSrc.getStr();
108 const sal_Unicode* p = pStart;
109 OUStringBuffer aBuf( rWSrc.getLength());
110 bool bRes = true;
111 bool bMinus = false;
112 nVal = 0;
113 SbxDataType eScanType = SbxSINGLE;
114 while( *p == ' ' || *p == '\t' )
115 p++;
116 if( *p == '-' )
118 p++;
119 bMinus = true;
121 if( ImpIsDigit( *p ) || ((*p == cNonIntntlDecSep || *p == cIntntlDecSep ||
122 (cIntntlDecSep && *p == cIntntlGrpSep)) && ImpIsDigit( *(p+1) )))
124 short exp = 0;
125 short decsep = 0;
126 short ndig = 0;
127 short ncdig = 0; // number of digits after decimal point
128 OUStringBuffer aSearchStr("0123456789DEde");
129 aSearchStr.append(cNonIntntlDecSep);
130 if( cIntntlDecSep != cNonIntntlDecSep )
131 aSearchStr.append(cIntntlDecSep);
132 if( bOnlyIntntl )
133 aSearchStr.append(cIntntlGrpSep);
134 const sal_Unicode* const pSearchStr = aSearchStr.getStr();
135 const sal_Unicode pDdEe[] = { 'D', 'd', 'E', 'e', 0 };
136 while( ImpStrChr( pSearchStr, *p ) )
138 aBuf.append( *p );
139 if( bOnlyIntntl && *p == cIntntlGrpSep )
141 p++;
142 continue;
144 if( *p == cNonIntntlDecSep || *p == cIntntlDecSep )
146 // Use the separator that is passed to stringToDouble()
147 aBuf[ p - pStart ] = cIntntlDecSep;
148 p++;
149 if( ++decsep > 1 )
150 continue;
152 else if( ImpStrChr( pDdEe, *p ) )
154 if( ++exp > 1 )
156 p++;
157 continue;
159 if( *p == 'D' || *p == 'd' )
160 eScanType = SbxDOUBLE;
161 aBuf[ p - pStart ] = 'E';
162 p++;
164 else
166 p++;
167 if( decsep && !exp )
168 ncdig++;
170 if( !exp )
171 ndig++;
174 if( decsep > 1 || exp > 1 )
175 bRes = false;
177 OUString aBufStr( aBuf.makeStringAndClear());
178 rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
179 sal_Int32 nParseEnd = 0;
180 nVal = rtl::math::stringToDouble( aBufStr, cIntntlDecSep, cIntntlGrpSep, &eStatus, &nParseEnd );
181 if( eStatus != rtl_math_ConversionStatus_Ok || nParseEnd != aBufStr.getLength() )
182 bRes = false;
184 if( !decsep && !exp )
186 if( nVal >= SbxMININT && nVal <= SbxMAXINT )
187 eScanType = SbxINTEGER;
188 else if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
189 eScanType = SbxLONG;
192 ndig = ndig - decsep;
193 // too many numbers for SINGLE?
194 if( ndig > 15 || ncdig > 6 )
195 eScanType = SbxDOUBLE;
197 // type detection?
198 const sal_Unicode pTypes[] = { '%', '!', '&', '#', 0 };
199 if( ImpStrChr( pTypes, *p ) )
200 p++;
202 // hex/octal number? read in and convert:
203 else if( *p == '&' )
205 p++;
206 eScanType = SbxLONG;
207 OUString aCmp( "0123456789ABCDEFabcdef" );
208 char base = 16;
209 char ndig = 8;
210 switch( *p++ )
212 case 'O':
213 case 'o':
214 aCmp = "01234567";
215 base = 8;
216 ndig = 11;
217 break;
218 case 'H':
219 case 'h':
220 break;
221 default :
222 bRes = false;
224 const sal_Unicode* const pCmp = aCmp.getStr();
225 while( ImpIsAlNum( *p ) ) /* XXX: really munge all alnum also when error? */
227 sal_Unicode ch = *p;
228 if( ImpStrChr( pCmp, ch ) )
230 if (ch > 0x60)
231 ch -= 0x20; // convert ASCII lower to upper case
232 aBuf.append( ch );
234 else
235 bRes = false;
236 p++;
238 OUString aBufStr( aBuf.makeStringAndClear());
239 long l = 0;
240 for( const sal_Unicode* q = aBufStr.getStr(); bRes && *q; q++ )
242 int i = *q - '0';
243 if( i > 9 )
244 i -= 7; // 'A'-'0' = 17 => 10, ...
245 l = ( l * base ) + i;
246 if( !ndig-- )
247 bRes = false;
249 if( *p == '&' )
250 p++;
251 nVal = (double) l;
252 if( l >= SbxMININT && l <= SbxMAXINT )
253 eScanType = SbxINTEGER;
255 #if HAVE_FEATURE_SCRIPTING
256 else if ( SbiRuntime::isVBAEnabled() )
258 OSL_TRACE("Reporting error converting");
259 return SbxERR_CONVERSION;
261 #endif
262 if( pLen )
263 *pLen = (sal_uInt16) ( p - pStart );
264 if( !bRes )
265 return SbxERR_CONVERSION;
266 if( bMinus )
267 nVal = -nVal;
268 rType = eScanType;
269 return SbxERR_OK;
272 // port for CDbl in the Basic
273 SbxError SbxValue::ScanNumIntnl( const OUString& rSrc, double& nVal, bool bSingle )
275 SbxDataType t;
276 sal_uInt16 nLen = 0;
277 SbxError nRetError = ImpScan( rSrc, nVal, t, &nLen,
278 /*bAllowIntntl*/false, /*bOnlyIntntl*/true );
279 // read completely?
280 if( nRetError == SbxERR_OK && nLen != rSrc.getLength() )
282 nRetError = SbxERR_CONVERSION;
284 if( bSingle )
286 SbxValues aValues( nVal );
287 nVal = (double)ImpGetSingle( &aValues ); // here error at overflow
289 return nRetError;
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 };
297 /***************************************************************************
299 |* void myftoa( double, char *, short, short, bool, bool )
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
309 ***************************************************************************/
311 static void myftoa( double nNum, char * pBuf, short nPrec, short nExpWidth,
312 bool bPt, bool bFix, sal_Unicode cForceThousandSep = 0 )
315 short nExp = 0;
316 short nDig = nPrec + 1;
317 short nDec; // number of positions before decimal point
318 int i;
320 sal_Unicode cDecimalSep, cThousandSep;
321 ImpGetIntntlSep( cDecimalSep, cThousandSep );
322 if( cForceThousandSep )
323 cThousandSep = cForceThousandSep;
325 // compute exponent
326 nExp = 0;
327 if( nNum > 0.0 )
329 while( nNum < 1.0 ) nNum *= 10.0, nExp--;
330 while( nNum >= 10.0 ) nNum /= 10.0, nExp++;
332 if( !bFix && !nExpWidth )
333 nDig = nDig + nExp;
334 else if( bFix && !nPrec )
335 nDig = nExp + 1;
337 // round number
338 if( (nNum += roundArray [( nDig > 16 ) ? 16 : nDig] ) >= 10.0 )
340 nNum = 1.0;
341 ++nExp;
342 if( !nExpWidth ) ++nDig;
345 // determine positions before decimal point
346 if( !nExpWidth )
348 if( nExp < 0 )
350 // #41691: also a 0 at bFix
351 *pBuf++ = '0';
352 if( nPrec ) *pBuf++ = (char)cDecimalSep;
353 i = -nExp - 1;
354 if( nDig <= 0 ) i = nPrec;
355 while( i-- ) *pBuf++ = '0';
356 nDec = 0;
358 else
359 nDec = nExp+1;
361 else
362 nDec = 1;
364 // output number
365 if( nDig > 0 )
367 int digit;
368 for( i = 0 ; ; ++i )
370 if( i < 16 )
372 digit = (int) nNum;
373 *pBuf++ = sal::static_int_cast< char >(digit + '0');
374 nNum =( nNum - digit ) * 10.0;
375 } else
376 *pBuf++ = '0';
377 if( --nDig == 0 ) break;
378 if( nDec )
380 nDec--;
381 if( !nDec )
382 *pBuf++ = (char)cDecimalSep;
383 else if( !(nDec % 3 ) && bPt )
384 *pBuf++ = (char)cThousandSep;
389 // output exponent
390 if( nExpWidth )
392 if( nExpWidth < 3 ) nExpWidth = 3;
393 nExpWidth -= 2;
394 *pBuf++ = 'E';
395 *pBuf++ =( nExp < 0 ) ?( (nExp = -nExp ), '-' ) : '+';
396 while( nExpWidth > 3 ) *pBuf++ = '0', nExpWidth--;
397 if( nExp >= 100 || nExpWidth == 3 )
399 *pBuf++ = sal::static_int_cast< char >(nExp/100 + '0');
400 nExp %= 100;
402 if( nExp/10 || nExpWidth >= 2 )
403 *pBuf++ = sal::static_int_cast< char >(nExp/10 + '0');
404 *pBuf++ = sal::static_int_cast< char >(nExp%10 + '0');
406 *pBuf = 0;
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.
414 void ImpCvtNum( double nNum, short nPrec, OUString& rRes, bool bCoreString )
416 char *q;
417 char cBuf[ 40 ], *p = cBuf;
419 sal_Unicode cDecimalSep, cThousandSep;
420 ImpGetIntntlSep( cDecimalSep, cThousandSep );
421 if( bCoreString )
422 cDecimalSep = '.';
424 if( nNum < 0.0 ) {
425 nNum = -nNum;
426 *p++ = '-';
428 double dMaxNumWithoutExp = (nPrec == 6) ? 1E6 : 1E14;
429 myftoa( nNum, p, nPrec,( nNum &&( nNum < 1E-1 || nNum >= dMaxNumWithoutExp ) ) ? 4:0,
430 false, true, cDecimalSep );
431 // remove trailing zeros
432 for( p = cBuf; *p &&( *p != 'E' ); p++ ) {}
433 q = p; p--;
434 while( nPrec && *p == '0' ) nPrec--, p--;
435 if( *p == cDecimalSep ) p--;
436 while( *q ) *++p = *q++;
437 *++p = 0;
438 rRes = OUString::createFromAscii( cBuf );
441 bool ImpConvStringExt( OUString& rSrc, SbxDataType eTargetType )
443 bool bChanged = false;
444 OUString aNewString;
446 // only special cases are handled, nothing on default
447 switch( eTargetType )
449 // consider international for floating point
450 case SbxSINGLE:
451 case SbxDOUBLE:
452 case SbxCURRENCY:
454 sal_Unicode cDecimalSep, cThousandSep;
455 ImpGetIntntlSep( cDecimalSep, cThousandSep );
456 aNewString = rSrc;
458 if( cDecimalSep != (sal_Unicode)'.' )
460 sal_Int32 nPos = aNewString.indexOf( cDecimalSep );
461 if( nPos != -1 )
463 sal_Unicode* pStr = const_cast<sal_Unicode*>(aNewString.getStr());
464 pStr[nPos] = (sal_Unicode)'.';
465 bChanged = true;
468 break;
471 // check as string in case of sal_Bool sal_True and sal_False
472 case SbxBOOL:
474 if( rSrc.equalsIgnoreAsciiCase("true") )
476 aNewString = OUString::number( SbxTRUE );
477 bChanged = true;
479 else if( rSrc.equalsIgnoreAsciiCase("false") )
481 aNewString = OUString::number( SbxFALSE );
482 bChanged = true;
484 break;
486 default: break;
489 if( bChanged )
490 rSrc = aNewString;
491 return bChanged;
495 // formatted number output
496 // the return value is the number of characters used
497 // from the format
499 static sal_uInt16 printfmtstr( const OUString& rStr, OUString& rRes, const OUString& rFmt )
501 OUStringBuffer aTemp;
502 const sal_Unicode* pStr = rStr.getStr();
503 const sal_Unicode* pFmtStart = rFmt.getStr();
504 const sal_Unicode* pFmt = pFmtStart;
506 switch( *pFmt )
508 case '!':
509 aTemp.append(*pStr++);
510 pFmt++;
511 break;
512 case '\\':
515 aTemp.append( *pStr ? *pStr++ : static_cast< sal_Unicode >(' '));
516 pFmt++;
518 while( *pFmt != '\\' );
519 aTemp.append(*pStr ? *pStr++ : static_cast< sal_Unicode >(' '));
520 pFmt++; break;
521 case '&':
522 aTemp = rStr;
523 pFmt++; break;
524 default:
525 aTemp = rStr;
526 break;
528 rRes = aTemp.makeStringAndClear();
529 return (sal_uInt16) ( pFmt - pFmtStart );
533 bool SbxValue::Scan( const OUString& rSrc, sal_uInt16* pLen )
535 SbxError eRes = SbxERR_OK;
536 if( !CanWrite() )
538 eRes = SbxERR_PROP_READONLY;
540 else
542 double n;
543 SbxDataType t;
544 eRes = ImpScan( rSrc, n, t, pLen );
545 if( eRes == SbxERR_OK )
547 if( !IsFixed() )
549 SetType( t );
551 PutDouble( n );
554 if( eRes )
556 SetError( eRes );
557 return false;
559 else
561 return true;
566 ResMgr* implGetResMgr()
568 static ResMgr* pResMgr = NULL;
569 if( !pResMgr )
571 pResMgr = ResMgr::CreateResMgr("sb", Application::GetSettings().GetUILanguageTag() );
573 return pResMgr;
576 class SbxValueFormatResId : public ResId
578 public:
579 SbxValueFormatResId( sal_uInt16 nId )
580 : ResId( nId, *implGetResMgr() )
585 enum VbaFormatType
587 VBA_FORMAT_TYPE_OFFSET, // standard number format
588 VBA_FORMAT_TYPE_USERDEFINED, // user defined number format
589 VBA_FORMAT_TYPE_NULL
592 struct VbaFormatInfo
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
600 #define VBA_FORMAT_OFFSET( pcUtf8, eOffset ) \
601 { VBA_FORMAT_TYPE_OFFSET, OUString(pcUtf8), eOffset, 0 }
603 #define VBA_FORMAT_USERDEFINED( pcUtf8, pcDefinedUtf8 ) \
604 { VBA_FORMAT_TYPE_USERDEFINED, OUString(pcUtf8), NF_NUMBER_STANDARD, pcDefinedUtf8 }
606 static VbaFormatInfo pFormatInfoTable[] =
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 }
621 VbaFormatInfo* getFormatInfo( const OUString& rFmt )
623 VbaFormatInfo* pInfo = NULL;
624 sal_Int16 i = 0;
625 while( (pInfo = pFormatInfoTable + i )->meType != VBA_FORMAT_TYPE_NULL )
627 if( rFmt.equalsIgnoreAsciiCase( pInfo->mpVbaFormat ) )
628 break;
629 i++;
631 return pInfo;
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 ">"
643 void SbxValue::Format( OUString& rRes, const OUString* pFmt ) const
645 short nComma = 0;
646 double d = 0;
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 if( pFmt && !SbxBasicFormater::isBasicFormat( *pFmt ) )
653 OUString aStr = GetOUString();
655 SvtSysLocale aSysLocale;
656 const CharClass& rCharClass = aSysLocale.GetCharClass();
658 if( pFmt->equalsIgnoreAsciiCase( VBAFORMAT_LOWERCASE ) )
660 rRes = rCharClass.lowercase( aStr );
661 return;
663 if( pFmt->equalsIgnoreAsciiCase( VBAFORMAT_UPPERCASE ) )
665 rRes = rCharClass.uppercase( aStr );
666 return;
669 LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
670 SvNumberFormatter aFormatter( comphelper::getProcessComponentContext(), eLangType );
672 sal_uInt32 nIndex = 0;
673 double nNumber;
674 Color* pCol;
676 bool bSuccess = aFormatter.IsNumberFormat( aStr, nIndex, nNumber );
678 // number format, use SvNumberFormatter to handle it.
679 if( bSuccess )
681 sal_Int32 nCheckPos = 0;
682 short nType;
683 OUString aFmtStr = *pFmt;
684 VbaFormatInfo* pInfo = getFormatInfo( aFmtStr );
685 if( pInfo && pInfo->meType != VBA_FORMAT_TYPE_NULL )
687 if( pInfo->meType == VBA_FORMAT_TYPE_OFFSET )
689 nIndex = aFormatter.GetFormatIndex( pInfo->meOffset, eLangType );
691 else
693 aFmtStr = OUString::createFromAscii(pInfo->mpOOoFormat);
694 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
696 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
698 else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_GENERALDATE )
699 || aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_C ))
701 if( nNumber <=-1.0 || nNumber >= 1.0 )
703 // short date
704 nIndex = aFormatter.GetFormatIndex( NF_DATE_SYSTEM_SHORT, eLangType );
705 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
707 // long time
708 if( floor( nNumber ) != nNumber )
710 aFmtStr = "H:MM:SS AM/PM";
711 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
712 OUString aTime;
713 aFormatter.GetOutputString( nNumber, nIndex, aTime, &pCol );
714 rRes += " " + aTime;
717 else
719 // long time only
720 aFmtStr = "H:MM:SS AM/PM";
721 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
722 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
725 else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_N ) ||
726 aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_NN ))
728 sal_Int32 nMin = implGetMinute( nNumber );
729 if( nMin < 10 && aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_NN ))
731 // Minute in two digits
732 sal_Unicode aBuf[2];
733 aBuf[0] = '0';
734 aBuf[1] = '0' + nMin;
735 rRes = OUString(aBuf, SAL_N_ELEMENTS(aBuf));
737 else
739 rRes = OUString::number(nMin);
742 else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_W ))
744 sal_Int32 nWeekDay = implGetWeekDay( nNumber );
745 rRes = OUString::number(nWeekDay);
747 else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_Y ))
749 sal_Int16 nYear = implGetDateYear( nNumber );
750 double dBaseDate;
751 implDateSerial( nYear, 1, 1, dBaseDate );
752 sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate );
753 rRes = OUString::number(nYear32);
755 else
757 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
758 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
761 return;
765 SbxDataType eType = GetType();
766 switch( eType )
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 nComma = 0; goto cvt;
778 case SbxSINGLE:
779 nComma = 6; goto cvt;
780 case SbxDOUBLE:
781 nComma = 14;
783 cvt:
784 if( eType != SbxNULL )
786 d = GetDouble();
788 // #45355 another point to jump in for isnumeric-String
789 cvt2:
790 if( pFmt )
792 SbxAppData& rAppData = GetSbxData_Impl();
794 LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
795 if( rAppData.pBasicFormater )
797 if( rAppData.eBasicFormaterLangType != eLangType )
799 delete rAppData.pBasicFormater;
800 rAppData.pBasicFormater = NULL;
803 rAppData.eBasicFormaterLangType = eLangType;
806 if( !rAppData.pBasicFormater )
808 SvtSysLocale aSysLocale;
809 const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
810 sal_Unicode cComma = rData.getNumDecimalSep()[0];
811 sal_Unicode c1000 = rData.getNumThousandSep()[0];
812 OUString aCurrencyStrg = rData.getCurrSymbol();
814 // initialize the Basic-formater help object:
815 // get resources for predefined output
816 // of the Format()-command, e. g. for "On/Off"
817 OUString aOnStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_ON).toString();
818 OUString aOffStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_OFF).toString();
819 OUString aYesStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_YES).toString();
820 OUString aNoStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_NO).toString();
821 OUString aTrueStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_TRUE).toString();
822 OUString aFalseStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_FALSE).toString();
823 OUString aCurrencyFormatStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_CURRENCY).toString();
825 rAppData.pBasicFormater = new SbxBasicFormater( cComma,c1000,aOnStrg,aOffStrg,
826 aYesStrg,aNoStrg,aTrueStrg,aFalseStrg,
827 aCurrencyStrg,aCurrencyFormatStrg );
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!
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 if( eType != SbxNULL )
841 rRes = rAppData.pBasicFormater->BasicFormat( d ,*pFmt );
843 else
845 rRes = rAppData.pBasicFormater->BasicFormatNull( *pFmt );
849 else
851 OUString aTmpString( rRes );
852 ImpCvtNum( GetDouble(), nComma, aTmpString );
853 rRes = aTmpString;
855 break;
856 case SbxSTRING:
857 if( pFmt )
859 // #45355 converting if numeric
860 if( IsNumericRTL() )
862 ScanNumIntnl( GetOUString(), d, /*bSingle*/false );
863 goto cvt2;
865 else
867 printfmtstr( GetOUString(), rRes, *pFmt );
870 else
872 rRes = GetOUString();
874 break;
875 default:
876 rRes = GetOUString();
881 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */