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