1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #include <vcl/errcode.hxx>
23 #include <unotools/resmgr.hxx>
24 #include "sbxconv.hxx"
26 #include <unotools/syslocale.hxx>
27 #include <unotools/charclass.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/settings.hxx>
34 #include <sbxbase.hxx>
35 #include <sbintern.hxx>
36 #include <basic/sbxform.hxx>
39 #include <runtime.hxx>
40 #include <strings.hrc>
42 #include <rtl/character.hxx>
43 #include <sal/log.hxx>
44 #include <svl/zforlist.hxx>
47 void ImpGetIntntlSep( sal_Unicode
& rcDecimalSep
, sal_Unicode
& rcThousandSep
, sal_Unicode
& rcDecimalSepAlt
)
49 SvtSysLocale aSysLocale
;
50 const LocaleDataWrapper
& rData
= aSysLocale
.GetLocaleData();
51 rcDecimalSep
= rData
.getNumDecimalSep()[0];
52 rcThousandSep
= rData
.getNumThousandSep()[0];
53 rcDecimalSepAlt
= rData
.getNumDecimalSepAlt().toChar();
57 /** NOTE: slightly differs from strchr() in that it does not consider the
58 terminating NULL character to be part of the string and returns bool
59 instead of pointer, if character is 0 returns false.
61 static bool ImpStrChr( const sal_Unicode
* p
, sal_Unicode c
)
74 // scanning a string according to BASIC-conventions
75 // but exponent may also be a D, so data type is SbxDOUBLE
76 // conversion error if data type is fixed and it doesn't fit
78 ErrCode
ImpScan( const OUString
& rWSrc
, double& nVal
, SbxDataType
& rType
,
79 sal_uInt16
* pLen
, bool bOnlyIntntl
)
81 sal_Unicode cIntntlDecSep
, cIntntlGrpSep
, cIntntlDecSepAlt
;
82 sal_Unicode cNonIntntlDecSep
= '.';
85 ImpGetIntntlSep( cIntntlDecSep
, cIntntlGrpSep
, cIntntlDecSepAlt
);
86 cNonIntntlDecSep
= cIntntlDecSep
;
87 // Ensure that the decimal separator alternative is really one.
88 if (cIntntlDecSepAlt
&& cIntntlDecSepAlt
== cNonIntntlDecSep
)
93 cIntntlDecSep
= cNonIntntlDecSep
;
94 cIntntlGrpSep
= 0; // no group separator accepted in non-i18n
98 const sal_Unicode
* const pStart
= rWSrc
.getStr();
99 const sal_Unicode
* p
= pStart
;
100 OUStringBuffer
aBuf( rWSrc
.getLength());
104 SbxDataType eScanType
= SbxSINGLE
;
105 while( *p
== ' ' || *p
== '\t' )
112 if( rtl::isAsciiDigit( *p
) || ((*p
== cNonIntntlDecSep
|| *p
== cIntntlDecSep
||
113 (cIntntlDecSep
&& *p
== cIntntlGrpSep
) || (cIntntlDecSepAlt
&& *p
== cIntntlDecSepAlt
)) &&
114 rtl::isAsciiDigit( *(p
+1) )))
116 // tdf#118442: Whitespace and minus are skipped; store the position to calculate index
117 const sal_Unicode
* const pDigitsStart
= p
;
121 short ncdig
= 0; // number of digits after decimal point
122 OUStringBuffer
aSearchStr("0123456789DEde");
123 aSearchStr
.append(cNonIntntlDecSep
);
124 if( cIntntlDecSep
!= cNonIntntlDecSep
)
125 aSearchStr
.append(cIntntlDecSep
);
126 if( cIntntlDecSepAlt
&& cIntntlDecSepAlt
!= cNonIntntlDecSep
)
127 aSearchStr
.append(cIntntlDecSepAlt
);
129 aSearchStr
.append(cIntntlGrpSep
);
130 const sal_Unicode
* const pSearchStr
= aSearchStr
.getStr();
131 const sal_Unicode pDdEe
[] = { 'D', 'd', 'E', 'e', 0 };
132 while( ImpStrChr( pSearchStr
, *p
) )
135 if( bOnlyIntntl
&& *p
== cIntntlGrpSep
)
140 if( *p
== cNonIntntlDecSep
|| *p
== cIntntlDecSep
|| (cIntntlDecSepAlt
&& *p
== cIntntlDecSepAlt
) )
142 // Use the separator that is passed to stringToDouble()
143 aBuf
[p
- pDigitsStart
] = cIntntlDecSep
;
148 else if( ImpStrChr( pDdEe
, *p
) )
155 if( *p
== 'D' || *p
== 'd' )
156 eScanType
= SbxDOUBLE
;
157 aBuf
[p
- pDigitsStart
] = 'E';
177 if( decsep
> 1 || exp
> 1 )
180 OUString
aBufStr( aBuf
.makeStringAndClear());
181 rtl_math_ConversionStatus eStatus
= rtl_math_ConversionStatus_Ok
;
182 sal_Int32 nParseEnd
= 0;
183 nVal
= rtl::math::stringToDouble( aBufStr
, cIntntlDecSep
, cIntntlGrpSep
, &eStatus
, &nParseEnd
);
184 if( eStatus
!= rtl_math_ConversionStatus_Ok
|| nParseEnd
!= aBufStr
.getLength() )
187 if( !decsep
&& !exp
)
189 if( nVal
>= SbxMININT
&& nVal
<= SbxMAXINT
)
190 eScanType
= SbxINTEGER
;
191 else if( nVal
>= SbxMINLNG
&& nVal
<= SbxMAXLNG
)
195 ndig
= ndig
- decsep
;
196 // too many numbers for SINGLE?
197 if( ndig
> 15 || ncdig
> 6 )
198 eScanType
= SbxDOUBLE
;
201 const sal_Unicode pTypes
[] = { '%', '!', '&', '#', 0 };
202 if( ImpStrChr( pTypes
, *p
) )
205 // hex/octal number? read in and convert:
210 OUString
aCmp( "0123456789ABCDEFabcdef" );
227 const sal_Unicode
* const pCmp
= aCmp
.getStr();
228 while( rtl::isAsciiAlphanumeric( *p
) ) /* XXX: really munge all alnum also when error? */
231 if( ImpStrChr( pCmp
, ch
) )
234 ch
-= 0x20; // convert ASCII lower to upper case
241 OUString
aBufStr( aBuf
.makeStringAndClear());
243 for( const sal_Unicode
* q
= aBufStr
.getStr(); bRes
&& *q
; q
++ )
247 i
-= 7; // 'A'-'0' = 17 => 10, ...
248 l
= ( l
* base
) + i
;
254 nVal
= static_cast<double>(l
);
255 if( l
>= SbxMININT
&& l
<= SbxMAXINT
)
256 eScanType
= SbxINTEGER
;
258 #if HAVE_FEATURE_SCRIPTING
259 else if ( SbiRuntime::isVBAEnabled() )
261 SAL_WARN("basic", "Reporting error converting");
262 return ERRCODE_BASIC_CONVERSION
;
266 *pLen
= static_cast<sal_uInt16
>( p
- pStart
);
268 return ERRCODE_BASIC_CONVERSION
;
275 // port for CDbl in the Basic
276 ErrCode
SbxValue::ScanNumIntnl( const OUString
& rSrc
, double& nVal
, bool bSingle
)
280 ErrCode nRetError
= ImpScan( rSrc
, nVal
, t
, &nLen
,
281 /*bOnlyIntntl*/true );
283 if( nRetError
== ERRCODE_NONE
&& nLen
!= rSrc
.getLength() )
285 nRetError
= ERRCODE_BASIC_CONVERSION
;
289 SbxValues
aValues( nVal
);
290 nVal
= static_cast<double>(ImpGetSingle( &aValues
)); // here error at overflow
296 static const double roundArray
[] = {
297 5.0e+0, 0.5e+0, 0.5e-1, 0.5e-2, 0.5e-3, 0.5e-4, 0.5e-5, 0.5e-6, 0.5e-7,
298 0.5e-8, 0.5e-9, 0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14,0.5e-15 };
302 |* void myftoa( double, char *, short, short, bool, bool )
304 |* description: conversion double --> ASCII
305 |* parameters: double the number
306 |* char * target buffer
307 |* short number of positions after decimal point
308 |* short range of the exponent ( 0=no E )
309 |* bool true: with 1000-separators
310 |* bool true: output without formatting
314 static void myftoa( double nNum
, char * pBuf
, short nPrec
, short nExpWidth
,
315 sal_Unicode cForceThousandSep
)
319 short nDig
= nPrec
+ 1;
320 short nDec
; // number of positions before decimal point
323 sal_Unicode cDecimalSep
, cThousandSep
, cDecimalSepAlt
;
324 ImpGetIntntlSep( cDecimalSep
, cThousandSep
, cDecimalSepAlt
);
325 if( cForceThousandSep
)
326 cThousandSep
= cForceThousandSep
;
337 while( nNum
>= 10.0 )
347 if( (nNum
+= roundArray
[std::min
<short>( nDig
, 16 )] ) >= 10.0 )
351 if( !nExpWidth
) ++nDig
;
354 // determine positions before decimal point
359 // #41691: also a 0 at bFix
361 if( nPrec
) *pBuf
++ = static_cast<char>(cDecimalSep
);
363 if( nDig
<= 0 ) i
= nPrec
;
364 while( i
-- ) *pBuf
++ = '0';
381 digit
= static_cast<int>(nNum
);
382 *pBuf
++ = sal::static_int_cast
< char >(digit
+ '0');
383 nNum
=( nNum
- digit
) * 10.0;
386 if( --nDig
== 0 ) break;
391 *pBuf
++ = static_cast<char>(cDecimalSep
);
399 if( nExpWidth
< 3 ) nExpWidth
= 3;
409 while( nExpWidth
> 3 )
414 if( nExp
>= 100 || nExpWidth
== 3 )
416 *pBuf
++ = sal::static_int_cast
< char >(nExp
/100 + '0');
419 if( nExp
/10 || nExpWidth
>= 2 )
420 *pBuf
++ = sal::static_int_cast
< char >(nExp
/10 + '0');
421 *pBuf
++ = sal::static_int_cast
< char >(nExp
%10 + '0');
426 // The number is prepared unformattedly with the given number of
427 // NK-positions. A leading minus is added if applicable.
428 // This routine is public because it's also used by the Put-functions
429 // in the class SbxImpSTRING.
431 void ImpCvtNum( double nNum
, short nPrec
, OUString
& rRes
, bool bCoreString
)
434 char cBuf
[ 40 ], *p
= cBuf
;
436 sal_Unicode cDecimalSep
, cThousandSep
, cDecimalSepAlt
;
437 ImpGetIntntlSep( cDecimalSep
, cThousandSep
, cDecimalSepAlt
);
445 double dMaxNumWithoutExp
= (nPrec
== 6) ? 1E6
: 1E14
;
446 myftoa( nNum
, p
, nPrec
,( nNum
&&( nNum
< 1E-1 || nNum
>= dMaxNumWithoutExp
) ) ? 4:0,
448 // remove trailing zeros
449 for( p
= cBuf
; *p
&&( *p
!= 'E' ); p
++ ) {}
451 while( nPrec
&& *p
== '0' )
456 if( *p
== cDecimalSep
) p
--;
457 while( *q
) *++p
= *q
++;
459 rRes
= OUString::createFromAscii( cBuf
);
462 bool ImpConvStringExt( OUString
& rSrc
, SbxDataType eTargetType
)
464 bool bChanged
= false;
467 // only special cases are handled, nothing on default
468 switch( eTargetType
)
470 // consider international for floating point
475 sal_Unicode cDecimalSep
, cThousandSep
, cDecimalSepAlt
;
476 ImpGetIntntlSep( cDecimalSep
, cThousandSep
, cDecimalSepAlt
);
479 if( cDecimalSep
!= '.' || (cDecimalSepAlt
&& cDecimalSepAlt
!= '.') )
481 sal_Int32 nPos
= aNewString
.indexOf( cDecimalSep
);
482 if( nPos
== -1 && cDecimalSepAlt
)
483 nPos
= aNewString
.indexOf( cDecimalSepAlt
);
486 sal_Unicode
* pStr
= const_cast<sal_Unicode
*>(aNewString
.getStr());
494 // check as string in case of sal_Bool sal_True and sal_False
497 if( rSrc
.equalsIgnoreAsciiCase("true") )
499 aNewString
= OUString::number( SbxTRUE
);
502 else if( rSrc
.equalsIgnoreAsciiCase("false") )
504 aNewString
= OUString::number( SbxFALSE
);
518 // formatted number output
519 // the return value is the number of characters used
522 static sal_uInt16
printfmtstr( const OUString
& rStr
, OUString
& rRes
, const OUString
& rFmt
)
524 OUStringBuffer aTemp
;
525 const sal_Unicode
* pStr
= rStr
.getStr();
526 const sal_Unicode
* pFmtStart
= rFmt
.getStr();
527 const sal_Unicode
* pFmt
= pFmtStart
;
532 aTemp
.append(*pStr
++);
538 aTemp
.append( *pStr
? *pStr
++ : u
' ');
541 while( *pFmt
&& *pFmt
!= '\\' );
542 aTemp
.append(*pStr
? *pStr
++ : u
' ');
551 rRes
= aTemp
.makeStringAndClear();
552 return static_cast<sal_uInt16
>( pFmt
- pFmtStart
);
556 bool SbxValue::Scan( const OUString
& rSrc
, sal_uInt16
* pLen
)
558 ErrCode eRes
= ERRCODE_NONE
;
561 eRes
= ERRCODE_BASIC_PROP_READONLY
;
567 eRes
= ImpScan( rSrc
, n
, t
, pLen
, false );
568 if( eRes
== ERRCODE_NONE
)
588 std::locale
BasResLocale()
590 return Translate::Create("sb");
593 OUString
BasResId(const char *pId
)
595 return Translate::get(pId
, BasResLocale());
601 enum class VbaFormatType
603 Offset
, // standard number format
604 UserDefined
, // user defined number format
610 VbaFormatType meType
;
611 OUStringLiteral mpVbaFormat
; // Format string in vba
612 NfIndexTableOffset meOffset
; // SvNumberFormatter format index, if meType = VbaFormatType::Offset
613 const char* mpOOoFormat
; // if meType = VbaFormatType::UserDefined
616 #if HAVE_FEATURE_SCRIPTING
617 const VbaFormatInfo pFormatInfoTable
[] =
619 { VbaFormatType::Offset
, OUStringLiteral("Long Date"), NF_DATE_SYSTEM_LONG
, nullptr },
620 { VbaFormatType::UserDefined
, OUStringLiteral("Medium Date"), NF_NUMBER_STANDARD
, "DD-MMM-YY" },
621 { VbaFormatType::Offset
, OUStringLiteral("Short Date"), NF_DATE_SYSTEM_SHORT
, nullptr },
622 { VbaFormatType::UserDefined
, OUStringLiteral("Long Time"), NF_NUMBER_STANDARD
, "H:MM:SS AM/PM" },
623 { VbaFormatType::Offset
, OUStringLiteral("Medium Time"), NF_TIME_HHMMAMPM
, nullptr },
624 { VbaFormatType::Offset
, OUStringLiteral("Short Time"), NF_TIME_HHMM
, nullptr },
625 { VbaFormatType::Offset
, OUStringLiteral("ddddd"), NF_DATE_SYSTEM_SHORT
, nullptr },
626 { VbaFormatType::Offset
, OUStringLiteral("dddddd"), NF_DATE_SYSTEM_LONG
, nullptr },
627 { VbaFormatType::UserDefined
, OUStringLiteral("ttttt"), NF_NUMBER_STANDARD
, "H:MM:SS AM/PM" },
628 { VbaFormatType::Offset
, OUStringLiteral("ww"), NF_DATE_WW
, nullptr },
629 { VbaFormatType::Null
, OUStringLiteral(""), NF_INDEX_TABLE_ENTRIES
, nullptr }
632 const VbaFormatInfo
* getFormatInfo( const OUString
& rFmt
)
634 const VbaFormatInfo
* pInfo
= pFormatInfoTable
;
635 while( pInfo
->meType
!= VbaFormatType::Null
)
637 if( rFmt
.equalsIgnoreAsciiCase( pInfo
->mpVbaFormat
) )
647 #if HAVE_FEATURE_SCRIPTING
648 #define VBAFORMAT_GENERALDATE "General Date"
649 #define VBAFORMAT_C "c"
650 #define VBAFORMAT_N "n"
651 #define VBAFORMAT_NN "nn"
652 #define VBAFORMAT_W "w"
653 #define VBAFORMAT_Y "y"
654 #define VBAFORMAT_LOWERCASE "<"
655 #define VBAFORMAT_UPPERCASE ">"
658 void SbxValue::Format( OUString
& rRes
, const OUString
* pFmt
) const
663 // pflin, It is better to use SvNumberFormatter to handle the date/time/number format.
664 // the SvNumberFormatter output is mostly compatible with
665 // VBA output besides the OOo-basic output
666 #if HAVE_FEATURE_SCRIPTING
667 if( pFmt
&& !SbxBasicFormater::isBasicFormat( *pFmt
) )
669 OUString aStr
= GetOUString();
671 SvtSysLocale aSysLocale
;
672 const CharClass
& rCharClass
= aSysLocale
.GetCharClass();
674 if( pFmt
->equalsIgnoreAsciiCase( VBAFORMAT_LOWERCASE
) )
676 rRes
= rCharClass
.lowercase( aStr
);
679 if( pFmt
->equalsIgnoreAsciiCase( VBAFORMAT_UPPERCASE
) )
681 rRes
= rCharClass
.uppercase( aStr
);
685 LanguageType eLangType
= Application::GetSettings().GetLanguageTag().getLanguageType();
686 std::shared_ptr
<SvNumberFormatter
> pFormatter
;
687 if (GetSbData()->pInst
)
689 pFormatter
= GetSbData()->pInst
->GetNumberFormatter();
693 sal_uInt32 n
; // Dummy
694 pFormatter
= SbiInstance::PrepareNumberFormatter( n
, n
, n
);
697 // Passing an index of a locale switches IsNumberFormat() to use that
698 // locale in case the formatter wasn't default created with it.
699 sal_uInt32 nIndex
= pFormatter
->GetStandardIndex( eLangType
);
703 bool bSuccess
= pFormatter
->IsNumberFormat( aStr
, nIndex
, nNumber
);
705 // number format, use SvNumberFormatter to handle it.
708 sal_Int32 nCheckPos
= 0;
709 SvNumFormatType nType
;
710 OUString aFmtStr
= *pFmt
;
711 const VbaFormatInfo
* pInfo
= getFormatInfo( aFmtStr
);
712 if( pInfo
->meType
!= VbaFormatType::Null
)
714 if( pInfo
->meType
== VbaFormatType::Offset
)
716 nIndex
= pFormatter
->GetFormatIndex( pInfo
->meOffset
, eLangType
);
720 aFmtStr
= OUString::createFromAscii(pInfo
->mpOOoFormat
);
721 pFormatter
->PutandConvertEntry( aFmtStr
, nCheckPos
, nType
, nIndex
, LANGUAGE_ENGLISH
, eLangType
, true);
723 pFormatter
->GetOutputString( nNumber
, nIndex
, rRes
, &pCol
);
725 else if( aFmtStr
.equalsIgnoreAsciiCase( VBAFORMAT_GENERALDATE
)
726 || aFmtStr
.equalsIgnoreAsciiCase( VBAFORMAT_C
))
728 if( nNumber
<=-1.0 || nNumber
>= 1.0 )
731 nIndex
= pFormatter
->GetFormatIndex( NF_DATE_SYSTEM_SHORT
, eLangType
);
732 pFormatter
->GetOutputString( nNumber
, nIndex
, rRes
, &pCol
);
735 if( floor( nNumber
) != nNumber
)
737 aFmtStr
= "H:MM:SS AM/PM";
738 pFormatter
->PutandConvertEntry( aFmtStr
, nCheckPos
, nType
, nIndex
, LANGUAGE_ENGLISH
, eLangType
, true);
740 pFormatter
->GetOutputString( nNumber
, nIndex
, aTime
, &pCol
);
747 aFmtStr
= "H:MM:SS AM/PM";
748 pFormatter
->PutandConvertEntry( aFmtStr
, nCheckPos
, nType
, nIndex
, LANGUAGE_ENGLISH
, eLangType
, true);
749 pFormatter
->GetOutputString( nNumber
, nIndex
, rRes
, &pCol
);
752 else if( aFmtStr
.equalsIgnoreAsciiCase( VBAFORMAT_N
) ||
753 aFmtStr
.equalsIgnoreAsciiCase( VBAFORMAT_NN
))
755 sal_Int32 nMin
= implGetMinute( nNumber
);
756 if( nMin
< 10 && aFmtStr
.equalsIgnoreAsciiCase( VBAFORMAT_NN
))
758 // Minute in two digits
761 aBuf
[1] = '0' + nMin
;
762 rRes
= OUString(aBuf
, SAL_N_ELEMENTS(aBuf
));
766 rRes
= OUString::number(nMin
);
769 else if( aFmtStr
.equalsIgnoreAsciiCase( VBAFORMAT_W
))
771 sal_Int32 nWeekDay
= implGetWeekDay( nNumber
);
772 rRes
= OUString::number(nWeekDay
);
774 else if( aFmtStr
.equalsIgnoreAsciiCase( VBAFORMAT_Y
))
776 sal_Int16 nYear
= implGetDateYear( nNumber
);
778 implDateSerial( nYear
, 1, 1, true, SbDateCorrection::None
, dBaseDate
);
779 sal_Int32 nYear32
= 1 + sal_Int32( nNumber
- dBaseDate
);
780 rRes
= OUString::number(nYear32
);
784 pFormatter
->PutandConvertEntry( aFmtStr
, nCheckPos
, nType
, nIndex
, LANGUAGE_ENGLISH
, eLangType
, true);
785 pFormatter
->GetOutputString( nNumber
, nIndex
, rRes
, &pCol
);
793 SbxDataType eType
= GetType();
804 case SbxNULL
: // #45929 NULL with a little cheating
805 nComma
= 0; goto cvt
;
807 nComma
= 6; goto cvt
;
812 if( eType
!= SbxNULL
)
816 // #45355 another point to jump in for isnumeric-String
820 SbxAppData
& rAppData
= GetSbxData_Impl();
822 LanguageType eLangType
= Application::GetSettings().GetLanguageTag().getLanguageType();
823 if( rAppData
.pBasicFormater
)
825 if( rAppData
.eBasicFormaterLangType
!= eLangType
)
827 rAppData
.pBasicFormater
.reset();
830 rAppData
.eBasicFormaterLangType
= eLangType
;
833 if( !rAppData
.pBasicFormater
)
835 SvtSysLocale aSysLocale
;
836 const LocaleDataWrapper
& rData
= aSysLocale
.GetLocaleData();
837 sal_Unicode cComma
= rData
.getNumDecimalSep()[0];
838 sal_Unicode c1000
= rData
.getNumThousandSep()[0];
839 const OUString
& aCurrencyStrg
= rData
.getCurrSymbol();
841 // initialize the Basic-formater help object:
842 // get resources for predefined output
843 // of the Format()-command, e. g. for "On/Off"
844 OUString aOnStrg
= BasResId(STR_BASICKEY_FORMAT_ON
);
845 OUString aOffStrg
= BasResId(STR_BASICKEY_FORMAT_OFF
);
846 OUString aYesStrg
= BasResId(STR_BASICKEY_FORMAT_YES
);
847 OUString aNoStrg
= BasResId(STR_BASICKEY_FORMAT_NO
);
848 OUString aTrueStrg
= BasResId(STR_BASICKEY_FORMAT_TRUE
);
849 OUString aFalseStrg
= BasResId(STR_BASICKEY_FORMAT_FALSE
);
850 OUString aCurrencyFormatStrg
= BasResId(STR_BASICKEY_FORMAT_CURRENCY
);
852 rAppData
.pBasicFormater
= std::make_unique
<SbxBasicFormater
>(
853 cComma
,c1000
,aOnStrg
,aOffStrg
,
854 aYesStrg
,aNoStrg
,aTrueStrg
,aFalseStrg
,
855 aCurrencyStrg
,aCurrencyFormatStrg
);
857 // Remark: For performance reasons there's only ONE BasicFormater-
858 // object created and 'stored', so that the expensive resource-
859 // loading is saved (for country-specific predefined outputs,
860 // e. g. "On/Off") and the continuous string-creation
862 // BUT: therefore this code is NOT multithreading capable!
864 // here are problems with ;;;Null because this method is only
865 // called, if SbxValue is a number!!!
866 // in addition rAppData.pBasicFormater->BasicFormatNull( *pFmt ); could be called!
867 if( eType
!= SbxNULL
)
869 rRes
= rAppData
.pBasicFormater
->BasicFormat( d
,*pFmt
);
873 rRes
= SbxBasicFormater::BasicFormatNull( *pFmt
);
878 ImpCvtNum( GetDouble(), nComma
, rRes
);
883 // #45355 converting if numeric
886 ScanNumIntnl( GetOUString(), d
);
891 printfmtstr( GetOUString(), rRes
, *pFmt
);
896 rRes
= GetOUString();
900 rRes
= GetOUString();
905 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */