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