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