Bump version to 6.4.7.2.M8
[LibreOffice.git] / basic / source / sbx / sbxscan.cxx
blob0c5c9a77222a039875f36590e9436a7ea14ef39f
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 <vcl/errcode.hxx>
23 #include <unotools/resmgr.hxx>
24 #include "sbxconv.hxx"
26 #include <unotools/syslocale.hxx>
27 #include <unotools/charclass.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/settings.hxx>
32 #include <math.h>
34 #include <sbxbase.hxx>
35 #include <sbintern.hxx>
36 #include <basic/sbxform.hxx>
38 #include <date.hxx>
39 #include <runtime.hxx>
40 #include <strings.hrc>
42 #include <rtl/character.hxx>
43 #include <sal/log.hxx>
44 #include <svl/zforlist.hxx>
47 void ImpGetIntntlSep( sal_Unicode& rcDecimalSep, sal_Unicode& rcThousandSep, sal_Unicode& rcDecimalSepAlt )
49 SvtSysLocale aSysLocale;
50 const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
51 rcDecimalSep = rData.getNumDecimalSep()[0];
52 rcThousandSep = rData.getNumThousandSep()[0];
53 rcDecimalSepAlt = rData.getNumDecimalSepAlt().toChar();
57 /** NOTE: slightly differs from strchr() in that it does not consider the
58 terminating NULL character to be part of the string and returns bool
59 instead of pointer, if character is 0 returns false.
61 static bool ImpStrChr( const sal_Unicode* p, sal_Unicode c )
63 if (!c)
64 return false;
65 while (*p)
67 if (*p++ == c)
68 return true;
70 return false;
74 // scanning a string according to BASIC-conventions
75 // but exponent may also be a D, so data type is SbxDOUBLE
76 // conversion error if data type is fixed and it doesn't fit
78 ErrCode ImpScan( const OUString& rWSrc, double& nVal, SbxDataType& rType,
79 sal_uInt16* pLen, bool bOnlyIntntl )
81 sal_Unicode cIntntlDecSep, cIntntlGrpSep, cIntntlDecSepAlt;
82 sal_Unicode cNonIntntlDecSep = '.';
83 if( bOnlyIntntl )
85 ImpGetIntntlSep( cIntntlDecSep, cIntntlGrpSep, cIntntlDecSepAlt );
86 cNonIntntlDecSep = cIntntlDecSep;
87 // Ensure that the decimal separator alternative is really one.
88 if (cIntntlDecSepAlt && cIntntlDecSepAlt == cNonIntntlDecSep)
89 cIntntlDecSepAlt = 0;
91 else
93 cIntntlDecSep = cNonIntntlDecSep;
94 cIntntlGrpSep = 0; // no group separator accepted in non-i18n
95 cIntntlDecSepAlt = 0;
98 const sal_Unicode* const pStart = rWSrc.getStr();
99 const sal_Unicode* p = pStart;
100 OUStringBuffer aBuf( rWSrc.getLength());
101 bool bRes = true;
102 bool bMinus = false;
103 nVal = 0;
104 SbxDataType eScanType = SbxSINGLE;
105 while( *p == ' ' || *p == '\t' )
106 p++;
107 if( *p == '-' )
109 p++;
110 bMinus = true;
112 if( rtl::isAsciiDigit( *p ) || ((*p == cNonIntntlDecSep || *p == cIntntlDecSep ||
113 (cIntntlDecSep && *p == cIntntlGrpSep) || (cIntntlDecSepAlt && *p == cIntntlDecSepAlt)) &&
114 rtl::isAsciiDigit( *(p+1) )))
116 // tdf#118442: Whitespace and minus are skipped; store the position to calculate index
117 const sal_Unicode* const pDigitsStart = p;
118 short exp = 0;
119 short decsep = 0;
120 short ndig = 0;
121 short ncdig = 0; // number of digits after decimal point
122 OUStringBuffer aSearchStr("0123456789DEde");
123 aSearchStr.append(cNonIntntlDecSep);
124 if( cIntntlDecSep != cNonIntntlDecSep )
125 aSearchStr.append(cIntntlDecSep);
126 if( cIntntlDecSepAlt && cIntntlDecSepAlt != cNonIntntlDecSep )
127 aSearchStr.append(cIntntlDecSepAlt);
128 if( bOnlyIntntl )
129 aSearchStr.append(cIntntlGrpSep);
130 const sal_Unicode* const pSearchStr = aSearchStr.getStr();
131 const sal_Unicode pDdEe[] = { 'D', 'd', 'E', 'e', 0 };
132 while( ImpStrChr( pSearchStr, *p ) )
134 aBuf.append( *p );
135 if( bOnlyIntntl && *p == cIntntlGrpSep )
137 p++;
138 continue;
140 if( *p == cNonIntntlDecSep || *p == cIntntlDecSep || (cIntntlDecSepAlt && *p == cIntntlDecSepAlt) )
142 // Use the separator that is passed to stringToDouble()
143 aBuf[p - pDigitsStart] = cIntntlDecSep;
144 p++;
145 if( ++decsep > 1 )
146 continue;
148 else if( ImpStrChr( pDdEe, *p ) )
150 if( ++exp > 1 )
152 p++;
153 continue;
155 if( *p == 'D' || *p == 'd' )
156 eScanType = SbxDOUBLE;
157 aBuf[p - pDigitsStart] = 'E';
158 p++;
159 if (*p == '+')
160 ++p;
161 else if (*p == '-')
163 aBuf.append('-');
164 ++p;
167 else
169 p++;
170 if( decsep && !exp )
171 ncdig++;
173 if( !exp )
174 ndig++;
177 if( decsep > 1 || exp > 1 )
178 bRes = false;
180 OUString aBufStr( aBuf.makeStringAndClear());
181 rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
182 sal_Int32 nParseEnd = 0;
183 nVal = rtl::math::stringToDouble( aBufStr, cIntntlDecSep, cIntntlGrpSep, &eStatus, &nParseEnd );
184 if( eStatus != rtl_math_ConversionStatus_Ok || nParseEnd != aBufStr.getLength() )
185 bRes = false;
187 if( !decsep && !exp )
189 if( nVal >= SbxMININT && nVal <= SbxMAXINT )
190 eScanType = SbxINTEGER;
191 else if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
192 eScanType = SbxLONG;
195 ndig = ndig - decsep;
196 // too many numbers for SINGLE?
197 if( ndig > 15 || ncdig > 6 )
198 eScanType = SbxDOUBLE;
200 // type detection?
201 const sal_Unicode pTypes[] = { '%', '!', '&', '#', 0 };
202 if( ImpStrChr( pTypes, *p ) )
203 p++;
205 // hex/octal number? read in and convert:
206 else if( *p == '&' )
208 p++;
209 eScanType = SbxLONG;
210 OUString aCmp( "0123456789ABCDEFabcdef" );
211 char base = 16;
212 char ndig = 8;
213 switch( *p++ )
215 case 'O':
216 case 'o':
217 aCmp = "01234567";
218 base = 8;
219 ndig = 11;
220 break;
221 case 'H':
222 case 'h':
223 break;
224 default :
225 bRes = false;
227 const sal_Unicode* const pCmp = aCmp.getStr();
228 while( rtl::isAsciiAlphanumeric( *p ) ) /* XXX: really munge all alnum also when error? */
230 sal_Unicode ch = *p;
231 if( ImpStrChr( pCmp, ch ) )
233 if (ch > 0x60)
234 ch -= 0x20; // convert ASCII lower to upper case
235 aBuf.append( ch );
237 else
238 bRes = false;
239 p++;
241 OUString aBufStr( aBuf.makeStringAndClear());
242 sal_Int32 l = 0;
243 for( const sal_Unicode* q = aBufStr.getStr(); bRes && *q; q++ )
245 int i = *q - '0';
246 if( i > 9 )
247 i -= 7; // 'A'-'0' = 17 => 10, ...
248 l = ( l * base ) + i;
249 if( !ndig-- )
250 bRes = false;
252 if( *p == '&' )
253 p++;
254 nVal = static_cast<double>(l);
255 if( l >= SbxMININT && l <= SbxMAXINT )
256 eScanType = SbxINTEGER;
258 #if HAVE_FEATURE_SCRIPTING
259 else if ( SbiRuntime::isVBAEnabled() )
261 SAL_WARN("basic", "Reporting error converting");
262 return ERRCODE_BASIC_CONVERSION;
264 #endif
265 if( pLen )
266 *pLen = static_cast<sal_uInt16>( p - pStart );
267 if( !bRes )
268 return ERRCODE_BASIC_CONVERSION;
269 if( bMinus )
270 nVal = -nVal;
271 rType = eScanType;
272 return ERRCODE_NONE;
275 // port for CDbl in the Basic
276 ErrCode SbxValue::ScanNumIntnl( const OUString& rSrc, double& nVal, bool bSingle )
278 SbxDataType t;
279 sal_uInt16 nLen = 0;
280 ErrCode nRetError = ImpScan( rSrc, nVal, t, &nLen,
281 /*bOnlyIntntl*/true );
282 // read completely?
283 if( nRetError == ERRCODE_NONE && nLen != rSrc.getLength() )
285 nRetError = ERRCODE_BASIC_CONVERSION;
287 if( bSingle )
289 SbxValues aValues( nVal );
290 nVal = static_cast<double>(ImpGetSingle( &aValues )); // here error at overflow
292 return nRetError;
296 static const double roundArray[] = {
297 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,
298 0.5e-8, 0.5e-9, 0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14,0.5e-15 };
302 |* void myftoa( double, char *, short, short, bool, bool )
304 |* description: conversion double --> ASCII
305 |* parameters: double the number
306 |* char * target buffer
307 |* short number of positions after decimal point
308 |* short range of the exponent ( 0=no E )
309 |* bool true: with 1000-separators
310 |* bool true: output without formatting
314 static void myftoa( double nNum, char * pBuf, short nPrec, short nExpWidth,
315 sal_Unicode cForceThousandSep )
318 short nExp = 0;
319 short nDig = nPrec + 1;
320 short nDec; // number of positions before decimal point
321 int i;
323 sal_Unicode cDecimalSep, cThousandSep, cDecimalSepAlt;
324 ImpGetIntntlSep( cDecimalSep, cThousandSep, cDecimalSepAlt );
325 if( cForceThousandSep )
326 cThousandSep = cForceThousandSep;
328 // compute exponent
329 nExp = 0;
330 if( nNum > 0.0 )
332 while( nNum < 1.0 )
334 nNum *= 10.0;
335 nExp--;
337 while( nNum >= 10.0 )
339 nNum /= 10.0;
340 nExp++;
343 if( !nPrec )
344 nDig = nExp + 1;
346 // round number
347 if( (nNum += roundArray [std::min<short>( nDig, 16 )] ) >= 10.0 )
349 nNum = 1.0;
350 ++nExp;
351 if( !nExpWidth ) ++nDig;
354 // determine positions before decimal point
355 if( !nExpWidth )
357 if( nExp < 0 )
359 // #41691: also a 0 at bFix
360 *pBuf++ = '0';
361 if( nPrec ) *pBuf++ = static_cast<char>(cDecimalSep);
362 i = -nExp - 1;
363 if( nDig <= 0 ) i = nPrec;
364 while( i-- ) *pBuf++ = '0';
365 nDec = 0;
367 else
368 nDec = nExp+1;
370 else
371 nDec = 1;
373 // output number
374 if( nDig > 0 )
376 int digit;
377 for( i = 0 ; ; ++i )
379 if( i < 16 )
381 digit = static_cast<int>(nNum);
382 *pBuf++ = sal::static_int_cast< char >(digit + '0');
383 nNum =( nNum - digit ) * 10.0;
384 } else
385 *pBuf++ = '0';
386 if( --nDig == 0 ) break;
387 if( nDec )
389 nDec--;
390 if( !nDec )
391 *pBuf++ = static_cast<char>(cDecimalSep);
396 // output exponent
397 if( nExpWidth )
399 if( nExpWidth < 3 ) nExpWidth = 3;
400 nExpWidth -= 2;
401 *pBuf++ = 'E';
402 if ( nExp < 0 )
404 nExp = -nExp;
405 *pBuf++ = '-';
407 else
408 *pBuf++ = '+';
409 while( nExpWidth > 3 )
411 *pBuf++ = '0';
412 nExpWidth--;
414 if( nExp >= 100 || nExpWidth == 3 )
416 *pBuf++ = sal::static_int_cast< char >(nExp/100 + '0');
417 nExp %= 100;
419 if( nExp/10 || nExpWidth >= 2 )
420 *pBuf++ = sal::static_int_cast< char >(nExp/10 + '0');
421 *pBuf++ = sal::static_int_cast< char >(nExp%10 + '0');
423 *pBuf = 0;
426 // The number is prepared unformattedly with the given number of
427 // NK-positions. A leading minus is added if applicable.
428 // This routine is public because it's also used by the Put-functions
429 // in the class SbxImpSTRING.
431 void ImpCvtNum( double nNum, short nPrec, OUString& rRes, bool bCoreString )
433 char *q;
434 char cBuf[ 40 ], *p = cBuf;
436 sal_Unicode cDecimalSep, cThousandSep, cDecimalSepAlt;
437 ImpGetIntntlSep( cDecimalSep, cThousandSep, cDecimalSepAlt );
438 if( bCoreString )
439 cDecimalSep = '.';
441 if( nNum < 0.0 ) {
442 nNum = -nNum;
443 *p++ = '-';
445 double dMaxNumWithoutExp = (nPrec == 6) ? 1E6 : 1E14;
446 myftoa( nNum, p, nPrec,( nNum &&( nNum < 1E-1 || nNum >= dMaxNumWithoutExp ) ) ? 4:0,
447 cDecimalSep );
448 // remove trailing zeros
449 for( p = cBuf; *p &&( *p != 'E' ); p++ ) {}
450 q = p; p--;
451 while( nPrec && *p == '0' )
453 nPrec--;
454 p--;
456 if( *p == cDecimalSep ) p--;
457 while( *q ) *++p = *q++;
458 *++p = 0;
459 rRes = OUString::createFromAscii( cBuf );
462 bool ImpConvStringExt( OUString& rSrc, SbxDataType eTargetType )
464 bool bChanged = false;
465 OUString aNewString;
467 // only special cases are handled, nothing on default
468 switch( eTargetType )
470 // consider international for floating point
471 case SbxSINGLE:
472 case SbxDOUBLE:
473 case SbxCURRENCY:
475 sal_Unicode cDecimalSep, cThousandSep, cDecimalSepAlt;
476 ImpGetIntntlSep( cDecimalSep, cThousandSep, cDecimalSepAlt );
477 aNewString = rSrc;
479 if( cDecimalSep != '.' || (cDecimalSepAlt && cDecimalSepAlt != '.') )
481 sal_Int32 nPos = aNewString.indexOf( cDecimalSep );
482 if( nPos == -1 && cDecimalSepAlt )
483 nPos = aNewString.indexOf( cDecimalSepAlt );
484 if( nPos != -1 )
486 sal_Unicode* pStr = const_cast<sal_Unicode*>(aNewString.getStr());
487 pStr[nPos] = '.';
488 bChanged = true;
491 break;
494 // check as string in case of sal_Bool sal_True and sal_False
495 case SbxBOOL:
497 if( rSrc.equalsIgnoreAsciiCase("true") )
499 aNewString = OUString::number( SbxTRUE );
500 bChanged = true;
502 else if( rSrc.equalsIgnoreAsciiCase("false") )
504 aNewString = OUString::number( SbxFALSE );
505 bChanged = true;
507 break;
509 default: break;
512 if( bChanged )
513 rSrc = aNewString;
514 return bChanged;
518 // formatted number output
519 // the return value is the number of characters used
520 // from the format
522 static sal_uInt16 printfmtstr( const OUString& rStr, OUString& rRes, const OUString& rFmt )
524 OUStringBuffer aTemp;
525 const sal_Unicode* pStr = rStr.getStr();
526 const sal_Unicode* pFmtStart = rFmt.getStr();
527 const sal_Unicode* pFmt = pFmtStart;
529 switch( *pFmt )
531 case '!':
532 aTemp.append(*pStr++);
533 pFmt++;
534 break;
535 case '\\':
538 aTemp.append( *pStr ? *pStr++ : u' ');
539 pFmt++;
541 while( *pFmt && *pFmt != '\\' );
542 aTemp.append(*pStr ? *pStr++ : u' ');
543 pFmt++; break;
544 case '&':
545 aTemp = rStr;
546 pFmt++; break;
547 default:
548 aTemp = rStr;
549 break;
551 rRes = aTemp.makeStringAndClear();
552 return static_cast<sal_uInt16>( pFmt - pFmtStart );
556 bool SbxValue::Scan( const OUString& rSrc, sal_uInt16* pLen )
558 ErrCode eRes = ERRCODE_NONE;
559 if( !CanWrite() )
561 eRes = ERRCODE_BASIC_PROP_READONLY;
563 else
565 double n;
566 SbxDataType t;
567 eRes = ImpScan( rSrc, n, t, pLen, false );
568 if( eRes == ERRCODE_NONE )
570 if( !IsFixed() )
572 SetType( t );
574 PutDouble( n );
577 if( eRes )
579 SetError( eRes );
580 return false;
582 else
584 return true;
588 std::locale BasResLocale()
590 return Translate::Create("sb");
593 OUString BasResId(const char *pId)
595 return Translate::get(pId, BasResLocale());
598 namespace
601 enum class VbaFormatType
603 Offset, // standard number format
604 UserDefined, // user defined number format
605 Null
608 struct VbaFormatInfo
610 VbaFormatType meType;
611 OUStringLiteral mpVbaFormat; // Format string in vba
612 NfIndexTableOffset meOffset; // SvNumberFormatter format index, if meType = VbaFormatType::Offset
613 const char* mpOOoFormat; // if meType = VbaFormatType::UserDefined
616 #if HAVE_FEATURE_SCRIPTING
617 const VbaFormatInfo pFormatInfoTable[] =
619 { VbaFormatType::Offset, OUStringLiteral("Long Date"), NF_DATE_SYSTEM_LONG, nullptr },
620 { VbaFormatType::UserDefined, OUStringLiteral("Medium Date"), NF_NUMBER_STANDARD, "DD-MMM-YY" },
621 { VbaFormatType::Offset, OUStringLiteral("Short Date"), NF_DATE_SYSTEM_SHORT, nullptr },
622 { VbaFormatType::UserDefined, OUStringLiteral("Long Time"), NF_NUMBER_STANDARD, "H:MM:SS AM/PM" },
623 { VbaFormatType::Offset, OUStringLiteral("Medium Time"), NF_TIME_HHMMAMPM, nullptr },
624 { VbaFormatType::Offset, OUStringLiteral("Short Time"), NF_TIME_HHMM, nullptr },
625 { VbaFormatType::Offset, OUStringLiteral("ddddd"), NF_DATE_SYSTEM_SHORT, nullptr },
626 { VbaFormatType::Offset, OUStringLiteral("dddddd"), NF_DATE_SYSTEM_LONG, nullptr },
627 { VbaFormatType::UserDefined, OUStringLiteral("ttttt"), NF_NUMBER_STANDARD, "H:MM:SS AM/PM" },
628 { VbaFormatType::Offset, OUStringLiteral("ww"), NF_DATE_WW, nullptr },
629 { VbaFormatType::Null, OUStringLiteral(""), NF_INDEX_TABLE_ENTRIES, nullptr }
632 const VbaFormatInfo* getFormatInfo( const OUString& rFmt )
634 const VbaFormatInfo* pInfo = pFormatInfoTable;
635 while( pInfo->meType != VbaFormatType::Null )
637 if( rFmt.equalsIgnoreAsciiCase( pInfo->mpVbaFormat ) )
638 break;
639 ++pInfo;
641 return pInfo;
643 #endif
645 } // namespace
647 #if HAVE_FEATURE_SCRIPTING
648 #define VBAFORMAT_GENERALDATE "General Date"
649 #define VBAFORMAT_C "c"
650 #define VBAFORMAT_N "n"
651 #define VBAFORMAT_NN "nn"
652 #define VBAFORMAT_W "w"
653 #define VBAFORMAT_Y "y"
654 #define VBAFORMAT_LOWERCASE "<"
655 #define VBAFORMAT_UPPERCASE ">"
656 #endif
658 void SbxValue::Format( OUString& rRes, const OUString* pFmt ) const
660 short nComma = 0;
661 double d = 0;
663 // pflin, It is better to use SvNumberFormatter to handle the date/time/number format.
664 // the SvNumberFormatter output is mostly compatible with
665 // VBA output besides the OOo-basic output
666 #if HAVE_FEATURE_SCRIPTING
667 if( pFmt && !SbxBasicFormater::isBasicFormat( *pFmt ) )
669 OUString aStr = GetOUString();
671 SvtSysLocale aSysLocale;
672 const CharClass& rCharClass = aSysLocale.GetCharClass();
674 if( pFmt->equalsIgnoreAsciiCase( VBAFORMAT_LOWERCASE ) )
676 rRes = rCharClass.lowercase( aStr );
677 return;
679 if( pFmt->equalsIgnoreAsciiCase( VBAFORMAT_UPPERCASE ) )
681 rRes = rCharClass.uppercase( aStr );
682 return;
685 LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
686 std::shared_ptr<SvNumberFormatter> pFormatter;
687 if (GetSbData()->pInst)
689 pFormatter = GetSbData()->pInst->GetNumberFormatter();
691 else
693 sal_uInt32 n; // Dummy
694 pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n );
697 // Passing an index of a locale switches IsNumberFormat() to use that
698 // locale in case the formatter wasn't default created with it.
699 sal_uInt32 nIndex = pFormatter->GetStandardIndex( eLangType);
700 double nNumber;
701 Color* pCol;
703 bool bSuccess = pFormatter->IsNumberFormat( aStr, nIndex, nNumber );
705 // number format, use SvNumberFormatter to handle it.
706 if( bSuccess )
708 sal_Int32 nCheckPos = 0;
709 SvNumFormatType nType;
710 OUString aFmtStr = *pFmt;
711 const VbaFormatInfo* pInfo = getFormatInfo( aFmtStr );
712 if( pInfo->meType != VbaFormatType::Null )
714 if( pInfo->meType == VbaFormatType::Offset )
716 nIndex = pFormatter->GetFormatIndex( pInfo->meOffset, eLangType );
718 else
720 aFmtStr = OUString::createFromAscii(pInfo->mpOOoFormat);
721 pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType, true);
723 pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
725 else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_GENERALDATE )
726 || aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_C ))
728 if( nNumber <=-1.0 || nNumber >= 1.0 )
730 // short date
731 nIndex = pFormatter->GetFormatIndex( NF_DATE_SYSTEM_SHORT, eLangType );
732 pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
734 // long time
735 if( floor( nNumber ) != nNumber )
737 aFmtStr = "H:MM:SS AM/PM";
738 pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType, true);
739 OUString aTime;
740 pFormatter->GetOutputString( nNumber, nIndex, aTime, &pCol );
741 rRes += " " + aTime;
744 else
746 // long time only
747 aFmtStr = "H:MM:SS AM/PM";
748 pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType, true);
749 pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
752 else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_N ) ||
753 aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_NN ))
755 sal_Int32 nMin = implGetMinute( nNumber );
756 if( nMin < 10 && aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_NN ))
758 // Minute in two digits
759 sal_Unicode aBuf[2];
760 aBuf[0] = '0';
761 aBuf[1] = '0' + nMin;
762 rRes = OUString(aBuf, SAL_N_ELEMENTS(aBuf));
764 else
766 rRes = OUString::number(nMin);
769 else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_W ))
771 sal_Int32 nWeekDay = implGetWeekDay( nNumber );
772 rRes = OUString::number(nWeekDay);
774 else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_Y ))
776 sal_Int16 nYear = implGetDateYear( nNumber );
777 double dBaseDate;
778 implDateSerial( nYear, 1, 1, true, SbDateCorrection::None, dBaseDate );
779 sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate );
780 rRes = OUString::number(nYear32);
782 else
784 pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType, true);
785 pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
788 return;
791 #endif
793 SbxDataType eType = GetType();
794 switch( eType )
796 case SbxCHAR:
797 case SbxBYTE:
798 case SbxINTEGER:
799 case SbxUSHORT:
800 case SbxLONG:
801 case SbxULONG:
802 case SbxINT:
803 case SbxUINT:
804 case SbxNULL: // #45929 NULL with a little cheating
805 nComma = 0; goto cvt;
806 case SbxSINGLE:
807 nComma = 6; goto cvt;
808 case SbxDOUBLE:
809 nComma = 14;
811 cvt:
812 if( eType != SbxNULL )
814 d = GetDouble();
816 // #45355 another point to jump in for isnumeric-String
817 cvt2:
818 if( pFmt )
820 SbxAppData& rAppData = GetSbxData_Impl();
822 LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
823 if( rAppData.pBasicFormater )
825 if( rAppData.eBasicFormaterLangType != eLangType )
827 rAppData.pBasicFormater.reset();
830 rAppData.eBasicFormaterLangType = eLangType;
833 if( !rAppData.pBasicFormater )
835 SvtSysLocale aSysLocale;
836 const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
837 sal_Unicode cComma = rData.getNumDecimalSep()[0];
838 sal_Unicode c1000 = rData.getNumThousandSep()[0];
839 const OUString& aCurrencyStrg = rData.getCurrSymbol();
841 // initialize the Basic-formater help object:
842 // get resources for predefined output
843 // of the Format()-command, e. g. for "On/Off"
844 OUString aOnStrg = BasResId(STR_BASICKEY_FORMAT_ON);
845 OUString aOffStrg = BasResId(STR_BASICKEY_FORMAT_OFF);
846 OUString aYesStrg = BasResId(STR_BASICKEY_FORMAT_YES);
847 OUString aNoStrg = BasResId(STR_BASICKEY_FORMAT_NO);
848 OUString aTrueStrg = BasResId(STR_BASICKEY_FORMAT_TRUE);
849 OUString aFalseStrg = BasResId(STR_BASICKEY_FORMAT_FALSE);
850 OUString aCurrencyFormatStrg = BasResId(STR_BASICKEY_FORMAT_CURRENCY);
852 rAppData.pBasicFormater = std::make_unique<SbxBasicFormater>(
853 cComma,c1000,aOnStrg,aOffStrg,
854 aYesStrg,aNoStrg,aTrueStrg,aFalseStrg,
855 aCurrencyStrg,aCurrencyFormatStrg );
857 // Remark: For performance reasons there's only ONE BasicFormater-
858 // object created and 'stored', so that the expensive resource-
859 // loading is saved (for country-specific predefined outputs,
860 // e. g. "On/Off") and the continuous string-creation
861 // operations, too.
862 // BUT: therefore this code is NOT multithreading capable!
864 // here are problems with ;;;Null because this method is only
865 // called, if SbxValue is a number!!!
866 // in addition rAppData.pBasicFormater->BasicFormatNull( *pFmt ); could be called!
867 if( eType != SbxNULL )
869 rRes = rAppData.pBasicFormater->BasicFormat( d ,*pFmt );
871 else
873 rRes = SbxBasicFormater::BasicFormatNull( *pFmt );
877 else
878 ImpCvtNum( GetDouble(), nComma, rRes );
879 break;
880 case SbxSTRING:
881 if( pFmt )
883 // #45355 converting if numeric
884 if( IsNumericRTL() )
886 ScanNumIntnl( GetOUString(), d );
887 goto cvt2;
889 else
891 printfmtstr( GetOUString(), rRes, *pFmt );
894 else
896 rRes = GetOUString();
898 break;
899 default:
900 rRes = GetOUString();
905 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */