1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_basic.hxx"
26 #include <tools/errcode.hxx>
27 #include <basic/sbx.hxx>
28 #include "sbxconv.hxx"
29
30 #include "unotools/syslocale.hxx"
31
32 #if defined ( UNX )
33 #include <stdlib.h>
34 #endif
35
36 #ifndef _APP_HXX //autogen
37 #include <vcl/svapp.hxx>
38 #endif
39 #include <math.h>
40 #include <string.h>
41 #include <ctype.h>
42
43 #include "sbxres.hxx"
44 #include <basic/sbxbase.hxx>
45 #include <basic/sbxform.hxx>
46 #include <svtools/svtools.hrc>
47
48 #include "basrid.hxx"
49 #include "runtime.hxx"
50
51 #include <svl/zforlist.hxx>
52 #include <comphelper/processfactory.hxx>
53
54
ImpGetIntntlSep(sal_Unicode & rcDecimalSep,sal_Unicode & rcThousandSep)55 void ImpGetIntntlSep( sal_Unicode& rcDecimalSep, sal_Unicode& rcThousandSep )
56 {
57 SvtSysLocale aSysLocale;
58 const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
59 rcDecimalSep = rData.getNumDecimalSep().GetBuffer()[0];
60 rcThousandSep = rData.getNumThousandSep().GetBuffer()[0];
61 }
62
63 // Scannen eines Strings nach BASIC-Konventionen
64 // Dies entspricht den ueblichen Konventionen, nur dass der Exponent
65 // auch ein D sein darf, was den Datentyp auf SbxDOUBLE festlegt.
66 // Die Routine versucht, den Datentyp so klein wie moeglich zu gestalten.
67 // Das ganze gibt auch noch einen Konversionsfehler, wenn der Datentyp
68 // Fixed ist und das ganze nicht hineinpasst!
69
ImpScan(const::rtl::OUString & rWSrc,double & nVal,SbxDataType & rType,sal_uInt16 * pLen,sal_Bool bAllowIntntl,sal_Bool bOnlyIntntl)70 SbxError ImpScan( const ::rtl::OUString& rWSrc, double& nVal, SbxDataType& rType,
71 sal_uInt16* pLen, sal_Bool bAllowIntntl, sal_Bool bOnlyIntntl )
72 {
73 ::rtl::OString aBStr( ::rtl::OUStringToOString( rWSrc, RTL_TEXTENCODING_ASCII_US ) );
74
75 // Bei International Komma besorgen
76 char cIntntlComma, cIntntl1000;
77 char cNonIntntlComma = '.';
78
79 sal_Unicode cDecimalSep, cThousandSep = 0;
80 if( bAllowIntntl || bOnlyIntntl )
81 {
82 ImpGetIntntlSep( cDecimalSep, cThousandSep );
83 cIntntlComma = (char)cDecimalSep;
84 cIntntl1000 = (char)cThousandSep;
85 }
86 // Sonst einfach auch auf . setzen
87 else
88 {
89 cIntntlComma = cNonIntntlComma;
90 cIntntl1000 = cNonIntntlComma; // Unschaedlich machen
91 }
92 // Nur International -> IntnlComma uebernehmen
93 if( bOnlyIntntl )
94 {
95 cNonIntntlComma = cIntntlComma;
96 cIntntl1000 = (char)cThousandSep;
97 }
98
99 const char* pStart = aBStr.getStr();
100 const char* p = pStart;
101 char buf[ 80 ], *q = buf;
102 sal_Bool bRes = sal_True;
103 sal_Bool bMinus = sal_False;
104 nVal = 0;
105 SbxDataType eScanType = SbxSINGLE;
106 // Whitespace wech
107 while( *p &&( *p == ' ' || *p == '\t' ) ) p++;
108 // Zahl? Dann einlesen und konvertieren.
109 if( *p == '-' )
110 p++, bMinus = sal_True;
111 if( isdigit( *p ) ||( (*p == cNonIntntlComma || *p == cIntntlComma ||
112 *p == cIntntl1000) && isdigit( *(p+1 ) ) ) )
113 {
114 short exp = 0; // >0: Exponentteil
115 short comma = 0; // >0: Nachkomma
116 short ndig = 0; // Anzahl Ziffern
117 short ncdig = 0; // Anzahl Ziffern nach Komma
118 ByteString aSearchStr( "0123456789DEde" );
119 // Kommas ergaenzen
120 aSearchStr += cNonIntntlComma;
121 if( cIntntlComma != cNonIntntlComma )
122 aSearchStr += cIntntlComma;
123 if( bOnlyIntntl )
124 aSearchStr += cIntntl1000;
125 const char* pSearchStr = aSearchStr.GetBuffer();
126 while( strchr( pSearchStr, *p ) && *p )
127 {
128 // 1000er-Trenner ueberlesen
129 if( bOnlyIntntl && *p == cIntntl1000 )
130 {
131 p++;
132 continue;
133 }
134
135 // Komma oder Exponent?
136 if( *p == cNonIntntlComma || *p == cIntntlComma )
137 {
138 // Immer '.' einfuegen, damit atof funktioniert
139 p++;
140 if( ++comma > 1 )
141 continue;
142 else
143 *q++ = '.';
144 }
145 else if( strchr( "DdEe", *p ) )
146 {
147 if( ++exp > 1 )
148 {
149 p++; continue;
150 }
151 if( toupper( *p ) == 'D' )
152 eScanType = SbxDOUBLE;
153 *q++ = 'E'; p++;
154 // Vorzeichen hinter Exponent?
155 if( *p == '+' )
156 p++;
157 else
158 if( *p == '-' )
159 *q++ = *p++;
160 }
161 else
162 {
163 *q++ = *p++;
164 if( comma && !exp ) ncdig++;
165 }
166 if( !exp ) ndig++;
167 }
168 *q = 0;
169 // Komma, Exponent mehrfach vorhanden?
170 if( comma > 1 || exp > 1 )
171 bRes = sal_False;
172 // Kann auf Integer gefaltet werden?
173 if( !comma && !exp )
174 {
175 if( nVal >= SbxMININT && nVal <= SbxMAXINT )
176 eScanType = SbxINTEGER;
177 else if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
178 eScanType = SbxLONG;
179 }
180
181 nVal = atof( buf );
182 ndig = ndig - comma;
183 // zu viele Zahlen fuer SINGLE?
184 if( ndig > 15 || ncdig > 6 )
185 eScanType = SbxDOUBLE;
186
187 // Typkennung?
188 if( strchr( "%!&#", *p ) && *p ) p++;
189 }
190 // Hex/Oktalzahl? Einlesen und konvertieren:
191 else if( *p == '&' )
192 {
193 p++;
194 eScanType = SbxLONG;
195 const char *cmp = "0123456789ABCDEF";
196 char base = 16;
197 char ndig = 8;
198 char xch = *p++;
199 switch( toupper( xch ) )
200 {
201 case 'O': cmp = "01234567"; base = 8; ndig = 11; break;
202 case 'H': break;
203 default : bRes = sal_False;
204 }
205 sal_Int32 l = 0;
206 int i;
207 while( isalnum( *p ) )
208 {
209 char ch = sal::static_int_cast< char >( toupper( *p ) );
210 p++;
211 if( strchr( cmp, ch ) ) *q++ = ch;
212 else bRes = sal_False;
213 }
214 *q = 0;
215 for( q = buf; *q; q++ )
216 {
217 i =( *q & 0xFF ) - '0';
218 if( i > 9 ) i -= 7;
219 l =( l * base ) + i;
220 if( !ndig-- )
221 bRes = sal_False;
222 }
223 if( *p == '&' ) p++;
224 nVal = (double) l;
225 if( l >= SbxMININT && l <= SbxMAXINT )
226 eScanType = SbxINTEGER;
227 }
228 else if ( SbiRuntime::isVBAEnabled() )
229 {
230 OSL_TRACE("Reporting error converting");
231 return SbxERR_CONVERSION;
232 }
233 if( pLen )
234 *pLen = (sal_uInt16) ( p - pStart );
235 if( !bRes )
236 return SbxERR_CONVERSION;
237 if( bMinus )
238 nVal = -nVal;
239 rType = eScanType;
240 return SbxERR_OK;
241 }
242
243 // Schnittstelle fuer CDbl im Basic
ScanNumIntnl(const String & rSrc,double & nVal,sal_Bool bSingle)244 SbxError SbxValue::ScanNumIntnl( const String& rSrc, double& nVal, sal_Bool bSingle )
245 {
246 SbxDataType t;
247 sal_uInt16 nLen = 0;
248 SbxError nRetError = ImpScan( rSrc, nVal, t, &nLen,
249 /*bAllowIntntl*/sal_False, /*bOnlyIntntl*/sal_True );
250 // Komplett gelesen?
251 if( nRetError == SbxERR_OK && nLen != rSrc.Len() )
252 nRetError = SbxERR_CONVERSION;
253
254 if( bSingle )
255 {
256 SbxValues aValues( nVal );
257 nVal = (double)ImpGetSingle( &aValues ); // Hier Error bei Overflow
258 }
259 return nRetError;
260 }
261
262 ////////////////////////////////////////////////////////////////////////////
263
264 static double roundArray[] = {
265 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,
266 0.5e-8, 0.5e-9, 0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14,0.5e-15 };
267
268 /***************************************************************************
269 |*
270 |* void myftoa( double, char *, short, short, sal_Bool, sal_Bool )
271 |*
272 |* Beschreibung: Konversion double --> ASCII
273 |* Parameter: double die Zahl.
274 |* char * der Zielpuffer
275 |* short Anzahl Nachkommastellen
276 |* short Weite des Exponenten( 0=kein E )
277 |* sal_Bool sal_True: mit 1000er Punkten
278 |* sal_Bool sal_True: formatfreie Ausgabe
279 |*
280 ***************************************************************************/
281
myftoa(double nNum,char * pBuf,short nPrec,short nExpWidth,sal_Bool bPt,sal_Bool bFix,sal_Unicode cForceThousandSep=0)282 static void myftoa( double nNum, char * pBuf, short nPrec, short nExpWidth,
283 sal_Bool bPt, sal_Bool bFix, sal_Unicode cForceThousandSep = 0 )
284 {
285
286 short nExp = 0; // Exponent
287 short nDig = nPrec + 1; // Anzahl Digits in Zahl
288 short nDec; // Anzahl Vorkommastellen
289 register int i, digit;
290
291 // Komma besorgen
292 sal_Unicode cDecimalSep, cThousandSep;
293 ImpGetIntntlSep( cDecimalSep, cThousandSep );
294 if( cForceThousandSep )
295 cThousandSep = cForceThousandSep;
296
297 // Exponentberechnung:
298 nExp = 0;
299 if( nNum > 0.0 )
300 {
301 while( nNum < 1.0 ) nNum *= 10.0, nExp--;
302 while( nNum >= 10.0 ) nNum /= 10.0, nExp++;
303 }
304 if( !bFix && !nExpWidth )
305 nDig = nDig + nExp;
306 else if( bFix && !nPrec )
307 nDig = nExp + 1;
308
309 // Zahl runden:
310 if( (nNum += roundArray [( nDig > 16 ) ? 16 : nDig] ) >= 10.0 )
311 {
312 nNum = 1.0;
313 ++nExp;
314 if( !nExpWidth ) ++nDig;
315 }
316
317 // Bestimmung der Vorkommastellen:
318 if( !nExpWidth )
319 {
320 if( nExp < 0 )
321 {
322 // #41691: Auch bei bFix eine 0 spendieren
323 *pBuf++ = '0';
324 if( nPrec ) *pBuf++ = (char)cDecimalSep;
325 i = -nExp - 1;
326 if( nDig <= 0 ) i = nPrec;
327 while( i-- ) *pBuf++ = '0';
328 nDec = 0;
329 }
330 else
331 nDec = nExp+1;
332 }
333 else
334 nDec = 1;
335
336 // Zahl ausgeben:
337 if( nDig > 0 )
338 {
339 for( i = 0 ; ; ++i )
340 {
341 if( i < 16 )
342 {
343 digit = (int) nNum;
344 *pBuf++ = sal::static_int_cast< char >(digit + '0');
345 nNum =( nNum - digit ) * 10.0;
346 } else
347 *pBuf++ = '0';
348 if( --nDig == 0 ) break;
349 if( nDec )
350 {
351 nDec--;
352 if( !nDec )
353 *pBuf++ = (char)cDecimalSep;
354 else if( !(nDec % 3 ) && bPt )
355 *pBuf++ = (char)cThousandSep;
356 }
357 }
358 }
359
360 // Exponent ausgeben:
361 if( nExpWidth )
362 {
363 if( nExpWidth < 3 ) nExpWidth = 3;
364 nExpWidth -= 2;
365 *pBuf++ = 'E';
366 *pBuf++ =( nExp < 0 ) ?( (nExp = -nExp ), '-' ) : '+';
367 while( nExpWidth > 3 ) *pBuf++ = '0', nExpWidth--;
368 if( nExp >= 100 || nExpWidth == 3 )
369 {
370 *pBuf++ = sal::static_int_cast< char >(nExp/100 + '0');
371 nExp %= 100;
372 }
373 if( nExp/10 || nExpWidth >= 2 )
374 *pBuf++ = sal::static_int_cast< char >(nExp/10 + '0');
375 *pBuf++ = sal::static_int_cast< char >(nExp%10 + '0');
376 }
377 *pBuf = 0;
378 }
379
380 // Die Zahl wird unformatiert mit der angegebenen Anzahl NK-Stellen
381 // aufbereitet. Evtl. wird ein Minus vorangestellt.
382 // Diese Routine ist public, weil sie auch von den Put-Funktionen
383 // der Klasse SbxImpSTRING verwendet wird.
384
385 #ifdef _MSC_VER
386 #pragma optimize( "", off )
387 #pragma warning(disable: 4748) // "... because optimizations are disabled ..."
388 #endif
389
ImpCvtNum(double nNum,short nPrec,::rtl::OUString & rRes,sal_Bool bCoreString)390 void ImpCvtNum( double nNum, short nPrec, ::rtl::OUString& rRes, sal_Bool bCoreString )
391 {
392 char *q;
393 char cBuf[ 40 ], *p = cBuf;
394
395 sal_Unicode cDecimalSep, cThousandSep;
396 ImpGetIntntlSep( cDecimalSep, cThousandSep );
397 if( bCoreString )
398 cDecimalSep = '.';
399
400 if( nNum < 0.0 ) {
401 nNum = -nNum;
402 *p++ = '-';
403 }
404 double dMaxNumWithoutExp = (nPrec == 6) ? 1E6 : 1E14;
405 myftoa( nNum, p, nPrec,( nNum &&( nNum < 1E-1 || nNum >= dMaxNumWithoutExp ) ) ? 4:0,
406 sal_False, sal_True, cDecimalSep );
407 // Trailing Zeroes weg:
408 for( p = cBuf; *p &&( *p != 'E' ); p++ ) {}
409 q = p; p--;
410 while( nPrec && *p == '0' ) nPrec--, p--;
411 if( *p == cDecimalSep ) p--;
412 while( *q ) *++p = *q++;
413 *++p = 0;
414 rRes = ::rtl::OUString::createFromAscii( cBuf );
415 }
416
417 #ifdef _MSC_VER
418 #pragma optimize( "", on )
419 #endif
420
ImpConvStringExt(::rtl::OUString & rSrc,SbxDataType eTargetType)421 sal_Bool ImpConvStringExt( ::rtl::OUString& rSrc, SbxDataType eTargetType )
422 {
423 // Merken, ob ueberhaupt was geaendert wurde
424 sal_Bool bChanged = sal_False;
425 ::rtl::OUString aNewString;
426
427 // Nur Spezial-F�lle behandeln, als Default tun wir nichts
428 switch( eTargetType )
429 {
430 // Bei Fliesskomma International beruecksichtigen
431 case SbxSINGLE:
432 case SbxDOUBLE:
433 case SbxCURRENCY:
434 {
435 ::rtl::OString aBStr( ::rtl::OUStringToOString( rSrc, RTL_TEXTENCODING_ASCII_US ) );
436
437 // Komma besorgen
438 sal_Unicode cDecimalSep, cThousandSep;
439 ImpGetIntntlSep( cDecimalSep, cThousandSep );
440 aNewString = rSrc;
441
442 // Ersetzen, wenn DecimalSep kein '.' (nur den ersten)
443 if( cDecimalSep != (sal_Unicode)'.' )
444 {
445 sal_Int32 nPos = aNewString.indexOf( cDecimalSep );
446 if( nPos != -1 )
447 {
448 sal_Unicode* pStr = (sal_Unicode*)aNewString.getStr();
449 pStr[nPos] = (sal_Unicode)'.';
450 bChanged = sal_True;
451 }
452 }
453 break;
454 }
455
456 // Bei sal_Bool sal_True und sal_False als String pruefen
457 case SbxBOOL:
458 {
459 if( rSrc.equalsIgnoreAsciiCaseAscii( "true" ) )
460 {
461 aNewString = ::rtl::OUString::valueOf( (sal_Int32)SbxTRUE );
462 bChanged = sal_True;
463 }
464 else
465 if( rSrc.equalsIgnoreAsciiCaseAscii( "false" ) )
466 {
467 aNewString = ::rtl::OUString::valueOf( (sal_Int32)SbxFALSE );
468 bChanged = sal_True;
469 }
470 break;
471 }
472 default: break;
473 }
474 // String bei Aenderung uebernehmen
475 if( bChanged )
476 rSrc = aNewString;
477 return bChanged;
478 }
479
480
481 // Formatierte Zahlenausgabe
482 // Der Returnwert ist die Anzahl Zeichen, die aus dem
483 // Format verwendt wurden.
484
485 #ifdef _old_format_code_
486 // lasse diesen Code vorl"aufig drin, zum 'abgucken'
487 // der bisherigen Implementation
488
printfmtnum(double nNum,XubString & rRes,const XubString & rWFmt)489 static sal_uInt16 printfmtnum( double nNum, XubString& rRes, const XubString& rWFmt )
490 {
491 const String& rFmt = rWFmt;
492 char cFill = ' '; // Fuellzeichen
493 char cPre = 0; // Startzeichen( evtl. "$" )
494 short nExpDig= 0; // Anzahl Exponentstellen
495 short nPrec = 0; // Anzahl Nachkommastellen
496 short nWidth = 0; // Zahlenweite gesamnt
497 short nLen; // Laenge konvertierte Zahl
498 sal_Bool bPoint = sal_False; // sal_True: mit 1000er Kommas
499 sal_Bool bTrail = sal_False; // sal_True, wenn folgendes Minus
500 sal_Bool bSign = sal_False; // sal_True: immer mit Vorzeichen
501 sal_Bool bNeg = sal_False; // sal_True: Zahl ist negativ
502 char cBuf [1024]; // Zahlenpuffer
503 char * p;
504 const char* pFmt = rFmt;
505 rRes.Erase();
506 // $$ und ** abfangen. Einfach wird als Zeichen ausgegeben.
507 if( *pFmt == '$' )
508 if( *++pFmt != '$' ) rRes += '$';
509 if( *pFmt == '*' )
510 if( *++pFmt != '*' ) rRes += '*';
511
512 switch( *pFmt++ )
513 {
514 case 0:
515 break;
516 case '+':
517 bSign = sal_True; nWidth++; break;
518 case '*':
519 nWidth++; cFill = '*';
520 if( *pFmt == '$' ) nWidth++, pFmt++, cPre = '$';
521 break;
522 case '$':
523 nWidth++; cPre = '$'; break;
524 case '#':
525 case '.':
526 case ',':
527 pFmt--; break;
528 }
529 // Vorkomma:
530 for( ;; )
531 {
532 while( *pFmt == '#' ) pFmt++, nWidth++;
533 // 1000er Kommas?
534 if( *pFmt == ',' )
535 {
536 nWidth++; pFmt++; bPoint = sal_True;
537 } else break;
538 }
539 // Nachkomma:
540 if( *pFmt == '.' )
541 {
542 while( *++pFmt == '#' ) nPrec++;
543 nWidth += nPrec + 1;
544 }
545 // Exponent:
546 while( *pFmt == '^' )
547 pFmt++, nExpDig++, nWidth++;
548 // Folgendes Minus:
549 if( !bSign && *pFmt == '-' )
550 pFmt++, bTrail = sal_True;
551
552 // Zahl konvertieren:
553 if( nPrec > 15 ) nPrec = 15;
554 if( nNum < 0.0 ) nNum = -nNum, bNeg = sal_True;
555 p = cBuf;
556 if( bSign ) *p++ = bNeg ? '-' : '+';
557 myftoa( nNum, p, nPrec, nExpDig, bPoint, sal_False );
558 nLen = strlen( cBuf );
559
560 // Ueberlauf?
561 if( cPre ) nLen++;
562 if( nLen > nWidth ) rRes += '%';
563 else {
564 nWidth -= nLen;
565 while( nWidth-- ) rRes += (xub_Unicode)cFill;
566 if( cPre ) rRes += (xub_Unicode)cPre;
567 }
568 rRes += (xub_Unicode*)&(cBuf[0]);
569 if( bTrail )
570 rRes += bNeg ? '-' : ' ';
571
572 return (sal_uInt16) ( pFmt - (const char*) rFmt );
573 }
574
575 #endif //_old_format_code_
576
printfmtstr(const XubString & rStr,XubString & rRes,const XubString & rFmt)577 static sal_uInt16 printfmtstr( const XubString& rStr, XubString& rRes, const XubString& rFmt )
578 {
579 const xub_Unicode* pStr = rStr.GetBuffer();
580 const xub_Unicode* pFmtStart = rFmt.GetBuffer();
581 const xub_Unicode* pFmt = pFmtStart;
582 rRes.Erase();
583 switch( *pFmt )
584 {
585 case '!':
586 rRes += *pStr++; pFmt++; break;
587 case '\\':
588 do
589 {
590 rRes += *pStr ? *pStr++ : static_cast< xub_Unicode >(' ');
591 pFmt++;
592 } while( *pFmt != '\\' );
593 rRes += *pStr ? *pStr++ : static_cast< xub_Unicode >(' ');
594 pFmt++; break;
595 case '&':
596 rRes = rStr;
597 pFmt++; break;
598 default:
599 rRes = rStr;
600 break;
601 }
602 return (sal_uInt16) ( pFmt - pFmtStart );
603 }
604
605 /////////////////////////////////////////////////////////////////////////
606
Scan(const XubString & rSrc,sal_uInt16 * pLen)607 sal_Bool SbxValue::Scan( const XubString& rSrc, sal_uInt16* pLen )
608 {
609 SbxError eRes = SbxERR_OK;
610 if( !CanWrite() )
611 eRes = SbxERR_PROP_READONLY;
612 else
613 {
614 double n;
615 SbxDataType t;
616 eRes = ImpScan( rSrc, n, t, pLen );
617 if( eRes == SbxERR_OK )
618 {
619 if( !IsFixed() )
620 SetType( t );
621 PutDouble( n );
622 }
623 }
624 if( eRes )
625 {
626 SetError( eRes ); return sal_False;
627 }
628 else
629 return sal_True;
630 }
631
632
implGetResMgr(void)633 ResMgr* implGetResMgr( void )
634 {
635 static ResMgr* pResMgr = NULL;
636 if( !pResMgr )
637 {
638 ::com::sun::star::lang::Locale aLocale = Application::GetSettings().GetUILocale();
639 pResMgr = ResMgr::CreateResMgr(CREATEVERSIONRESMGR_NAME(sb), aLocale );
640 }
641 return pResMgr;
642 }
643
644 class SbxValueFormatResId : public ResId
645 {
646 public:
SbxValueFormatResId(sal_uInt16 nId)647 SbxValueFormatResId( sal_uInt16 nId )
648 : ResId( nId, *implGetResMgr() )
649 {}
650 };
651
652
653 enum VbaFormatType
654 {
655 VBA_FORMAT_TYPE_OFFSET, // standard number format
656 VBA_FORMAT_TYPE_USERDEFINED, // user defined number format
657 VBA_FORMAT_TYPE_NULL
658 };
659
660 struct VbaFormatInfo
661 {
662 VbaFormatType meType;
663 const char* mpVbaFormat; // Format string in vba
664 NfIndexTableOffset meOffset; // SvNumberFormatter format index, if meType = VBA_FORMAT_TYPE_OFFSET
665 const char* mpOOoFormat; // if meType = VBA_FORMAT_TYPE_USERDEFINED
666 };
667
668 #define VBA_FORMAT_OFFSET( pcUtf8, eOffset ) \
669 { VBA_FORMAT_TYPE_OFFSET, pcUtf8, eOffset, 0 }
670
671 #define VBA_FORMAT_USERDEFINED( pcUtf8, pcDefinedUtf8 ) \
672 { VBA_FORMAT_TYPE_USERDEFINED, pcUtf8, NF_NUMBER_STANDARD, pcDefinedUtf8 }
673
674 static VbaFormatInfo pFormatInfoTable[] =
675 {
676 VBA_FORMAT_OFFSET( "Long Date", NF_DATE_SYSTEM_LONG ),
677 VBA_FORMAT_USERDEFINED( "Medium Date", "DD-MMM-YY" ),
678 VBA_FORMAT_OFFSET( "Short Date", NF_DATE_SYSTEM_SHORT ),
679 VBA_FORMAT_USERDEFINED( "Long Time", "H:MM:SS AM/PM" ),
680 VBA_FORMAT_OFFSET( "Medium Time", NF_TIME_HHMMAMPM ),
681 VBA_FORMAT_OFFSET( "Short Time", NF_TIME_HHMM ),
682 VBA_FORMAT_OFFSET( "ddddd", NF_DATE_SYSTEM_SHORT ),
683 VBA_FORMAT_OFFSET( "dddddd", NF_DATE_SYSTEM_LONG ),
684 VBA_FORMAT_USERDEFINED( "ttttt", "H:MM:SS AM/PM" ),
685 VBA_FORMAT_OFFSET( "ww", NF_DATE_WW ),
686 { VBA_FORMAT_TYPE_NULL, 0, NF_INDEX_TABLE_ENTRIES, 0 }
687 };
688
getFormatInfo(const String & rFmt)689 VbaFormatInfo* getFormatInfo( const String& rFmt )
690 {
691 VbaFormatInfo* pInfo = NULL;
692 sal_Int16 i = 0;
693 while( (pInfo = pFormatInfoTable + i )->mpVbaFormat != NULL )
694 {
695 if( rFmt.EqualsIgnoreCaseAscii( pInfo->mpVbaFormat ) )
696 break;
697 i++;
698 }
699 return pInfo;
700 }
701
702 #define VBAFORMAT_GENERALDATE "General Date"
703 #define VBAFORMAT_C "c"
704 #define VBAFORMAT_N "n"
705 #define VBAFORMAT_NN "nn"
706 #define VBAFORMAT_W "w"
707 #define VBAFORMAT_Y "y"
708 #define VBAFORMAT_LOWERCASE "<"
709 #define VBAFORMAT_UPPERCASE ">"
710
711 // From methods1.cxx
712 sal_Int16 implGetWeekDay( double aDate, bool bFirstDayParam = false, sal_Int16 nFirstDay = 0 );
713 // from methods.cxx
714 sal_Int16 implGetMinute( double dDate );
715 sal_Int16 implGetDateYear( double aDate );
716 sal_Bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, double& rdRet );
717
Format(XubString & rRes,const XubString * pFmt) const718 void SbxValue::Format( XubString& rRes, const XubString* pFmt ) const
719 {
720 short nComma = 0;
721 double d = 0;
722
723 // pflin, It is better to use SvNumberFormatter to handle the date/time/number format.
724 // the SvNumberFormatter output is mostly compatible with
725 // VBA output besides the OOo-basic output
726 if( pFmt && !SbxBasicFormater::isBasicFormat( *pFmt ) )
727 {
728 String aStr = GetString();
729
730 SvtSysLocale aSysLocale;
731 const CharClass& rCharClass = aSysLocale.GetCharClass();
732
733 if( pFmt->EqualsIgnoreCaseAscii( VBAFORMAT_LOWERCASE ) )
734 {
735 rCharClass.toLower( aStr );
736 rRes = aStr;
737 return;
738 }
739 if( pFmt->EqualsIgnoreCaseAscii( VBAFORMAT_UPPERCASE ) )
740 {
741 rCharClass.toUpper( aStr );
742 rRes = aStr;
743 return;
744 }
745
746 LanguageType eLangType = GetpApp()->GetSettings().GetLanguage();
747 com::sun::star::uno::Reference< com::sun::star::lang::XMultiServiceFactory >
748 xFactory = comphelper::getProcessServiceFactory();
749 SvNumberFormatter aFormatter( xFactory, eLangType );
750
751 sal_uInt32 nIndex;
752 xub_StrLen nCheckPos = 0;
753 short nType;
754 double nNumber;
755 Color* pCol;
756
757 sal_Bool bSuccess = aFormatter.IsNumberFormat( aStr, nIndex, nNumber );
758
759 // number format, use SvNumberFormatter to handle it.
760 if( bSuccess )
761 {
762 String aFmtStr = *pFmt;
763 VbaFormatInfo* pInfo = getFormatInfo( aFmtStr );
764 if( pInfo && pInfo->meType != VBA_FORMAT_TYPE_NULL )
765 {
766 if( pInfo->meType == VBA_FORMAT_TYPE_OFFSET )
767 {
768 nIndex = aFormatter.GetFormatIndex( pInfo->meOffset, eLangType );
769 }
770 else
771 {
772 aFmtStr.AssignAscii( pInfo->mpOOoFormat );
773 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
774 }
775 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
776 }
777 else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_GENERALDATE )
778 || aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_C ))
779 {
780 if( nNumber <=-1.0 || nNumber >= 1.0 )
781 {
782 // short date
783 nIndex = aFormatter.GetFormatIndex( NF_DATE_SYSTEM_SHORT, eLangType );
784 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
785
786 // long time
787 if( floor( nNumber ) != nNumber )
788 {
789 aFmtStr.AssignAscii( "H:MM:SS AM/PM" );
790 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
791 String aTime;
792 aFormatter.GetOutputString( nNumber, nIndex, aTime, &pCol );
793 rRes.AppendAscii(" ");
794 rRes += aTime;
795 }
796 }
797 else
798 {
799 // long time only
800 aFmtStr.AssignAscii( "H:MM:SS AM/PM" );
801 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
802 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
803 }
804 }
805 else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_N )
806 || aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_NN ))
807 {
808 sal_Int32 nMin = implGetMinute( nNumber );
809 if( nMin < 10 && aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_NN ) )
810 {
811 // Minute in two digits
812 sal_Unicode* p = rRes.AllocBuffer( 2 );
813 *p++ = '0';
814 *p = sal_Unicode( '0' + nMin );
815 }
816 else
817 {
818 rRes = String::CreateFromInt32( nMin );
819 }
820 }
821 else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_W ))
822 {
823 sal_Int32 nWeekDay = implGetWeekDay( nNumber );
824 rRes = String::CreateFromInt32( nWeekDay );
825 }
826 else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_Y ))
827 {
828 sal_Int16 nYear = implGetDateYear( nNumber );
829 double dBaseDate;
830 implDateSerial( nYear, 1, 1, dBaseDate );
831 sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate );
832 rRes = String::CreateFromInt32( nYear32 );
833 }
834 else
835 {
836 aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
837 aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
838 }
839
840 return;
841 }
842 }
843
844 SbxDataType eType = GetType();
845 switch( eType )
846 {
847 case SbxCHAR:
848 case SbxBYTE:
849 case SbxINTEGER:
850 case SbxUSHORT:
851 case SbxLONG:
852 case SbxULONG:
853 case SbxINT:
854 case SbxUINT:
855 case SbxNULL: // #45929 NULL mit durchschummeln
856 nComma = 0; goto cvt;
857 case SbxSINGLE:
858 nComma = 6; goto cvt;
859 case SbxDOUBLE:
860 nComma = 14;
861
862 cvt:
863 if( eType != SbxNULL )
864 d = GetDouble();
865
866 // #45355 weiterer Einsprungpunkt fuer isnumeric-String
867 cvt2:
868 if( pFmt )
869 {
870 // hole die 'statischen' Daten f"ur Sbx
871 SbxAppData* pData = GetSbxData_Impl();
872
873 LanguageType eLangType = GetpApp()->GetSettings().GetLanguage();
874 if( pData->pBasicFormater )
875 {
876 if( pData->eBasicFormaterLangType != eLangType )
877 {
878 delete pData->pBasicFormater;
879 pData->pBasicFormater = NULL;
880 }
881 }
882 pData->eBasicFormaterLangType = eLangType;
883
884 // falls bisher noch kein BasicFormater-Objekt
885 // existiert, so erzeuge dieses
886 if( !pData->pBasicFormater )
887 {
888 SvtSysLocale aSysLocale;
889 const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
890 sal_Unicode cComma = rData.getNumDecimalSep().GetBuffer()[0];
891 sal_Unicode c1000 = rData.getNumThousandSep().GetBuffer()[0];
892 String aCurrencyStrg = rData.getCurrSymbol();
893
894 // Initialisierung des Basic-Formater-Hilfsobjekts:
895 // hole die Resourcen f"ur die vordefinierten Ausgaben
896 // des Format()-Befehls, z.B. f"ur "On/Off".
897 String aOnStrg = String( SbxValueFormatResId(
898 STR_BASICKEY_FORMAT_ON ) );
899 String aOffStrg = String( SbxValueFormatResId(
900 STR_BASICKEY_FORMAT_OFF) );
901 String aYesStrg = String( SbxValueFormatResId(
902 STR_BASICKEY_FORMAT_YES) );
903 String aNoStrg = String( SbxValueFormatResId(
904 STR_BASICKEY_FORMAT_NO) );
905 String aTrueStrg = String( SbxValueFormatResId(
906 STR_BASICKEY_FORMAT_TRUE) );
907 String aFalseStrg = String( SbxValueFormatResId(
908 STR_BASICKEY_FORMAT_FALSE) );
909 String aCurrencyFormatStrg = String( SbxValueFormatResId(
910 STR_BASICKEY_FORMAT_CURRENCY) );
911 // erzeuge das Basic-Formater-Objekt
912 pData->pBasicFormater
913 = new SbxBasicFormater( cComma,c1000,aOnStrg,aOffStrg,
914 aYesStrg,aNoStrg,aTrueStrg,aFalseStrg,
915 aCurrencyStrg,aCurrencyFormatStrg );
916 }
917 // Bem.: Aus Performance-Gr"unden wird nur EIN BasicFormater-
918 // Objekt erzeugt und 'gespeichert', dadurch erspart man
919 // sich das teure Resourcen-Laden (f"ur landesspezifische
920 // vordefinierte Ausgaben, z.B. "On/Off") und die st"andige
921 // String-Erzeugungs Operationen.
922 // ABER: dadurch ist dieser Code NICHT multithreading f"ahig !
923
924 // hier gibt es Probleme mit ;;;Null, da diese Methode nur aufgerufen
925 // wird, wenn der SbxValue eine Zahl ist !!!
926 // dazu koennte: pData->pBasicFormater->BasicFormatNull( *pFmt ); aufgerufen werden !
927 if( eType != SbxNULL )
928 {
929 rRes = pData->pBasicFormater->BasicFormat( d ,*pFmt );
930 }
931 else
932 {
933 rRes = pData->pBasicFormater->BasicFormatNull( *pFmt );
934 }
935
936 // Die alte Implementierung:
937 //old: printfmtnum( GetDouble(), rRes, *pFmt );
938 }
939 else
940 {
941 ::rtl::OUString aTmpString( rRes );
942 ImpCvtNum( GetDouble(), nComma, aTmpString );
943 rRes = aTmpString;
944 }
945 break;
946 case SbxSTRING:
947 if( pFmt )
948 {
949 // #45355 wenn es numerisch ist, muss gewandelt werden
950 if( IsNumericRTL() )
951 {
952 ScanNumIntnl( GetString(), d, /*bSingle*/sal_False );
953 goto cvt2;
954 }
955 else
956 {
957 // Sonst String-Formatierung
958 printfmtstr( GetString(), rRes, *pFmt );
959 }
960 }
961 else
962 rRes = GetString();
963 break;
964 default:
965 rRes = GetString();
966 }
967 }
968
969
970