xref: /core/basic/source/sbx/sbxscan.cxx (revision c76fb95d)
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 <sal/config.h>
21 
22 #include <string_view>
23 
24 #include <config_features.h>
25 
26 #include <comphelper/errcode.hxx>
27 #include <unotools/resmgr.hxx>
28 #include "sbxconv.hxx"
29 #include <rtlproto.hxx>
30 
31 #include <unotools/syslocale.hxx>
32 #include <unotools/charclass.hxx>
33 
34 #include <vcl/svapp.hxx>
35 #include <vcl/settings.hxx>
36 
37 #include <math.h>
38 
39 #include <sbxbase.hxx>
40 #include <sbintern.hxx>
41 #include <sbxform.hxx>
42 
43 #include <date.hxx>
44 #include <runtime.hxx>
45 #include <strings.hrc>
46 
47 #include <rtl/character.hxx>
48 #include <rtl/math.hxx>
49 #include <svl/numformat.hxx>
50 #include <svl/zforlist.hxx>
51 #include <o3tl/string_view.hxx>
52 
53 
54 void ImpGetIntntlSep( sal_Unicode& rcDecimalSep, sal_Unicode& rcThousandSep, sal_Unicode& rcDecimalSepAlt )
55 {
56     SvtSysLocale aSysLocale;
57     const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
58     rcDecimalSep = rData.getNumDecimalSep()[0];
59     rcThousandSep = rData.getNumThousandSep()[0];
60     rcDecimalSepAlt = rData.getNumDecimalSepAlt().toChar();
61 }
62 
63 
64 static bool ImpStrChr( std::u16string_view str, sal_Unicode c ) { return str.find(c) != std::u16string_view::npos; }
65 
66 
67 // scanning a string according to BASIC-conventions
68 // but exponent may also be a D, so data type is SbxDOUBLE
69 // conversion error if data type is fixed and it doesn't fit
70 
71 ErrCode ImpScan( const OUString& rWSrc, double& nVal, SbxDataType& rType,
72                   sal_uInt16* pLen, bool bOnlyIntntl )
73 {
74     sal_Unicode cIntntlDecSep, cIntntlGrpSep, cIntntlDecSepAlt;
75     sal_Unicode cNonIntntlDecSep = '.';
76     if( bOnlyIntntl )
77     {
78         ImpGetIntntlSep( cIntntlDecSep, cIntntlGrpSep, cIntntlDecSepAlt );
79         cNonIntntlDecSep = cIntntlDecSep;
80         // Ensure that the decimal separator alternative is really one.
81         if (cIntntlDecSepAlt && cIntntlDecSepAlt == cNonIntntlDecSep)
82             cIntntlDecSepAlt = 0;
83     }
84     else
85     {
86         cIntntlDecSep = cNonIntntlDecSep;
87         cIntntlGrpSep = 0;  // no group separator accepted in non-i18n
88         cIntntlDecSepAlt = 0;
89     }
90 
91     const sal_Unicode* const pStart = rWSrc.getStr();
92     const sal_Unicode* p = pStart;
93     OUStringBuffer aBuf( rWSrc.getLength());
94     bool bRes = true;
95     bool bMinus = false;
96     nVal = 0;
97     SbxDataType eScanType = SbxSINGLE;
98     while( *p == ' ' || *p == '\t' )
99         p++;
100     if (*p == '+')
101         p++;
102     else if( *p == '-' )
103     {
104         p++;
105         bMinus = true;
106     }
107     if( rtl::isAsciiDigit( *p ) || ((*p == cNonIntntlDecSep || *p == cIntntlDecSep ||
108                     (cIntntlDecSep && *p == cIntntlGrpSep) || (cIntntlDecSepAlt && *p == cIntntlDecSepAlt)) &&
109                 rtl::isAsciiDigit( *(p+1) )))
110     {
111         // tdf#118442: Whitespace and minus are skipped; store the position to calculate index
112         const sal_Unicode* const pDigitsStart = p;
113         short exp = 0;
114         short decsep = 0;
115         short ndig = 0;
116         short ncdig = 0;    // number of digits after decimal point
117         OUStringBuffer aSearchStr(OUString::Concat("0123456789DEde") + OUStringChar(cNonIntntlDecSep));
118         if( cIntntlDecSep != cNonIntntlDecSep )
119             aSearchStr.append(cIntntlDecSep);
120         if( cIntntlDecSepAlt && cIntntlDecSepAlt != cNonIntntlDecSep )
121             aSearchStr.append(cIntntlDecSepAlt);
122         if( bOnlyIntntl )
123             aSearchStr.append(cIntntlGrpSep);
124         const OUString pSearchStr = aSearchStr.makeStringAndClear();
125         static const OUStringLiteral pDdEe = u"DdEe";
126         while( ImpStrChr( pSearchStr, *p ) )
127         {
128             aBuf.append( *p );
129             if( bOnlyIntntl && *p == cIntntlGrpSep )
130             {
131                 p++;
132                 continue;
133             }
134             if( *p == cNonIntntlDecSep || *p == cIntntlDecSep || (cIntntlDecSepAlt && *p == cIntntlDecSepAlt) )
135             {
136                 // Use the separator that is passed to stringToDouble()
137                 aBuf[p - pDigitsStart] = cIntntlDecSep;
138                 p++;
139                 if( ++decsep > 1 )
140                     continue;
141             }
142             else if( ImpStrChr( pDdEe, *p ) )
143             {
144                 if( ++exp > 1 )
145                 {
146                     p++;
147                     continue;
148                 }
149                 if( *p == 'D' || *p == 'd' )
150                     eScanType = SbxDOUBLE;
151                 aBuf[p - pDigitsStart] = 'E';
152                 p++;
153                 if (*p == '+')
154                     ++p;
155                 else if (*p == '-')
156                 {
157                     aBuf.append('-');
158                     ++p;
159                 }
160             }
161             else
162             {
163                 p++;
164                 if( decsep && !exp )
165                     ncdig++;
166             }
167             if( !exp )
168                 ndig++;
169         }
170 
171         if( decsep > 1 || exp > 1 )
172             bRes = false;
173 
174         rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
175         sal_Int32 nParseEnd = 0;
176         nVal = rtl::math::stringToDouble( aBuf, cIntntlDecSep, cIntntlGrpSep, &eStatus, &nParseEnd );
177         if( eStatus != rtl_math_ConversionStatus_Ok || nParseEnd != aBuf.getLength() )
178             bRes = false;
179 
180         if( !decsep && !exp )
181         {
182             if( nVal >= SbxMININT && nVal <= SbxMAXINT )
183                 eScanType = SbxINTEGER;
184             else if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
185                 eScanType = SbxLONG;
186         }
187 
188         ndig = ndig - decsep;
189         // too many numbers for SINGLE?
190         if( ndig > 15 || ncdig > 6 )
191             eScanType = SbxDOUBLE;
192 
193         // type detection?
194         static const OUStringLiteral pTypes = u"%!&#";
195         if( ImpStrChr( pTypes, *p ) )
196             p++;
197     }
198     // hex/octal number? read in and convert:
199     else if( *p == '&' )
200     {
201         p++;
202         eScanType = SbxLONG;
203         OUString aCmp( "0123456789ABCDEF" );
204         char base = 16;
205         char ndig = 8;
206         switch( *p++ )
207         {
208             case 'O':
209             case 'o':
210                 aCmp = "01234567";
211                 base = 8;
212                 ndig = 11;
213                 break;
214             case 'H':
215             case 'h':
216                 break;
217             default :
218                 bRes = false;
219         }
220         while( rtl::isAsciiAlphanumeric( *p ) )    /* XXX: really munge all alnum also when error? */
221         {
222             sal_Unicode ch = rtl::toAsciiUpperCase(*p);
223             if( ImpStrChr( aCmp, ch ) )
224                 aBuf.append( ch );
225             else
226                 bRes = false;
227             p++;
228         }
229         OUString aBufStr( aBuf.makeStringAndClear());
230         sal_Int32 l = 0;
231         for( const sal_Unicode* q = aBufStr.getStr(); bRes && *q; q++ )
232         {
233             int i = *q - '0';
234             if( i > 9 )
235                 i -= 7;     // 'A'-'0' = 17 => 10, ...
236             l = ( l * base ) + i;
237             if( !ndig-- )
238                 bRes = false;
239         }
240         if( *p == '&' )
241             p++;
242         nVal = static_cast<double>(l);
243         if( l >= SbxMININT && l <= SbxMAXINT )
244             eScanType = SbxINTEGER;
245     }
246 #if HAVE_FEATURE_SCRIPTING
247     else if ( SbiRuntime::isVBAEnabled() )
248     {
249         return ERRCODE_BASIC_CONVERSION;
250     }
251 #endif
252     // tdf#146672 - skip whitespaces and tabs at the end of the scanned string
253     while (*p == ' ' || *p == '\t')
254         p++;
255     if( pLen )
256         *pLen = static_cast<sal_uInt16>( p - pStart );
257     if( !bRes )
258         return ERRCODE_BASIC_CONVERSION;
259     if( bMinus )
260         nVal = -nVal;
261     rType = eScanType;
262     return ERRCODE_NONE;
263 }
264 
265 // port for CDbl in the Basic
266 ErrCode SbxValue::ScanNumIntnl( const OUString& rSrc, double& nVal, bool bSingle )
267 {
268     SbxDataType t;
269     sal_uInt16 nLen = 0;
270     ErrCode nRetError = ImpScan( rSrc, nVal, t, &nLen,
271         /*bOnlyIntntl*/true );
272     // read completely?
273     if( nRetError == ERRCODE_NONE && nLen != rSrc.getLength() )
274     {
275         nRetError = ERRCODE_BASIC_CONVERSION;
276     }
277     if( bSingle )
278     {
279         SbxValues aValues( nVal );
280         nVal = static_cast<double>(ImpGetSingle( &aValues ));    // here error at overflow
281     }
282     return nRetError;
283 }
284 
285 // The number is prepared unformattedly with the given number of
286 // NK-positions. A leading minus is added if applicable.
287 // This routine is public because it's also used by the Put-functions
288 // in the class SbxImpSTRING.
289 
290 void ImpCvtNum( double nNum, short nPrec, OUString& rRes, bool bCoreString )
291 {
292     sal_Unicode cDecimalSep, cThousandSep, cDecimalSepAlt;
293     ImpGetIntntlSep( cDecimalSep, cThousandSep, cDecimalSepAlt );
294     if( bCoreString )
295         cDecimalSep = '.';
296 
297     // tdf#143575 - use rtl::math::doubleToUString to convert numbers to strings in basic
298     rRes = rtl::math::doubleToUString(nNum, rtl_math_StringFormat_Automatic, nPrec, cDecimalSep, true);
299 }
300 
301 bool ImpConvStringExt( OUString& rSrc, SbxDataType eTargetType )
302 {
303     bool bChanged = false;
304     OUString aNewString;
305 
306     // only special cases are handled, nothing on default
307     switch( eTargetType )
308     {
309         // consider international for floating point
310         case SbxSINGLE:
311         case SbxDOUBLE:
312         case SbxCURRENCY:
313         {
314             sal_Unicode cDecimalSep, cThousandSep, cDecimalSepAlt;
315             ImpGetIntntlSep( cDecimalSep, cThousandSep, cDecimalSepAlt );
316             aNewString = rSrc;
317 
318             if( cDecimalSep != '.' || (cDecimalSepAlt && cDecimalSepAlt != '.') )
319             {
320                 sal_Int32 nPos = aNewString.indexOf( cDecimalSep );
321                 if( nPos == -1 && cDecimalSepAlt )
322                     nPos = aNewString.indexOf( cDecimalSepAlt );
323                 if( nPos != -1 )
324                 {
325                     sal_Unicode* pStr = const_cast<sal_Unicode*>(aNewString.getStr());
326                     pStr[nPos] = '.';
327                     bChanged = true;
328                 }
329             }
330             break;
331         }
332 
333         // check as string in case of sal_Bool sal_True and sal_False
334         case SbxBOOL:
335         {
336             if( rSrc.equalsIgnoreAsciiCase("true") )
337             {
338                 aNewString = OUString::number( SbxTRUE );
339                 bChanged = true;
340             }
341             else if( rSrc.equalsIgnoreAsciiCase("false") )
342             {
343                 aNewString = OUString::number( SbxFALSE );
344                 bChanged = true;
345             }
346             break;
347         }
348         default: break;
349     }
350 
351     if( bChanged )
352         rSrc = aNewString;
353     return bChanged;
354 }
355 
356 
357 // formatted number output
358 // the return value is the number of characters used
359 // from the format
360 
361 static sal_uInt16 printfmtstr( const OUString& rStr, OUString& rRes, const OUString& rFmt )
362 {
363     OUStringBuffer aTemp;
364     const sal_Unicode* pStr = rStr.getStr();
365     const sal_Unicode* pFmtStart = rFmt.getStr();
366     const sal_Unicode* pFmt = pFmtStart;
367 
368     switch( *pFmt )
369     {
370     case '!':
371         aTemp.append(*pStr++);
372         pFmt++;
373         break;
374     case '\\':
375         do
376         {
377             aTemp.append( *pStr ? *pStr++ : u' ');
378             pFmt++;
379         }
380         while( *pFmt && *pFmt != '\\' );
381         aTemp.append(*pStr ? *pStr++ : u' ');
382         pFmt++; break;
383     case '&':
384         aTemp = rStr;
385         pFmt++; break;
386     default:
387         aTemp = rStr;
388         break;
389     }
390     rRes = aTemp.makeStringAndClear();
391     return static_cast<sal_uInt16>( pFmt - pFmtStart );
392 }
393 
394 
395 bool SbxValue::Scan( const OUString& rSrc, sal_uInt16* pLen )
396 {
397     ErrCode eRes = ERRCODE_NONE;
398     if( !CanWrite() )
399     {
400         eRes = ERRCODE_BASIC_PROP_READONLY;
401     }
402     else
403     {
404         double n;
405         SbxDataType t;
406         eRes = ImpScan( rSrc, n, t, pLen, !LibreOffice6FloatingPointMode() );
407         if( eRes == ERRCODE_NONE )
408         {
409             if( !IsFixed() )
410             {
411                 SetType( t );
412             }
413             PutDouble( n );
414         }
415     }
416     if( eRes )
417     {
418         SetError( eRes );
419         return false;
420     }
421     else
422     {
423         return true;
424     }
425 }
426 
427 std::locale BasResLocale()
428 {
429     return Translate::Create("sb");
430 }
431 
432 OUString BasResId(TranslateId aId)
433 {
434     return Translate::get(aId, BasResLocale());
435 }
436 
437 namespace
438 {
439 
440 enum class VbaFormatType
441 {
442     Offset,      // standard number format
443     UserDefined, // user defined number format
444     Null
445 };
446 
447 #if HAVE_FEATURE_SCRIPTING
448 
449 struct VbaFormatInfo
450 {
451     VbaFormatType meType;
452     std::u16string_view mpVbaFormat; // Format string in vba
453     NfIndexTableOffset meOffset; // SvNumberFormatter format index, if meType = VbaFormatType::Offset
454     const char* mpOOoFormat;     // if meType = VbaFormatType::UserDefined
455 };
456 
457 const VbaFormatInfo pFormatInfoTable[] =
458 {
459     { VbaFormatType::Offset,      std::u16string_view(u"Long Date"),   NF_DATE_SYSTEM_LONG,    nullptr },
460     { VbaFormatType::UserDefined, std::u16string_view(u"Medium Date"), NF_NUMBER_STANDARD,     "DD-MMM-YY" },
461     { VbaFormatType::Offset,      std::u16string_view(u"Short Date"),  NF_DATE_SYSTEM_SHORT,   nullptr },
462     { VbaFormatType::UserDefined, std::u16string_view(u"Long Time"),   NF_NUMBER_STANDARD,     "H:MM:SS AM/PM" },
463     { VbaFormatType::Offset,      std::u16string_view(u"Medium Time"), NF_TIME_HHMMAMPM,       nullptr },
464     { VbaFormatType::Offset,      std::u16string_view(u"Short Time"),  NF_TIME_HHMM,           nullptr },
465     { VbaFormatType::Offset,      std::u16string_view(u"ddddd"),       NF_DATE_SYSTEM_SHORT,   nullptr },
466     { VbaFormatType::Offset,      std::u16string_view(u"dddddd"),      NF_DATE_SYSTEM_LONG,    nullptr },
467     { VbaFormatType::UserDefined, std::u16string_view(u"ttttt"),       NF_NUMBER_STANDARD,     "H:MM:SS AM/PM" },
468     { VbaFormatType::Offset,      std::u16string_view(u"ww"),          NF_DATE_WW,             nullptr },
469     { VbaFormatType::Null,        std::u16string_view(u""),            NF_INDEX_TABLE_ENTRIES, nullptr }
470 };
471 
472 const VbaFormatInfo* getFormatInfo( std::u16string_view rFmt )
473 {
474     const VbaFormatInfo* pInfo = pFormatInfoTable;
475     while( pInfo->meType != VbaFormatType::Null )
476     {
477         if( o3tl::equalsIgnoreAsciiCase( rFmt, pInfo->mpVbaFormat ) )
478             break;
479         ++pInfo;
480     }
481     return pInfo;
482 }
483 #endif
484 
485 } // namespace
486 
487 #if HAVE_FEATURE_SCRIPTING
488 constexpr OUStringLiteral VBAFORMAT_GENERALDATE = u"General Date";
489 constexpr OUStringLiteral VBAFORMAT_C = u"c";
490 constexpr OUStringLiteral VBAFORMAT_N = u"n";
491 constexpr OUStringLiteral VBAFORMAT_NN = u"nn";
492 constexpr OUStringLiteral VBAFORMAT_W = u"w";
493 constexpr OUStringLiteral VBAFORMAT_Y = u"y";
494 constexpr OUStringLiteral VBAFORMAT_LOWERCASE = u"<";
495 constexpr OUStringLiteral VBAFORMAT_UPPERCASE = u">";
496 #endif
497 
498 void SbxValue::Format( OUString& rRes, const OUString* pFmt ) const
499 {
500     short nComma = 0;
501     double d = 0;
502 
503     // pflin, It is better to use SvNumberFormatter to handle the date/time/number format.
504     // the SvNumberFormatter output is mostly compatible with
505     // VBA output besides the OOo-basic output
506 #if HAVE_FEATURE_SCRIPTING
507     if( pFmt && !SbxBasicFormater::isBasicFormat( *pFmt ) )
508     {
509         OUString aStr = GetOUString();
510 
511         SvtSysLocale aSysLocale;
512         const CharClass& rCharClass = aSysLocale.GetCharClass();
513 
514         if( pFmt->equalsIgnoreAsciiCase( VBAFORMAT_LOWERCASE ) )
515         {
516             rRes = rCharClass.lowercase( aStr );
517             return;
518         }
519         if( pFmt->equalsIgnoreAsciiCase( VBAFORMAT_UPPERCASE ) )
520         {
521             rRes = rCharClass.uppercase( aStr );
522             return;
523         }
524 
525         LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
526         std::shared_ptr<SvNumberFormatter> pFormatter;
527         if (GetSbData()->pInst)
528         {
529             pFormatter = GetSbData()->pInst->GetNumberFormatter();
530         }
531         else
532         {
533             sal_uInt32 n;   // Dummy
534             pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n );
535         }
536 
537         // Passing an index of a locale switches IsNumberFormat() to use that
538         // locale in case the formatter wasn't default created with it.
539         sal_uInt32 nIndex = pFormatter->GetStandardIndex( eLangType);
540         double nNumber;
541         const Color* pCol;
542 
543         bool bSuccess = pFormatter->IsNumberFormat( aStr, nIndex, nNumber );
544 
545         // number format, use SvNumberFormatter to handle it.
546         if( bSuccess )
547         {
548             sal_Int32 nCheckPos = 0;
549             SvNumFormatType nType;
550             OUString aFmtStr = *pFmt;
551             const VbaFormatInfo* pInfo = getFormatInfo( aFmtStr );
552             if( pInfo->meType != VbaFormatType::Null )
553             {
554                 if( pInfo->meType == VbaFormatType::Offset )
555                 {
556                     nIndex = pFormatter->GetFormatIndex( pInfo->meOffset, eLangType );
557                 }
558                 else
559                 {
560                     aFmtStr = OUString::createFromAscii(pInfo->mpOOoFormat);
561                     pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH_US, eLangType, true);
562                 }
563                 pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
564             }
565             else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_GENERALDATE )
566                     || aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_C ))
567             {
568                 if( nNumber <=-1.0 || nNumber >= 1.0 )
569                 {
570                     // short date
571                     nIndex = pFormatter->GetFormatIndex( NF_DATE_SYSTEM_SHORT, eLangType );
572                     pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
573 
574                     // long time
575                     if( floor( nNumber ) != nNumber )
576                     {
577                         aFmtStr = "H:MM:SS AM/PM";
578                         pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH_US, eLangType, true);
579                         OUString aTime;
580                         pFormatter->GetOutputString( nNumber, nIndex, aTime, &pCol );
581                         rRes += " " + aTime;
582                     }
583                 }
584                 else
585                 {
586                     // long time only
587                     aFmtStr = "H:MM:SS AM/PM";
588                     pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH_US, eLangType, true);
589                     pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
590                 }
591             }
592             else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_N ) ||
593                      aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_NN ))
594             {
595                 sal_Int32 nMin = implGetMinute( nNumber );
596                 if( nMin < 10 && aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_NN ))
597                 {
598                     // Minute in two digits
599                      sal_Unicode aBuf[2];
600                      aBuf[0] = '0';
601                      aBuf[1] = '0' + nMin;
602                      rRes = OUString(aBuf, std::size(aBuf));
603                 }
604                 else
605                 {
606                     rRes = OUString::number(nMin);
607                 }
608             }
609             else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_W ))
610             {
611                 sal_Int32 nWeekDay = implGetWeekDay( nNumber );
612                 rRes = OUString::number(nWeekDay);
613             }
614             else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_Y ))
615             {
616                 sal_Int16 nYear = implGetDateYear( nNumber );
617                 double dBaseDate;
618                 implDateSerial( nYear, 1, 1, true, SbDateCorrection::None, dBaseDate );
619                 sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate );
620                 rRes = OUString::number(nYear32);
621             }
622             else
623             {
624                 pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH_US, eLangType, true);
625                 pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
626             }
627 
628             return;
629         }
630     }
631 #endif
632 
633     SbxDataType eType = GetType();
634     switch( eType )
635     {
636     case SbxCHAR:
637     case SbxBYTE:
638     case SbxINTEGER:
639     case SbxUSHORT:
640     case SbxLONG:
641     case SbxULONG:
642     case SbxINT:
643     case SbxUINT:
644     case SbxNULL:       // #45929 NULL with a little cheating
645         nComma = 0;     goto cvt;
646     case SbxSINGLE:
647         nComma = 6;     goto cvt;
648     case SbxDOUBLE:
649         nComma = 14;
650 
651     cvt:
652         if( eType != SbxNULL )
653         {
654             d = GetDouble();
655         }
656         // #45355 another point to jump in for isnumeric-String
657     cvt2:
658         if( pFmt )
659         {
660             SbxAppData& rAppData = GetSbxData_Impl();
661 
662             LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
663             if( rAppData.pBasicFormater )
664             {
665                 if( rAppData.eBasicFormaterLangType != eLangType )
666                 {
667                     rAppData.pBasicFormater.reset();
668                 }
669             }
670             rAppData.eBasicFormaterLangType = eLangType;
671 
672 
673             if( !rAppData.pBasicFormater )
674             {
675                 SvtSysLocale aSysLocale;
676                 const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
677                 sal_Unicode cComma = rData.getNumDecimalSep()[0];
678                 sal_Unicode c1000  = rData.getNumThousandSep()[0];
679                 const OUString& aCurrencyStrg = rData.getCurrSymbol();
680 
681                 // initialize the Basic-formater help object:
682                 // get resources for predefined output
683                 // of the Format()-command, e. g. for "On/Off"
684                 OUString aOnStrg = BasResId(STR_BASICKEY_FORMAT_ON);
685                 OUString aOffStrg = BasResId(STR_BASICKEY_FORMAT_OFF);
686                 OUString aYesStrg = BasResId(STR_BASICKEY_FORMAT_YES);
687                 OUString aNoStrg = BasResId(STR_BASICKEY_FORMAT_NO);
688                 OUString aTrueStrg = BasResId(STR_BASICKEY_FORMAT_TRUE);
689                 OUString aFalseStrg = BasResId(STR_BASICKEY_FORMAT_FALSE);
690                 OUString aCurrencyFormatStrg = BasResId(STR_BASICKEY_FORMAT_CURRENCY);
691 
692                 rAppData.pBasicFormater = std::make_unique<SbxBasicFormater>(
693                                                                 cComma,c1000,aOnStrg,aOffStrg,
694                                                                 aYesStrg,aNoStrg,aTrueStrg,aFalseStrg,
695                                                                 aCurrencyStrg,aCurrencyFormatStrg );
696             }
697             // Remark: For performance reasons there's only ONE BasicFormater-
698             //    object created and 'stored', so that the expensive resource-
699             //    loading is saved (for country-specific predefined outputs,
700             //    e. g. "On/Off") and the continuous string-creation
701             //    operations, too.
702             // BUT: therefore this code is NOT multithreading capable!
703 
704             // here are problems with ;;;Null because this method is only
705             // called, if SbxValue is a number!!!
706             // in addition rAppData.pBasicFormater->BasicFormatNull( *pFmt ); could be called!
707             if( eType != SbxNULL )
708             {
709                 rRes = rAppData.pBasicFormater->BasicFormat( d ,*pFmt );
710             }
711             else
712             {
713                 rRes = SbxBasicFormater::BasicFormatNull( *pFmt );
714             }
715 
716         }
717         else
718             ImpCvtNum( GetDouble(), nComma, rRes );
719         break;
720     case SbxSTRING:
721         if( pFmt )
722         {
723             // #45355 converting if numeric
724             if( IsNumericRTL() )
725             {
726                 ScanNumIntnl( GetOUString(), d );
727                 goto cvt2;
728             }
729             else
730             {
731                 printfmtstr( GetOUString(), rRes, *pFmt );
732             }
733         }
734         else
735         {
736             rRes = GetOUString();
737         }
738         break;
739     default:
740         rRes = GetOUString();
741     }
742 }
743 
744 
745 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
746