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: sbxform.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"
36 #include <basic/sbxform.hxx>
39 TODO: gibt es noch irgend welche Star-Basic Besonderheiten ?
41 was bedeutet: * als Platzhalter
43 BEMERKUNG: Visual-Basic behandelt folgende (ung"ultige) Format-Strings
46 ##0##.##0## --> ##000.000##
48 (diese Klasse verh"alt sich genau so).
51 #include <stdio.h> // f"ur: sprintf()
52 #include <float.h> // f"ur: DBL_DIG, DBL_EPSILON
53 #include <math.h> // f"ur: floor(), fabs(), log10(), pow()
55 //=================================================================
56 //=========================== DEFINES =============================
57 //=================================================================
61 #define MAX_NO_OF_EXP_DIGITS 5
62 // +4 wegen dem Wertebereich: zwischen -308 und +308
63 // +1 f"ur abschliessende 0
64 #define MAX_NO_OF_DIGITS DBL_DIG
65 #define MAX_DOUBLE_BUFFER_LENGTH MAX_NO_OF_DIGITS + 9
67 // +1 f"ur Ziffer vor dem Dezimal-Punkt
68 // +1 f"ur Dezimal-Punkt
69 // +2 f"ur Exponent E und Exp. Vorzeichen
70 // +3 f"ur den Wert des Exponenten
71 // +1 f"ur abschliessende 0
73 // Defines f"ur die Ziffern:
74 #define ASCII_0 '0' // 48
75 #define ASCII_9 '9' // 57
77 #define CREATE_1000SEP_CHAR '@'
79 #define FORMAT_SEPARATOR ';'
81 // vordefinierte Formate f"ur den Format$()-Befehl:
82 #define BASICFORMAT_GENERALNUMBER "General Number"
83 #define BASICFORMAT_CURRENCY "Currency"
84 #define BASICFORMAT_FIXED "Fixed"
85 #define BASICFORMAT_STANDARD "Standard"
86 #define BASICFORMAT_PERCENT "Percent"
87 #define BASICFORMAT_SCIENTIFIC "Scientific"
88 #define BASICFORMAT_YESNO "Yes/No"
89 #define BASICFORMAT_TRUEFALSE "True/False"
90 #define BASICFORMAT_ONOFF "On/Off"
92 #define EMPTYFORMATSTRING ""
94 // Bem.: Visual-Basic hat bei Floating-Point-Zahlen maximal 12 Stellen
95 // nach dem Dezimal-Punkt.
96 // Alle Format-Strings sind kompatibel zu Visual-Basic:
97 #define GENERALNUMBER_FORMAT "0.############"
98 // max. 12 Stellen in Visual-Basic !
99 #define CURRENCY_FORMAT "@$0.00;@($0.00)"
100 #define FIXED_FORMAT "0.00"
101 #define STANDARD_FORMAT "@0.00"
102 #define PERCENT_FORMAT "0.00%"
103 #define SCIENTIFIC_FORMAT "#.00E+00"
104 // BEMERKUNG: das Zeichen @ bedeutet, das Tausender-Separatoren erzeugt
105 // weden sollen. Dies ist eine StarBasic 'Erweiterung'.
107 //=================================================================
109 // zur Bestimmung der Anzahl Stellen in dNumber
110 double get_number_of_digits( double dNumber
)
111 //double floor_log10_fabs( double dNumber )
114 // 0 hat zumindest auch eine Stelle !
115 return 0.0; //ehemals 1.0, jetzt 0.0 wegen #40025;
117 return floor( log10( fabs( dNumber
) ) );
120 //=================================================================
121 //======================= IMPLEMENTATION ==========================
122 //=================================================================
124 SbxBasicFormater::SbxBasicFormater( sal_Unicode _cDecPoint
, sal_Unicode _cThousandSep
,
131 String _sCurrencyStrg
,
132 String _sCurrencyFormatStrg
)
134 cDecPoint
= _cDecPoint
;
135 cThousandSep
= _cThousandSep
;
137 sOffStrg
= _sOffStrg
;
138 sYesStrg
= _sYesStrg
;
140 sTrueStrg
= _sTrueStrg
;
141 sFalseStrg
= _sFalseStrg
;
142 sCurrencyStrg
= _sCurrencyStrg
;
143 sCurrencyFormatStrg
= _sCurrencyFormatStrg
;
146 // Funktion zur Ausgabe eines Fehler-Textes (zum Debuggen)
148 void SbxBasicFormater::ShowError( char * sErrMsg )
150 // cout << "ERROR in Format$(): " << sErrMsg << endl;
153 // verschiebt alle Zeichen des Strings, angefangen von der nStartPos,
154 // um eine Position zu gr"osseren Indizes, d.h. es wird Platz f"ur
155 // ein neues (einzuf"ugendes) Zeichen geschafft.
156 // ACHTUNG: der String MUSS gross genug sein !
157 inline void SbxBasicFormater::ShiftString( String
& sStrg
, USHORT nStartPos
)
159 sStrg
.Erase( nStartPos
,1 );
162 // Funktion um ein Zeichen an einen String anzuh"angen
163 inline void SbxBasicFormater::StrAppendChar( String
& sStrg
, sal_Unicode ch
)
168 // h"angt die "ubergebene Ziffer nDigit an den "ubergebenen String sStrg
169 // an, dabei wird "uberpr"uft ob nDigit eine g"ultige Ziffer ist,
170 // falls dies nicht der Fall ist, wird nichts gemacht.
171 void SbxBasicFormater::AppendDigit( String
& sStrg
, short nDigit
)
173 if( nDigit
>=0 && nDigit
<=9 )
174 StrAppendChar( sStrg
, (sal_Unicode
)(nDigit
+ASCII_0
) );
177 // verschiebt den Dezimal-Punkt um eine Stelle nach links
178 void SbxBasicFormater::LeftShiftDecimalPoint( String
& sStrg
)
180 USHORT nPos
= sStrg
.Search( cDecPoint
);
182 if( nPos
!=STRING_NOTFOUND
)
184 // vertausche Dezimal-Punkt
185 sStrg
.SetChar( nPos
, sStrg
.GetChar( nPos
- 1 ) );
186 sStrg
.SetChar( nPos
-1, cDecPoint
);
190 // rundet in einem String die Ziffer an der angegebenen Stelle,
191 // es wird ein Flag zur"uckgeliefert, falls ein Overflow auftrat,
192 // d.h. 99.99 --> 100.00, d.h. ein Gr"ossenordung ge"andert wurde
193 // (geschieht beim Runden einer 9).
194 void SbxBasicFormater::StrRoundDigit( String
& sStrg
, short nPos
, BOOL
& bOverflow
)
196 // wurde ggf ein falscher Index uebergeben --> Aufruf ignorieren
201 // "uberspringe den Dezimalpunkt und Tausender-Trennzeichen
202 sal_Unicode c
= sStrg
.GetChar( nPos
);
203 if( nPos
>0 && (c
== cDecPoint
|| c
== cThousandSep
) )
205 StrRoundDigit( sStrg
,nPos
-1,bOverflow
);
206 // AENDERUNG ab 9.3.1997: nach rekursivem Call die Methode SOFORT beenden !
209 // "uberspringe alle nicht-Ziffern:
211 // in einem g"ultigen Format-String sollte die Ausgabe
212 // der Zahl an einem St"uck geschen, d.h. Sonderzeichen sollten
213 // NUR vor ODER nach der Zahl stehen und nicht mitten in der
214 // Format-Angabe f"ur die Zahl
215 while( nPos
>=0 && (sStrg
.GetChar( nPos
)<ASCII_0
|| sStrg
.GetChar( nPos
)>ASCII_9
) )
217 // muss ggf. noch Platz f"ur eine weitere (f"uhrende) Ziffer
218 // geschaffen werden ?
221 ShiftString( sStrg
,0 );
222 // f"uhrende 1 einf"ugen: z.B. 99.99 f"ur 0.0
223 sStrg
.SetChar( 0, '1' );
228 // ist die zu rundende Position eine Ziffer ?
229 sal_Unicode c2
= sStrg
.GetChar( nPos
);
230 if( c2
>= ASCII_0
&& c2
<= ASCII_9
)
232 // muss eine 9 gerundet werden? Falls: Ja --> rekursiver Aufruf
235 sStrg
.SetChar( nPos
, '0' );
236 StrRoundDigit( sStrg
,nPos
-1,bOverflow
);
239 sStrg
.SetChar( nPos
, c2
+1 );
243 // --> Nein, d.h. Platz f"ur Ziffer schaffen: z.B. -99.99 f"ur #0.0
244 // da gerundet wird MUSS es immer eine g"ultige Position
246 ShiftString( sStrg
,nPos
+1 );
247 // f"uhrende 1 einf"ugen
248 sStrg
.SetChar( nPos
+1, '1' );
254 // rundet in einem String die Ziffer an der angegebenen Stelle
255 void SbxBasicFormater::StrRoundDigit( String
& sStrg
, short nPos
)
259 StrRoundDigit( sStrg
,nPos
,bOverflow
);
262 // parse den Formatstring von der "ubergebenen Position zur"uck
263 // und l"osche ggf. "uberf"ussige 0en, z.B. 4.50 in 0.0#
264 void SbxBasicFormater::ParseBack( String
& sStrg
, const String
& sFormatStrg
,
267 // WICHTIG: nFormatPos kann auch negativ sein, in diesem Fall Aufruf ignorieren
268 for( short i
=nFormatPos
;
269 i
>0 && sFormatStrg
.GetChar( i
) == '#' && sStrg
.GetChar( (sStrg
.Len()-1) ) == '0';
271 { sStrg
.Erase( sStrg
.Len()-1 ); }
278 Zahl wird mit maximaler (sinnvollen) Genauigkeit in einen String
279 umgewandelt (mit sprintf()), dieser String wird dann im Schleifen-
280 Durchlauf nach der entsprechenden Ziffer durchsucht.
282 // initialisiert die Daten der Klasse um einen Scan-Durchlauf durchzuf"uhren
283 void SbxBasicFormater::InitScan( double _dNum
)
285 char sBuffer
[ MAX_DOUBLE_BUFFER_LENGTH
];
288 InitExp( get_number_of_digits( dNum
) );
289 // maximal 15 Nachkomma-Stellen, Format-Beispiel: -1.234000000000000E-001
290 /*int nCount =*/ sprintf( sBuffer
,"%+22.15lE",dNum
);
291 sSciNumStrg
.AssignAscii( sBuffer
);
294 void SbxBasicFormater::InitExp( double _dNewExp
)
296 char sBuffer
[ MAX_DOUBLE_BUFFER_LENGTH
];
297 // bestimme den Exponenten (kann immer GENAU durch int dargestellt werden)
298 nNumExp
= (short)_dNewExp
;
300 /*int nCount =*/ sprintf( sBuffer
,"%+i",nNumExp
);
301 sNumExpStrg
.AssignAscii( sBuffer
);
302 // bestimme die Anzahl der Stellen im Exponenten
303 nExpExp
= (short)get_number_of_digits( (double)nNumExp
);
306 // bestimmt die Ziffer an der angegebenen Stelle (gedacht zur Anwendung im
308 short SbxBasicFormater::GetDigitAtPosScan( short nPos
, BOOL
& bFoundFirstDigit
)
310 // Versuch eine gr"ossere Ziffer zu lesen,
311 // z.B. Stelle 4 in 1.234,
312 // oder eine Ziffer ausserhalb der Aufl"osung der
313 // Zahl (double) zu lesen (z.B. max. 15 Stellen).
314 if( nPos
>nNumExp
|| abs(nNumExp
-nPos
)>MAX_NO_OF_DIGITS
)
316 // bestimme den Index der Stelle in dem Number-String:
317 // "uberlese das Vorzeichen
319 // falls notwendig den Dezimal-Punkt "uberlesen:
323 // Abfrage der ersten (g"ultigen) Ziffer der Zahl --> Flag setzen
325 bFoundFirstDigit
= TRUE
;
326 return (short)(sSciNumStrg
.GetChar( no
) - ASCII_0
);
329 short SbxBasicFormater::GetDigitAtPosExpScan( short nPos
, BOOL
& bFoundFirstDigit
)
331 // ist die abgefragte Stelle zu gross f"ur den Exponenten ?
335 // bestimme den Index der Stelle in dem Number-String:
336 // "uberlese das Vorzeichen
339 // Abfrage der ersten (g"ultigen) Ziffer der Zahl --> Flag setzen
341 bFoundFirstDigit
= TRUE
;
342 return (short)(sNumExpStrg
.GetChar( no
) - ASCII_0
);
345 // es kann ein Wert f"ur den Exponent angegeben werden, da ggf. die
346 // Zahl ggf. NICHT normiert (z.B. 1.2345e-03) dargestellt werden soll,
347 // sondern eventuell 123.345e-3 !
348 short SbxBasicFormater::GetDigitAtPosExpScan( double dNewExponent
, short nPos
,
349 BOOL
& bFoundFirstDigit
)
351 // neuer Exponent wurde "ubergeben, aktualisiere
352 // die tempor"aren Klassen-Variablen
353 InitExp( dNewExponent
);
354 // und jetzt die Stelle bestimmen
355 return GetDigitAtPosExpScan( nPos
,bFoundFirstDigit
);
360 /* Probleme mit der folgenden Methode:
362 TODO: ggf einen 'intelligenten' Peek-Parser um Rundungsfehler bei
363 double-Zahlen herauszufinden ? z.B. f"ur 0.00115 #.#e-000
365 Problem mit: format( 0.3345 , "0.000" )
366 Problem mit: format( 0.00115 , "0.0000" )
369 // liefert die Ziffer an der angegebenen '10er System'-Position,
370 // d.h. positive nPos f"ur Stellen vor dem Komma und negative
371 // f"ur Stellen nach dem Komma.
372 // nPos==0 bedeutet erste Stelle vor dem Komma, also 10^0.
373 // liefert 0..9 f"ur g"ultige Ziffern und -1 f"ur nicht vorhanden,
374 // d.h. falls die "ubergebene Zahl zu klein ist
375 // (z.B. Stelle 5 bei dNumber=123).
376 // Weiter wird in dNextNumber die um die f"uhrenden Stellen
377 // (bis nPos) gek"urzte Zahl zur"uckgeliefert, z.B.
378 // GetDigitAtPos( 3434.565 , 2 , dNewNumber ) --> dNewNumber = 434.565
379 // dies kann f"ur Schleifenabarbeitung g"unstiger sein, d.h.
380 // die Zahlen immer von der gr"ossten Stelle abarbeiten/scanen.
381 // In bFoundFirstDigit wird ggf. ein Flag gesetzt wenn eine Ziffer
382 // gefunden wurde, dies wird dazu verwendet um 'Fehler' beim Parsen 202
385 // ACHTUNG: anscheinend gibt es manchmal noch Probleme mit Rundungs-Fehlern!
386 short SbxBasicFormater::GetDigitAtPos( double dNumber
, short nPos
,
387 double& dNextNumber
, BOOL
& bFoundFirstDigit
)
388 // ACHTUNG: nPos kann auch negativ werden, f"ur Stellen nach dem Dezimal-Punkt
390 double dTemp
= dNumber
;
394 // erst mal aus der Zahl eine positive Zahl machen:
395 dNumber
= fabs( dNumber
);
398 // "uberpr"ufe ob Zahl zu klein f"ur angegebene Stelle ist
399 nMaxDigit
= (short)get_number_of_digits( dNumber
);
400 // f"uhrende Ziffern 'l"oschen'
401 // Bem.: Fehler nur bei Zahlen gr"osser 0, d.h. bei Ziffern vor dem
403 if( nMaxDigit
<nPos
&& !bFoundFirstDigit
&& nPos
>=0 )
405 // Ziffer gefunden, setze Flag:
406 bFoundFirstDigit
= TRUE
;
407 for( short i
=nMaxDigit
; i
>=nPos
; i
-- )
409 double dI
= (double)i
;
410 double dTemp1
= pow( 10.0,dI
);
411 // pr"apariere nun die gesuchte Ziffer:
412 dDigit
= floor( pow( 10.0,log10( fabs( dNumber
) )-dI
) );
413 dNumber
-= dTemp1
* dDigit
;
415 // Zuweisung f"ur optimierte Schleifen-Durchl"aufe
416 dNextNumber
= dNumber
;
417 // und zum Schluss noch die float-Rundungsungenauigkeiten heraus filtern
418 return RoundDigit( dDigit
);
421 // rundet eine double-Zahl zwischen 0 und 9 auf die genaue
422 // Integer-Zahl, z.B. 2.8 -> 3 und 2.2 -> 2
423 short SbxBasicFormater::RoundDigit( double dNumber
)
425 // ist der Wertebereich g"ultig ?
426 if( dNumber
<0.0 || dNumber
>10.0 )
428 short nTempHigh
= (short)(dNumber
+0.5); // ggf. floor( )
434 // kopiert den entsprechenden Teil des Format-Strings, falls vorhanden,
435 // und liefert diesen zur"uck.
436 // Somit wird ein neuer String erzeugt, der vom Aufrufer wieder freigegeben
438 String
SbxBasicFormater::GetPosFormatString( const String
& sFormatStrg
, BOOL
& bFound
)
440 bFound
= FALSE
; // default...
441 USHORT nPos
= sFormatStrg
.Search( FORMAT_SEPARATOR
);
443 if( nPos
!=STRING_NOTFOUND
)
446 // der Format-String f"ur die positiven Zahlen ist alles
447 // vor dem ersten ';'
448 return sFormatStrg
.Copy( 0,nPos
);
450 // kein ; gefunden, liefere Leerstring
452 aRetStr
.AssignAscii( EMPTYFORMATSTRING
);
456 // siehe auch GetPosFormatString()
457 String
SbxBasicFormater::GetNegFormatString( const String
& sFormatStrg
, BOOL
& bFound
)
459 bFound
= FALSE
; // default...
460 USHORT nPos
= sFormatStrg
.Search( FORMAT_SEPARATOR
);
462 if( nPos
!=STRING_NOTFOUND
)
464 // der Format-String f"ur die negative Zahlen ist alles
465 // zwischen dem ersten und dem zweiten ';'.
466 // Daher: hole erst mal alles nach dem ersten ';'
467 String sTempStrg
= sFormatStrg
.Copy( nPos
+1 );
468 // und suche darin ggf. ein weiteres ';'
469 nPos
= sTempStrg
.Search( FORMAT_SEPARATOR
);
471 if( nPos
==STRING_NOTFOUND
)
472 // keins gefunden, liefere alles...
475 // ansonsten den String zwischen den beiden ';' liefern
476 return sTempStrg
.Copy( 0,nPos
);
479 aRetStr
.AssignAscii( EMPTYFORMATSTRING
);
483 // siehe auch GetPosFormatString()
484 String
SbxBasicFormater::Get0FormatString( const String
& sFormatStrg
, BOOL
& bFound
)
486 bFound
= FALSE
; // default...
487 USHORT nPos
= sFormatStrg
.Search( FORMAT_SEPARATOR
);
489 if( nPos
!=STRING_NOTFOUND
)
491 // der Format-String f"ur die Null ist alles
492 // was nach dem zweiten ';' kommt.
493 // Daher: hole erst mal alles nach dem ersten ';'
494 String sTempStrg
= sFormatStrg
.Copy( nPos
+1 );
495 // und suche darin ggf. ein weiteres ';'
496 nPos
= sTempStrg
.Search( FORMAT_SEPARATOR
);
497 if( nPos
!=STRING_NOTFOUND
)
500 sTempStrg
= sTempStrg
.Copy( nPos
+1 );
501 nPos
= sTempStrg
.Search( FORMAT_SEPARATOR
);
502 if( nPos
==STRING_NOTFOUND
)
503 // keins gefunden, liefere alles...
506 return sTempStrg
.Copy( 0,nPos
);
509 // kein ; gefunden, liefere Leerstring
511 aRetStr
.AssignAscii( EMPTYFORMATSTRING
);
515 // siehe auch GetPosFormatString()
516 String
SbxBasicFormater::GetNullFormatString( const String
& sFormatStrg
, BOOL
& bFound
)
518 bFound
= FALSE
; // default...
519 USHORT nPos
= sFormatStrg
.Search( FORMAT_SEPARATOR
);
521 if( nPos
!=STRING_NOTFOUND
)
523 // der Format-String f"ur die Null ist alles
524 // was nach dem dritten ';' kommt.
525 // Daher: hole erst mal alles nach dem ersten ';'
526 String sTempStrg
= sFormatStrg
.Copy( nPos
+1 );
527 // und suche darin ggf. ein weiteres ';'
528 nPos
= sTempStrg
.Search( FORMAT_SEPARATOR
);
529 if( nPos
!=STRING_NOTFOUND
)
531 // und suche nun nach dem dritten ';'
532 sTempStrg
= sTempStrg
.Copy( nPos
+1 );
533 nPos
= sTempStrg
.Search( FORMAT_SEPARATOR
);
534 if( nPos
!=STRING_NOTFOUND
)
537 return sTempStrg
.Copy( nPos
+1 );
541 // kein ; gefunden, liefere Leerstring
543 aRetStr
.AssignAscii( EMPTYFORMATSTRING
);
547 // analysiert den Format-String, liefert Wert <> 0 falls ein Fehler
549 short SbxBasicFormater::AnalyseFormatString( const String
& sFormatStrg
,
550 short& nNoOfDigitsLeft
, short& nNoOfDigitsRight
,
551 short& nNoOfOptionalDigitsLeft
,
552 short& nNoOfExponentDigits
, short& nNoOfOptionalExponentDigits
,
553 BOOL
& bPercent
, BOOL
& bCurrency
, BOOL
& bScientific
,
554 BOOL
& bGenerateThousandSeparator
,
555 short& nMultipleThousandSeparators
)
560 nLen
= sFormatStrg
.Len();
561 // initialisiere alle Z"ahler und Flags
563 nNoOfDigitsRight
= 0;
564 nNoOfOptionalDigitsLeft
= 0;
565 nNoOfExponentDigits
= 0;
566 nNoOfOptionalExponentDigits
= 0;
570 // ab 11.7.97: sobald ein Komma in dem Format String gefunden wird,
571 // werden alle 3 Zehnerpotenzen markiert (d.h. tausender, milionen, ...)
572 // bisher wurde nur an den gesetzten Position ein Tausender-Separator
573 // ausgegeben oder wenn ein @ im Format-String stand.
574 // Dies war ein Missverstaendnis der VB Kompatiblitaet.
575 bGenerateThousandSeparator
= sFormatStrg
.Search( ',' ) != STRING_NOTFOUND
;
576 nMultipleThousandSeparators
= 0;
577 // und untersuche den Format-String nach den gew"unschten Informationen
578 for( USHORT i
=0; i
<nLen
; i
++ )
580 sal_Unicode c
= sFormatStrg
.GetChar( i
);
587 // TODO hier ggf. bessere Fehler-"Uberpr"ufung der Mantisse auf g"ultige Syntax (siehe Grammatik)
588 // ACHTUNG: 'undefiniertes' Verhalten falls # und 0
589 // gemischt werden !!!
590 // BEMERKUNG: eigentlich sind #-Platzhalter bei Scientific
591 // Darstellung vor dem Dezimal-Punkt sinnlos !
593 nNoOfOptionalDigitsLeft
++;
597 else if( nState
==-1 ) // suche 0 im Exponent
599 if( c
=='#' ) // # schaltet den Zustand weiter
601 nNoOfOptionalExponentDigits
++;
604 nNoOfExponentDigits
++;
606 else if( nState
==-2 ) // suche # im Exponent
609 // ERROR: 0 nach # im Exponent ist NICHT erlaubt !!
611 nNoOfOptionalExponentDigits
++;
612 nNoOfExponentDigits
++;
618 return -1; // ERROR: zu viele Dezimal-Punkte
625 return -2; // ERROR: zu viele Prozent-Zeichen
633 sal_Unicode ch
= sFormatStrg
.GetChar( i
+1 );
634 // vorl"aufig wird NUR auf zwei aufeinanderfolgede
636 if( ch
!=0 && (ch
==',' || ch
=='.') )
637 nMultipleThousandSeparators
++;
641 // #i13821 not when no digits before
642 if( nNoOfDigitsLeft
> 0 || nNoOfDigitsRight
> 0 )
644 nState
= -1; // breche jetzt das Z"ahlen der Stellen ab
650 return -3; // ERROR: zu viele Exponent-Zeichen
653 // EIGENES Kommando-Zeichen, das die Erzeugung der
654 // Tausender-Trennzeichen einschaltet
659 case CREATE_1000SEP_CHAR
:
660 bGenerateThousandSeparator
= TRUE
;
667 // das Flag bCreateSign zeigt an, dass bei der Mantisse ein Vorzeichen
668 // erzeugt werden soll
669 void SbxBasicFormater::ScanFormatString( double dNumber
,
670 const String
& sFormatStrg
, String
& sReturnStrg
,
673 short /*nErr,*/nNoOfDigitsLeft
,nNoOfDigitsRight
,nNoOfOptionalDigitsLeft
,
674 nNoOfExponentDigits
,nNoOfOptionalExponentDigits
,
675 nMultipleThousandSeparators
;
676 BOOL bPercent
,bCurrency
,bScientific
,bGenerateThousandSeparator
;
678 // Initialisiere den Return-String
679 sReturnStrg
= String();
681 // analysiere den Format-String, d.h. bestimme folgende Werte:
683 - Anzahl der Ziffern vor dem Komma
684 - Anzahl der Ziffern nach dem Komma
685 - optionale Ziffern vor dem Komma
686 - Anzahl der Ziffern im Exponent
687 - optionale Ziffern im Exponent
688 - Prozent-Zeichen gefunden ?
689 - () f"ur negatives Vorzeichen ?
690 - Exponetial-Schreibweise ?
691 - sollen Tausender-Separatoren erzeugt werden ?
692 - wird ein Prozent-Zeichen gefunden ? --> dNumber *= 100.0;
693 - gibt es aufeinanderfolgende Tausender-Trennzeichen ?
694 ,, oder ,. --> dNumber /= 1000.0;
695 - sonstige Fehler ? mehrfache Dezimalpunkte, E's, etc.
696 --> Fehler werden zur Zeit einfach ignoriert
698 /*nErr =*/ AnalyseFormatString( sFormatStrg
,nNoOfDigitsLeft
,nNoOfDigitsRight
,
699 nNoOfOptionalDigitsLeft
,nNoOfExponentDigits
,
700 nNoOfOptionalExponentDigits
,
701 bPercent
,bCurrency
,bScientific
,bGenerateThousandSeparator
,
702 nMultipleThousandSeparators
);
703 /* es werden alle Fehler ignoriert, wie in Visual-Basic
708 //sprintf( sBuffer,"bad format-string >%s< err=%i",sFormatStrg,nErr );
709 strcpy( sBuffer,"bad format-string" );
710 ShowError( sBuffer );
715 // Spezialbehandlung f"ur Spezialzeichen
718 // TODO: diese Vorgabe (,, oder ,.) ist NICHT Visual-Basic kompatibel !
719 // Frage: soll das hier stehen bleiben (Anforderungen) ?
720 if( nMultipleThousandSeparators
)
723 // einige Arbeits-Variablen
726 short nState
,nDigitPos
,nExponentPos
,nMaxDigit
,nMaxExponentDigit
;
727 BOOL bFirstDigit
,bFirstExponentDigit
,bFoundFirstDigit
,
728 bIsNegative
,bZeroSpaceOn
, bSignHappend
,bDigitPosNegative
;
730 // Initialisierung der Arbeits-Variablen
731 bSignHappend
= FALSE
;
732 bFoundFirstDigit
= FALSE
;
733 bIsNegative
= dNumber
<0.0;
734 nLen
= sFormatStrg
.Len();
735 dExponent
= get_number_of_digits( dNumber
);
737 nMaxExponentDigit
= 0;
738 nMaxDigit
= (short)dExponent
;
739 bDigitPosNegative
= false;
742 //if( nNoOfOptionalDigitsLeft>0 )
743 // ShowError( "# in scientific-format in front of the decimal-point has no effect" );
744 // beim Exponent ggf. "uberz"ahlige Stellen vor dem Komma abziehen
745 dExponent
= dExponent
- (double)(nNoOfDigitsLeft
-1);
746 nDigitPos
= nMaxDigit
;
747 nMaxExponentDigit
= (short)get_number_of_digits( dExponent
);
748 nExponentPos
= nNoOfExponentDigits
-1 - nNoOfOptionalExponentDigits
;
752 nDigitPos
= nNoOfDigitsLeft
-1; // Z"ahlweise f"angt bei 0 an, 10^0
753 // hier ben"otigt man keine Exponent-Daten !
754 bDigitPosNegative
= (nDigitPos
< 0);
757 bFirstExponentDigit
= TRUE
;
758 nState
= 0; // 0 --> Mantisse; 1 --> Exponent
765 // scanne jetzt den Format-String:
766 sal_Unicode cForce
= 0;
767 for( i
=0; i
<nLen
; i
++ )
777 c
= sFormatStrg
.GetChar( i
);
784 // Behandlung der Mantisse
787 //org:bFirstDigit = FALSE;
788 // ggf. Vorzeichen erzeugen
789 // Bem.: bei bCurrency soll das negative
790 // Vorzeichen durch () angezeigt werden
791 if( bIsNegative
&& !bCreateSign
/*!bCurrency*/ && !bSignHappend
)
793 // nur einmal ein Vorzeichen ausgeben
795 StrAppendChar( sReturnStrg
,'-' );
797 // hier jetzt "uberz"ahlige Stellen ausgeben,
798 // d.h. vom Format-String nicht erfasste Stellen
799 if( nMaxDigit
>nDigitPos
)
801 for( short j
=nMaxDigit
; j
>nDigitPos
; j
-- )
805 AppendDigit( sReturnStrg
,nTempDigit
= GetDigitAtPosScan( j
,bFoundFirstDigit
) );
807 AppendDigit( sReturnStrg
,nTempDigit
= GetDigitAtPos( dNumber
,j
,dNumber
,bFoundFirstDigit
) );
809 // wurde wirklich eine Ziffer eingefuegt ?
810 if( nTempDigit
!=_NO_DIGIT
)
811 // jetzt wurde wirklich eine Ziffer ausgegeben, Flag setzen
813 // muss ggf. ein Tausender-Trennzeichen erzeugt werden?
814 if( bGenerateThousandSeparator
&& ( c
=='0' || nMaxDigit
>=nDigitPos
) && j
>0 && (j
% 3 == 0) )
815 StrAppendChar( sReturnStrg
,cThousandSep
);
819 // muss f"ur eine leere Stelle eventuell eine 0 ausgegeben werden ?
820 if( nMaxDigit
<nDigitPos
&& ( c
=='0' || bZeroSpaceOn
) )
822 AppendDigit( sReturnStrg
,0 ); // Ja
823 // jetzt wurde wirklich eine Ziffer ausgegeben, Flag setzen
826 // BEM.: bei Visual-Basic schaltet die erste 0 f"ur alle
827 // nachfolgenden # (bis zum Dezimal-Punkt) die 0 ein,
828 // dieses Verhalten wird hier mit dem Flag simmuliert.
829 // muss ggf. ein Tausender-Trennzeichen erzeugt werden?
830 if( bGenerateThousandSeparator
&& ( c
=='0' || nMaxDigit
>=nDigitPos
) && nDigitPos
>0 && (nDigitPos
% 3 == 0) )
831 StrAppendChar( sReturnStrg
,cThousandSep
);
837 AppendDigit( sReturnStrg
,nTempDigit
= GetDigitAtPosScan( nDigitPos
,bFoundFirstDigit
) );
839 AppendDigit( sReturnStrg
,nTempDigit
= GetDigitAtPos( dNumber
,nDigitPos
,dNumber
,bFoundFirstDigit
) );
841 // wurde wirklich eine Ziffer eingefuegt ?
842 if( nTempDigit
!=_NO_DIGIT
)
843 // jetzt wurde wirklich eine Ziffer ausgegeben, Flag setzen
845 // muss ggf. ein Tausender-Trennzeichen erzeugt werden?
846 if( bGenerateThousandSeparator
&& ( c
=='0' || nMaxDigit
>=nDigitPos
) && nDigitPos
>0 && (nDigitPos
% 3 == 0) )
847 StrAppendChar( sReturnStrg
,cThousandSep
);
849 // und Position aktualisieren
854 // Behandlung des Exponenten
855 if( bFirstExponentDigit
)
857 // Vorzeichen wurde schon bei e/E ausgegeben
858 bFirstExponentDigit
= FALSE
;
859 if( nMaxExponentDigit
>nExponentPos
)
860 // hier jetzt "uberz"ahlige Stellen ausgeben,
861 // d.h. vom Format-String nicht erfasste Stellen
863 for( short j
=nMaxExponentDigit
; j
>nExponentPos
; j
-- )
866 AppendDigit( sReturnStrg
,GetDigitAtPosExpScan( dExponent
,j
,bFoundFirstDigit
) );
868 AppendDigit( sReturnStrg
,GetDigitAtPos( dExponent
,j
,dExponent
,bFoundFirstDigit
) );
873 // muss f"ur eine leere Stelle eventuell eine 0 ausgegeben werden ?
874 if( nMaxExponentDigit
<nExponentPos
&& c
=='0' )
875 AppendDigit( sReturnStrg
,0 ); // Ja
878 AppendDigit( sReturnStrg
,GetDigitAtPosExpScan( dExponent
,nExponentPos
,bFoundFirstDigit
) );
880 AppendDigit( sReturnStrg
,GetDigitAtPos( dExponent
,nExponentPos
,dExponent
,bFoundFirstDigit
) );
886 if( bDigitPosNegative
) // #i13821: If no digits before .
888 bDigitPosNegative
= false;
895 StrAppendChar( sReturnStrg
,cDecPoint
);
898 // ggf. "uberf"ussige 0en l"oschen, z.B. 4.500e4 in 0.0##e-00
899 ParseBack( sReturnStrg
,sFormatStrg
,i
-1 );
900 // gebe Prozent-Zeichen aus
901 sReturnStrg
.Insert('%');
905 // muss Mantisse noch gerundet werden, bevor der Exponent angezeigt wird ?
907 // gibt es ueberhaupt eine Mantisse ?
910 // anscheinend nicht, d.h. ungueltiger Format String, z.B. E000.00
911 // d.h. ignoriere diese e bzw. E Zeichen
912 // ggf. einen Fehler (wie Visual Basic) ausgeben ?
914 // #i13821: VB 6 behaviour
915 StrAppendChar( sReturnStrg
,c
);
919 BOOL bOverflow
= FALSE
;
921 short nNextDigit
= GetDigitAtPosScan( nDigitPos
,bFoundFirstDigit
);
923 short nNextDigit
= GetDigitAtPos( dNumber
,nDigitPos
,dNumber
,bFoundFirstDigit
);
926 StrRoundDigit( sReturnStrg
,sReturnStrg
.Len()-1,bOverflow
);
929 // es wurde eine f"uhrende 9 gerundet, d.h.
930 // verschiebe den Dezimal-Punkt um eine Stelle nach links
931 LeftShiftDecimalPoint( sReturnStrg
);
932 // und l"osche die letzte Ziffer, diese wird
933 // duch die f"uhrende 1 ersetzt:
934 sReturnStrg
.SetChar( sReturnStrg
.Len()-1 , 0 );
935 // der Exponent muss um 1 erh"oht werden,
936 // da der Dezimalpunkt verschoben wurde
939 // ggf. "uberf"ussige 0en l"oschen, z.B. 4.500e4 in 0.0##e-00
940 ParseBack( sReturnStrg
,sFormatStrg
,i
-1 );
942 // "andere Zustand des Scanners
944 // gebe Exponent-Zeichen aus
945 StrAppendChar( sReturnStrg
,c
);
946 // i++; // MANIPULATION der Schleifen-Variable !
947 c
= sFormatStrg
.GetChar( ++i
);
948 // und gebe Vorzeichen / Exponent aus
953 // falls Exponent < 0 gebe - aus
955 StrAppendChar( sReturnStrg
,'-' );
959 // gebe auf jeden Fall das Vorzeichen des Exponenten aus !
961 StrAppendChar( sReturnStrg
,'-' );
963 StrAppendChar( sReturnStrg
,'+' );
966 // ShowError( "operator e/E did not find + or -" );
969 // ShowError( "operator e/E ended with 0" );
972 // ACHTUNG: nur falls Zahl bisher ausgegeben wurde
973 // das Zeichen ausgeben
974 ////--> Siehe Kommentar vom 11.7. in AnalyseFormatString()
975 ////if( !bFirstDigit )
976 //// // gebe Tausender-Trennzeichen aus
977 //// StrAppendChar( sReturnStrg,cThousandSep );
983 // ggf. "uberf"ussige 0en l"oschen, z.B. 4.500e4 in 0.0##e-00
984 ParseBack( sReturnStrg
,sFormatStrg
,i
-1 );
986 StrAppendChar( sReturnStrg
,c
);
989 // den String fuer die Waehrung dranhengen:
990 sReturnStrg
+= sCurrencyStrg
;
995 // ggf. "uberf"ussige 0en l"oschen, z.B. 4.500e4 in 0.0##e-00
996 ParseBack( sReturnStrg
,sFormatStrg
,i
-1 );
997 // gebe das jeweilige Zeichen direkt aus
998 StrAppendChar( sReturnStrg
,c
);
1001 // ggf. "uberf"ussige 0en l"oschen, z.B. 4.500e4 in 0.0##e-00
1002 // falls Sonderzeichen am Ende oder mitten in
1003 // Format-String vorkommen
1004 ParseBack( sReturnStrg
,sFormatStrg
,i
-1 );
1005 // Sonderzeichen gefunden, gebe N"ACHSTES
1006 // Zeichen direkt aus (falls es existiert)
1008 c
= sFormatStrg
.GetChar( ++i
);
1010 StrAppendChar( sReturnStrg
,c
);
1012 // ShowError( "operator \\ ended with 0" );
1014 case CREATE_1000SEP_CHAR
:
1015 // hier ignorieren, Aktion wurde schon in
1016 // AnalyseFormatString durchgef"uhrt
1019 // auch die Zeichen und Ziffern ausgeben (wie in Visual-Basic)
1020 if( ( c
>='a' && c
<='z' ) ||
1021 ( c
>='A' && c
<='Z' ) ||
1022 ( c
>='1' && c
<='9' ) )
1023 StrAppendChar( sReturnStrg
,c
);
1026 // ehemals: ShowError( "bad character in format-string" );
1029 // Format-String wurde vollst"andig gescanned,
1030 // muss die letzte Stelle nun gerundet werden ?
1031 // Dies hier ist jedoch NUR notwendig, falls das
1032 // Zahlenformat NICHT Scientific-Format ist !
1035 #ifdef _with_sprintf
1036 short nNextDigit
= GetDigitAtPosScan( nDigitPos
,bFoundFirstDigit
);
1038 short nNextDigit
= GetDigitAtPos( dNumber
,nDigitPos
,dNumber
,bFoundFirstDigit
);
1041 StrRoundDigit( sReturnStrg
,sReturnStrg
.Len()-1 );
1043 // und ganz zum Schluss:
1044 // ggf. "uberf"ussige 0en l"oschen, z.B. 4.500e4 in 0.0##e-00#,
1045 // ABER nur Stellen nach dem Dezimal-Punkt k"onnen gel"oscht werden
1046 if( nNoOfDigitsRight
>0 )
1047 ParseBack( sReturnStrg
,sFormatStrg
,sFormatStrg
.Len()-1 );
1051 String
SbxBasicFormater::BasicFormatNull( String sFormatStrg
)
1053 BOOL bNullFormatFound
;
1054 String sNullFormatStrg
= GetNullFormatString( sFormatStrg
,bNullFormatFound
);
1056 if( bNullFormatFound
)
1057 return sNullFormatStrg
;
1059 aRetStr
.AssignAscii( "null" );
1063 String
SbxBasicFormater::BasicFormat( double dNumber
, String sFormatStrg
)
1065 BOOL bPosFormatFound
,bNegFormatFound
,b0FormatFound
;
1067 // analysiere Format-String auf vordefinierte Formate:
1068 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_GENERALNUMBER
) )
1069 sFormatStrg
.AssignAscii( GENERALNUMBER_FORMAT
);
1070 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_CURRENCY
) )
1071 sFormatStrg
= sCurrencyFormatStrg
; // old: CURRENCY_FORMAT;
1072 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_FIXED
) )
1073 sFormatStrg
.AssignAscii( FIXED_FORMAT
);
1074 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_STANDARD
) )
1075 sFormatStrg
.AssignAscii( STANDARD_FORMAT
);
1076 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_PERCENT
) )
1077 sFormatStrg
.AssignAscii( PERCENT_FORMAT
);
1078 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_SCIENTIFIC
) )
1079 sFormatStrg
.AssignAscii( SCIENTIFIC_FORMAT
);
1080 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_YESNO
) )
1081 return ( dNumber
==0.0 ) ? sNoStrg
: sYesStrg
;
1082 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_TRUEFALSE
) )
1083 return ( dNumber
==0.0 ) ? sFalseStrg
: sTrueStrg
;
1084 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_ONOFF
) )
1085 return ( dNumber
==0.0 ) ? sOffStrg
: sOnStrg
;
1087 // analysiere Format-String auf ';', d.h. Format-Strings f"ur
1088 // positive-, negative- und 0-Werte
1089 String sPosFormatStrg
= GetPosFormatString( sFormatStrg
, bPosFormatFound
);
1090 String sNegFormatStrg
= GetNegFormatString( sFormatStrg
, bNegFormatFound
);
1091 String s0FormatStrg
= Get0FormatString( sFormatStrg
, b0FormatFound
);
1092 //String sNullFormatStrg = GetNullFormatString( sFormatStrg, bNullFormatFound );
1099 sTempStrg
= sFormatStrg
;
1102 // wurde ggf. Leer-String uebergeben ?
1103 if( s0FormatStrg
.Len() == 0 && bPosFormatFound
)
1104 // --> Ja, dann verwende String fuer positive Werte
1105 sTempStrg
= sPosFormatStrg
;
1107 sTempStrg
= s0FormatStrg
;
1109 else if( bPosFormatFound
)
1111 // verwende String fuer positive Werte
1112 sTempStrg
= sPosFormatStrg
;
1114 ScanFormatString( dNumber
, sTempStrg
, sReturnStrg
,/*bCreateSign=*/FALSE
);
1120 if( bNegFormatFound
)
1122 // wurde ggf. Leer-String uebergeben ?
1123 if( sNegFormatStrg
.Len() == 0 && bPosFormatFound
)
1125 // --> Ja, dann verwende String fuer positive Werte
1126 // und setzte Minus-Zeichen davor !
1127 sTempStrg
= String::CreateFromAscii("-");
1128 sTempStrg
+= sPosFormatStrg
;
1131 sTempStrg
= sNegFormatStrg
;
1134 sTempStrg
= sFormatStrg
;
1135 // falls KEIN Format-String speziell f"ur negative Werte angegeben
1136 // wurde, so soll das Vorzeichen ausgegeben werden
1137 ScanFormatString( dNumber
, sTempStrg
, sReturnStrg
,/*bCreateSign=*/bNegFormatFound
/*sNegFormatStrg!=EMPTYFORMATSTRING*/ );
1139 else // if( dNumber>0.0 )
1141 ScanFormatString( dNumber
,
1142 (/*sPosFormatStrg!=EMPTYFORMATSTRING*/bPosFormatFound
? sPosFormatStrg
: sFormatStrg
),
1143 sReturnStrg
,/*bCreateSign=*/FALSE
);
1149 BOOL
SbxBasicFormater::isBasicFormat( String sFormatStrg
)
1151 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_GENERALNUMBER
) )
1153 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_CURRENCY
) )
1155 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_FIXED
) )
1157 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_STANDARD
) )
1159 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_PERCENT
) )
1161 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_SCIENTIFIC
) )
1163 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_YESNO
) )
1165 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_TRUEFALSE
) )
1167 if( sFormatStrg
.EqualsIgnoreCaseAscii( BASICFORMAT_ONOFF
) )