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