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