1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: sbxscan.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_basic.hxx"
33 #include <tools/errcode.hxx>
34 #include <basic/sbx.hxx>
35 #include "sbxconv.hxx"
37 #include "svtools/syslocale.hxx"
43 #ifndef _APP_HXX //autogen
44 #include <vcl/svapp.hxx>
51 #include <basic/sbxbase.hxx>
52 #include <basic/sbxform.hxx>
53 #include <svtools/svtools.hrc>
56 #include "runtime.hxx"
58 #include <svtools/zforlist.hxx>
59 #include <comphelper/processfactory.hxx>
62 void ImpGetIntntlSep( sal_Unicode
& rcDecimalSep
, sal_Unicode
& rcThousandSep
)
64 SvtSysLocale aSysLocale
;
65 const LocaleDataWrapper
& rData
= aSysLocale
.GetLocaleData();
66 rcDecimalSep
= rData
.getNumDecimalSep().GetBuffer()[0];
67 rcThousandSep
= rData
.getNumThousandSep().GetBuffer()[0];
70 // Scannen eines Strings nach BASIC-Konventionen
71 // Dies entspricht den ueblichen Konventionen, nur dass der Exponent
72 // auch ein D sein darf, was den Datentyp auf SbxDOUBLE festlegt.
73 // Die Routine versucht, den Datentyp so klein wie moeglich zu gestalten.
74 // Das ganze gibt auch noch einen Konversionsfehler, wenn der Datentyp
75 // Fixed ist und das ganze nicht hineinpasst!
77 SbxError
ImpScan( const XubString
& rWSrc
, double& nVal
, SbxDataType
& rType
,
78 USHORT
* pLen
, BOOL bAllowIntntl
, BOOL bOnlyIntntl
)
80 ByteString
aBStr( rWSrc
, RTL_TEXTENCODING_ASCII_US
);
82 // Bei International Komma besorgen
83 char cIntntlComma
, cIntntl1000
;
84 char cNonIntntlComma
= '.';
86 sal_Unicode cDecimalSep
, cThousandSep
= 0;
87 if( bAllowIntntl
|| bOnlyIntntl
)
89 ImpGetIntntlSep( cDecimalSep
, cThousandSep
);
90 cIntntlComma
= (char)cDecimalSep
;
91 cIntntl1000
= (char)cThousandSep
;
93 // Sonst einfach auch auf . setzen
96 cIntntlComma
= cNonIntntlComma
;
97 cIntntl1000
= cNonIntntlComma
; // Unschaedlich machen
99 // Nur International -> IntnlComma uebernehmen
102 cNonIntntlComma
= cIntntlComma
;
103 cIntntl1000
= (char)cThousandSep
;
106 const char* pStart
= aBStr
.GetBuffer();
107 const char* p
= pStart
;
108 char buf
[ 80 ], *q
= buf
;
112 SbxDataType eScanType
= SbxSINGLE
;
114 while( *p
&&( *p
== ' ' || *p
== '\t' ) ) p
++;
115 // Zahl? Dann einlesen und konvertieren.
118 if( isdigit( *p
) ||( (*p
== cNonIntntlComma
|| *p
== cIntntlComma
||
119 *p
== cIntntl1000
) && isdigit( *(p
+1 ) ) ) )
121 short exp
= 0; // >0: Exponentteil
122 short comma
= 0; // >0: Nachkomma
123 short ndig
= 0; // Anzahl Ziffern
124 short ncdig
= 0; // Anzahl Ziffern nach Komma
125 ByteString
aSearchStr( "0123456789DEde" );
127 aSearchStr
+= cNonIntntlComma
;
128 if( cIntntlComma
!= cNonIntntlComma
)
129 aSearchStr
+= cIntntlComma
;
131 aSearchStr
+= cIntntl1000
;
132 const char* pSearchStr
= aSearchStr
.GetBuffer();
133 while( strchr( pSearchStr
, *p
) && *p
)
135 // 1000er-Trenner ueberlesen
136 if( bOnlyIntntl
&& *p
== cIntntl1000
)
142 // Komma oder Exponent?
143 if( *p
== cNonIntntlComma
|| *p
== cIntntlComma
)
145 // Immer '.' einfuegen, damit atof funktioniert
152 else if( strchr( "DdEe", *p
) )
158 if( toupper( *p
) == 'D' )
159 eScanType
= SbxDOUBLE
;
161 // Vorzeichen hinter Exponent?
171 if( comma
&& !exp
) ncdig
++;
176 // Komma, Exponent mehrfach vorhanden?
177 if( comma
> 1 || exp
> 1 )
179 // Kann auf Integer gefaltet werden?
182 if( nVal
>= SbxMININT
&& nVal
<= SbxMAXINT
)
183 eScanType
= SbxINTEGER
;
184 else if( nVal
>= SbxMINLNG
&& nVal
<= SbxMAXLNG
)
190 // zu viele Zahlen fuer SINGLE?
191 if( ndig
> 15 || ncdig
> 6 )
192 eScanType
= SbxDOUBLE
;
195 if( strchr( "%!&#", *p
) && *p
) p
++;
197 // Hex/Oktalzahl? Einlesen und konvertieren:
202 const char *cmp
= "0123456789ABCDEF";
206 switch( toupper( xch
) )
208 case 'O': cmp
= "01234567"; base
= 8; ndig
= 11; break;
210 default : bRes
= FALSE
;
214 while( isalnum( *p
) )
216 char ch
= sal::static_int_cast
< char >( toupper( *p
) );
218 if( strchr( cmp
, ch
) ) *q
++ = ch
;
222 for( q
= buf
; *q
; q
++ )
224 i
=( *q
& 0xFF ) - '0';
232 if( l
>= SbxMININT
&& l
<= SbxMAXINT
)
233 eScanType
= SbxINTEGER
;
235 else if ( SbiRuntime::isVBAEnabled() )
237 OSL_TRACE("Reporting error converting");
238 return SbxERR_CONVERSION
;
241 *pLen
= (USHORT
) ( p
- pStart
);
243 return SbxERR_CONVERSION
;
250 // Schnittstelle fuer CDbl im Basic
251 SbxError
SbxValue::ScanNumIntnl( const String
& rSrc
, double& nVal
, BOOL bSingle
)
255 SbxError nRetError
= ImpScan( rSrc
, nVal
, t
, &nLen
,
256 /*bAllowIntntl*/FALSE
, /*bOnlyIntntl*/TRUE
);
258 if( nRetError
== SbxERR_OK
&& nLen
!= rSrc
.Len() )
259 nRetError
= SbxERR_CONVERSION
;
263 SbxValues
aValues( nVal
);
264 nVal
= (double)ImpGetSingle( &aValues
); // Hier Error bei Overflow
269 ////////////////////////////////////////////////////////////////////////////
271 static double roundArray
[] = {
272 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,
273 0.5e-8, 0.5e-9, 0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14,0.5e-15 };
275 /***************************************************************************
277 |* void myftoa( double, char *, short, short, BOOL, BOOL )
279 |* Beschreibung: Konversion double --> ASCII
280 |* Parameter: double die Zahl.
281 |* char * der Zielpuffer
282 |* short Anzahl Nachkommastellen
283 |* short Weite des Exponenten( 0=kein E )
284 |* BOOL TRUE: mit 1000er Punkten
285 |* BOOL TRUE: formatfreie Ausgabe
287 ***************************************************************************/
289 static void myftoa( double nNum
, char * pBuf
, short nPrec
, short nExpWidth
,
290 BOOL bPt
, BOOL bFix
, sal_Unicode cForceThousandSep
= 0 )
293 short nExp
= 0; // Exponent
294 short nDig
= nPrec
+ 1; // Anzahl Digits in Zahl
295 short nDec
; // Anzahl Vorkommastellen
296 register int i
, digit
;
299 sal_Unicode cDecimalSep
, cThousandSep
;
300 ImpGetIntntlSep( cDecimalSep
, cThousandSep
);
301 if( cForceThousandSep
)
302 cThousandSep
= cForceThousandSep
;
304 // Exponentberechnung:
308 while( nNum
< 1.0 ) nNum
*= 10.0, nExp
--;
309 while( nNum
>= 10.0 ) nNum
/= 10.0, nExp
++;
311 if( !bFix
&& !nExpWidth
)
313 else if( bFix
&& !nPrec
)
317 if( (nNum
+= roundArray
[( nDig
> 16 ) ? 16 : nDig
] ) >= 10.0 )
321 if( !nExpWidth
) ++nDig
;
324 // Bestimmung der Vorkommastellen:
329 // #41691: Auch bei bFix eine 0 spendieren
331 if( nPrec
) *pBuf
++ = (char)cDecimalSep
;
333 if( nDig
<= 0 ) i
= nPrec
;
334 while( i
-- ) *pBuf
++ = '0';
351 *pBuf
++ = sal::static_int_cast
< char >(digit
+ '0');
352 nNum
=( nNum
- digit
) * 10.0;
355 if( --nDig
== 0 ) break;
360 *pBuf
++ = (char)cDecimalSep
;
361 else if( !(nDec
% 3 ) && bPt
)
362 *pBuf
++ = (char)cThousandSep
;
367 // Exponent ausgeben:
370 if( nExpWidth
< 3 ) nExpWidth
= 3;
373 *pBuf
++ =( nExp
< 0 ) ?( (nExp
= -nExp
), '-' ) : '+';
374 while( nExpWidth
> 3 ) *pBuf
++ = '0', nExpWidth
--;
375 if( nExp
>= 100 || nExpWidth
== 3 )
377 *pBuf
++ = sal::static_int_cast
< char >(nExp
/100 + '0');
380 if( nExp
/10 || nExpWidth
>= 2 )
381 *pBuf
++ = sal::static_int_cast
< char >(nExp
/10 + '0');
382 *pBuf
++ = sal::static_int_cast
< char >(nExp
%10 + '0');
387 // Die Zahl wird unformatiert mit der angegebenen Anzahl NK-Stellen
388 // aufbereitet. Evtl. wird ein Minus vorangestellt.
389 // Diese Routine ist public, weil sie auch von den Put-Funktionen
390 // der Klasse SbxImpSTRING verwendet wird.
393 #pragma optimize( "", off )
394 #pragma warning(disable: 4748) // "... because optimizations are disabled ..."
397 void ImpCvtNum( double nNum
, short nPrec
, XubString
& rRes
, BOOL bCoreString
)
400 char cBuf
[ 40 ], *p
= cBuf
;
402 sal_Unicode cDecimalSep
, cThousandSep
;
403 ImpGetIntntlSep( cDecimalSep
, cThousandSep
);
411 double dMaxNumWithoutExp
= (nPrec
== 6) ? 1E6
: 1E14
;
412 myftoa( nNum
, p
, nPrec
,( nNum
&&( nNum
< 1E-1 || nNum
> dMaxNumWithoutExp
) ) ? 4:0,
413 FALSE
, TRUE
, cDecimalSep
);
414 // Trailing Zeroes weg:
415 for( p
= cBuf
; *p
&&( *p
!= 'E' ); p
++ ) {}
417 while( nPrec
&& *p
== '0' ) nPrec
--, p
--;
418 if( *p
== cDecimalSep
) p
--;
419 while( *q
) *++p
= *q
++;
421 rRes
= String::CreateFromAscii( cBuf
);
425 #pragma optimize( "", on )
428 BOOL
ImpConvStringExt( XubString
& rSrc
, SbxDataType eTargetType
)
430 // Merken, ob ueberhaupt was geaendert wurde
431 BOOL bChanged
= FALSE
;
434 // Nur Spezial-Fälle behandeln, als Default tun wir nichts
435 switch( eTargetType
)
437 // Bei Fliesskomma International beruecksichtigen
442 ByteString
aBStr( rSrc
, RTL_TEXTENCODING_ASCII_US
);
445 sal_Unicode cDecimalSep
, cThousandSep
;
446 ImpGetIntntlSep( cDecimalSep
, cThousandSep
);
449 // Ersetzen, wenn DecimalSep kein '.' (nur den ersten)
450 if( cDecimalSep
!= (sal_Unicode
)'.' )
452 USHORT nPos
= aNewString
.Search( cDecimalSep
);
453 if( nPos
!= STRING_NOTFOUND
)
455 aNewString
.SetChar( nPos
, '.' );
462 // Bei BOOL TRUE und FALSE als String pruefen
465 if( rSrc
.EqualsIgnoreCaseAscii( "true" ) )
467 aNewString
= String::CreateFromInt32(SbxTRUE
);
471 if( rSrc
.EqualsIgnoreCaseAscii( "false" ) )
473 aNewString
= String::CreateFromInt32(SbxFALSE
);
480 // String bei Aenderung uebernehmen
487 // Formatierte Zahlenausgabe
488 // Der Returnwert ist die Anzahl Zeichen, die aus dem
489 // Format verwendt wurden.
491 #ifdef _old_format_code_
492 // lasse diesen Code vorl"aufig drin, zum 'abgucken'
493 // der bisherigen Implementation
495 static USHORT
printfmtnum( double nNum
, XubString
& rRes
, const XubString
& rWFmt
)
497 const String
& rFmt
= rWFmt
;
498 char cFill
= ' '; // Fuellzeichen
499 char cPre
= 0; // Startzeichen( evtl. "$" )
500 short nExpDig
= 0; // Anzahl Exponentstellen
501 short nPrec
= 0; // Anzahl Nachkommastellen
502 short nWidth
= 0; // Zahlenweite gesamnt
503 short nLen
; // Laenge konvertierte Zahl
504 BOOL bPoint
= FALSE
; // TRUE: mit 1000er Kommas
505 BOOL bTrail
= FALSE
; // TRUE, wenn folgendes Minus
506 BOOL bSign
= FALSE
; // TRUE: immer mit Vorzeichen
507 BOOL bNeg
= FALSE
; // TRUE: Zahl ist negativ
508 char cBuf
[1024]; // Zahlenpuffer
510 const char* pFmt
= rFmt
;
512 // $$ und ** abfangen. Einfach wird als Zeichen ausgegeben.
514 if( *++pFmt
!= '$' ) rRes
+= '$';
516 if( *++pFmt
!= '*' ) rRes
+= '*';
523 bSign
= TRUE
; nWidth
++; break;
525 nWidth
++; cFill
= '*';
526 if( *pFmt
== '$' ) nWidth
++, pFmt
++, cPre
= '$';
529 nWidth
++; cPre
= '$'; break;
538 while( *pFmt
== '#' ) pFmt
++, nWidth
++;
542 nWidth
++; pFmt
++; bPoint
= TRUE
;
548 while( *++pFmt
== '#' ) nPrec
++;
552 while( *pFmt
== '^' )
553 pFmt
++, nExpDig
++, nWidth
++;
555 if( !bSign
&& *pFmt
== '-' )
556 pFmt
++, bTrail
= TRUE
;
558 // Zahl konvertieren:
559 if( nPrec
> 15 ) nPrec
= 15;
560 if( nNum
< 0.0 ) nNum
= -nNum
, bNeg
= TRUE
;
562 if( bSign
) *p
++ = bNeg
? '-' : '+';
563 myftoa( nNum
, p
, nPrec
, nExpDig
, bPoint
, FALSE
);
564 nLen
= strlen( cBuf
);
568 if( nLen
> nWidth
) rRes
+= '%';
571 while( nWidth
-- ) rRes
+= (xub_Unicode
)cFill
;
572 if( cPre
) rRes
+= (xub_Unicode
)cPre
;
574 rRes
+= (xub_Unicode
*)&(cBuf
[0]);
576 rRes
+= bNeg
? '-' : ' ';
578 return (USHORT
) ( pFmt
- (const char*) rFmt
);
581 #endif //_old_format_code_
583 static USHORT
printfmtstr( const XubString
& rStr
, XubString
& rRes
, const XubString
& rFmt
)
585 const xub_Unicode
* pStr
= rStr
.GetBuffer();
586 const xub_Unicode
* pFmtStart
= rFmt
.GetBuffer();
587 const xub_Unicode
* pFmt
= pFmtStart
;
592 rRes
+= *pStr
++; pFmt
++; break;
596 rRes
+= *pStr
? *pStr
++ : static_cast< xub_Unicode
>(' ');
598 } while( *pFmt
!= '\\' );
599 rRes
+= *pStr
? *pStr
++ : static_cast< xub_Unicode
>(' ');
608 return (USHORT
) ( pFmt
- pFmtStart
);
611 /////////////////////////////////////////////////////////////////////////
613 BOOL
SbxValue::Scan( const XubString
& rSrc
, USHORT
* pLen
)
615 SbxError eRes
= SbxERR_OK
;
617 eRes
= SbxERR_PROP_READONLY
;
622 eRes
= ImpScan( rSrc
, n
, t
, pLen
);
623 if( eRes
== SbxERR_OK
)
632 SetError( eRes
); return FALSE
;
639 ResMgr
* implGetResMgr( void )
641 static ResMgr
* pResMgr
= NULL
;
644 ::com::sun::star::lang::Locale aLocale
= Application::GetSettings().GetUILocale();
645 pResMgr
= ResMgr::CreateResMgr(CREATEVERSIONRESMGR_NAME(sb
), aLocale
);
650 class SbxValueFormatResId
: public ResId
653 SbxValueFormatResId( USHORT nId
)
654 : ResId( nId
, *implGetResMgr() )
661 VBA_FORMAT_TYPE_OFFSET
, // standard number format
662 VBA_FORMAT_TYPE_USERDEFINED
, // user defined number format
668 VbaFormatType meType
;
669 const char* mpVbaFormat
; // Format string in vba
670 NfIndexTableOffset meOffset
; // SvNumberFormatter format index, if meType = VBA_FORMAT_TYPE_OFFSET
671 const char* mpOOoFormat
; // if meType = VBA_FORMAT_TYPE_USERDEFINED
674 #define VBA_FORMAT_OFFSET( pcUtf8, eOffset ) \
675 { VBA_FORMAT_TYPE_OFFSET, pcUtf8, eOffset, 0 }
677 #define VBA_FORMAT_USERDEFINED( pcUtf8, pcDefinedUtf8 ) \
678 { VBA_FORMAT_TYPE_USERDEFINED, pcUtf8, NF_NUMBER_STANDARD, pcDefinedUtf8 }
680 static VbaFormatInfo pFormatInfoTable
[] =
682 VBA_FORMAT_OFFSET( "Long Date", NF_DATE_SYSTEM_LONG
),
683 VBA_FORMAT_USERDEFINED( "Medium Date", "DD-MMM-YY" ),
684 VBA_FORMAT_OFFSET( "Short Date", NF_DATE_SYSTEM_SHORT
),
685 VBA_FORMAT_USERDEFINED( "Long Time", "H:MM:SS AM/PM" ),
686 VBA_FORMAT_OFFSET( "Medium Time", NF_TIME_HHMMAMPM
),
687 VBA_FORMAT_OFFSET( "Short Time", NF_TIME_HHMM
),
688 VBA_FORMAT_OFFSET( "ddddd", NF_DATE_SYSTEM_SHORT
),
689 VBA_FORMAT_OFFSET( "dddddd", NF_DATE_SYSTEM_LONG
),
690 VBA_FORMAT_USERDEFINED( "ttttt", "H:MM:SS AM/PM" ),
691 VBA_FORMAT_OFFSET( "ww", NF_DATE_WW
),
692 { VBA_FORMAT_TYPE_NULL
, 0, NF_INDEX_TABLE_ENTRIES
, 0 }
695 VbaFormatInfo
* getFormatInfo( const String
& rFmt
)
697 VbaFormatInfo
* pInfo
= NULL
;
699 while( (pInfo
= pFormatInfoTable
+ i
)->mpVbaFormat
!= NULL
)
701 if( rFmt
.EqualsIgnoreCaseAscii( pInfo
->mpVbaFormat
) )
708 #define VBAFORMAT_GENERALDATE "General Date"
709 #define VBAFORMAT_C "c"
710 #define VBAFORMAT_N "n"
711 #define VBAFORMAT_NN "nn"
712 #define VBAFORMAT_W "w"
713 #define VBAFORMAT_Y "y"
714 #define VBAFORMAT_LOWERCASE "<"
715 #define VBAFORMAT_UPPERCASE ">"
718 INT16
implGetWeekDay( double aDate
, bool bFirstDayParam
= false, INT16 nFirstDay
= 0 );
720 INT16
implGetMinute( double dDate
);
721 INT16
implGetDateYear( double aDate
);
722 BOOL
implDateSerial( INT16 nYear
, INT16 nMonth
, INT16 nDay
, double& rdRet
);
724 void SbxValue::Format( XubString
& rRes
, const XubString
* pFmt
) const
729 // pflin, It is better to use SvNumberFormatter to handle the date/time/number format.
730 // the SvNumberFormatter output is mostly compatible with
731 // VBA output besides the OOo-basic output
732 if( pFmt
&& !SbxBasicFormater::isBasicFormat( *pFmt
) )
734 String aStr
= GetString();
736 if( pFmt
->EqualsIgnoreCaseAscii( VBAFORMAT_LOWERCASE
) )
738 rRes
= aStr
.ToLowerAscii();
741 if( pFmt
->EqualsIgnoreCaseAscii( VBAFORMAT_UPPERCASE
) )
743 rRes
= aStr
.ToUpperAscii();
747 LanguageType eLangType
= GetpApp()->GetSettings().GetLanguage();
748 com::sun::star::uno::Reference
< com::sun::star::lang::XMultiServiceFactory
>
749 xFactory
= comphelper::getProcessServiceFactory();
750 SvNumberFormatter
aFormatter( xFactory
, eLangType
);
753 xub_StrLen nCheckPos
= 0;
758 BOOL bSuccess
= aFormatter
.IsNumberFormat( aStr
, nIndex
, nNumber
);
760 // number format, use SvNumberFormatter to handle it.
763 String aFmtStr
= *pFmt
;
764 VbaFormatInfo
* pInfo
= getFormatInfo( aFmtStr
);
765 if( pInfo
&& pInfo
->meType
!= VBA_FORMAT_TYPE_NULL
)
767 if( pInfo
->meType
== VBA_FORMAT_TYPE_OFFSET
)
769 nIndex
= aFormatter
.GetFormatIndex( pInfo
->meOffset
, eLangType
);
773 aFmtStr
.AssignAscii( pInfo
->mpOOoFormat
);
774 aFormatter
.PutandConvertEntry( aFmtStr
, nCheckPos
, nType
, nIndex
, LANGUAGE_ENGLISH
, eLangType
);
776 aFormatter
.GetOutputString( nNumber
, nIndex
, rRes
, &pCol
);
778 else if( aFmtStr
.EqualsIgnoreCaseAscii( VBAFORMAT_GENERALDATE
)
779 || aFmtStr
.EqualsIgnoreCaseAscii( VBAFORMAT_C
))
781 if( nNumber
<=-1.0 || nNumber
>= 1.0 )
784 nIndex
= aFormatter
.GetFormatIndex( NF_DATE_SYSTEM_SHORT
, eLangType
);
785 aFormatter
.GetOutputString( nNumber
, nIndex
, rRes
, &pCol
);
788 if( floor( nNumber
) != nNumber
)
790 aFmtStr
.AssignAscii( "H:MM:SS AM/PM" );
791 aFormatter
.PutandConvertEntry( aFmtStr
, nCheckPos
, nType
, nIndex
, LANGUAGE_ENGLISH
, eLangType
);
793 aFormatter
.GetOutputString( nNumber
, nIndex
, aTime
, &pCol
);
794 rRes
.AppendAscii(" ");
801 aFmtStr
.AssignAscii( "H:MM:SS AM/PM" );
802 aFormatter
.PutandConvertEntry( aFmtStr
, nCheckPos
, nType
, nIndex
, LANGUAGE_ENGLISH
, eLangType
);
803 aFormatter
.GetOutputString( nNumber
, nIndex
, rRes
, &pCol
);
806 else if( aFmtStr
.EqualsIgnoreCaseAscii( VBAFORMAT_N
)
807 || aFmtStr
.EqualsIgnoreCaseAscii( VBAFORMAT_NN
))
809 INT32 nMin
= implGetMinute( nNumber
);
810 if( nMin
< 10 && aFmtStr
.EqualsIgnoreCaseAscii( VBAFORMAT_NN
) )
812 // Minute in two digits
813 sal_Unicode
* p
= rRes
.AllocBuffer( 2 );
815 *p
= sal_Unicode( '0' + nMin
);
819 rRes
= String::CreateFromInt32( nMin
);
822 else if( aFmtStr
.EqualsIgnoreCaseAscii( VBAFORMAT_W
))
824 INT32 nWeekDay
= implGetWeekDay( nNumber
);
825 rRes
= String::CreateFromInt32( nWeekDay
);
827 else if( aFmtStr
.EqualsIgnoreCaseAscii( VBAFORMAT_Y
))
829 INT16 nYear
= implGetDateYear( nNumber
);
831 implDateSerial( nYear
, 1, 1, dBaseDate
);
832 INT32 nYear32
= 1 + INT32( nNumber
- dBaseDate
);
833 rRes
= String::CreateFromInt32( nYear32
);
837 aFormatter
.PutandConvertEntry( aFmtStr
, nCheckPos
, nType
, nIndex
, LANGUAGE_ENGLISH
, eLangType
);
838 aFormatter
.GetOutputString( nNumber
, nIndex
, rRes
, &pCol
);
845 SbxDataType eType
= GetType();
856 case SbxNULL
: // #45929 NULL mit durchschummeln
857 nComma
= 0; goto cvt
;
859 nComma
= 6; goto cvt
;
864 if( eType
!= SbxNULL
)
867 // #45355 weiterer Einsprungpunkt fuer isnumeric-String
871 // hole die 'statischen' Daten f"ur Sbx
872 SbxAppData
* pData
= GetSbxData_Impl();
874 LanguageType eLangType
= GetpApp()->GetSettings().GetLanguage();
875 if( pData
->pBasicFormater
)
877 if( pData
->eBasicFormaterLangType
!= eLangType
)
879 delete pData
->pBasicFormater
;
880 pData
->pBasicFormater
= NULL
;
883 pData
->eBasicFormaterLangType
= eLangType
;
885 // falls bisher noch kein BasicFormater-Objekt
886 // existiert, so erzeuge dieses
887 if( !pData
->pBasicFormater
)
889 SvtSysLocale aSysLocale
;
890 const LocaleDataWrapper
& rData
= aSysLocale
.GetLocaleData();
891 sal_Unicode cComma
= rData
.getNumDecimalSep().GetBuffer()[0];
892 sal_Unicode c1000
= rData
.getNumThousandSep().GetBuffer()[0];
893 String aCurrencyStrg
= rData
.getCurrSymbol();
895 // Initialisierung des Basic-Formater-Hilfsobjekts:
896 // hole die Resourcen f"ur die vordefinierten Ausgaben
897 // des Format()-Befehls, z.B. f"ur "On/Off".
898 String aOnStrg
= String( SbxValueFormatResId(
899 STR_BASICKEY_FORMAT_ON
) );
900 String aOffStrg
= String( SbxValueFormatResId(
901 STR_BASICKEY_FORMAT_OFF
) );
902 String aYesStrg
= String( SbxValueFormatResId(
903 STR_BASICKEY_FORMAT_YES
) );
904 String aNoStrg
= String( SbxValueFormatResId(
905 STR_BASICKEY_FORMAT_NO
) );
906 String aTrueStrg
= String( SbxValueFormatResId(
907 STR_BASICKEY_FORMAT_TRUE
) );
908 String aFalseStrg
= String( SbxValueFormatResId(
909 STR_BASICKEY_FORMAT_FALSE
) );
910 String aCurrencyFormatStrg
= String( SbxValueFormatResId(
911 STR_BASICKEY_FORMAT_CURRENCY
) );
912 // erzeuge das Basic-Formater-Objekt
913 pData
->pBasicFormater
914 = new SbxBasicFormater( cComma
,c1000
,aOnStrg
,aOffStrg
,
915 aYesStrg
,aNoStrg
,aTrueStrg
,aFalseStrg
,
916 aCurrencyStrg
,aCurrencyFormatStrg
);
918 // Bem.: Aus Performance-Gr"unden wird nur EIN BasicFormater-
919 // Objekt erzeugt und 'gespeichert', dadurch erspart man
920 // sich das teure Resourcen-Laden (f"ur landesspezifische
921 // vordefinierte Ausgaben, z.B. "On/Off") und die st"andige
922 // String-Erzeugungs Operationen.
923 // ABER: dadurch ist dieser Code NICHT multithreading f"ahig !
925 // hier gibt es Probleme mit ;;;Null, da diese Methode nur aufgerufen
926 // wird, wenn der SbxValue eine Zahl ist !!!
927 // dazu koennte: pData->pBasicFormater->BasicFormatNull( *pFmt ); aufgerufen werden !
928 if( eType
!= SbxNULL
)
930 rRes
= pData
->pBasicFormater
->BasicFormat( d
,*pFmt
);
934 rRes
= pData
->pBasicFormater
->BasicFormatNull( *pFmt
);
937 // Die alte Implementierung:
938 //old: printfmtnum( GetDouble(), rRes, *pFmt );
941 ImpCvtNum( GetDouble(), nComma
, rRes
);
946 // #45355 wenn es numerisch ist, muss gewandelt werden
949 ScanNumIntnl( GetString(), d
, /*bSingle*/FALSE
);
954 // Sonst String-Formatierung
955 printfmtstr( GetString(), rRes
, *pFmt
);