1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <rtl/ustrbuf.hxx>
22 #include <basic/sberrors.hxx>
23 #include <basic/sbxvar.hxx>
24 #include "sbxconv.hxx"
27 static OUString
ImpCurrencyToString( sal_Int64 rVal
)
29 bool isNeg
= ( rVal
< 0 );
30 sal_Int64 absVal
= isNeg
? -rVal
: rVal
;
32 sal_Unicode
const cDecimalSep
= '.';
34 OUString aAbsStr
= OUString::number( absVal
);
36 sal_Int32 initialLen
= aAbsStr
.getLength();
38 bool bLessThanOne
= false;
39 if ( initialLen
<= 4 ) // if less the 1
42 sal_Int32 nCapacity
= 6; // minimum e.g. 0.0000
46 nCapacity
= initialLen
+ 1;
52 OUStringBuffer
aBuf( nCapacity
);
53 aBuf
.setLength( nCapacity
);
56 sal_Int32 nDigitCount
= 0;
57 sal_Int32 nInsertIndex
= nCapacity
- 1;
58 sal_Int32 nEndIndex
= isNeg
? 1 : 0;
60 for ( sal_Int32 charCpyIndex
= aAbsStr
.getLength() - 1; nInsertIndex
>= nEndIndex
; ++nDigitCount
)
62 if ( nDigitCount
== 4 )
63 aBuf
[nInsertIndex
--] = cDecimalSep
;
64 if ( nDigitCount
< initialLen
)
65 aBuf
[nInsertIndex
--] = aAbsStr
[ charCpyIndex
-- ];
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
73 // Val OOo (Cur) VBA (Cur)
74 // --- --------- ---------
78 aBuf
[nInsertIndex
--] = '0';
81 aBuf
[nInsertIndex
] = '-';
83 aAbsStr
= aBuf
.makeStringAndClear();
88 static sal_Int64
ImpStringToCurrency( const OUString
&rStr
)
91 sal_Int32 nFractDigit
= 4;
93 sal_Unicode
const cDeciPnt
= '.';
94 sal_Unicode
const c1000Sep
= ',';
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 )
102 OUString
sTmp( rStr
.trim() );
103 const sal_Unicode
* p
= sTmp
.getStr();
105 // normalise string number by removing thousand & decimal point separators
106 OUStringBuffer
sNormalisedNumString( sTmp
.getLength() + nFractDigit
);
108 if ( *p
== '-' || *p
== '+' )
109 sNormalisedNumString
.append( *p
);
111 while ( *p
>= '0' && *p
<= '9' )
113 sNormalisedNumString
.append( *p
++ );
114 // #TODO in vba mode set runtime error when a space ( or other )
115 // illegal character is found
120 bool bRoundUp
= false;
125 while( nFractDigit
&& *p
>= '0' && *p
<= '9' )
127 sNormalisedNumString
.append( *p
++ );
130 // Consume trailing content
133 // Round up if necessary
134 if( *p
>= '5' && *p
<= '9' )
136 while( *p
>= '0' && *p
<= '9' )
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
145 if ( p
!= sTmp
.getStr() + sTmp
.getLength() )
146 SbxBase::SetError( ERRCODE_BASIC_CONVERSION
);
149 sNormalisedNumString
.append( '0' );
153 sal_Int64 result
= sNormalisedNumString
.makeStringAndClear().toInt64();
161 sal_Int64
ImpGetCurrency( const SbxValues
* p
)
170 SbxBase::SetError( ERRCODE_BASIC_CONVERSION
);
175 nRes
= p
->nInt64
; break;
177 nRes
= sal_Int64(CURRENCY_FACTOR
) * static_cast<sal_Int64
>(p
->nByte
);
180 nRes
= sal_Int64(CURRENCY_FACTOR
) * reinterpret_cast<sal_Int64
>(p
->pChar
);
184 nRes
= sal_Int64(CURRENCY_FACTOR
) * static_cast<sal_Int64
>(p
->nInteger
);
187 nRes
= sal_Int64(CURRENCY_FACTOR
) * static_cast<sal_Int64
>(p
->nUShort
);
190 nRes
= sal_Int64(CURRENCY_FACTOR
) * static_cast<sal_Int64
>(p
->nLong
);
193 nRes
= sal_Int64(CURRENCY_FACTOR
) * static_cast<sal_Int64
>(p
->nULong
);
198 nRes
= p
->nInt64
* CURRENCY_FACTOR
; break;
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
)
206 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW
); nRes
= SAL_MAX_INT64
;
211 nRes
= p
->nInt64
* CURRENCY_FACTOR
; break;
214 if ( nRes
> SAL_MAX_INT64
)
216 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW
); nRes
= SAL_MAX_INT64
;
218 else if ( nRes
< SAL_MIN_INT64
)
220 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW
); nRes
= SAL_MIN_INT64
;
224 //TODO: bring back SbxINT64 types here for limits -1 with flag value at SAL_MAX/MIN
226 if( p
->nSingle
* CURRENCY_FACTOR
+ 0.5 > float(SAL_MAX_INT64
)
227 || p
->nSingle
* CURRENCY_FACTOR
- 0.5 < float(SAL_MIN_INT64
) )
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
);
235 nRes
= ImpDoubleToCurrency( static_cast<double>(p
->nSingle
) );
240 if( p
->nDouble
* CURRENCY_FACTOR
+ 0.5 > double(SAL_MAX_INT64
)
241 || p
->nDouble
* CURRENCY_FACTOR
- 0.5 < double(SAL_MIN_INT64
) )
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
);
249 nRes
= ImpDoubleToCurrency( p
->nDouble
);
253 case SbxBYREF
| SbxDECIMAL
:
257 p
->pDecimal
->getDouble( d
);
258 nRes
= ImpDoubleToCurrency( d
);
263 case SbxBYREF
| SbxSTRING
:
269 nRes
= ImpStringToCurrency( *p
->pOUString
);
273 SbxValue
* pVal
= dynamic_cast<SbxValue
*>( p
->pObj
);
275 nRes
= pVal
->GetCurrency();
278 SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT
);
284 case SbxBYREF
| SbxCHAR
:
285 nRes
= sal_Int64(CURRENCY_FACTOR
) * static_cast<sal_Int64
>(*p
->pChar
);
287 case SbxBYREF
| SbxBYTE
:
288 nRes
= sal_Int64(CURRENCY_FACTOR
) * static_cast<sal_Int64
>(*p
->pByte
);
290 case SbxBYREF
| SbxBOOL
:
291 case SbxBYREF
| SbxINTEGER
:
292 nRes
= sal_Int64(CURRENCY_FACTOR
) * static_cast<sal_Int64
>(*p
->pInteger
);
294 case SbxBYREF
| SbxERROR
:
295 case SbxBYREF
| SbxUSHORT
:
296 nRes
= sal_Int64(CURRENCY_FACTOR
) * static_cast<sal_Int64
>(*p
->pUShort
);
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
;
315 aTmp
.eType
= SbxDataType( p
->eType
& ~SbxBYREF
);
316 p
= &aTmp
; goto start
;
319 SbxBase::SetError( ERRCODE_BASIC_CONVERSION
);
326 void ImpPutCurrency( SbxValues
* p
, const sal_Int64 r
)
332 // Here are tests necessary
334 aTmp
.pChar
= &p
->nChar
; goto direct
;
336 aTmp
.pByte
= &p
->nByte
; goto direct
;
339 aTmp
.pInteger
= &p
->nInteger
; goto direct
;
341 aTmp
.pLong
= &p
->nLong
; goto direct
;
343 aTmp
.pULong
= &p
->nULong
; goto direct
;
346 aTmp
.pUShort
= &p
->nUShort
; goto direct
;
348 aTmp
.eType
= SbxDataType( p
->eType
| SbxBYREF
);
349 p
= &aTmp
; goto start
;
351 // from here no longer
353 p
->nSingle
= static_cast<float>( r
/ CURRENCY_FACTOR
); break;
356 p
->nDouble
= ImpCurrencyToDouble( r
); break;
358 p
->uInt64
= r
/ CURRENCY_FACTOR
; break;
360 p
->nInt64
= r
/ CURRENCY_FACTOR
; break;
363 p
->nInt64
= r
; break;
366 case SbxBYREF
| SbxDECIMAL
:
368 SbxDecimal
* pDec
= ImpCreateDecimal( p
);
369 if( !pDec
->setDouble( ImpCurrencyToDouble( r
) / CURRENCY_FACTOR
) )
370 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW
);
373 case SbxBYREF
| SbxSTRING
:
377 p
->pOUString
= new OUString
;
379 *p
->pOUString
= ImpCurrencyToString( r
);
383 SbxValue
* pVal
= dynamic_cast<SbxValue
*>( p
->pObj
);
385 pVal
->PutCurrency( r
);
387 SbxBase::SetError( ERRCODE_BASIC_NO_OBJECT
);
390 case SbxBYREF
| SbxCHAR
:
392 sal_Int64 val
= r
/ CURRENCY_FACTOR
;
393 if( val
> SbxMAXCHAR
)
395 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW
); val
= SbxMAXCHAR
;
397 else if( val
< SbxMINCHAR
)
399 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW
); val
= SbxMINCHAR
;
401 *p
->pChar
= static_cast<sal_Unicode
>(val
); break;
403 case SbxBYREF
| SbxBYTE
:
405 sal_Int64 val
= r
/ CURRENCY_FACTOR
;
406 if( val
> SbxMAXBYTE
)
408 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW
); val
= SbxMAXBYTE
;
412 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW
); val
= 0;
414 *p
->pByte
= static_cast<sal_uInt8
>(val
); break;
416 case SbxBYREF
| SbxINTEGER
:
417 case SbxBYREF
| SbxBOOL
:
419 sal_Int64 val
= r
/ CURRENCY_FACTOR
;
422 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW
); val
= SbxMAXINT
;
424 else if( r
< SbxMININT
)
426 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW
); val
= SbxMININT
;
428 *p
->pInteger
= static_cast<sal_uInt16
>(val
); break;
430 case SbxBYREF
| SbxERROR
:
431 case SbxBYREF
| SbxUSHORT
:
433 sal_Int64 val
= r
/ CURRENCY_FACTOR
;
434 if( val
> SbxMAXUINT
)
436 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW
); val
= SbxMAXUINT
;
440 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW
); val
= 0;
442 *p
->pUShort
= static_cast<sal_uInt16
>(val
); break;
444 case SbxBYREF
| SbxLONG
:
446 sal_Int64 val
= r
/ CURRENCY_FACTOR
;
447 if( val
> SbxMAXLNG
)
449 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW
); val
= SbxMAXLNG
;
451 else if( val
< SbxMINLNG
)
453 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW
); val
= SbxMINLNG
;
455 *p
->pLong
= static_cast<sal_Int32
>(val
); break;
457 case SbxBYREF
| SbxULONG
:
459 sal_Int64 val
= r
/ CURRENCY_FACTOR
;
460 if( val
> SbxMAXULNG
)
462 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW
); val
= SbxMAXULNG
;
466 SbxBase::SetError( ERRCODE_BASIC_MATH_OVERFLOW
); val
= 0;
468 *p
->pULong
= static_cast<sal_uInt32
>(val
); break;
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;
482 SbxBase::SetError( ERRCODE_BASIC_CONVERSION
);
486 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */