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