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