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