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 <rtl/ustrbuf.hxx> 21 22 #include <basic/sberrors.hxx> 23 #include <basic/sbxvar.hxx> 24 #include "sbxconv.hxx" 25 26 27 static OUString ImpCurrencyToString( sal_Int64 rVal ) 28 { 29 bool isNeg = ( rVal < 0 ); 30 sal_Int64 absVal = isNeg ? -rVal : rVal; 31 32 sal_Unicode const cDecimalSep = '.'; 33 34 OUString aAbsStr = OUString::number( absVal ); 35 36 sal_Int32 initialLen = aAbsStr.getLength(); 37 38 bool bLessThanOne = false; 39 if ( initialLen <= 4 ) // if less the 1 40 bLessThanOne = true; 41 42 sal_Int32 nCapacity = 6; // minimum e.g. 0.0000 43 44 if ( !bLessThanOne ) 45 { 46 nCapacity = initialLen + 1; 47 } 48 49 if ( isNeg ) 50 ++nCapacity; 51 52 OUStringBuffer aBuf( nCapacity ); 53 aBuf.setLength( nCapacity ); 54 55 56 sal_Int32 nDigitCount = 0; 57 sal_Int32 nInsertIndex = nCapacity - 1; 58 sal_Int32 nEndIndex = isNeg ? 1 : 0; 59 60 for ( sal_Int32 charCpyIndex = aAbsStr.getLength() - 1; nInsertIndex >= nEndIndex; ++nDigitCount ) 61 { 62 if ( nDigitCount == 4 ) 63 aBuf[nInsertIndex--] = cDecimalSep; 64 if ( nDigitCount < initialLen ) 65 aBuf[nInsertIndex--] = aAbsStr[ charCpyIndex-- ]; 66 else 67 // Handle leading 0's to right of decimal point 68 // Note: in VBA the stringification is a little more complex 69 // but more natural as only the necessary digits 70 // to the right of the decimal places are displayed 71 // It would be great to conditionally be able to display like that too 72 73 // Val OOo (Cur) VBA (Cur) 74 // --- --------- --------- 75 // 0 0.0000 0 76 // 0.1 0.1000 0.1 77 78 aBuf[nInsertIndex--] = '0'; 79 } 80 if ( isNeg ) 81 aBuf[nInsertIndex] = '-'; 82 83 aAbsStr = aBuf.makeStringAndClear(); 84 return aAbsStr; 85 } 86 87 88 static sal_Int64 ImpStringToCurrency( const OUString &rStr ) 89 { 90 91 sal_Int32 nFractDigit = 4; 92 93 sal_Unicode const cDeciPnt = '.'; 94 sal_Unicode const c1000Sep = ','; 95 96 // lets use the existing string number conversions 97 // there is a performance impact here ( multiple string copies ) 98 // but better I think than a home brewed string parser, if we need a parser 99 // we should share some existing ( possibly from calc is there a currency 100 // conversion there ? #TODO check ) 101 102 OUString sTmp( rStr.trim() ); 103 const sal_Unicode* p = sTmp.getStr(); 104 105 // normalise string number by removing thousand & decimal point separators 106 OUStringBuffer sNormalisedNumString( sTmp.getLength() + nFractDigit ); 107 108 if ( *p == '-' || *p == '+' ) 109 sNormalisedNumString.append( *p ); 110 111 while ( *p >= '0' && *p <= '9' ) 112 { 113 sNormalisedNumString.append( *p++ ); 114 // #TODO in vba mode set runtime error when a space ( or other ) 115 // illegal character is found 116 if( *p == c1000Sep ) 117 p++; 118 } 119 120 bool bRoundUp = false; 121 122 if( *p == cDeciPnt ) 123 { 124 p++; 125 while( nFractDigit && *p >= '0' && *p <= '9' ) 126 { 127 sNormalisedNumString.append( *p++ ); 128 nFractDigit--; 129 } 130 // Consume trailing content 131 if ( p != nullptr ) 132 { 133 // Round up if necessary 134 if( *p >= '5' && *p <= '9' ) 135 bRoundUp = true; 136 while( *p >= '0' && *p <= '9' ) 137 p++; 138 } 139 140 } 141 // can we raise error here ? ( previous behaviour was more forgiving ) 142 // so... not sure that could break existing code, let's see if anyone 143 // complains. 144 145 if ( p != sTmp.getStr() + sTmp.getLength() ) 146 SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); 147 while( nFractDigit ) 148 { 149 sNormalisedNumString.append( '0' ); 150 nFractDigit--; 151 } 152 153 sal_Int64 result = sNormalisedNumString.makeStringAndClear().toInt64(); 154 155 if ( bRoundUp ) 156 ++result; 157 return result; 158 } 159 160 161 sal_Int64 ImpGetCurrency( const SbxValues* p ) 162 { 163 SbxValues aTmp; 164 sal_Int64 nRes; 165 start: 166 switch( +p->eType ) 167 { 168 case SbxERROR: 169 case SbxNULL: 170 SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); 171 nRes = 0; break; 172 case SbxEMPTY: 173 nRes = 0; break; 174 case SbxCURRENCY: 175 nRes = p->nInt64; break; 176 case SbxBYTE: 177 nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(p->nByte); 178 break; 179 case SbxCHAR: 180 nRes = sal_Int64(CURRENCY_FACTOR) * reinterpret_cast<sal_Int64>(p->pChar); 181 break; 182 case SbxBOOL: 183 case SbxINTEGER: 184 nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(p->nInteger); 185 break; 186 case SbxUSHORT: 187 nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(p->nUShort); 188 break; 189 case SbxLONG: 190 nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(p->nLong); 191 break; 192 case SbxULONG: 193 nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(p->nULong); 194 break; 195 196 case SbxSALINT64: 197 { 198 nRes = p->nInt64 * CURRENCY_FACTOR; break; 199 #if 0 200 // Huh, is the 'break' above intentional? That means this 201 // is unreachable, obviously. Avoid warning by ifdeffing 202 // this out for now. Do not delete this #if 0 block unless 203 // you know for sure the 'break' above is intentional. 204 if ( nRes > SAL_MAX_INT64 ) 205 { 206 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SAL_MAX_INT64; 207 } 208 #endif 209 } 210 case SbxSALUINT64: 211 nRes = p->nInt64 * CURRENCY_FACTOR; break; 212 #if 0 213 // As above 214 if ( nRes > SAL_MAX_INT64 ) 215 { 216 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SAL_MAX_INT64; 217 } 218 else if ( nRes < SAL_MIN_INT64 ) 219 { 220 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); nRes = SAL_MIN_INT64; 221 } 222 break; 223 #endif 224 //TODO: bring back SbxINT64 types here for limits -1 with flag value at SAL_MAX/MIN 225 case SbxSINGLE: 226 if( p->nSingle * CURRENCY_FACTOR + 0.5 > float(SAL_MAX_INT64) 227 || p->nSingle * CURRENCY_FACTOR - 0.5 < float(SAL_MIN_INT64) ) 228 { 229 nRes = SAL_MAX_INT64; 230 if( p->nSingle * CURRENCY_FACTOR - 0.5 < float(SAL_MIN_INT64) ) 231 nRes = SAL_MIN_INT64; 232 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); 233 break; 234 } 235 nRes = ImpDoubleToCurrency( static_cast<double>(p->nSingle) ); 236 break; 237 238 case SbxDATE: 239 case SbxDOUBLE: 240 if( p->nDouble * CURRENCY_FACTOR + 0.5 > double(SAL_MAX_INT64) 241 || p->nDouble * CURRENCY_FACTOR - 0.5 < double(SAL_MIN_INT64) ) 242 { 243 nRes = SAL_MAX_INT64; 244 if( p->nDouble * CURRENCY_FACTOR - 0.5 < double(SAL_MIN_INT64) ) 245 nRes = SAL_MIN_INT64; 246 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); 247 break; 248 } 249 nRes = ImpDoubleToCurrency( p->nDouble ); 250 break; 251 252 case SbxDECIMAL: 253 case SbxBYREF | SbxDECIMAL: 254 { 255 double d = 0.0; 256 if( p->pDecimal ) 257 p->pDecimal->getDouble( d ); 258 nRes = ImpDoubleToCurrency( d ); 259 break; 260 } 261 262 263 case SbxBYREF | SbxSTRING: 264 case SbxSTRING: 265 case SbxLPSTR: 266 if( !p->pOUString ) 267 nRes=0; 268 else 269 nRes = ImpStringToCurrency( *p->pOUString ); 270 break; 271 case SbxOBJECT: 272 { 273 SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj ); 274 if( pVal ) 275 nRes = pVal->GetCurrency(); 276 else 277 { 278 SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT ); 279 nRes=0; 280 } 281 break; 282 } 283 284 case SbxBYREF | SbxCHAR: 285 nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(*p->pChar); 286 break; 287 case SbxBYREF | SbxBYTE: 288 nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(*p->pByte); 289 break; 290 case SbxBYREF | SbxBOOL: 291 case SbxBYREF | SbxINTEGER: 292 nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(*p->pInteger); 293 break; 294 case SbxBYREF | SbxERROR: 295 case SbxBYREF | SbxUSHORT: 296 nRes = sal_Int64(CURRENCY_FACTOR) * static_cast<sal_Int64>(*p->pUShort); 297 break; 298 299 // from here on had to be tested 300 case SbxBYREF | SbxLONG: 301 aTmp.nLong = *p->pLong; goto ref; 302 case SbxBYREF | SbxULONG: 303 aTmp.nULong = *p->pULong; goto ref; 304 case SbxBYREF | SbxSINGLE: 305 aTmp.nSingle = *p->pSingle; goto ref; 306 case SbxBYREF | SbxDATE: 307 case SbxBYREF | SbxDOUBLE: 308 aTmp.nDouble = *p->pDouble; goto ref; 309 case SbxBYREF | SbxCURRENCY: 310 case SbxBYREF | SbxSALINT64: 311 aTmp.nInt64 = *p->pnInt64; goto ref; 312 case SbxBYREF | SbxSALUINT64: 313 aTmp.uInt64 = *p->puInt64; goto ref; 314 ref: 315 aTmp.eType = SbxDataType( p->eType & ~SbxBYREF ); 316 p = &aTmp; goto start; 317 318 default: 319 SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); 320 nRes=0; 321 } 322 return nRes; 323 } 324 325 326 void ImpPutCurrency( SbxValues* p, const sal_Int64 r ) 327 { 328 SbxValues aTmp; 329 start: 330 switch( +p->eType ) 331 { 332 // Here are tests necessary 333 case SbxCHAR: 334 aTmp.pChar = &p->nChar; goto direct; 335 case SbxBYTE: 336 aTmp.pByte = &p->nByte; goto direct; 337 case SbxINTEGER: 338 case SbxBOOL: 339 aTmp.pInteger = &p->nInteger; goto direct; 340 case SbxLONG: 341 aTmp.pLong = &p->nLong; goto direct; 342 case SbxULONG: 343 aTmp.pULong = &p->nULong; goto direct; 344 case SbxERROR: 345 case SbxUSHORT: 346 aTmp.pUShort = &p->nUShort; goto direct; 347 direct: 348 aTmp.eType = SbxDataType( p->eType | SbxBYREF ); 349 p = &aTmp; goto start; 350 351 // from here no longer 352 case SbxSINGLE: 353 p->nSingle = static_cast<float>( r / CURRENCY_FACTOR ); break; 354 case SbxDATE: 355 case SbxDOUBLE: 356 p->nDouble = ImpCurrencyToDouble( r ); break; 357 case SbxSALUINT64: 358 p->uInt64 = r / CURRENCY_FACTOR; break; 359 case SbxSALINT64: 360 p->nInt64 = r / CURRENCY_FACTOR; break; 361 362 case SbxCURRENCY: 363 p->nInt64 = r; break; 364 365 case SbxDECIMAL: 366 case SbxBYREF | SbxDECIMAL: 367 { 368 SbxDecimal* pDec = ImpCreateDecimal( p ); 369 if( !pDec->setDouble( ImpCurrencyToDouble( r ) / CURRENCY_FACTOR ) ) 370 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); 371 break; 372 } 373 case SbxBYREF | SbxSTRING: 374 case SbxSTRING: 375 case SbxLPSTR: 376 if( !p->pOUString ) 377 p->pOUString = new OUString; 378 379 *p->pOUString = ImpCurrencyToString( r ); 380 break; 381 case SbxOBJECT: 382 { 383 SbxValue* pVal = dynamic_cast<SbxValue*>( p->pObj ); 384 if( pVal ) 385 pVal->PutCurrency( r ); 386 else 387 SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT ); 388 break; 389 } 390 case SbxBYREF | SbxCHAR: 391 { 392 sal_Int64 val = r / CURRENCY_FACTOR; 393 if( val > SbxMAXCHAR ) 394 { 395 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXCHAR; 396 } 397 else if( val < SbxMINCHAR ) 398 { 399 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMINCHAR; 400 } 401 *p->pChar = static_cast<sal_Unicode>(val); break; 402 } 403 case SbxBYREF | SbxBYTE: 404 { 405 sal_Int64 val = r / CURRENCY_FACTOR; 406 if( val > SbxMAXBYTE ) 407 { 408 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXBYTE; 409 } 410 else if( val < 0 ) 411 { 412 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = 0; 413 } 414 *p->pByte = static_cast<sal_uInt8>(val); break; 415 } 416 case SbxBYREF | SbxINTEGER: 417 case SbxBYREF | SbxBOOL: 418 { 419 sal_Int64 val = r / CURRENCY_FACTOR; 420 if( r > SbxMAXINT ) 421 { 422 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXINT; 423 } 424 else if( r < SbxMININT ) 425 { 426 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMININT; 427 } 428 *p->pInteger = static_cast<sal_uInt16>(val); break; 429 } 430 case SbxBYREF | SbxERROR: 431 case SbxBYREF | SbxUSHORT: 432 { 433 sal_Int64 val = r / CURRENCY_FACTOR; 434 if( val > SbxMAXUINT ) 435 { 436 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXUINT; 437 } 438 else if( val < 0 ) 439 { 440 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = 0; 441 } 442 *p->pUShort = static_cast<sal_uInt16>(val); break; 443 } 444 case SbxBYREF | SbxLONG: 445 { 446 sal_Int64 val = r / CURRENCY_FACTOR; 447 if( val > SbxMAXLNG ) 448 { 449 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXLNG; 450 } 451 else if( val < SbxMINLNG ) 452 { 453 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMINLNG; 454 } 455 *p->pLong = static_cast<sal_Int32>(val); break; 456 } 457 case SbxBYREF | SbxULONG: 458 { 459 sal_Int64 val = r / CURRENCY_FACTOR; 460 if( val > SbxMAXULNG ) 461 { 462 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = SbxMAXULNG; 463 } 464 else if( val < 0 ) 465 { 466 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW ); val = 0; 467 } 468 *p->pULong = static_cast<sal_uInt32>(val); break; 469 } 470 case SbxBYREF | SbxCURRENCY: 471 *p->pnInt64 = r; break; 472 case SbxBYREF | SbxSALINT64: 473 *p->pnInt64 = r / CURRENCY_FACTOR; break; 474 case SbxBYREF | SbxSALUINT64: 475 *p->puInt64 = static_cast<sal_uInt64>(r) / CURRENCY_FACTOR; break; 476 case SbxBYREF | SbxSINGLE: 477 p->nSingle = static_cast<float>( r / CURRENCY_FACTOR ); break; 478 case SbxBYREF | SbxDATE: 479 case SbxBYREF | SbxDOUBLE: 480 *p->pDouble = ImpCurrencyToDouble( r ); break; 481 default: 482 SbxBase::SetError( ERRCODE_BASIC_CONVERSION ); 483 } 484 } 485 486 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 487
