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