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 
ImpCurrencyToString(sal_Int64 rVal)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 
ImpStringToCurrency(const OUString & rStr)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 
ImpGetCurrency(const SbxValues * p)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 
ImpPutCurrency(SbxValues * p,const sal_Int64 r)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