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