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