1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 // MARKER(update_precomp.py): autogen include statement, do not remove 23 #include "precompiled_basic.hxx" 24 #include <tools/errcode.hxx> 25 #include <basic/sbx.hxx> 26 #include "sbxconv.hxx" 27 28 #include "unotools/syslocale.hxx" 29 30 #if defined ( UNX ) 31 #include <stdlib.h> 32 #endif 33 34 #ifndef _APP_HXX //autogen 35 #include <vcl/svapp.hxx> 36 #endif 37 #include <math.h> 38 #include <string.h> 39 #include <ctype.h> 40 41 #include "sbxres.hxx" 42 #include <basic/sbxbase.hxx> 43 #include <basic/sbxform.hxx> 44 #include <svtools/svtools.hrc> 45 46 #include "basrid.hxx" 47 #include "runtime.hxx" 48 49 #include <svl/zforlist.hxx> 50 #include <comphelper/processfactory.hxx> 51 52 53 void ImpGetIntntlSep( sal_Unicode& rcDecimalSep, sal_Unicode& rcThousandSep ) 54 { 55 SvtSysLocale aSysLocale; 56 const LocaleDataWrapper& rData = aSysLocale.GetLocaleData(); 57 rcDecimalSep = rData.getNumDecimalSep().GetBuffer()[0]; 58 rcThousandSep = rData.getNumThousandSep().GetBuffer()[0]; 59 } 60 61 // Scannen eines Strings nach BASIC-Konventionen 62 // Dies entspricht den üblichen Konventionen, nur dass der Exponent 63 // auch ein D sein darf, was den Datentyp auf SbxDOUBLE festlegt. 64 // Die Routine versucht, den Datentyp so klein wie möglich zu gestalten. 65 // Das ganze gibt auch noch einen Konversionsfehler, wenn der Datentyp 66 // Fixed ist und das ganze nicht hineinpasst! 67 68 SbxError ImpScan( const ::rtl::OUString& rWSrc, double& nVal, SbxDataType& rType, 69 sal_uInt16* pLen, sal_Bool bAllowIntntl, sal_Bool bOnlyIntntl ) 70 { 71 ::rtl::OString aBStr( ::rtl::OUStringToOString( rWSrc, RTL_TEXTENCODING_ASCII_US ) ); 72 73 // Bei International Komma besorgen 74 char cIntntlComma, cIntntl1000; 75 char cNonIntntlComma = '.'; 76 77 sal_Unicode cDecimalSep, cThousandSep = 0; 78 if( bAllowIntntl || bOnlyIntntl ) 79 { 80 ImpGetIntntlSep( cDecimalSep, cThousandSep ); 81 cIntntlComma = (char)cDecimalSep; 82 cIntntl1000 = (char)cThousandSep; 83 } 84 // Sonst einfach auch auf . setzen 85 else 86 { 87 cIntntlComma = cNonIntntlComma; 88 cIntntl1000 = cNonIntntlComma; // Unschädlich machen 89 } 90 // Nur International -> IntnlComma übernehmen 91 if( bOnlyIntntl ) 92 { 93 cNonIntntlComma = cIntntlComma; 94 cIntntl1000 = (char)cThousandSep; 95 } 96 97 const char* pStart = aBStr.getStr(); 98 const char* p = pStart; 99 char buf[ 80 ], *q = buf; 100 sal_Bool bRes = sal_True; 101 sal_Bool bMinus = sal_False; 102 nVal = 0; 103 SbxDataType eScanType = SbxSINGLE; 104 // Whitespace wech 105 while( *p &&( *p == ' ' || *p == '\t' ) ) p++; 106 // Zahl? Dann einlesen und konvertieren. 107 if( *p == '-' ) 108 p++, bMinus = sal_True; 109 if( isdigit( *p ) ||( (*p == cNonIntntlComma || *p == cIntntlComma || 110 *p == cIntntl1000) && isdigit( *(p+1 ) ) ) ) 111 { 112 short exp = 0; // >0: Exponentteil 113 short comma = 0; // >0: Nachkomma 114 short ndig = 0; // Anzahl Ziffern 115 short ncdig = 0; // Anzahl Ziffern nach Komma 116 ByteString aSearchStr( "0123456789DEde" ); 117 // Kommas ergaenzen 118 aSearchStr += cNonIntntlComma; 119 if( cIntntlComma != cNonIntntlComma ) 120 aSearchStr += cIntntlComma; 121 if( bOnlyIntntl ) 122 aSearchStr += cIntntl1000; 123 const char* pSearchStr = aSearchStr.GetBuffer(); 124 while( strchr( pSearchStr, *p ) && *p ) 125 { 126 // 1000er-Trenner überlesen 127 if( bOnlyIntntl && *p == cIntntl1000 ) 128 { 129 p++; 130 continue; 131 } 132 133 // Komma oder Exponent? 134 if( *p == cNonIntntlComma || *p == cIntntlComma ) 135 { 136 // Immer '.' einfügen, damit atof funktioniert 137 p++; 138 if( ++comma > 1 ) 139 continue; 140 else 141 *q++ = '.'; 142 } 143 else if( strchr( "DdEe", *p ) ) 144 { 145 if( ++exp > 1 ) 146 { 147 p++; continue; 148 } 149 if( toupper( *p ) == 'D' ) 150 eScanType = SbxDOUBLE; 151 *q++ = 'E'; p++; 152 // Vorzeichen hinter Exponent? 153 if( *p == '+' ) 154 p++; 155 else 156 if( *p == '-' ) 157 *q++ = *p++; 158 } 159 else 160 { 161 *q++ = *p++; 162 if( comma && !exp ) ncdig++; 163 } 164 if( !exp ) ndig++; 165 } 166 *q = 0; 167 // Komma, Exponent mehrfach vorhanden? 168 if( comma > 1 || exp > 1 ) 169 bRes = sal_False; 170 // Kann auf Integer gefaltet werden? 171 if( !comma && !exp ) 172 { 173 if( nVal >= SbxMININT && nVal <= SbxMAXINT ) 174 eScanType = SbxINTEGER; 175 else if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG ) 176 eScanType = SbxLONG; 177 } 178 179 nVal = atof( buf ); 180 ndig = ndig - comma; 181 // zu viele Zahlen für SINGLE? 182 if( ndig > 15 || ncdig > 6 ) 183 eScanType = SbxDOUBLE; 184 185 // Typkennung? 186 if( strchr( "%!&#", *p ) && *p ) p++; 187 } 188 // Hex/Oktalzahl? Einlesen und konvertieren: 189 else if( *p == '&' ) 190 { 191 p++; 192 eScanType = SbxLONG; 193 const char *cmp = "0123456789ABCDEF"; 194 char base = 16; 195 char ndig = 8; 196 char xch = *p++; 197 switch( toupper( xch ) ) 198 { 199 case 'O': cmp = "01234567"; base = 8; ndig = 11; break; 200 case 'H': break; 201 default : bRes = sal_False; 202 } 203 sal_Int32 l = 0; 204 int i; 205 while( isalnum( *p ) ) 206 { 207 char ch = sal::static_int_cast< char >( toupper( *p ) ); 208 p++; 209 if( strchr( cmp, ch ) ) *q++ = ch; 210 else bRes = sal_False; 211 } 212 *q = 0; 213 for( q = buf; *q; q++ ) 214 { 215 i =( *q & 0xFF ) - '0'; 216 if( i > 9 ) i -= 7; 217 l =( l * base ) + i; 218 if( !ndig-- ) 219 bRes = sal_False; 220 } 221 if( *p == '&' ) p++; 222 nVal = (double) l; 223 if( l >= SbxMININT && l <= SbxMAXINT ) 224 eScanType = SbxINTEGER; 225 } 226 else if ( SbiRuntime::isVBAEnabled() ) 227 { 228 OSL_TRACE("Reporting error converting"); 229 return SbxERR_CONVERSION; 230 } 231 if( pLen ) 232 *pLen = (sal_uInt16) ( p - pStart ); 233 if( !bRes ) 234 return SbxERR_CONVERSION; 235 if( bMinus ) 236 nVal = -nVal; 237 rType = eScanType; 238 return SbxERR_OK; 239 } 240 241 // Schnittstelle für CDbl im Basic 242 SbxError SbxValue::ScanNumIntnl( const String& rSrc, double& nVal, sal_Bool bSingle ) 243 { 244 SbxDataType t; 245 sal_uInt16 nLen = 0; 246 SbxError nRetError = ImpScan( rSrc, nVal, t, &nLen, 247 /*bAllowIntntl*/sal_False, /*bOnlyIntntl*/sal_True ); 248 // Komplett gelesen? 249 if( nRetError == SbxERR_OK && nLen != rSrc.Len() ) 250 nRetError = SbxERR_CONVERSION; 251 252 if( bSingle ) 253 { 254 SbxValues aValues( nVal ); 255 nVal = (double)ImpGetSingle( &aValues ); // Hier Error bei Overflow 256 } 257 return nRetError; 258 } 259 260 //////////////////////////////////////////////////////////////////////////// 261 262 static double roundArray[] = { 263 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, 264 0.5e-8, 0.5e-9, 0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14,0.5e-15 }; 265 266 /*************************************************************************** 267 |* 268 |* void myftoa( double, char *, short, short, sal_Bool, sal_Bool ) 269 |* 270 |* Beschreibung: Konversion double --> ASCII 271 |* Parameter: double die Zahl. 272 |* char * der Zielpuffer 273 |* short Anzahl Nachkommastellen 274 |* short Weite des Exponenten( 0=kein E ) 275 |* sal_Bool sal_True: mit 1000er Punkten 276 |* sal_Bool sal_True: formatfreie Ausgabe 277 |* 278 ***************************************************************************/ 279 280 static void myftoa( double nNum, char * pBuf, short nPrec, short nExpWidth, 281 sal_Bool bPt, sal_Bool bFix, sal_Unicode cForceThousandSep = 0 ) 282 { 283 284 short nExp = 0; // Exponent 285 short nDig = nPrec + 1; // Anzahl Digits in Zahl 286 short nDec; // Anzahl Vorkommastellen 287 register int i, digit; 288 289 // Komma besorgen 290 sal_Unicode cDecimalSep, cThousandSep; 291 ImpGetIntntlSep( cDecimalSep, cThousandSep ); 292 if( cForceThousandSep ) 293 cThousandSep = cForceThousandSep; 294 295 // Exponentberechnung: 296 nExp = 0; 297 if( nNum > 0.0 ) 298 { 299 while( nNum < 1.0 ) nNum *= 10.0, nExp--; 300 while( nNum >= 10.0 ) nNum /= 10.0, nExp++; 301 } 302 if( !bFix && !nExpWidth ) 303 nDig = nDig + nExp; 304 else if( bFix && !nPrec ) 305 nDig = nExp + 1; 306 307 // Zahl runden: 308 if( (nNum += roundArray [( nDig > 16 ) ? 16 : nDig] ) >= 10.0 ) 309 { 310 nNum = 1.0; 311 ++nExp; 312 if( !nExpWidth ) ++nDig; 313 } 314 315 // Bestimmung der Vorkommastellen: 316 if( !nExpWidth ) 317 { 318 if( nExp < 0 ) 319 { 320 // #41691: Auch bei bFix eine 0 spendieren 321 *pBuf++ = '0'; 322 if( nPrec ) *pBuf++ = (char)cDecimalSep; 323 i = -nExp - 1; 324 if( nDig <= 0 ) i = nPrec; 325 while( i-- ) *pBuf++ = '0'; 326 nDec = 0; 327 } 328 else 329 nDec = nExp+1; 330 } 331 else 332 nDec = 1; 333 334 // Zahl ausgeben: 335 if( nDig > 0 ) 336 { 337 for( i = 0 ; ; ++i ) 338 { 339 if( i < 16 ) 340 { 341 digit = (int) nNum; 342 *pBuf++ = sal::static_int_cast< char >(digit + '0'); 343 nNum =( nNum - digit ) * 10.0; 344 } else 345 *pBuf++ = '0'; 346 if( --nDig == 0 ) break; 347 if( nDec ) 348 { 349 nDec--; 350 if( !nDec ) 351 *pBuf++ = (char)cDecimalSep; 352 else if( !(nDec % 3 ) && bPt ) 353 *pBuf++ = (char)cThousandSep; 354 } 355 } 356 } 357 358 // Exponent ausgeben: 359 if( nExpWidth ) 360 { 361 if( nExpWidth < 3 ) nExpWidth = 3; 362 nExpWidth -= 2; 363 *pBuf++ = 'E'; 364 *pBuf++ =( nExp < 0 ) ?( (nExp = -nExp ), '-' ) : '+'; 365 while( nExpWidth > 3 ) *pBuf++ = '0', nExpWidth--; 366 if( nExp >= 100 || nExpWidth == 3 ) 367 { 368 *pBuf++ = sal::static_int_cast< char >(nExp/100 + '0'); 369 nExp %= 100; 370 } 371 if( nExp/10 || nExpWidth >= 2 ) 372 *pBuf++ = sal::static_int_cast< char >(nExp/10 + '0'); 373 *pBuf++ = sal::static_int_cast< char >(nExp%10 + '0'); 374 } 375 *pBuf = 0; 376 } 377 378 // Die Zahl wird unformatiert mit der angegebenen Anzahl NK-Stellen 379 // aufbereitet. Evtl. wird ein Minus vorangestellt. 380 // Diese Routine ist public, weil sie auch von den Put-Funktionen 381 // der Klasse SbxImpSTRING verwendet wird. 382 383 #ifdef _MSC_VER 384 #pragma optimize( "", off ) 385 #pragma warning(disable: 4748) // "... because optimizations are disabled ..." 386 #endif 387 388 void ImpCvtNum( double nNum, short nPrec, ::rtl::OUString& rRes, sal_Bool bCoreString ) 389 { 390 char *q; 391 char cBuf[ 40 ], *p = cBuf; 392 393 sal_Unicode cDecimalSep, cThousandSep; 394 ImpGetIntntlSep( cDecimalSep, cThousandSep ); 395 if( bCoreString ) 396 cDecimalSep = '.'; 397 398 if( nNum < 0.0 ) { 399 nNum = -nNum; 400 *p++ = '-'; 401 } 402 double dMaxNumWithoutExp = (nPrec == 6) ? 1E6 : 1E14; 403 myftoa( nNum, p, nPrec,( nNum &&( nNum < 1E-1 || nNum >= dMaxNumWithoutExp ) ) ? 4:0, 404 sal_False, sal_True, cDecimalSep ); 405 // Trailing Zeroes weg: 406 for( p = cBuf; *p &&( *p != 'E' ); p++ ) {} 407 q = p; p--; 408 while( nPrec && *p == '0' ) nPrec--, p--; 409 if( *p == cDecimalSep ) p--; 410 while( *q ) *++p = *q++; 411 *++p = 0; 412 rRes = ::rtl::OUString::createFromAscii( cBuf ); 413 } 414 415 #ifdef _MSC_VER 416 #pragma optimize( "", on ) 417 #endif 418 419 sal_Bool ImpConvStringExt( ::rtl::OUString& rSrc, SbxDataType eTargetType ) 420 { 421 // Merken, ob überhaupt was geändert wurde 422 sal_Bool bChanged = sal_False; 423 ::rtl::OUString aNewString; 424 425 // Nur Spezial-Fälle behandeln, als Default tun wir nichts 426 switch( eTargetType ) 427 { 428 // Bei Fliesskomma International berücksichtigen 429 case SbxSINGLE: 430 case SbxDOUBLE: 431 case SbxCURRENCY: 432 { 433 ::rtl::OString aBStr( ::rtl::OUStringToOString( rSrc, RTL_TEXTENCODING_ASCII_US ) ); 434 435 // Komma besorgen 436 sal_Unicode cDecimalSep, cThousandSep; 437 ImpGetIntntlSep( cDecimalSep, cThousandSep ); 438 aNewString = rSrc; 439 440 // Ersetzen, wenn DecimalSep kein '.' (nur den ersten) 441 if( cDecimalSep != (sal_Unicode)'.' ) 442 { 443 sal_Int32 nPos = aNewString.indexOf( cDecimalSep ); 444 if( nPos != -1 ) 445 { 446 sal_Unicode* pStr = (sal_Unicode*)aNewString.getStr(); 447 pStr[nPos] = (sal_Unicode)'.'; 448 bChanged = sal_True; 449 } 450 } 451 break; 452 } 453 454 // Bei sal_Bool sal_True und sal_False als String prüfen 455 case SbxBOOL: 456 { 457 if( rSrc.equalsIgnoreAsciiCaseAscii( "true" ) ) 458 { 459 aNewString = ::rtl::OUString::valueOf( (sal_Int32)SbxTRUE ); 460 bChanged = sal_True; 461 } 462 else 463 if( rSrc.equalsIgnoreAsciiCaseAscii( "false" ) ) 464 { 465 aNewString = ::rtl::OUString::valueOf( (sal_Int32)SbxFALSE ); 466 bChanged = sal_True; 467 } 468 break; 469 } 470 default: break; 471 } 472 // String bei Änderung übernehmen 473 if( bChanged ) 474 rSrc = aNewString; 475 return bChanged; 476 } 477 478 479 // Formatierte Zahlenausgabe 480 // Der Returnwert ist die Anzahl Zeichen, die aus dem 481 // Format verwendet wurden. 482 483 #ifdef _old_format_code_ 484 // lasse diesen Code vorl"aufig drin, zum 'abgucken' 485 // der bisherigen Implementation 486 487 static sal_uInt16 printfmtnum( double nNum, XubString& rRes, const XubString& rWFmt ) 488 { 489 const String& rFmt = rWFmt; 490 char cFill = ' '; // Fuellzeichen 491 char cPre = 0; // Startzeichen( evtl. "$" ) 492 short nExpDig= 0; // Anzahl Exponentstellen 493 short nPrec = 0; // Anzahl Nachkommastellen 494 short nWidth = 0; // Zahlenweite gesamnt 495 short nLen; // Laenge konvertierte Zahl 496 sal_Bool bPoint = sal_False; // sal_True: mit 1000er Kommas 497 sal_Bool bTrail = sal_False; // sal_True, wenn folgendes Minus 498 sal_Bool bSign = sal_False; // sal_True: immer mit Vorzeichen 499 sal_Bool bNeg = sal_False; // sal_True: Zahl ist negativ 500 char cBuf [1024]; // Zahlenpuffer 501 char * p; 502 const char* pFmt = rFmt; 503 rRes.Erase(); 504 // $$ und ** abfangen. Einfach wird als Zeichen ausgegeben. 505 if( *pFmt == '$' ) 506 if( *++pFmt != '$' ) rRes += '$'; 507 if( *pFmt == '*' ) 508 if( *++pFmt != '*' ) rRes += '*'; 509 510 switch( *pFmt++ ) 511 { 512 case 0: 513 break; 514 case '+': 515 bSign = sal_True; nWidth++; break; 516 case '*': 517 nWidth++; cFill = '*'; 518 if( *pFmt == '$' ) nWidth++, pFmt++, cPre = '$'; 519 break; 520 case '$': 521 nWidth++; cPre = '$'; break; 522 case '#': 523 case '.': 524 case ',': 525 pFmt--; break; 526 } 527 // Vorkomma: 528 for( ;; ) 529 { 530 while( *pFmt == '#' ) pFmt++, nWidth++; 531 // 1000er Kommas? 532 if( *pFmt == ',' ) 533 { 534 nWidth++; pFmt++; bPoint = sal_True; 535 } else break; 536 } 537 // Nachkomma: 538 if( *pFmt == '.' ) 539 { 540 while( *++pFmt == '#' ) nPrec++; 541 nWidth += nPrec + 1; 542 } 543 // Exponent: 544 while( *pFmt == '^' ) 545 pFmt++, nExpDig++, nWidth++; 546 // Folgendes Minus: 547 if( !bSign && *pFmt == '-' ) 548 pFmt++, bTrail = sal_True; 549 550 // Zahl konvertieren: 551 if( nPrec > 15 ) nPrec = 15; 552 if( nNum < 0.0 ) nNum = -nNum, bNeg = sal_True; 553 p = cBuf; 554 if( bSign ) *p++ = bNeg ? '-' : '+'; 555 myftoa( nNum, p, nPrec, nExpDig, bPoint, sal_False ); 556 nLen = strlen( cBuf ); 557 558 // Überlauf? 559 if( cPre ) nLen++; 560 if( nLen > nWidth ) rRes += '%'; 561 else { 562 nWidth -= nLen; 563 while( nWidth-- ) rRes += (xub_Unicode)cFill; 564 if( cPre ) rRes += (xub_Unicode)cPre; 565 } 566 rRes += (xub_Unicode*)&(cBuf[0]); 567 if( bTrail ) 568 rRes += bNeg ? '-' : ' '; 569 570 return (sal_uInt16) ( pFmt - (const char*) rFmt ); 571 } 572 573 #endif //_old_format_code_ 574 575 static sal_uInt16 printfmtstr( const XubString& rStr, XubString& rRes, const XubString& rFmt ) 576 { 577 const xub_Unicode* pStr = rStr.GetBuffer(); 578 const xub_Unicode* pFmtStart = rFmt.GetBuffer(); 579 const xub_Unicode* pFmt = pFmtStart; 580 rRes.Erase(); 581 switch( *pFmt ) 582 { 583 case '!': 584 rRes += *pStr++; pFmt++; break; 585 case '\\': 586 do 587 { 588 rRes += *pStr ? *pStr++ : static_cast< xub_Unicode >(' '); 589 pFmt++; 590 } while( *pFmt != '\\' ); 591 rRes += *pStr ? *pStr++ : static_cast< xub_Unicode >(' '); 592 pFmt++; break; 593 case '&': 594 rRes = rStr; 595 pFmt++; break; 596 default: 597 rRes = rStr; 598 break; 599 } 600 return (sal_uInt16) ( pFmt - pFmtStart ); 601 } 602 603 ///////////////////////////////////////////////////////////////////////// 604 605 sal_Bool SbxValue::Scan( const XubString& rSrc, sal_uInt16* pLen ) 606 { 607 SbxError eRes = SbxERR_OK; 608 if( !CanWrite() ) 609 eRes = SbxERR_PROP_READONLY; 610 else 611 { 612 double n; 613 SbxDataType t; 614 eRes = ImpScan( rSrc, n, t, pLen ); 615 if( eRes == SbxERR_OK ) 616 { 617 if( !IsFixed() ) 618 SetType( t ); 619 PutDouble( n ); 620 } 621 } 622 if( eRes ) 623 { 624 SetError( eRes ); return sal_False; 625 } 626 else 627 return sal_True; 628 } 629 630 631 ResMgr* implGetResMgr( void ) 632 { 633 static ResMgr* pResMgr = NULL; 634 if( !pResMgr ) 635 { 636 ::com::sun::star::lang::Locale aLocale = Application::GetSettings().GetUILocale(); 637 pResMgr = ResMgr::CreateResMgr(CREATEVERSIONRESMGR_NAME(sb), aLocale ); 638 } 639 return pResMgr; 640 } 641 642 class SbxValueFormatResId : public ResId 643 { 644 public: 645 SbxValueFormatResId( sal_uInt16 nId ) 646 : ResId( nId, *implGetResMgr() ) 647 {} 648 }; 649 650 651 enum VbaFormatType 652 { 653 VBA_FORMAT_TYPE_OFFSET, // standard number format 654 VBA_FORMAT_TYPE_USERDEFINED, // user defined number format 655 VBA_FORMAT_TYPE_NULL 656 }; 657 658 struct VbaFormatInfo 659 { 660 VbaFormatType meType; 661 const char* mpVbaFormat; // Format string in vba 662 NfIndexTableOffset meOffset; // SvNumberFormatter format index, if meType = VBA_FORMAT_TYPE_OFFSET 663 const char* mpOOoFormat; // if meType = VBA_FORMAT_TYPE_USERDEFINED 664 }; 665 666 #define VBA_FORMAT_OFFSET( pcUtf8, eOffset ) \ 667 { VBA_FORMAT_TYPE_OFFSET, pcUtf8, eOffset, 0 } 668 669 #define VBA_FORMAT_USERDEFINED( pcUtf8, pcDefinedUtf8 ) \ 670 { VBA_FORMAT_TYPE_USERDEFINED, pcUtf8, NF_NUMBER_STANDARD, pcDefinedUtf8 } 671 672 static VbaFormatInfo pFormatInfoTable[] = 673 { 674 VBA_FORMAT_OFFSET( "Long Date", NF_DATE_SYSTEM_LONG ), 675 VBA_FORMAT_USERDEFINED( "Medium Date", "DD-MMM-YY" ), 676 VBA_FORMAT_OFFSET( "Short Date", NF_DATE_SYSTEM_SHORT ), 677 VBA_FORMAT_USERDEFINED( "Long Time", "H:MM:SS AM/PM" ), 678 VBA_FORMAT_OFFSET( "Medium Time", NF_TIME_HHMMAMPM ), 679 VBA_FORMAT_OFFSET( "Short Time", NF_TIME_HHMM ), 680 VBA_FORMAT_OFFSET( "ddddd", NF_DATE_SYSTEM_SHORT ), 681 VBA_FORMAT_OFFSET( "dddddd", NF_DATE_SYSTEM_LONG ), 682 VBA_FORMAT_USERDEFINED( "ttttt", "H:MM:SS AM/PM" ), 683 VBA_FORMAT_OFFSET( "ww", NF_DATE_WW ), 684 { VBA_FORMAT_TYPE_NULL, 0, NF_INDEX_TABLE_ENTRIES, 0 } 685 }; 686 687 VbaFormatInfo* getFormatInfo( const String& rFmt ) 688 { 689 VbaFormatInfo* pInfo = NULL; 690 sal_Int16 i = 0; 691 while( (pInfo = pFormatInfoTable + i )->mpVbaFormat != NULL ) 692 { 693 if( rFmt.EqualsIgnoreCaseAscii( pInfo->mpVbaFormat ) ) 694 break; 695 i++; 696 } 697 return pInfo; 698 } 699 700 #define VBAFORMAT_GENERALDATE "General Date" 701 #define VBAFORMAT_C "c" 702 #define VBAFORMAT_N "n" 703 #define VBAFORMAT_NN "nn" 704 #define VBAFORMAT_W "w" 705 #define VBAFORMAT_Y "y" 706 #define VBAFORMAT_LOWERCASE "<" 707 #define VBAFORMAT_UPPERCASE ">" 708 709 // From methods1.cxx 710 sal_Int16 implGetWeekDay( double aDate, bool bFirstDayParam = false, sal_Int16 nFirstDay = 0 ); 711 // from methods.cxx 712 sal_Int16 implGetMinute( double dDate ); 713 sal_Int16 implGetDateYear( double aDate ); 714 sal_Bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, double& rdRet ); 715 716 void SbxValue::Format( XubString& rRes, const XubString* pFmt ) const 717 { 718 short nComma = 0; 719 double d = 0; 720 721 // pflin, It is better to use SvNumberFormatter to handle the date/time/number format. 722 // the SvNumberFormatter output is mostly compatible with 723 // VBA output besides the AOO-BASIC output 724 if( pFmt && !SbxBasicFormater::isBasicFormat( *pFmt ) ) 725 { 726 String aStr = GetString(); 727 728 SvtSysLocale aSysLocale; 729 const CharClass& rCharClass = aSysLocale.GetCharClass(); 730 731 if( pFmt->EqualsIgnoreCaseAscii( VBAFORMAT_LOWERCASE ) ) 732 { 733 rCharClass.toLower( aStr ); 734 rRes = aStr; 735 return; 736 } 737 if( pFmt->EqualsIgnoreCaseAscii( VBAFORMAT_UPPERCASE ) ) 738 { 739 rCharClass.toUpper( aStr ); 740 rRes = aStr; 741 return; 742 } 743 744 LanguageType eLangType = GetpApp()->GetSettings().GetLanguage(); 745 com::sun::star::uno::Reference< com::sun::star::lang::XMultiServiceFactory > 746 xFactory = comphelper::getProcessServiceFactory(); 747 SvNumberFormatter aFormatter( xFactory, eLangType ); 748 749 sal_uInt32 nIndex; 750 xub_StrLen nCheckPos = 0; 751 short nType; 752 double nNumber; 753 Color* pCol; 754 755 sal_Bool bSuccess = aFormatter.IsNumberFormat( aStr, nIndex, nNumber ); 756 757 // number format, use SvNumberFormatter to handle it. 758 if( bSuccess ) 759 { 760 String aFmtStr = *pFmt; 761 VbaFormatInfo* pInfo = getFormatInfo( aFmtStr ); 762 if( pInfo && pInfo->meType != VBA_FORMAT_TYPE_NULL ) 763 { 764 if( pInfo->meType == VBA_FORMAT_TYPE_OFFSET ) 765 { 766 nIndex = aFormatter.GetFormatIndex( pInfo->meOffset, eLangType ); 767 } 768 else 769 { 770 aFmtStr.AssignAscii( pInfo->mpOOoFormat ); 771 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType ); 772 } 773 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol ); 774 } 775 else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_GENERALDATE ) 776 || aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_C )) 777 { 778 if( nNumber <=-1.0 || nNumber >= 1.0 ) 779 { 780 // short date 781 nIndex = aFormatter.GetFormatIndex( NF_DATE_SYSTEM_SHORT, eLangType ); 782 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol ); 783 784 // long time 785 if( floor( nNumber ) != nNumber ) 786 { 787 aFmtStr.AssignAscii( "H:MM:SS AM/PM" ); 788 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType ); 789 String aTime; 790 aFormatter.GetOutputString( nNumber, nIndex, aTime, &pCol ); 791 rRes.AppendAscii(" "); 792 rRes += aTime; 793 } 794 } 795 else 796 { 797 // long time only 798 aFmtStr.AssignAscii( "H:MM:SS AM/PM" ); 799 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType ); 800 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol ); 801 } 802 } 803 else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_N ) 804 || aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_NN )) 805 { 806 sal_Int32 nMin = implGetMinute( nNumber ); 807 if( nMin < 10 && aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_NN ) ) 808 { 809 // Minute in two digits 810 sal_Unicode* p = rRes.AllocBuffer( 2 ); 811 *p++ = '0'; 812 *p = sal_Unicode( '0' + nMin ); 813 } 814 else 815 { 816 rRes = String::CreateFromInt32( nMin ); 817 } 818 } 819 else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_W )) 820 { 821 sal_Int32 nWeekDay = implGetWeekDay( nNumber ); 822 rRes = String::CreateFromInt32( nWeekDay ); 823 } 824 else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_Y )) 825 { 826 sal_Int16 nYear = implGetDateYear( nNumber ); 827 double dBaseDate; 828 implDateSerial( nYear, 1, 1, dBaseDate ); 829 sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate ); 830 rRes = String::CreateFromInt32( nYear32 ); 831 } 832 else 833 { 834 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType ); 835 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol ); 836 } 837 838 return; 839 } 840 } 841 842 SbxDataType eType = GetType(); 843 switch( eType ) 844 { 845 case SbxCHAR: 846 case SbxBYTE: 847 case SbxINTEGER: 848 case SbxUSHORT: 849 case SbxLONG: 850 case SbxULONG: 851 case SbxINT: 852 case SbxUINT: 853 case SbxNULL: // #45929 NULL mit durchschummeln 854 nComma = 0; goto cvt; 855 case SbxSINGLE: 856 nComma = 6; goto cvt; 857 case SbxDOUBLE: 858 nComma = 14; 859 860 cvt: 861 if( eType != SbxNULL ) 862 d = GetDouble(); 863 864 // #45355 weiterer Einsprungpunkt für isnumeric-String 865 cvt2: 866 if( pFmt ) 867 { 868 // hole die 'statischen' Daten f"ur Sbx 869 SbxAppData* pData = GetSbxData_Impl(); 870 871 LanguageType eLangType = GetpApp()->GetSettings().GetLanguage(); 872 if( pData->pBasicFormater ) 873 { 874 if( pData->eBasicFormaterLangType != eLangType ) 875 { 876 delete pData->pBasicFormater; 877 pData->pBasicFormater = NULL; 878 } 879 } 880 pData->eBasicFormaterLangType = eLangType; 881 882 // falls bisher noch kein BasicFormater-Objekt 883 // existiert, so erzeuge dieses 884 if( !pData->pBasicFormater ) 885 { 886 SvtSysLocale aSysLocale; 887 const LocaleDataWrapper& rData = aSysLocale.GetLocaleData(); 888 sal_Unicode cComma = rData.getNumDecimalSep().GetBuffer()[0]; 889 sal_Unicode c1000 = rData.getNumThousandSep().GetBuffer()[0]; 890 String aCurrencyStrg = rData.getCurrSymbol(); 891 892 // Initialisierung des Basic-Formater-Hilfsobjekts: 893 // hole die Ressourcen für die vordefinierten Ausgaben 894 // des Format()-Befehls, z.B. für "On/Off". 895 String aOnStrg = String( SbxValueFormatResId( 896 STR_BASICKEY_FORMAT_ON ) ); 897 String aOffStrg = String( SbxValueFormatResId( 898 STR_BASICKEY_FORMAT_OFF) ); 899 String aYesStrg = String( SbxValueFormatResId( 900 STR_BASICKEY_FORMAT_YES) ); 901 String aNoStrg = String( SbxValueFormatResId( 902 STR_BASICKEY_FORMAT_NO) ); 903 String aTrueStrg = String( SbxValueFormatResId( 904 STR_BASICKEY_FORMAT_TRUE) ); 905 String aFalseStrg = String( SbxValueFormatResId( 906 STR_BASICKEY_FORMAT_FALSE) ); 907 String aCurrencyFormatStrg = String( SbxValueFormatResId( 908 STR_BASICKEY_FORMAT_CURRENCY) ); 909 // erzeuge das Basic-Formater-Objekt 910 pData->pBasicFormater 911 = new SbxBasicFormater( cComma,c1000,aOnStrg,aOffStrg, 912 aYesStrg,aNoStrg,aTrueStrg,aFalseStrg, 913 aCurrencyStrg,aCurrencyFormatStrg ); 914 } 915 // Bem.: Aus Performance-Gründen wird nur EIN BasicFormater- 916 // Objekt erzeugt und 'gespeichert', dadurch erspart man 917 // sich das teure Ressourcen-Laden (für landesspezifische 918 // vordefinierte Ausgaben, z.B. "On/Off") und die ständige 919 // String-Erzeugungs Operationen. 920 // ABER: dadurch ist dieser Code NICHT multithreading f"ahig ! 921 922 // hier gibt es Probleme mit ;;;Null, da diese Methode nur aufgerufen 923 // wird, wenn der SbxValue eine Zahl ist !!! 924 // dazu koennte: pData->pBasicFormater->BasicFormatNull( *pFmt ); aufgerufen werden ! 925 if( eType != SbxNULL ) 926 { 927 rRes = pData->pBasicFormater->BasicFormat( d ,*pFmt ); 928 } 929 else 930 { 931 rRes = pData->pBasicFormater->BasicFormatNull( *pFmt ); 932 } 933 934 // Die alte Implementierung: 935 //old: printfmtnum( GetDouble(), rRes, *pFmt ); 936 } 937 else 938 { 939 ::rtl::OUString aTmpString( rRes ); 940 ImpCvtNum( GetDouble(), nComma, aTmpString ); 941 rRes = aTmpString; 942 } 943 break; 944 case SbxSTRING: 945 if( pFmt ) 946 { 947 // #45355 wenn es numerisch ist, muss gewandelt werden 948 if( IsNumericRTL() ) 949 { 950 ScanNumIntnl( GetString(), d, /*bSingle*/sal_False ); 951 goto cvt2; 952 } 953 else 954 { 955 // Sonst String-Formatierung 956 printfmtstr( GetString(), rRes, *pFmt ); 957 } 958 } 959 else 960 rRes = GetString(); 961 break; 962 default: 963 rRes = GetString(); 964 } 965 } 966 967 /* vim: set noet sw=4 ts=4: */ 968