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
