xref: /AOO41X/main/basic/source/sbx/sbxscan.cxx (revision 0c8b819cba58d752925fe3b4b585b9da6c3db38b)
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