Branch data 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 : : #include <tools/errcode.hxx>
21 : : #include <basic/sbx.hxx>
22 : : #include "sbxconv.hxx"
23 : :
24 : : #include "unotools/syslocale.hxx"
25 : :
26 : : #if defined ( UNX )
27 : : #include <stdlib.h>
28 : : #endif
29 : :
30 : : #include <vcl/svapp.hxx>
31 : : #include <math.h>
32 : : #include <string.h>
33 : : #include <ctype.h>
34 : :
35 : : #include "sbxres.hxx"
36 : : #include <basic/sbxbase.hxx>
37 : : #include <basic/sbxfac.hxx>
38 : : #include <basic/sbxform.hxx>
39 : : #include <svtools/svtools.hrc>
40 : :
41 : : #include "basrid.hxx"
42 : : #include "runtime.hxx"
43 : :
44 : : #include <rtl/strbuf.hxx>
45 : : #include <svl/zforlist.hxx>
46 : : #include <comphelper/processfactory.hxx>
47 : :
48 : :
49 : 8 : void ImpGetIntntlSep( sal_Unicode& rcDecimalSep, sal_Unicode& rcThousandSep )
50 : : {
51 [ + - ]: 8 : SvtSysLocale aSysLocale;
52 [ + - ]: 8 : const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
53 [ + - ]: 8 : rcDecimalSep = rData.getNumDecimalSep()[0];
54 [ + - ][ + - ]: 8 : rcThousandSep = rData.getNumThousandSep()[0];
55 : 8 : }
56 : :
57 : : // scanning a string according to BASIC-conventions
58 : : // but exponent may also be a D, so data type is SbxDOUBLED
59 : : // conversion error if data type is fixed and it doesn't fit
60 : :
61 : 0 : SbxError ImpScan( const ::rtl::OUString& rWSrc, double& nVal, SbxDataType& rType,
62 : : sal_uInt16* pLen, sal_Bool bAllowIntntl, sal_Bool bOnlyIntntl )
63 : : {
64 [ # # ]: 0 : ::rtl::OString aBStr( ::rtl::OUStringToOString( rWSrc, RTL_TEXTENCODING_ASCII_US ) );
65 : :
66 : : char cIntntlComma, cIntntl1000;
67 : 0 : char cNonIntntlComma = '.';
68 : :
69 : 0 : sal_Unicode cDecimalSep, cThousandSep = 0;
70 [ # # ][ # # ]: 0 : if( bAllowIntntl || bOnlyIntntl )
71 : : {
72 [ # # ]: 0 : ImpGetIntntlSep( cDecimalSep, cThousandSep );
73 : 0 : cIntntlComma = (char)cDecimalSep;
74 : 0 : cIntntl1000 = (char)cThousandSep;
75 : : }
76 : :
77 : : else
78 : : {
79 : 0 : cIntntlComma = cNonIntntlComma;
80 : 0 : cIntntl1000 = cNonIntntlComma;
81 : : }
82 : :
83 [ # # ]: 0 : if( bOnlyIntntl )
84 : : {
85 : 0 : cNonIntntlComma = cIntntlComma;
86 : 0 : cIntntl1000 = (char)cThousandSep;
87 : : }
88 : :
89 : 0 : const char* pStart = aBStr.getStr();
90 : 0 : const char* p = pStart;
91 : 0 : char buf[ 80 ], *q = buf;
92 : 0 : bool bRes = true;
93 : 0 : bool bMinus = false;
94 : 0 : nVal = 0;
95 : 0 : SbxDataType eScanType = SbxSINGLE;
96 [ # # ][ # # ]: 0 : while( *p &&( *p == ' ' || *p == '\t' ) ) p++;
[ # # ][ # # ]
97 [ # # ]: 0 : if( *p == '-' )
98 : 0 : p++, bMinus = true;
99 [ # # ][ # # ]: 0 : if( isdigit( *p ) ||( (*p == cNonIntntlComma || *p == cIntntlComma ||
[ # # ][ # # ]
[ # # ]
100 : 0 : *p == cIntntl1000) && isdigit( *(p+1 ) ) ) )
101 : : {
102 : 0 : short exp = 0;
103 : 0 : short comma = 0;
104 : 0 : short ndig = 0;
105 : 0 : short ncdig = 0; // number of digits after decimal point
106 [ # # ]: 0 : rtl::OStringBuffer aSearchStr(RTL_CONSTASCII_STRINGPARAM("0123456789DEde"));
107 [ # # ]: 0 : aSearchStr.append(cNonIntntlComma);
108 [ # # ]: 0 : if( cIntntlComma != cNonIntntlComma )
109 [ # # ]: 0 : aSearchStr.append(cIntntlComma);
110 [ # # ]: 0 : if( bOnlyIntntl )
111 [ # # ]: 0 : aSearchStr.append(cIntntl1000);
112 : 0 : const char* pSearchStr = aSearchStr.getStr();
113 [ # # ][ # # ]: 0 : while( strchr( pSearchStr, *p ) && *p )
[ # # ]
114 : : {
115 [ # # ][ # # ]: 0 : if( bOnlyIntntl && *p == cIntntl1000 )
116 : : {
117 : 0 : p++;
118 : 0 : continue;
119 : : }
120 : :
121 [ # # ][ # # ]: 0 : if( *p == cNonIntntlComma || *p == cIntntlComma )
122 : : {
123 : : // always insert '.' so that atof works
124 : 0 : p++;
125 [ # # ]: 0 : if( ++comma > 1 )
126 : 0 : continue;
127 : : else
128 : 0 : *q++ = '.';
129 : : }
130 [ # # ]: 0 : else if( strchr( "DdEe", *p ) )
131 : : {
132 [ # # ]: 0 : if( ++exp > 1 )
133 : : {
134 : 0 : p++; continue;
135 : : }
136 [ # # ]: 0 : if( toupper( *p ) == 'D' )
137 : 0 : eScanType = SbxDOUBLE;
138 : 0 : *q++ = 'E'; p++;
139 : :
140 [ # # ]: 0 : if( *p == '+' )
141 : 0 : p++;
142 : : else
143 [ # # ]: 0 : if( *p == '-' )
144 : 0 : *q++ = *p++;
145 : : }
146 : : else
147 : : {
148 : 0 : *q++ = *p++;
149 [ # # ][ # # ]: 0 : if( comma && !exp ) ncdig++;
150 : : }
151 [ # # ]: 0 : if( !exp ) ndig++;
152 : : }
153 : 0 : *q = 0;
154 : :
155 [ # # ][ # # ]: 0 : if( comma > 1 || exp > 1 )
156 : 0 : bRes = false;
157 : :
158 [ # # ][ # # ]: 0 : if( !comma && !exp )
159 : : {
160 [ # # ][ # # ]: 0 : if( nVal >= SbxMININT && nVal <= SbxMAXINT )
161 : 0 : eScanType = SbxINTEGER;
162 [ # # ][ # # ]: 0 : else if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
163 : 0 : eScanType = SbxLONG;
164 : : }
165 : :
166 : 0 : nVal = atof( buf );
167 : 0 : ndig = ndig - comma;
168 : : // too many numbers for SINGLE?
169 [ # # ][ # # ]: 0 : if( ndig > 15 || ncdig > 6 )
170 : 0 : eScanType = SbxDOUBLE;
171 : :
172 : : // type detection?
173 [ # # ][ # # ]: 0 : if( strchr( "%!&#", *p ) && *p ) p++;
174 : : }
175 : : // hex/octal number? read in and convert:
176 [ # # ]: 0 : else if( *p == '&' )
177 : : {
178 : 0 : p++;
179 : 0 : eScanType = SbxLONG;
180 : 0 : const char *cmp = "0123456789ABCDEF";
181 : 0 : char base = 16;
182 : 0 : char ndig = 8;
183 : 0 : char xch = *p++;
184 [ # # # ]: 0 : switch( toupper( xch ) )
185 : : {
186 : 0 : case 'O': cmp = "01234567"; base = 8; ndig = 11; break;
187 : 0 : case 'H': break;
188 : 0 : default : bRes = false;
189 : : }
190 : 0 : long l = 0;
191 : : int i;
192 [ # # ]: 0 : while( isalnum( *p ) )
193 : : {
194 : 0 : char ch = sal::static_int_cast< char >( toupper( *p ) );
195 : 0 : p++;
196 [ # # ]: 0 : if( strchr( cmp, ch ) ) *q++ = ch;
197 : 0 : else bRes = false;
198 : : }
199 : 0 : *q = 0;
200 [ # # ]: 0 : for( q = buf; *q; q++ )
201 : : {
202 : 0 : i =( *q & 0xFF ) - '0';
203 [ # # ]: 0 : if( i > 9 ) i -= 7;
204 : 0 : l =( l * base ) + i;
205 [ # # ]: 0 : if( !ndig-- )
206 : 0 : bRes = false;
207 : : }
208 [ # # ]: 0 : if( *p == '&' ) p++;
209 : 0 : nVal = (double) l;
210 [ # # ][ # # ]: 0 : if( l >= SbxMININT && l <= SbxMAXINT )
211 : 0 : eScanType = SbxINTEGER;
212 : : }
213 : : #ifndef DISABLE_SCRIPTING
214 [ # # ][ # # ]: 0 : else if ( SbiRuntime::isVBAEnabled() )
215 : : {
216 : : OSL_TRACE("Reporting error converting");
217 : 0 : return SbxERR_CONVERSION;
218 : : }
219 : : #endif
220 [ # # ]: 0 : if( pLen )
221 : 0 : *pLen = (sal_uInt16) ( p - pStart );
222 [ # # ]: 0 : if( !bRes )
223 : 0 : return SbxERR_CONVERSION;
224 [ # # ]: 0 : if( bMinus )
225 : 0 : nVal = -nVal;
226 : 0 : rType = eScanType;
227 : 0 : return SbxERR_OK;
228 : : }
229 : :
230 : : // port for CDbl in the Basic
231 : 0 : SbxError SbxValue::ScanNumIntnl( const String& rSrc, double& nVal, sal_Bool bSingle )
232 : : {
233 : : SbxDataType t;
234 : 0 : sal_uInt16 nLen = 0;
235 : : SbxError nRetError = ImpScan( rSrc, nVal, t, &nLen,
236 [ # # ][ # # ]: 0 : /*bAllowIntntl*/sal_False, /*bOnlyIntntl*/sal_True );
237 : : // read completely?
238 [ # # ][ # # ]: 0 : if( nRetError == SbxERR_OK && nLen != rSrc.Len() )
[ # # ]
239 : 0 : nRetError = SbxERR_CONVERSION;
240 : :
241 [ # # ]: 0 : if( bSingle )
242 : : {
243 : 0 : SbxValues aValues( nVal );
244 [ # # ]: 0 : nVal = (double)ImpGetSingle( &aValues ); // here error at overflow
245 : : }
246 : 0 : return nRetError;
247 : : }
248 : :
249 : :
250 : : static double roundArray[] = {
251 : : 5.0e+0, 0.5e+0, 0.5e-1, 0.5e-2, 0.5e-3, 0.5e-4, 0.5e-5, 0.5e-6, 0.5e-7,
252 : : 0.5e-8, 0.5e-9, 0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14,0.5e-15 };
253 : :
254 : : /***************************************************************************
255 : : |*
256 : : |* void myftoa( double, char *, short, short, bool, bool )
257 : : |*
258 : : |* description: conversion double --> ASCII
259 : : |* parameters: double the number
260 : : |* char * target buffer
261 : : |* short number of positions after decimal point
262 : : |* short range of the exponent ( 0=no E )
263 : : |* bool true: with 1000-separators
264 : : |* bool true: output without formatting
265 : : |*
266 : : ***************************************************************************/
267 : :
268 : 4 : static void myftoa( double nNum, char * pBuf, short nPrec, short nExpWidth,
269 : : bool bPt, bool bFix, sal_Unicode cForceThousandSep = 0 )
270 : : {
271 : :
272 : 4 : short nExp = 0;
273 : 4 : short nDig = nPrec + 1;
274 : : short nDec; // number of positions before decimal point
275 : : register int i;
276 : :
277 : : sal_Unicode cDecimalSep, cThousandSep;
278 [ + - ]: 4 : ImpGetIntntlSep( cDecimalSep, cThousandSep );
279 [ + - ]: 4 : if( cForceThousandSep )
280 : 4 : cThousandSep = cForceThousandSep;
281 : :
282 : : // compute exponent
283 : 4 : nExp = 0;
284 [ + + ]: 4 : if( nNum > 0.0 )
285 : : {
286 [ - + ]: 2 : while( nNum < 1.0 ) nNum *= 10.0, nExp--;
287 [ + + ]: 4 : while( nNum >= 10.0 ) nNum /= 10.0, nExp++;
288 : : }
289 [ - + ][ # # ]: 4 : if( !bFix && !nExpWidth )
290 : 0 : nDig = nDig + nExp;
291 [ + - ][ + - ]: 4 : else if( bFix && !nPrec )
292 : 4 : nDig = nExp + 1;
293 : :
294 : : // round number
295 [ - + ]: 4 : if( (nNum += roundArray [( nDig > 16 ) ? 16 : nDig] ) >= 10.0 )
296 : : {
297 : 0 : nNum = 1.0;
298 : 0 : ++nExp;
299 [ # # ]: 0 : if( !nExpWidth ) ++nDig;
300 : : }
301 : :
302 : : // determine positions before decimal point
303 [ + - ]: 4 : if( !nExpWidth )
304 : : {
305 [ - + ]: 4 : if( nExp < 0 )
306 : : {
307 : : // #41691: also a 0 at bFix
308 : 0 : *pBuf++ = '0';
309 [ # # ]: 0 : if( nPrec ) *pBuf++ = (char)cDecimalSep;
310 : 0 : i = -nExp - 1;
311 [ # # ]: 0 : if( nDig <= 0 ) i = nPrec;
312 [ # # ]: 0 : while( i-- ) *pBuf++ = '0';
313 : 0 : nDec = 0;
314 : : }
315 : : else
316 : 4 : nDec = nExp+1;
317 : : }
318 : : else
319 : 0 : nDec = 1;
320 : :
321 : : // output number
322 [ + - ]: 4 : if( nDig > 0 )
323 : : {
324 : : register int digit;
325 : 10 : for( i = 0 ; ; ++i )
326 : : {
327 [ + - ]: 6 : if( i < 16 )
328 : : {
329 : 6 : digit = (int) nNum;
330 : 6 : *pBuf++ = sal::static_int_cast< char >(digit + '0');
331 : 6 : nNum =( nNum - digit ) * 10.0;
332 : : } else
333 : 0 : *pBuf++ = '0';
334 [ + + ]: 6 : if( --nDig == 0 ) break;
335 [ + - ]: 2 : if( nDec )
336 : : {
337 : 2 : nDec--;
338 [ - + ]: 2 : if( !nDec )
339 : 0 : *pBuf++ = (char)cDecimalSep;
340 [ - + ][ # # ]: 2 : else if( !(nDec % 3 ) && bPt )
341 : 0 : *pBuf++ = (char)cThousandSep;
342 : : }
343 : : }
344 : : }
345 : :
346 : : // output exponent
347 [ - + ]: 4 : if( nExpWidth )
348 : : {
349 [ # # ]: 0 : if( nExpWidth < 3 ) nExpWidth = 3;
350 : 0 : nExpWidth -= 2;
351 : 0 : *pBuf++ = 'E';
352 [ # # ]: 0 : *pBuf++ =( nExp < 0 ) ?( (nExp = -nExp ), '-' ) : '+';
353 [ # # ]: 0 : while( nExpWidth > 3 ) *pBuf++ = '0', nExpWidth--;
354 [ # # ][ # # ]: 0 : if( nExp >= 100 || nExpWidth == 3 )
355 : : {
356 : 0 : *pBuf++ = sal::static_int_cast< char >(nExp/100 + '0');
357 : 0 : nExp %= 100;
358 : : }
359 [ # # ][ # # ]: 0 : if( nExp/10 || nExpWidth >= 2 )
360 : 0 : *pBuf++ = sal::static_int_cast< char >(nExp/10 + '0');
361 : 0 : *pBuf++ = sal::static_int_cast< char >(nExp%10 + '0');
362 : : }
363 : 4 : *pBuf = 0;
364 : 4 : }
365 : :
366 : : // The number is prepared unformattedly with the given number of
367 : : // NK-positions. A leading minus is added if applicable.
368 : : // This routine is public because it's also used by the Put-functions
369 : : // in the class SbxImpSTRING.
370 : :
371 : : #ifdef _MSC_VER
372 : : #pragma optimize( "", off )
373 : : #pragma warning(disable: 4748) // "... because optimizations are disabled ..."
374 : : #endif
375 : :
376 : 4 : void ImpCvtNum( double nNum, short nPrec, ::rtl::OUString& rRes, sal_Bool bCoreString )
377 : : {
378 : : char *q;
379 : 4 : char cBuf[ 40 ], *p = cBuf;
380 : :
381 : : sal_Unicode cDecimalSep, cThousandSep;
382 [ + - ]: 4 : ImpGetIntntlSep( cDecimalSep, cThousandSep );
383 [ - + ]: 4 : if( bCoreString )
384 : 0 : cDecimalSep = '.';
385 : :
386 [ - + ]: 4 : if( nNum < 0.0 ) {
387 : 0 : nNum = -nNum;
388 : 0 : *p++ = '-';
389 : : }
390 [ - + ]: 4 : double dMaxNumWithoutExp = (nPrec == 6) ? 1E6 : 1E14;
391 : : myftoa( nNum, p, nPrec,( nNum &&( nNum < 1E-1 || nNum >= dMaxNumWithoutExp ) ) ? 4:0,
392 [ + + ][ + - ]: 4 : false, true, cDecimalSep );
[ - + ][ + - ]
393 : : // remove trailing zeros
394 [ + + ][ + - ]: 10 : for( p = cBuf; *p &&( *p != 'E' ); p++ ) {}
[ + + ]
395 : 4 : q = p; p--;
396 [ - + ][ # # ]: 4 : while( nPrec && *p == '0' ) nPrec--, p--;
[ - + ]
397 [ - + ]: 4 : if( *p == cDecimalSep ) p--;
398 [ - + ]: 4 : while( *q ) *++p = *q++;
399 : 4 : *++p = 0;
400 : 4 : rRes = ::rtl::OUString::createFromAscii( cBuf );
401 : 4 : }
402 : :
403 : : #ifdef _MSC_VER
404 : : #pragma optimize( "", on )
405 : : #endif
406 : :
407 : 0 : sal_Bool ImpConvStringExt( ::rtl::OUString& rSrc, SbxDataType eTargetType )
408 : : {
409 : 0 : sal_Bool bChanged = sal_False;
410 : 0 : ::rtl::OUString aNewString;
411 : :
412 : : // only special cases are handled, nothing on default
413 [ # # # ]: 0 : switch( eTargetType )
414 : : {
415 : : // consider international for floating point
416 : : case SbxSINGLE:
417 : : case SbxDOUBLE:
418 : : case SbxCURRENCY:
419 : : {
420 [ # # ]: 0 : ::rtl::OString aBStr( ::rtl::OUStringToOString( rSrc, RTL_TEXTENCODING_ASCII_US ) );
421 : :
422 : : sal_Unicode cDecimalSep, cThousandSep;
423 [ # # ]: 0 : ImpGetIntntlSep( cDecimalSep, cThousandSep );
424 : 0 : aNewString = rSrc;
425 : :
426 [ # # ]: 0 : if( cDecimalSep != (sal_Unicode)'.' )
427 : : {
428 : 0 : sal_Int32 nPos = aNewString.indexOf( cDecimalSep );
429 [ # # ]: 0 : if( nPos != -1 )
430 : : {
431 : 0 : sal_Unicode* pStr = (sal_Unicode*)aNewString.getStr();
432 : 0 : pStr[nPos] = (sal_Unicode)'.';
433 : 0 : bChanged = sal_True;
434 : : }
435 : : }
436 : 0 : break;
437 : : }
438 : :
439 : : // check as string in case of sal_Bool sal_True and sal_False
440 : : case SbxBOOL:
441 : : {
442 [ # # ]: 0 : if( rSrc.equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("true")) )
443 : : {
444 : 0 : aNewString = ::rtl::OUString::valueOf( (sal_Int32)SbxTRUE );
445 : 0 : bChanged = sal_True;
446 : : }
447 : : else
448 [ # # ]: 0 : if( rSrc.equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("false")) )
449 : : {
450 : 0 : aNewString = ::rtl::OUString::valueOf( (sal_Int32)SbxFALSE );
451 : 0 : bChanged = sal_True;
452 : : }
453 : 0 : break;
454 : : }
455 : 0 : default: break;
456 : : }
457 : :
458 [ # # ]: 0 : if( bChanged )
459 : 0 : rSrc = aNewString;
460 : 0 : return bChanged;
461 : : }
462 : :
463 : :
464 : : // formatted number output
465 : : // the return value is the number of characters used
466 : : // from the format
467 : :
468 : : #ifdef _old_format_code_
469 : : // leave the code provisionally to copy the previous implementation
470 : :
471 : : static sal_uInt16 printfmtnum( double nNum, XubString& rRes, const XubString& rWFmt )
472 : : {
473 : : const String& rFmt = rWFmt;
474 : : char cFill = ' '; // filling characters
475 : : char cPre = 0; // start character ( maybe "$" )
476 : : short nExpDig= 0; // number of exponent positions
477 : : short nPrec = 0; // number of positions after decimal point
478 : : short nWidth = 0; // number range completely
479 : : short nLen; // length of converted number
480 : : bool bPoint = false; // true: with 1000 seperators
481 : : sal_Bool bTrail = sal_False; // sal_True, if following minus
482 : : sal_Bool bSign = sal_False; // sal_True: always with leading sign
483 : : sal_Bool bNeg = sal_False; // sal_True: number is negative
484 : : char cBuf [1024]; // number buffer
485 : : char * p;
486 : : const char* pFmt = rFmt;
487 : : rRes.Erase();
488 : : // catch $$ and **, is simply output as character
489 : : if( *pFmt == '$' )
490 : : if( *++pFmt != '$' ) rRes += '$';
491 : : if( *pFmt == '*' )
492 : : if( *++pFmt != '*' ) rRes += '*';
493 : :
494 : : switch( *pFmt++ )
495 : : {
496 : : case 0:
497 : : break;
498 : : case '+':
499 : : bSign = sal_True; nWidth++; break;
500 : : case '*':
501 : : nWidth++; cFill = '*';
502 : : if( *pFmt == '$' ) nWidth++, pFmt++, cPre = '$';
503 : : break;
504 : : case '$':
505 : : nWidth++; cPre = '$'; break;
506 : : case '#':
507 : : case '.':
508 : : case ',':
509 : : pFmt--; break;
510 : : }
511 : : // pre point
512 : : for( ;; )
513 : : {
514 : : while( *pFmt == '#' ) pFmt++, nWidth++;
515 : : // 1000 separators?
516 : : if( *pFmt == ',' )
517 : : {
518 : : nWidth++; pFmt++; bPoint = true;
519 : : } else break;
520 : : }
521 : : // after point
522 : : if( *pFmt == '.' )
523 : : {
524 : : while( *++pFmt == '#' ) nPrec++;
525 : : nWidth += nPrec + 1;
526 : : }
527 : : // exponent
528 : : while( *pFmt == '^' )
529 : : pFmt++, nExpDig++, nWidth++;
530 : : // following minus
531 : : if( !bSign && *pFmt == '-' )
532 : : pFmt++, bTrail = sal_True;
533 : :
534 : : // convert number
535 : : if( nPrec > 15 ) nPrec = 15;
536 : : if( nNum < 0.0 ) nNum = -nNum, bNeg = sal_True;
537 : : p = cBuf;
538 : : if( bSign ) *p++ = bNeg ? '-' : '+';
539 : : myftoa( nNum, p, nPrec, nExpDig, bPoint, false );
540 : : nLen = strlen( cBuf );
541 : :
542 : : // overflow?
543 : : if( cPre ) nLen++;
544 : : if( nLen > nWidth ) rRes += '%';
545 : : else {
546 : : nWidth -= nLen;
547 : : while( nWidth-- ) rRes += (xub_Unicode)cFill;
548 : : if( cPre ) rRes += (xub_Unicode)cPre;
549 : : }
550 : : rRes += (xub_Unicode*)&(cBuf[0]);
551 : : if( bTrail )
552 : : rRes += bNeg ? '-' : ' ';
553 : :
554 : : return (sal_uInt16) ( pFmt - (const char*) rFmt );
555 : : }
556 : :
557 : : #endif //_old_format_code_
558 : :
559 : 0 : static sal_uInt16 printfmtstr( const XubString& rStr, XubString& rRes, const XubString& rFmt )
560 : : {
561 : 0 : const xub_Unicode* pStr = rStr.GetBuffer();
562 : 0 : const xub_Unicode* pFmtStart = rFmt.GetBuffer();
563 : 0 : const xub_Unicode* pFmt = pFmtStart;
564 : 0 : rRes.Erase();
565 [ # # # # ]: 0 : switch( *pFmt )
566 : : {
567 : : case '!':
568 : 0 : rRes += *pStr++; pFmt++; break;
569 : : case '\\':
570 [ # # ]: 0 : do
571 : : {
572 [ # # ]: 0 : rRes += *pStr ? *pStr++ : static_cast< xub_Unicode >(' ');
573 : 0 : pFmt++;
574 : : } while( *pFmt != '\\' );
575 [ # # ]: 0 : rRes += *pStr ? *pStr++ : static_cast< xub_Unicode >(' ');
576 : 0 : pFmt++; break;
577 : : case '&':
578 : 0 : rRes = rStr;
579 : 0 : pFmt++; break;
580 : : default:
581 : 0 : rRes = rStr;
582 : 0 : break;
583 : : }
584 : 0 : return (sal_uInt16) ( pFmt - pFmtStart );
585 : : }
586 : :
587 : :
588 : 0 : sal_Bool SbxValue::Scan( const XubString& rSrc, sal_uInt16* pLen )
589 : : {
590 : 0 : SbxError eRes = SbxERR_OK;
591 [ # # ]: 0 : if( !CanWrite() )
592 : 0 : eRes = SbxERR_PROP_READONLY;
593 : : else
594 : : {
595 : : double n;
596 : : SbxDataType t;
597 [ # # ][ # # ]: 0 : eRes = ImpScan( rSrc, n, t, pLen );
598 [ # # ]: 0 : if( eRes == SbxERR_OK )
599 : : {
600 [ # # ][ # # ]: 0 : if( !IsFixed() )
601 [ # # ]: 0 : SetType( t );
602 [ # # ]: 0 : PutDouble( n );
603 : : }
604 : : }
605 [ # # ]: 0 : if( eRes )
606 : : {
607 : 0 : SetError( eRes ); return sal_False;
608 : : }
609 : : else
610 : 0 : return sal_True;
611 : : }
612 : :
613 : :
614 : 0 : ResMgr* implGetResMgr( void )
615 : : {
616 : : static ResMgr* pResMgr = NULL;
617 [ # # ]: 0 : if( !pResMgr )
618 : : {
619 [ # # ][ # # ]: 0 : ::com::sun::star::lang::Locale aLocale = Application::GetSettings().GetUILocale();
620 [ # # ]: 0 : pResMgr = ResMgr::CreateResMgr("sb", aLocale );
621 : : }
622 : 0 : return pResMgr;
623 : : }
624 : :
625 : : class SbxValueFormatResId : public ResId
626 : : {
627 : : public:
628 : 0 : SbxValueFormatResId( sal_uInt16 nId )
629 : 0 : : ResId( nId, *implGetResMgr() )
630 : 0 : {}
631 : : };
632 : :
633 : :
634 : : enum VbaFormatType
635 : : {
636 : : VBA_FORMAT_TYPE_OFFSET, // standard number format
637 : : VBA_FORMAT_TYPE_USERDEFINED, // user defined number format
638 : : VBA_FORMAT_TYPE_NULL
639 : : };
640 : :
641 : : struct VbaFormatInfo
642 : : {
643 : : VbaFormatType meType;
644 : : const char* mpVbaFormat; // Format string in vba
645 : : NfIndexTableOffset meOffset; // SvNumberFormatter format index, if meType = VBA_FORMAT_TYPE_OFFSET
646 : : const char* mpOOoFormat; // if meType = VBA_FORMAT_TYPE_USERDEFINED
647 : : };
648 : :
649 : : #define VBA_FORMAT_OFFSET( pcUtf8, eOffset ) \
650 : : { VBA_FORMAT_TYPE_OFFSET, pcUtf8, eOffset, 0 }
651 : :
652 : : #define VBA_FORMAT_USERDEFINED( pcUtf8, pcDefinedUtf8 ) \
653 : : { VBA_FORMAT_TYPE_USERDEFINED, pcUtf8, NF_NUMBER_STANDARD, pcDefinedUtf8 }
654 : :
655 : : static VbaFormatInfo pFormatInfoTable[] =
656 : : {
657 : : VBA_FORMAT_OFFSET( "Long Date", NF_DATE_SYSTEM_LONG ),
658 : : VBA_FORMAT_USERDEFINED( "Medium Date", "DD-MMM-YY" ),
659 : : VBA_FORMAT_OFFSET( "Short Date", NF_DATE_SYSTEM_SHORT ),
660 : : VBA_FORMAT_USERDEFINED( "Long Time", "H:MM:SS AM/PM" ),
661 : : VBA_FORMAT_OFFSET( "Medium Time", NF_TIME_HHMMAMPM ),
662 : : VBA_FORMAT_OFFSET( "Short Time", NF_TIME_HHMM ),
663 : : VBA_FORMAT_OFFSET( "ddddd", NF_DATE_SYSTEM_SHORT ),
664 : : VBA_FORMAT_OFFSET( "dddddd", NF_DATE_SYSTEM_LONG ),
665 : : VBA_FORMAT_USERDEFINED( "ttttt", "H:MM:SS AM/PM" ),
666 : : VBA_FORMAT_OFFSET( "ww", NF_DATE_WW ),
667 : : { VBA_FORMAT_TYPE_NULL, 0, NF_INDEX_TABLE_ENTRIES, 0 }
668 : : };
669 : :
670 : 0 : VbaFormatInfo* getFormatInfo( const String& rFmt )
671 : : {
672 : 0 : VbaFormatInfo* pInfo = NULL;
673 : 0 : sal_Int16 i = 0;
674 [ # # ]: 0 : while( (pInfo = pFormatInfoTable + i )->mpVbaFormat != NULL )
675 : : {
676 [ # # ]: 0 : if( rFmt.EqualsIgnoreCaseAscii( pInfo->mpVbaFormat ) )
677 : 0 : break;
678 : 0 : i++;
679 : : }
680 : 0 : return pInfo;
681 : : }
682 : :
683 : : #define VBAFORMAT_GENERALDATE "General Date"
684 : : #define VBAFORMAT_C "c"
685 : : #define VBAFORMAT_N "n"
686 : : #define VBAFORMAT_NN "nn"
687 : : #define VBAFORMAT_W "w"
688 : : #define VBAFORMAT_Y "y"
689 : : #define VBAFORMAT_LOWERCASE "<"
690 : : #define VBAFORMAT_UPPERCASE ">"
691 : :
692 : : // From methods1.cxx
693 : : sal_Int16 implGetWeekDay( double aDate, bool bFirstDayParam = false, sal_Int16 nFirstDay = 0 );
694 : : // from methods.cxx
695 : : sal_Int16 implGetMinute( double dDate );
696 : : sal_Int16 implGetDateYear( double aDate );
697 : : bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, double& rdRet );
698 : :
699 : 0 : void SbxValue::Format( XubString& rRes, const XubString* pFmt ) const
700 : : {
701 : 0 : short nComma = 0;
702 : 0 : double d = 0;
703 : :
704 : : // pflin, It is better to use SvNumberFormatter to handle the date/time/number format.
705 : : // the SvNumberFormatter output is mostly compatible with
706 : : // VBA output besides the OOo-basic output
707 [ # # ][ # # ]: 0 : if( pFmt && !SbxBasicFormater::isBasicFormat( *pFmt ) )
[ # # ][ # # ]
[ # # ][ # # ]
[ # # # # ]
708 : : {
709 [ # # ][ # # ]: 0 : String aStr = GetString();
710 : :
711 [ # # ][ # # ]: 0 : if( pFmt->EqualsIgnoreCaseAscii( VBAFORMAT_LOWERCASE ) )
712 : : {
713 [ # # ][ # # ]: 0 : rRes = aStr.ToLowerAscii();
714 : : return;
715 : : }
716 [ # # ][ # # ]: 0 : if( pFmt->EqualsIgnoreCaseAscii( VBAFORMAT_UPPERCASE ) )
717 : : {
718 [ # # ][ # # ]: 0 : rRes = aStr.ToUpperAscii();
719 : : return;
720 : : }
721 : :
722 [ # # ][ # # ]: 0 : LanguageType eLangType = GetpApp()->GetSettings().GetLanguage();
[ # # ]
723 : : com::sun::star::uno::Reference< com::sun::star::lang::XMultiServiceFactory >
724 [ # # ]: 0 : xFactory = comphelper::getProcessServiceFactory();
725 [ # # ]: 0 : SvNumberFormatter aFormatter( xFactory, eLangType );
726 : :
727 : : sal_uInt32 nIndex;
728 : : double nNumber;
729 : : Color* pCol;
730 : :
731 [ # # ]: 0 : sal_Bool bSuccess = aFormatter.IsNumberFormat( aStr, nIndex, nNumber );
732 : :
733 : : // number format, use SvNumberFormatter to handle it.
734 [ # # ]: 0 : if( bSuccess )
735 : : {
736 : 0 : xub_StrLen nCheckPos = 0;
737 : : short nType;
738 [ # # ]: 0 : String aFmtStr = *pFmt;
739 [ # # ]: 0 : VbaFormatInfo* pInfo = getFormatInfo( aFmtStr );
740 [ # # ][ # # ]: 0 : if( pInfo && pInfo->meType != VBA_FORMAT_TYPE_NULL )
741 : : {
742 [ # # ]: 0 : if( pInfo->meType == VBA_FORMAT_TYPE_OFFSET )
743 : : {
744 [ # # ]: 0 : nIndex = aFormatter.GetFormatIndex( pInfo->meOffset, eLangType );
745 : : }
746 : : else
747 : : {
748 [ # # ]: 0 : aFmtStr.AssignAscii( pInfo->mpOOoFormat );
749 [ # # ]: 0 : aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
750 : : }
751 [ # # ]: 0 : aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
752 : : }
753 [ # # ][ # # ]: 0 : else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_GENERALDATE )
[ # # ][ # # ]
754 [ # # ]: 0 : || aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_C ))
755 : : {
756 [ # # ][ # # ]: 0 : if( nNumber <=-1.0 || nNumber >= 1.0 )
757 : : {
758 : : // short date
759 [ # # ]: 0 : nIndex = aFormatter.GetFormatIndex( NF_DATE_SYSTEM_SHORT, eLangType );
760 [ # # ]: 0 : aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
761 : :
762 : : // long time
763 [ # # ]: 0 : if( floor( nNumber ) != nNumber )
764 : : {
765 [ # # ]: 0 : aFmtStr.AssignAscii( "H:MM:SS AM/PM" );
766 [ # # ]: 0 : aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
767 [ # # ]: 0 : String aTime;
768 [ # # ]: 0 : aFormatter.GetOutputString( nNumber, nIndex, aTime, &pCol );
769 [ # # ]: 0 : rRes.AppendAscii(" ");
770 [ # # ][ # # ]: 0 : rRes += aTime;
771 : 0 : }
772 : : }
773 : : else
774 : : {
775 : : // long time only
776 [ # # ]: 0 : aFmtStr.AssignAscii( "H:MM:SS AM/PM" );
777 [ # # ]: 0 : aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
778 [ # # ]: 0 : aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
779 : : }
780 : : }
781 [ # # ][ # # ]: 0 : else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_N )
[ # # ][ # # ]
782 [ # # ]: 0 : || aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_NN ))
783 : : {
784 [ # # ]: 0 : sal_Int32 nMin = implGetMinute( nNumber );
785 [ # # ][ # # ]: 0 : if( nMin < 10 && aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_NN ) )
[ # # ][ # # ]
786 : : {
787 : : // Minute in two digits
788 : : sal_Unicode aBuf[2];
789 : 0 : aBuf[0] = '0';
790 : 0 : aBuf[1] = '0' + nMin;
791 [ # # ]: 0 : rRes = rtl::OUString(aBuf, SAL_N_ELEMENTS(aBuf));
792 : : }
793 : : else
794 : : {
795 [ # # ]: 0 : rRes = rtl::OUString::valueOf(nMin);
796 : : }
797 : : }
798 [ # # ][ # # ]: 0 : else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_W ))
799 : : {
800 [ # # ]: 0 : sal_Int32 nWeekDay = implGetWeekDay( nNumber );
801 [ # # ]: 0 : rRes = rtl::OUString::valueOf(nWeekDay);
802 : : }
803 [ # # ][ # # ]: 0 : else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_Y ))
804 : : {
805 [ # # ]: 0 : sal_Int16 nYear = implGetDateYear( nNumber );
806 : : double dBaseDate;
807 [ # # ]: 0 : implDateSerial( nYear, 1, 1, dBaseDate );
808 : 0 : sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate );
809 [ # # ]: 0 : rRes = rtl::OUString::valueOf(nYear32);
810 : : }
811 : : else
812 : : {
813 [ # # ]: 0 : aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
814 [ # # ]: 0 : aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
815 : : }
816 : :
817 [ # # ]: 0 : return;
818 [ # # ][ # # ]: 0 : }
[ # # ][ # # ]
[ # # ]
819 : : }
820 : :
821 [ # # ]: 0 : SbxDataType eType = GetType();
822 [ # # # # : 0 : switch( eType )
# ]
823 : : {
824 : : case SbxCHAR:
825 : : case SbxBYTE:
826 : : case SbxINTEGER:
827 : : case SbxUSHORT:
828 : : case SbxLONG:
829 : : case SbxULONG:
830 : : case SbxINT:
831 : : case SbxUINT:
832 : : case SbxNULL: // #45929 NULL with a little cheating
833 : 0 : nComma = 0; goto cvt;
834 : : case SbxSINGLE:
835 : 0 : nComma = 6; goto cvt;
836 : : case SbxDOUBLE:
837 : 0 : nComma = 14;
838 : :
839 : : cvt:
840 [ # # ]: 0 : if( eType != SbxNULL )
841 [ # # ]: 0 : d = GetDouble();
842 : :
843 : : // #45355 another point to jump in for isnumeric-String
844 : : cvt2:
845 [ # # ]: 0 : if( pFmt )
846 : : {
847 [ # # ]: 0 : SbxAppData& rAppData = GetSbxData_Impl();
848 : :
849 [ # # ][ # # ]: 0 : LanguageType eLangType = GetpApp()->GetSettings().GetLanguage();
[ # # ]
850 [ # # ]: 0 : if( rAppData.pBasicFormater )
851 : : {
852 [ # # ]: 0 : if( rAppData.eBasicFormaterLangType != eLangType )
853 : : {
854 [ # # ][ # # ]: 0 : delete rAppData.pBasicFormater;
855 : 0 : rAppData.pBasicFormater = NULL;
856 : : }
857 : : }
858 : 0 : rAppData.eBasicFormaterLangType = eLangType;
859 : :
860 : :
861 [ # # ]: 0 : if( !rAppData.pBasicFormater )
862 : : {
863 [ # # ]: 0 : SvtSysLocale aSysLocale;
864 [ # # ]: 0 : const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
865 [ # # ]: 0 : sal_Unicode cComma = rData.getNumDecimalSep()[0];
866 [ # # ]: 0 : sal_Unicode c1000 = rData.getNumThousandSep()[0];
867 [ # # ][ # # ]: 0 : String aCurrencyStrg = rData.getCurrSymbol();
868 : :
869 : : // initialize the Basic-formater help object:
870 : : // get resources for predefined output
871 : : // of the Format()-command, e. g. for "On/Off"
872 : : rtl::OUString aOnStrg = SbxValueFormatResId(
873 [ # # ][ # # ]: 0 : STR_BASICKEY_FORMAT_ON).toString();
874 : : rtl::OUString aOffStrg = SbxValueFormatResId(
875 [ # # ][ # # ]: 0 : STR_BASICKEY_FORMAT_OFF).toString();
876 : : rtl::OUString aYesStrg = SbxValueFormatResId(
877 [ # # ][ # # ]: 0 : STR_BASICKEY_FORMAT_YES).toString();
878 : : rtl::OUString aNoStrg = SbxValueFormatResId(
879 [ # # ][ # # ]: 0 : STR_BASICKEY_FORMAT_NO).toString();
880 : : rtl::OUString aTrueStrg = SbxValueFormatResId(
881 [ # # ][ # # ]: 0 : STR_BASICKEY_FORMAT_TRUE).toString();
882 : : rtl::OUString aFalseStrg = SbxValueFormatResId(
883 [ # # ][ # # ]: 0 : STR_BASICKEY_FORMAT_FALSE).toString();
884 : : rtl::OUString aCurrencyFormatStrg = SbxValueFormatResId(
885 [ # # ][ # # ]: 0 : STR_BASICKEY_FORMAT_CURRENCY).toString();
886 : :
887 : : rAppData.pBasicFormater
888 : : = new SbxBasicFormater( cComma,c1000,aOnStrg,aOffStrg,
889 : : aYesStrg,aNoStrg,aTrueStrg,aFalseStrg,
890 [ # # ][ # # ]: 0 : aCurrencyStrg,aCurrencyFormatStrg );
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
891 : : }
892 : : // Remark: For performance reasons there's only ONE BasicFormater-
893 : : // object created and 'stored', so that the expensive resource-
894 : : // loading is saved (for country-specific predefined outputs,
895 : : // e. g. "On/Off") and the continous string-creation
896 : : // operations, too.
897 : : // BUT: therefore this code is NOT multithreading capable!
898 : :
899 : : // here are problems with ;;;Null because this method is only
900 : : // called, if SbxValue is a number!!!
901 : : // in addition rAppData.pBasicFormater->BasicFormatNull( *pFmt ); could be called!
902 [ # # ]: 0 : if( eType != SbxNULL )
903 : : {
904 [ # # ][ # # ]: 0 : rRes = rAppData.pBasicFormater->BasicFormat( d ,*pFmt );
[ # # ][ # # ]
[ # # ]
905 : : }
906 : : else
907 : : {
908 [ # # ][ # # ]: 0 : rRes = rAppData.pBasicFormater->BasicFormatNull( *pFmt );
[ # # ][ # # ]
[ # # ]
909 : : }
910 : :
911 : : }
912 : : else
913 : : {
914 [ # # ]: 0 : ::rtl::OUString aTmpString( rRes );
915 [ # # ][ # # ]: 0 : ImpCvtNum( GetDouble(), nComma, aTmpString );
916 [ # # ]: 0 : rRes = aTmpString;
917 : : }
918 : 0 : break;
919 : : case SbxSTRING:
920 [ # # ]: 0 : if( pFmt )
921 : : {
922 : : // #45355 converting if numeric
923 [ # # ][ # # ]: 0 : if( IsNumericRTL() )
924 : : {
925 [ # # ][ # # ]: 0 : ScanNumIntnl( GetString(), d, /*bSingle*/sal_False );
926 : 0 : goto cvt2;
927 : : }
928 : : else
929 : : {
930 [ # # ][ # # ]: 0 : printfmtstr( GetString(), rRes, *pFmt );
931 : : }
932 : : }
933 : : else
934 [ # # ][ # # ]: 0 : rRes = GetString();
935 : 0 : break;
936 : : default:
937 [ # # ][ # # ]: 0 : rRes = GetString();
938 : : }
939 : : }
940 : :
941 : :
942 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|