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