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 1576 : void ImpGetIntntlSep( sal_Unicode& rcDecimalSep, sal_Unicode& rcThousandSep )
53 : {
54 1576 : SvtSysLocale aSysLocale;
55 1576 : const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
56 1576 : rcDecimalSep = rData.getNumDecimalSep()[0];
57 1576 : rcThousandSep = rData.getNumThousandSep()[0];
58 1576 : }
59 :
60 52 : inline bool ImpIsDigit( sal_Unicode c )
61 : {
62 52 : 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 252 : bool ImpStrChr( const sal_Unicode* p, sal_Unicode c )
70 : {
71 252 : if (!c)
72 64 : return false;
73 894 : while (*p)
74 : {
75 614 : if (*p++ == c)
76 96 : return true;
77 : }
78 92 : 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 52 : SbxError ImpScan( const OUString& rWSrc, double& nVal, SbxDataType& rType,
91 : sal_uInt16* pLen, bool bAllowIntntl, bool bOnlyIntntl )
92 : {
93 : sal_Unicode cIntntlDecSep, cIntntlGrpSep;
94 52 : sal_Unicode cNonIntntlDecSep = '.';
95 52 : if( bAllowIntntl || bOnlyIntntl )
96 : {
97 4 : ImpGetIntntlSep( cIntntlDecSep, cIntntlGrpSep );
98 8 : if( bOnlyIntntl )
99 4 : cNonIntntlDecSep = cIntntlDecSep;
100 : }
101 : else
102 : {
103 48 : cIntntlDecSep = cNonIntntlDecSep;
104 48 : cIntntlGrpSep = 0; // no group separator accepted in non-i18n
105 : }
106 :
107 52 : const sal_Unicode* const pStart = rWSrc.getStr();
108 52 : const sal_Unicode* p = pStart;
109 52 : OUStringBuffer aBuf( rWSrc.getLength());
110 52 : bool bRes = true;
111 52 : bool bMinus = false;
112 52 : nVal = 0;
113 52 : SbxDataType eScanType = SbxSINGLE;
114 104 : while( *p == ' ' || *p == '\t' )
115 0 : p++;
116 52 : if( *p == '-' )
117 : {
118 0 : p++;
119 0 : bMinus = true;
120 : }
121 84 : if( ImpIsDigit( *p ) || ((*p == cNonIntntlDecSep || *p == cIntntlDecSep ||
122 20 : (cIntntlDecSep && *p == cIntntlGrpSep)) && ImpIsDigit( *(p+1) )))
123 : {
124 32 : short exp = 0;
125 32 : short decsep = 0;
126 32 : short ndig = 0;
127 32 : short ncdig = 0; // number of digits after decimal point
128 32 : OUStringBuffer aSearchStr("0123456789DEde");
129 32 : aSearchStr.append(cNonIntntlDecSep);
130 32 : if( cIntntlDecSep != cNonIntntlDecSep )
131 0 : aSearchStr.append(cIntntlDecSep);
132 32 : if( bOnlyIntntl )
133 4 : aSearchStr.append(cIntntlGrpSep);
134 32 : const sal_Unicode* const pSearchStr = aSearchStr.getStr();
135 32 : const sal_Unicode pDdEe[] = { 'D', 'd', 'E', 'e', 0 };
136 160 : while( ImpStrChr( pSearchStr, *p ) )
137 : {
138 96 : aBuf.append( *p );
139 96 : if( bOnlyIntntl && *p == cIntntlGrpSep )
140 : {
141 2 : p++;
142 2 : continue;
143 : }
144 94 : if( *p == cNonIntntlDecSep || *p == cIntntlDecSep )
145 : {
146 : // Use the separator that is passed to stringToDouble()
147 2 : aBuf[ p - pStart ] = cIntntlDecSep;
148 2 : p++;
149 4 : if( ++decsep > 1 )
150 0 : continue;
151 : }
152 92 : 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 92 : p++;
167 92 : if( decsep && !exp )
168 6 : ncdig++;
169 : }
170 94 : if( !exp )
171 94 : ndig++;
172 : }
173 :
174 32 : if( decsep > 1 || exp > 1 )
175 0 : bRes = false;
176 :
177 64 : OUString aBufStr( aBuf.makeStringAndClear());
178 32 : rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
179 32 : sal_Int32 nParseEnd = 0;
180 32 : nVal = rtl::math::stringToDouble( aBufStr, cIntntlDecSep, cIntntlGrpSep, &eStatus, &nParseEnd );
181 32 : if( eStatus != rtl_math_ConversionStatus_Ok || nParseEnd != aBufStr.getLength() )
182 0 : bRes = false;
183 :
184 32 : if( !decsep && !exp )
185 : {
186 30 : if( nVal >= SbxMININT && nVal <= SbxMAXINT )
187 28 : eScanType = SbxINTEGER;
188 2 : else if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
189 2 : eScanType = SbxLONG;
190 : }
191 :
192 32 : ndig = ndig - decsep;
193 : // too many numbers for SINGLE?
194 32 : if( ndig > 15 || ncdig > 6 )
195 0 : eScanType = SbxDOUBLE;
196 :
197 : // type detection?
198 32 : const sal_Unicode pTypes[] = { '%', '!', '&', '#', 0 };
199 32 : if( ImpStrChr( pTypes, *p ) )
200 32 : p++;
201 : }
202 : // hex/octal number? read in and convert:
203 20 : 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 20 : else if ( SbiRuntime::isVBAEnabled() )
257 : {
258 : OSL_TRACE("Reporting error converting");
259 20 : return SbxERR_CONVERSION;
260 : }
261 : #endif
262 32 : if( pLen )
263 4 : *pLen = (sal_uInt16) ( p - pStart );
264 32 : if( !bRes )
265 0 : return SbxERR_CONVERSION;
266 32 : if( bMinus )
267 0 : nVal = -nVal;
268 32 : rType = eScanType;
269 32 : return SbxERR_OK;
270 : }
271 :
272 : // port for CDbl in the Basic
273 4 : SbxError SbxValue::ScanNumIntnl( const OUString& rSrc, double& nVal, bool bSingle )
274 : {
275 : SbxDataType t;
276 4 : sal_uInt16 nLen = 0;
277 : SbxError nRetError = ImpScan( rSrc, nVal, t, &nLen,
278 4 : /*bAllowIntntl*/false, /*bOnlyIntntl*/true );
279 : // read completely?
280 4 : if( nRetError == SbxERR_OK && nLen != rSrc.getLength() )
281 : {
282 0 : nRetError = SbxERR_CONVERSION;
283 : }
284 4 : if( bSingle )
285 : {
286 0 : SbxValues aValues( nVal );
287 0 : nVal = (double)ImpGetSingle( &aValues ); // here error at overflow
288 : }
289 4 : 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 786 : static void myftoa( double nNum, char * pBuf, short nPrec, short nExpWidth,
312 : bool bPt, bool bFix, sal_Unicode cForceThousandSep = 0 )
313 : {
314 :
315 786 : short nExp = 0;
316 786 : short nDig = nPrec + 1;
317 : short nDec; // number of positions before decimal point
318 : int i;
319 :
320 : sal_Unicode cDecimalSep, cThousandSep;
321 786 : ImpGetIntntlSep( cDecimalSep, cThousandSep );
322 786 : if( cForceThousandSep )
323 786 : cThousandSep = cForceThousandSep;
324 :
325 : // compute exponent
326 786 : nExp = 0;
327 786 : if( nNum > 0.0 )
328 : {
329 654 : while( nNum < 1.0 ) nNum *= 10.0, nExp--;
330 654 : while( nNum >= 10.0 ) nNum /= 10.0, nExp++;
331 : }
332 786 : if( !bFix && !nExpWidth )
333 0 : nDig = nDig + nExp;
334 786 : else if( bFix && !nPrec )
335 412 : nDig = nExp + 1;
336 :
337 : // round number
338 786 : 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 786 : if( !nExpWidth )
347 : {
348 776 : if( nExp < 0 )
349 : {
350 : // #41691: also a 0 at bFix
351 56 : *pBuf++ = '0';
352 56 : if( nPrec ) *pBuf++ = (char)cDecimalSep;
353 56 : i = -nExp - 1;
354 56 : if( nDig <= 0 ) i = nPrec;
355 56 : while( i-- ) *pBuf++ = '0';
356 56 : nDec = 0;
357 : }
358 : else
359 720 : nDec = nExp+1;
360 : }
361 : else
362 10 : nDec = 1;
363 :
364 : // output number
365 786 : if( nDig > 0 )
366 : {
367 : int digit;
368 6194 : for( i = 0 ; ; ++i )
369 : {
370 6194 : if( i < 16 )
371 : {
372 6194 : digit = (int) nNum;
373 6194 : *pBuf++ = sal::static_int_cast< char >(digit + '0');
374 6194 : nNum =( nNum - digit ) * 10.0;
375 : } else
376 0 : *pBuf++ = '0';
377 6194 : if( --nDig == 0 ) break;
378 5408 : if( nDec )
379 : {
380 830 : nDec--;
381 830 : if( !nDec )
382 318 : *pBuf++ = (char)cDecimalSep;
383 512 : else if( !(nDec % 3 ) && bPt )
384 0 : *pBuf++ = (char)cThousandSep;
385 : }
386 5408 : }
387 : }
388 :
389 : // output exponent
390 786 : if( nExpWidth )
391 : {
392 10 : if( nExpWidth < 3 ) nExpWidth = 3;
393 10 : nExpWidth -= 2;
394 10 : *pBuf++ = 'E';
395 10 : *pBuf++ =( nExp < 0 ) ?( (nExp = -nExp ), '-' ) : '+';
396 10 : while( nExpWidth > 3 ) *pBuf++ = '0', nExpWidth--;
397 10 : if( nExp >= 100 || nExpWidth == 3 )
398 : {
399 0 : *pBuf++ = sal::static_int_cast< char >(nExp/100 + '0');
400 0 : nExp %= 100;
401 : }
402 10 : if( nExp/10 || nExpWidth >= 2 )
403 10 : *pBuf++ = sal::static_int_cast< char >(nExp/10 + '0');
404 10 : *pBuf++ = sal::static_int_cast< char >(nExp%10 + '0');
405 : }
406 786 : *pBuf = 0;
407 786 : }
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 786 : void ImpCvtNum( double nNum, short nPrec, OUString& rRes, bool bCoreString )
415 : {
416 : char *q;
417 786 : char cBuf[ 40 ], *p = cBuf;
418 :
419 : sal_Unicode cDecimalSep, cThousandSep;
420 786 : ImpGetIntntlSep( cDecimalSep, cThousandSep );
421 786 : if( bCoreString )
422 0 : cDecimalSep = '.';
423 :
424 786 : if( nNum < 0.0 ) {
425 50 : nNum = -nNum;
426 50 : *p++ = '-';
427 : }
428 786 : double dMaxNumWithoutExp = (nPrec == 6) ? 1E6 : 1E14;
429 654 : myftoa( nNum, p, nPrec,( nNum &&( nNum < 1E-1 || nNum >= dMaxNumWithoutExp ) ) ? 4:0,
430 796 : false, true, cDecimalSep );
431 : // remove trailing zeros
432 786 : for( p = cBuf; *p &&( *p != 'E' ); p++ ) {}
433 786 : q = p; p--;
434 786 : while( nPrec && *p == '0' ) nPrec--, p--;
435 786 : if( *p == cDecimalSep ) p--;
436 786 : while( *q ) *++p = *q++;
437 786 : *++p = 0;
438 786 : rRes = OUString::createFromAscii( cBuf );
439 786 : }
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 = (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 14 : ResMgr* implGetResMgr( void )
567 : {
568 : static ResMgr* pResMgr = NULL;
569 14 : if( !pResMgr )
570 : {
571 2 : pResMgr = ResMgr::CreateResMgr("sb", Application::GetSettings().GetUILanguageTag() );
572 : }
573 14 : return pResMgr;
574 : }
575 :
576 : class SbxValueFormatResId : public ResId
577 : {
578 : public:
579 14 : SbxValueFormatResId( sal_uInt16 nId )
580 14 : : ResId( nId, *implGetResMgr() )
581 14 : {}
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 4411 : 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 802 : 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 401 : };
620 :
621 24 : VbaFormatInfo* getFormatInfo( const OUString& rFmt )
622 : {
623 24 : VbaFormatInfo* pInfo = NULL;
624 24 : sal_Int16 i = 0;
625 288 : while( (pInfo = pFormatInfoTable + i )->meType != VBA_FORMAT_TYPE_NULL )
626 : {
627 240 : if( rFmt.equalsIgnoreAsciiCase( pInfo->mpVbaFormat ) )
628 0 : break;
629 240 : i++;
630 : }
631 24 : 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 60 : void SbxValue::Format( OUString& rRes, const OUString* pFmt ) const
644 : {
645 60 : short nComma = 0;
646 60 : 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 60 : if( pFmt && !SbxBasicFormater::isBasicFormat( *pFmt ) )
652 : {
653 28 : OUString aStr = GetOUString();
654 :
655 28 : SvtSysLocale aSysLocale;
656 28 : const CharClass& rCharClass = aSysLocale.GetCharClass();
657 :
658 28 : if( pFmt->equalsIgnoreAsciiCase( VBAFORMAT_LOWERCASE ) )
659 : {
660 2 : rRes = rCharClass.lowercase( aStr );
661 2 : return;
662 : }
663 26 : if( pFmt->equalsIgnoreAsciiCase( VBAFORMAT_UPPERCASE ) )
664 : {
665 2 : rRes = rCharClass.uppercase( aStr );
666 2 : return;
667 : }
668 :
669 24 : LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
670 24 : SvNumberFormatter aFormatter( comphelper::getProcessComponentContext(), eLangType );
671 :
672 24 : sal_uInt32 nIndex = 0;
673 : double nNumber;
674 : Color* pCol;
675 :
676 24 : bool bSuccess = aFormatter.IsNumberFormat( aStr, nIndex, nNumber );
677 :
678 : // number format, use SvNumberFormatter to handle it.
679 24 : if( bSuccess )
680 : {
681 24 : sal_Int32 nCheckPos = 0;
682 : short nType;
683 24 : OUString aFmtStr = *pFmt;
684 24 : VbaFormatInfo* pInfo = getFormatInfo( aFmtStr );
685 24 : 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 48 : else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_GENERALDATE )
699 24 : || 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 48 : else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_N ) ||
726 24 : 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 24 : else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_W ))
743 : {
744 0 : sal_Int32 nWeekDay = implGetWeekDay( nNumber );
745 0 : rRes = OUString::number(nWeekDay);
746 : }
747 24 : 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 24 : aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
758 24 : aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
759 : }
760 :
761 24 : return;
762 0 : }
763 : }
764 :
765 32 : SbxDataType eType = GetType();
766 32 : 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 12 : nComma = 0; goto cvt;
778 : case SbxSINGLE:
779 0 : nComma = 6; goto cvt;
780 : case SbxDOUBLE:
781 16 : nComma = 14;
782 :
783 : cvt:
784 28 : if( eType != SbxNULL )
785 : {
786 28 : d = GetDouble();
787 : }
788 : // #45355 another point to jump in for isnumeric-String
789 : cvt2:
790 28 : if( pFmt )
791 : {
792 24 : SbxAppData& rAppData = GetSbxData_Impl();
793 :
794 24 : LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
795 24 : if( rAppData.pBasicFormater )
796 : {
797 22 : if( rAppData.eBasicFormaterLangType != eLangType )
798 : {
799 0 : delete rAppData.pBasicFormater;
800 0 : rAppData.pBasicFormater = NULL;
801 : }
802 : }
803 24 : rAppData.eBasicFormaterLangType = eLangType;
804 :
805 :
806 24 : if( !rAppData.pBasicFormater )
807 : {
808 2 : SvtSysLocale aSysLocale;
809 2 : const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
810 2 : sal_Unicode cComma = rData.getNumDecimalSep()[0];
811 2 : sal_Unicode c1000 = rData.getNumThousandSep()[0];
812 4 : 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 4 : OUString aOnStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_ON).toString();
818 4 : OUString aOffStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_OFF).toString();
819 4 : OUString aYesStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_YES).toString();
820 4 : OUString aNoStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_NO).toString();
821 4 : OUString aTrueStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_TRUE).toString();
822 4 : OUString aFalseStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_FALSE).toString();
823 4 : OUString aCurrencyFormatStrg = SbxValueFormatResId(STR_BASICKEY_FORMAT_CURRENCY).toString();
824 :
825 : rAppData.pBasicFormater = new SbxBasicFormater( cComma,c1000,aOnStrg,aOffStrg,
826 : aYesStrg,aNoStrg,aTrueStrg,aFalseStrg,
827 4 : 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 24 : if( eType != SbxNULL )
840 : {
841 24 : rRes = rAppData.pBasicFormater->BasicFormat( d ,*pFmt );
842 : }
843 : else
844 : {
845 0 : rRes = rAppData.pBasicFormater->BasicFormatNull( *pFmt );
846 : }
847 :
848 : }
849 : else
850 : {
851 4 : OUString aTmpString( rRes );
852 4 : ImpCvtNum( GetDouble(), nComma, aTmpString );
853 4 : rRes = aTmpString;
854 : }
855 28 : break;
856 : case SbxSTRING:
857 2 : 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 2 : rRes = GetOUString();
873 : }
874 2 : break;
875 : default:
876 2 : rRes = GetOUString();
877 : }
878 1203 : }
879 :
880 :
881 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|