diff options
Diffstat (limited to 'basic/source/sbx/sbxform.cxx')
-rw-r--r-- | basic/source/sbx/sbxform.cxx | 1171 |
1 files changed, 1171 insertions, 0 deletions
diff --git a/basic/source/sbx/sbxform.cxx b/basic/source/sbx/sbxform.cxx new file mode 100644 index 000000000000..9df7e0430719 --- /dev/null +++ b/basic/source/sbx/sbxform.cxx @@ -0,0 +1,1171 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: sbxform.cxx,v $ + * $Revision: 1.8 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_basic.hxx" + +#include <stdlib.h> + +#include <basic/sbxform.hxx> + +/* +TODO: gibt es noch irgend welche Star-Basic Besonderheiten ? + + was bedeutet: * als Platzhalter + +BEMERKUNG: Visual-Basic behandelt folgende (ung"ultige) Format-Strings + wie angezeigt: + + ##0##.##0## --> ##000.000## + + (diese Klasse verh"alt sich genau so). +*/ + +#include <stdio.h> // f"ur: sprintf() +#include <float.h> // f"ur: DBL_DIG, DBL_EPSILON +#include <math.h> // f"ur: floor(), fabs(), log10(), pow() + +//================================================================= +//=========================== DEFINES ============================= +//================================================================= + +#define _NO_DIGIT -1 + +#define MAX_NO_OF_EXP_DIGITS 5 + // +4 wegen dem Wertebereich: zwischen -308 und +308 + // +1 f"ur abschliessende 0 +#define MAX_NO_OF_DIGITS DBL_DIG +#define MAX_DOUBLE_BUFFER_LENGTH MAX_NO_OF_DIGITS + 9 + // +1 f"ur Vorzeichen + // +1 f"ur Ziffer vor dem Dezimal-Punkt + // +1 f"ur Dezimal-Punkt + // +2 f"ur Exponent E und Exp. Vorzeichen + // +3 f"ur den Wert des Exponenten + // +1 f"ur abschliessende 0 + +// Defines f"ur die Ziffern: +#define ASCII_0 '0' // 48 +#define ASCII_9 '9' // 57 + +#define CREATE_1000SEP_CHAR '@' + +#define FORMAT_SEPARATOR ';' + +// vordefinierte Formate f"ur den Format$()-Befehl: +#define BASICFORMAT_GENERALNUMBER "General Number" +#define BASICFORMAT_CURRENCY "Currency" +#define BASICFORMAT_FIXED "Fixed" +#define BASICFORMAT_STANDARD "Standard" +#define BASICFORMAT_PERCENT "Percent" +#define BASICFORMAT_SCIENTIFIC "Scientific" +#define BASICFORMAT_YESNO "Yes/No" +#define BASICFORMAT_TRUEFALSE "True/False" +#define BASICFORMAT_ONOFF "On/Off" + +#define EMPTYFORMATSTRING "" + +// Bem.: Visual-Basic hat bei Floating-Point-Zahlen maximal 12 Stellen +// nach dem Dezimal-Punkt. +// Alle Format-Strings sind kompatibel zu Visual-Basic: +#define GENERALNUMBER_FORMAT "0.############" + // max. 12 Stellen in Visual-Basic ! +#define CURRENCY_FORMAT "@$0.00;@($0.00)" +#define FIXED_FORMAT "0.00" +#define STANDARD_FORMAT "@0.00" +#define PERCENT_FORMAT "0.00%" +#define SCIENTIFIC_FORMAT "#.00E+00" +// BEMERKUNG: das Zeichen @ bedeutet, das Tausender-Separatoren erzeugt +// weden sollen. Dies ist eine StarBasic 'Erweiterung'. + +//================================================================= + +// zur Bestimmung der Anzahl Stellen in dNumber +double get_number_of_digits( double dNumber ) +//double floor_log10_fabs( double dNumber ) +{ + if( dNumber==0.0 ) + // 0 hat zumindest auch eine Stelle ! + return 0.0; //ehemals 1.0, jetzt 0.0 wegen #40025; + else + return floor( log10( fabs( dNumber ) ) ); +} + +//================================================================= +//======================= IMPLEMENTATION ========================== +//================================================================= + +SbxBasicFormater::SbxBasicFormater( sal_Unicode _cDecPoint, sal_Unicode _cThousandSep, + String _sOnStrg, + String _sOffStrg, + String _sYesStrg, + String _sNoStrg, + String _sTrueStrg, + String _sFalseStrg, + String _sCurrencyStrg, + String _sCurrencyFormatStrg ) +{ + cDecPoint = _cDecPoint; + cThousandSep = _cThousandSep; + sOnStrg = _sOnStrg; + sOffStrg = _sOffStrg; + sYesStrg = _sYesStrg; + sNoStrg = _sNoStrg; + sTrueStrg = _sTrueStrg; + sFalseStrg = _sFalseStrg; + sCurrencyStrg = _sCurrencyStrg; + sCurrencyFormatStrg = _sCurrencyFormatStrg; +} + +// Funktion zur Ausgabe eines Fehler-Textes (zum Debuggen) +/* +void SbxBasicFormater::ShowError( char * sErrMsg ) +{ +// cout << "ERROR in Format$(): " << sErrMsg << endl; +} +*/ +// verschiebt alle Zeichen des Strings, angefangen von der nStartPos, +// um eine Position zu gr"osseren Indizes, d.h. es wird Platz f"ur +// ein neues (einzuf"ugendes) Zeichen geschafft. +// ACHTUNG: der String MUSS gross genug sein ! +inline void SbxBasicFormater::ShiftString( String& sStrg, USHORT nStartPos ) +{ + sStrg.Erase( nStartPos,1 ); +} + +// Funktion um ein Zeichen an einen String anzuh"angen +inline void SbxBasicFormater::StrAppendChar( String& sStrg, sal_Unicode ch ) +{ + sStrg.Insert( ch ); +} + +// h"angt die "ubergebene Ziffer nDigit an den "ubergebenen String sStrg +// an, dabei wird "uberpr"uft ob nDigit eine g"ultige Ziffer ist, +// falls dies nicht der Fall ist, wird nichts gemacht. +void SbxBasicFormater::AppendDigit( String& sStrg, short nDigit ) +{ + if( nDigit>=0 && nDigit<=9 ) + StrAppendChar( sStrg, (sal_Unicode)(nDigit+ASCII_0) ); +} + +// verschiebt den Dezimal-Punkt um eine Stelle nach links +void SbxBasicFormater::LeftShiftDecimalPoint( String& sStrg ) +{ + USHORT nPos = sStrg.Search( cDecPoint ); + + if( nPos!=STRING_NOTFOUND ) + { + // vertausche Dezimal-Punkt + sStrg.SetChar( nPos, sStrg.GetChar( nPos - 1 ) ); + sStrg.SetChar( nPos-1, cDecPoint ); + } +} + +// rundet in einem String die Ziffer an der angegebenen Stelle, +// es wird ein Flag zur"uckgeliefert, falls ein Overflow auftrat, +// d.h. 99.99 --> 100.00, d.h. ein Gr"ossenordung ge"andert wurde +// (geschieht beim Runden einer 9). +void SbxBasicFormater::StrRoundDigit( String& sStrg, short nPos, BOOL& bOverflow ) +{ + // wurde ggf ein falscher Index uebergeben --> Aufruf ignorieren + if( nPos<0 ) + return; + + bOverflow = FALSE; + // "uberspringe den Dezimalpunkt und Tausender-Trennzeichen + sal_Unicode c = sStrg.GetChar( nPos ); + if( nPos>0 && (c == cDecPoint || c == cThousandSep) ) + { + StrRoundDigit( sStrg,nPos-1,bOverflow ); + // AENDERUNG ab 9.3.1997: nach rekursivem Call die Methode SOFORT beenden ! + return; + } + // "uberspringe alle nicht-Ziffern: + // BEMERKUNG: + // in einem g"ultigen Format-String sollte die Ausgabe + // der Zahl an einem St"uck geschen, d.h. Sonderzeichen sollten + // NUR vor ODER nach der Zahl stehen und nicht mitten in der + // Format-Angabe f"ur die Zahl + while( nPos>=0 && (sStrg.GetChar( nPos )<ASCII_0 || sStrg.GetChar( nPos )>ASCII_9) ) + nPos--; + // muss ggf. noch Platz f"ur eine weitere (f"uhrende) Ziffer + // geschaffen werden ? + if( nPos==-1 ) + { + ShiftString( sStrg,0 ); + // f"uhrende 1 einf"ugen: z.B. 99.99 f"ur 0.0 + sStrg.SetChar( 0, '1' ); + bOverflow = TRUE; + } + else + { + // ist die zu rundende Position eine Ziffer ? + sal_Unicode c2 = sStrg.GetChar( nPos ); + if( c2 >= ASCII_0 && c2 <= ASCII_9 ) + { + // muss eine 9 gerundet werden? Falls: Ja --> rekursiver Aufruf + if( c2 == ASCII_9 ) + { + sStrg.SetChar( nPos, '0' ); + StrRoundDigit( sStrg,nPos-1,bOverflow ); + } + else + sStrg.SetChar( nPos, c2+1 ); + } + else + { + // --> Nein, d.h. Platz f"ur Ziffer schaffen: z.B. -99.99 f"ur #0.0 + // da gerundet wird MUSS es immer eine g"ultige Position + // nPos+1 geben ! + ShiftString( sStrg,nPos+1 ); + // f"uhrende 1 einf"ugen + sStrg.SetChar( nPos+1, '1' ); + bOverflow = TRUE; + } + } +} + +// rundet in einem String die Ziffer an der angegebenen Stelle +void SbxBasicFormater::StrRoundDigit( String& sStrg, short nPos ) +{ + BOOL bOverflow; + + StrRoundDigit( sStrg,nPos,bOverflow ); +} + +// parse den Formatstring von der "ubergebenen Position zur"uck +// und l"osche ggf. "uberf"ussige 0en, z.B. 4.50 in 0.0# +void SbxBasicFormater::ParseBack( String& sStrg, const String& sFormatStrg, + short nFormatPos ) +{ + // WICHTIG: nFormatPos kann auch negativ sein, in diesem Fall Aufruf ignorieren + for( short i=nFormatPos; + i>0 && sFormatStrg.GetChar( i ) == '#' && sStrg.GetChar( (sStrg.Len()-1) ) == '0'; + i-- ) + { sStrg.Erase( sStrg.Len()-1 ); } +} + +#ifdef _with_sprintf + +/* + Bemerkung: + Zahl wird mit maximaler (sinnvollen) Genauigkeit in einen String + umgewandelt (mit sprintf()), dieser String wird dann im Schleifen- + Durchlauf nach der entsprechenden Ziffer durchsucht. +*/ +// initialisiert die Daten der Klasse um einen Scan-Durchlauf durchzuf"uhren +void SbxBasicFormater::InitScan( double _dNum ) +{ + char sBuffer[ MAX_DOUBLE_BUFFER_LENGTH ]; + + dNum = _dNum; + InitExp( get_number_of_digits( dNum ) ); + // maximal 15 Nachkomma-Stellen, Format-Beispiel: -1.234000000000000E-001 + /*int nCount =*/ sprintf( sBuffer,"%+22.15lE",dNum ); + sSciNumStrg.AssignAscii( sBuffer ); +} + +void SbxBasicFormater::InitExp( double _dNewExp ) +{ + char sBuffer[ MAX_DOUBLE_BUFFER_LENGTH ]; + // bestimme den Exponenten (kann immer GENAU durch int dargestellt werden) + nNumExp = (short)_dNewExp; + // und dessen String + /*int nCount =*/ sprintf( sBuffer,"%+i",nNumExp ); + sNumExpStrg.AssignAscii( sBuffer ); + // bestimme die Anzahl der Stellen im Exponenten + nExpExp = (short)get_number_of_digits( (double)nNumExp ); +} + +// bestimmt die Ziffer an der angegebenen Stelle (gedacht zur Anwendung im +// Scan-Durchlauf) +short SbxBasicFormater::GetDigitAtPosScan( short nPos, BOOL& bFoundFirstDigit ) +{ + // Versuch eine gr"ossere Ziffer zu lesen, + // z.B. Stelle 4 in 1.234, + // oder eine Ziffer ausserhalb der Aufl"osung der + // Zahl (double) zu lesen (z.B. max. 15 Stellen). + if( nPos>nNumExp || abs(nNumExp-nPos)>MAX_NO_OF_DIGITS ) + return _NO_DIGIT; + // bestimme den Index der Stelle in dem Number-String: + // "uberlese das Vorzeichen + USHORT no = 1; + // falls notwendig den Dezimal-Punkt "uberlesen: + if( nPos<nNumExp ) + no++; + no += nNumExp-nPos; + // Abfrage der ersten (g"ultigen) Ziffer der Zahl --> Flag setzen + if( nPos==nNumExp ) + bFoundFirstDigit = TRUE; + return (short)(sSciNumStrg.GetChar( no ) - ASCII_0); +} + +short SbxBasicFormater::GetDigitAtPosExpScan( short nPos, BOOL& bFoundFirstDigit ) +{ + // ist die abgefragte Stelle zu gross f"ur den Exponenten ? + if( nPos>nExpExp ) + return -1; + + // bestimme den Index der Stelle in dem Number-String: + // "uberlese das Vorzeichen + USHORT no = 1; + no += nExpExp-nPos; + // Abfrage der ersten (g"ultigen) Ziffer der Zahl --> Flag setzen + if( nPos==nExpExp ) + bFoundFirstDigit = TRUE; + return (short)(sNumExpStrg.GetChar( no ) - ASCII_0); +} + +// es kann ein Wert f"ur den Exponent angegeben werden, da ggf. die +// Zahl ggf. NICHT normiert (z.B. 1.2345e-03) dargestellt werden soll, +// sondern eventuell 123.345e-3 ! +short SbxBasicFormater::GetDigitAtPosExpScan( double dNewExponent, short nPos, + BOOL& bFoundFirstDigit ) +{ + // neuer Exponent wurde "ubergeben, aktualisiere + // die tempor"aren Klassen-Variablen + InitExp( dNewExponent ); + // und jetzt die Stelle bestimmen + return GetDigitAtPosExpScan( nPos,bFoundFirstDigit ); +} + +#else + +/* Probleme mit der folgenden Methode: + +TODO: ggf einen 'intelligenten' Peek-Parser um Rundungsfehler bei + double-Zahlen herauszufinden ? z.B. f"ur 0.00115 #.#e-000 + + Problem mit: format( 0.3345 , "0.000" ) + Problem mit: format( 0.00115 , "0.0000" ) + +*/ +// liefert die Ziffer an der angegebenen '10er System'-Position, +// d.h. positive nPos f"ur Stellen vor dem Komma und negative +// f"ur Stellen nach dem Komma. +// nPos==0 bedeutet erste Stelle vor dem Komma, also 10^0. +// liefert 0..9 f"ur g"ultige Ziffern und -1 f"ur nicht vorhanden, +// d.h. falls die "ubergebene Zahl zu klein ist +// (z.B. Stelle 5 bei dNumber=123). +// Weiter wird in dNextNumber die um die f"uhrenden Stellen +// (bis nPos) gek"urzte Zahl zur"uckgeliefert, z.B. +// GetDigitAtPos( 3434.565 , 2 , dNewNumber ) --> dNewNumber = 434.565 +// dies kann f"ur Schleifenabarbeitung g"unstiger sein, d.h. +// die Zahlen immer von der gr"ossten Stelle abarbeiten/scanen. +// In bFoundFirstDigit wird ggf. ein Flag gesetzt wenn eine Ziffer +// gefunden wurde, dies wird dazu verwendet um 'Fehler' beim Parsen 202 +// zu vermeiden, die +// +// ACHTUNG: anscheinend gibt es manchmal noch Probleme mit Rundungs-Fehlern! +short SbxBasicFormater::GetDigitAtPos( double dNumber, short nPos, + double& dNextNumber, BOOL& bFoundFirstDigit ) +// ACHTUNG: nPos kann auch negativ werden, f"ur Stellen nach dem Dezimal-Punkt +{ + double dTemp = dNumber; + double dDigit,dPos; + short nMaxDigit; + + // erst mal aus der Zahl eine positive Zahl machen: + dNumber = fabs( dNumber ); + dPos = (double)nPos; + + // "uberpr"ufe ob Zahl zu klein f"ur angegebene Stelle ist + nMaxDigit = (short)get_number_of_digits( dNumber ); + // f"uhrende Ziffern 'l"oschen' + // Bem.: Fehler nur bei Zahlen gr"osser 0, d.h. bei Ziffern vor dem + // Dezimal-Punkt + if( nMaxDigit<nPos && !bFoundFirstDigit && nPos>=0 ) + return _NO_DIGIT; + // Ziffer gefunden, setze Flag: + bFoundFirstDigit = TRUE; + for( short i=nMaxDigit; i>=nPos; i-- ) + { + double dI = (double)i; + double dTemp1 = pow( 10.0,dI ); + // pr"apariere nun die gesuchte Ziffer: + dDigit = floor( pow( 10.0,log10( fabs( dNumber ) )-dI ) ); + dNumber -= dTemp1 * dDigit; + } + // Zuweisung f"ur optimierte Schleifen-Durchl"aufe + dNextNumber = dNumber; + // und zum Schluss noch die float-Rundungsungenauigkeiten heraus filtern + return RoundDigit( dDigit ); +} + +// rundet eine double-Zahl zwischen 0 und 9 auf die genaue +// Integer-Zahl, z.B. 2.8 -> 3 und 2.2 -> 2 +short SbxBasicFormater::RoundDigit( double dNumber ) +{ + // ist der Wertebereich g"ultig ? + if( dNumber<0.0 || dNumber>10.0 ) + return -1; + short nTempHigh = (short)(dNumber+0.5); // ggf. floor( ) + return nTempHigh; +} + +#endif + +// kopiert den entsprechenden Teil des Format-Strings, falls vorhanden, +// und liefert diesen zur"uck. +// Somit wird ein neuer String erzeugt, der vom Aufrufer wieder freigegeben +// werden muss +String SbxBasicFormater::GetPosFormatString( const String& sFormatStrg, BOOL & bFound ) +{ + bFound = FALSE; // default... + USHORT nPos = sFormatStrg.Search( FORMAT_SEPARATOR ); + + if( nPos!=STRING_NOTFOUND ) + { + bFound = TRUE; + // der Format-String f"ur die positiven Zahlen ist alles + // vor dem ersten ';' + return sFormatStrg.Copy( 0,nPos ); + } + // kein ; gefunden, liefere Leerstring + String aRetStr; + aRetStr.AssignAscii( EMPTYFORMATSTRING ); + return aRetStr; +} + +// siehe auch GetPosFormatString() +String SbxBasicFormater::GetNegFormatString( const String& sFormatStrg, BOOL & bFound ) +{ + bFound = FALSE; // default... + USHORT nPos = sFormatStrg.Search( FORMAT_SEPARATOR ); + + if( nPos!=STRING_NOTFOUND ) + { + // der Format-String f"ur die negative Zahlen ist alles + // zwischen dem ersten und dem zweiten ';'. + // Daher: hole erst mal alles nach dem ersten ';' + String sTempStrg = sFormatStrg.Copy( nPos+1 ); + // und suche darin ggf. ein weiteres ';' + nPos = sTempStrg.Search( FORMAT_SEPARATOR ); + bFound = TRUE; + if( nPos==STRING_NOTFOUND ) + // keins gefunden, liefere alles... + return sTempStrg; + else + // ansonsten den String zwischen den beiden ';' liefern + return sTempStrg.Copy( 0,nPos ); + } + String aRetStr; + aRetStr.AssignAscii( EMPTYFORMATSTRING ); + return aRetStr; +} + +// siehe auch GetPosFormatString() +String SbxBasicFormater::Get0FormatString( const String& sFormatStrg, BOOL & bFound ) +{ + bFound = FALSE; // default... + USHORT nPos = sFormatStrg.Search( FORMAT_SEPARATOR ); + + if( nPos!=STRING_NOTFOUND ) + { + // der Format-String f"ur die Null ist alles + // was nach dem zweiten ';' kommt. + // Daher: hole erst mal alles nach dem ersten ';' + String sTempStrg = sFormatStrg.Copy( nPos+1 ); + // und suche darin ggf. ein weiteres ';' + nPos = sTempStrg.Search( FORMAT_SEPARATOR ); + if( nPos!=STRING_NOTFOUND ) + { + bFound = TRUE; + sTempStrg = sTempStrg.Copy( nPos+1 ); + nPos = sTempStrg.Search( FORMAT_SEPARATOR ); + if( nPos==STRING_NOTFOUND ) + // keins gefunden, liefere alles... + return sTempStrg; + else + return sTempStrg.Copy( 0,nPos ); + } + } + // kein ; gefunden, liefere Leerstring + String aRetStr; + aRetStr.AssignAscii( EMPTYFORMATSTRING ); + return aRetStr; +} + +// siehe auch GetPosFormatString() +String SbxBasicFormater::GetNullFormatString( const String& sFormatStrg, BOOL & bFound ) +{ + bFound = FALSE; // default... + USHORT nPos = sFormatStrg.Search( FORMAT_SEPARATOR ); + + if( nPos!=STRING_NOTFOUND ) + { + // der Format-String f"ur die Null ist alles + // was nach dem dritten ';' kommt. + // Daher: hole erst mal alles nach dem ersten ';' + String sTempStrg = sFormatStrg.Copy( nPos+1 ); + // und suche darin ggf. ein weiteres ';' + nPos = sTempStrg.Search( FORMAT_SEPARATOR ); + if( nPos!=STRING_NOTFOUND ) + { + // und suche nun nach dem dritten ';' + sTempStrg = sTempStrg.Copy( nPos+1 ); + nPos = sTempStrg.Search( FORMAT_SEPARATOR ); + if( nPos!=STRING_NOTFOUND ) + { + bFound = TRUE; + return sTempStrg.Copy( nPos+1 ); + } + } + } + // kein ; gefunden, liefere Leerstring + String aRetStr; + aRetStr.AssignAscii( EMPTYFORMATSTRING ); + return aRetStr; +} + +// analysiert den Format-String, liefert Wert <> 0 falls ein Fehler +// aufgetreten ist +short SbxBasicFormater::AnalyseFormatString( const String& sFormatStrg, + short& nNoOfDigitsLeft, short& nNoOfDigitsRight, + short& nNoOfOptionalDigitsLeft, + short& nNoOfExponentDigits, short& nNoOfOptionalExponentDigits, + BOOL& bPercent, BOOL& bCurrency, BOOL& bScientific, + BOOL& bGenerateThousandSeparator, + short& nMultipleThousandSeparators ) +{ + USHORT nLen; + short nState = 0; + + nLen = sFormatStrg.Len(); + // initialisiere alle Z"ahler und Flags + nNoOfDigitsLeft = 0; + nNoOfDigitsRight = 0; + nNoOfOptionalDigitsLeft = 0; + nNoOfExponentDigits = 0; + nNoOfOptionalExponentDigits = 0; + bPercent = FALSE; + bCurrency = FALSE; + bScientific = FALSE; + // ab 11.7.97: sobald ein Komma in dem Format String gefunden wird, + // werden alle 3 Zehnerpotenzen markiert (d.h. tausender, milionen, ...) + // bisher wurde nur an den gesetzten Position ein Tausender-Separator + // ausgegeben oder wenn ein @ im Format-String stand. + // Dies war ein Missverstaendnis der VB Kompatiblitaet. + bGenerateThousandSeparator = sFormatStrg.Search( ',' ) != STRING_NOTFOUND; + nMultipleThousandSeparators = 0; + // und untersuche den Format-String nach den gew"unschten Informationen + for( USHORT i=0; i<nLen; i++ ) + { + sal_Unicode c = sFormatStrg.GetChar( i ); + switch( c ) { + case '#': + case '0': + if( nState==0 ) + { + nNoOfDigitsLeft++; +// TODO hier ggf. bessere Fehler-"Uberpr"ufung der Mantisse auf g"ultige Syntax (siehe Grammatik) + // ACHTUNG: 'undefiniertes' Verhalten falls # und 0 + // gemischt werden !!! + // BEMERKUNG: eigentlich sind #-Platzhalter bei Scientific + // Darstellung vor dem Dezimal-Punkt sinnlos ! + if( c=='#' ) + nNoOfOptionalDigitsLeft++; + } + else if( nState==1 ) + nNoOfDigitsRight++; + else if( nState==-1 ) // suche 0 im Exponent + { + if( c=='#' ) // # schaltet den Zustand weiter + { + nNoOfOptionalExponentDigits++; + nState = -2; + } + nNoOfExponentDigits++; + } + else if( nState==-2 ) // suche # im Exponent + { + if( c=='0' ) + // ERROR: 0 nach # im Exponent ist NICHT erlaubt !! + return -4; + nNoOfOptionalExponentDigits++; + nNoOfExponentDigits++; + } + break; + case '.': + nState++; + if( nState>1 ) + return -1; // ERROR: zu viele Dezimal-Punkte + break; + case '%': + bPercent = TRUE; + /* old: + bPercent++; + if( bPercent>1 ) + return -2; // ERROR: zu viele Prozent-Zeichen + */ + break; + case '(': + bCurrency = TRUE; + break; + case ',': + { + sal_Unicode ch = sFormatStrg.GetChar( i+1 ); + // vorl"aufig wird NUR auf zwei aufeinanderfolgede + // Zeichen gepr"uft + if( ch!=0 && (ch==',' || ch=='.') ) + nMultipleThousandSeparators++; + } break; + case 'e': + case 'E': + // #i13821 not when no digits before + if( nNoOfDigitsLeft > 0 || nNoOfDigitsRight > 0 ) + { + nState = -1; // breche jetzt das Z"ahlen der Stellen ab + bScientific = TRUE; + } + /* old: + bScientific++; + if( bScientific>1 ) + return -3; // ERROR: zu viele Exponent-Zeichen + */ + break; + // EIGENES Kommando-Zeichen, das die Erzeugung der + // Tausender-Trennzeichen einschaltet + case '\\': + // Ignore next char + i++; + break; + case CREATE_1000SEP_CHAR: + bGenerateThousandSeparator = TRUE; + break; + } + } + return 0; +} + +// das Flag bCreateSign zeigt an, dass bei der Mantisse ein Vorzeichen +// erzeugt werden soll +void SbxBasicFormater::ScanFormatString( double dNumber, + const String& sFormatStrg, String& sReturnStrg, + BOOL bCreateSign ) +{ + short /*nErr,*/nNoOfDigitsLeft,nNoOfDigitsRight,nNoOfOptionalDigitsLeft, + nNoOfExponentDigits,nNoOfOptionalExponentDigits, + nMultipleThousandSeparators; + BOOL bPercent,bCurrency,bScientific,bGenerateThousandSeparator; + + // Initialisiere den Return-String + sReturnStrg = String(); + + // analysiere den Format-String, d.h. bestimme folgende Werte: + /* + - Anzahl der Ziffern vor dem Komma + - Anzahl der Ziffern nach dem Komma + - optionale Ziffern vor dem Komma + - Anzahl der Ziffern im Exponent + - optionale Ziffern im Exponent + - Prozent-Zeichen gefunden ? + - () f"ur negatives Vorzeichen ? + - Exponetial-Schreibweise ? + - sollen Tausender-Separatoren erzeugt werden ? + - wird ein Prozent-Zeichen gefunden ? --> dNumber *= 100.0; + - gibt es aufeinanderfolgende Tausender-Trennzeichen ? + ,, oder ,. --> dNumber /= 1000.0; + - sonstige Fehler ? mehrfache Dezimalpunkte, E's, etc. + --> Fehler werden zur Zeit einfach ignoriert + */ + /*nErr =*/ AnalyseFormatString( sFormatStrg,nNoOfDigitsLeft,nNoOfDigitsRight, + nNoOfOptionalDigitsLeft,nNoOfExponentDigits, + nNoOfOptionalExponentDigits, + bPercent,bCurrency,bScientific,bGenerateThousandSeparator, + nMultipleThousandSeparators ); + /* es werden alle Fehler ignoriert, wie in Visual-Basic + if( nErr!=0 ) + { + char sBuffer[512]; + + //sprintf( sBuffer,"bad format-string >%s< err=%i",sFormatStrg,nErr ); + strcpy( sBuffer,"bad format-string" ); + ShowError( sBuffer ); + } + else + */ + { + // Spezialbehandlung f"ur Spezialzeichen + if( bPercent ) + dNumber *= 100.0; +// TODO: diese Vorgabe (,, oder ,.) ist NICHT Visual-Basic kompatibel ! + // Frage: soll das hier stehen bleiben (Anforderungen) ? + if( nMultipleThousandSeparators ) + dNumber /= 1000.0; + + // einige Arbeits-Variablen + double dExponent; + short i,nLen; + short nState,nDigitPos,nExponentPos,nMaxDigit,nMaxExponentDigit; + BOOL bFirstDigit,bFirstExponentDigit,bFoundFirstDigit, + bIsNegative,bZeroSpaceOn, bSignHappend,bDigitPosNegative; + + // Initialisierung der Arbeits-Variablen + bSignHappend = FALSE; + bFoundFirstDigit = FALSE; + bIsNegative = dNumber<0.0; + nLen = sFormatStrg.Len(); + dExponent = get_number_of_digits( dNumber ); + nExponentPos = 0; + nMaxExponentDigit = 0; + nMaxDigit = (short)dExponent; + bDigitPosNegative = false; + if( bScientific ) + { + //if( nNoOfOptionalDigitsLeft>0 ) + // ShowError( "# in scientific-format in front of the decimal-point has no effect" ); + // beim Exponent ggf. "uberz"ahlige Stellen vor dem Komma abziehen + dExponent = dExponent - (double)(nNoOfDigitsLeft-1); + nDigitPos = nMaxDigit; + nMaxExponentDigit = (short)get_number_of_digits( dExponent ); + nExponentPos = nNoOfExponentDigits-1 - nNoOfOptionalExponentDigits; + } + else + { + nDigitPos = nNoOfDigitsLeft-1; // Z"ahlweise f"angt bei 0 an, 10^0 + // hier ben"otigt man keine Exponent-Daten ! + bDigitPosNegative = (nDigitPos < 0); + } + bFirstDigit = TRUE; + bFirstExponentDigit = TRUE; + nState = 0; // 0 --> Mantisse; 1 --> Exponent + bZeroSpaceOn = 0; + + +#ifdef _with_sprintf + InitScan( dNumber ); +#endif + // scanne jetzt den Format-String: + sal_Unicode cForce = 0; + for( i=0; i<nLen; i++ ) + { + sal_Unicode c; + if( cForce ) + { + c = cForce; + cForce = 0; + } + else + { + c = sFormatStrg.GetChar( i ); + } + switch( c ) { + case '0': + case '#': + if( nState==0 ) + { + // Behandlung der Mantisse + if( bFirstDigit ) + { + //org:bFirstDigit = FALSE; + // ggf. Vorzeichen erzeugen + // Bem.: bei bCurrency soll das negative + // Vorzeichen durch () angezeigt werden + if( bIsNegative && !bCreateSign/*!bCurrency*/ && !bSignHappend ) + { + // nur einmal ein Vorzeichen ausgeben + bSignHappend = TRUE; + StrAppendChar( sReturnStrg,'-' ); + } + // hier jetzt "uberz"ahlige Stellen ausgeben, + // d.h. vom Format-String nicht erfasste Stellen + if( nMaxDigit>nDigitPos ) + { + for( short j=nMaxDigit; j>nDigitPos; j-- ) + { + short nTempDigit; +#ifdef _with_sprintf + AppendDigit( sReturnStrg,nTempDigit = GetDigitAtPosScan( j,bFoundFirstDigit ) ); +#else + AppendDigit( sReturnStrg,nTempDigit = GetDigitAtPos( dNumber,j,dNumber,bFoundFirstDigit ) ); +#endif + // wurde wirklich eine Ziffer eingefuegt ? + if( nTempDigit!=_NO_DIGIT ) + // jetzt wurde wirklich eine Ziffer ausgegeben, Flag setzen + bFirstDigit = FALSE; + // muss ggf. ein Tausender-Trennzeichen erzeugt werden? + if( bGenerateThousandSeparator && ( c=='0' || nMaxDigit>=nDigitPos ) && j>0 && (j % 3 == 0) ) + StrAppendChar( sReturnStrg,cThousandSep ); + } + } + } + // muss f"ur eine leere Stelle eventuell eine 0 ausgegeben werden ? + if( nMaxDigit<nDigitPos && ( c=='0' || bZeroSpaceOn ) ) + { + AppendDigit( sReturnStrg,0 ); // Ja + // jetzt wurde wirklich eine Ziffer ausgegeben, Flag setzen + bFirstDigit = FALSE; + bZeroSpaceOn = 1; + // BEM.: bei Visual-Basic schaltet die erste 0 f"ur alle + // nachfolgenden # (bis zum Dezimal-Punkt) die 0 ein, + // dieses Verhalten wird hier mit dem Flag simmuliert. + // muss ggf. ein Tausender-Trennzeichen erzeugt werden? + if( bGenerateThousandSeparator && ( c=='0' || nMaxDigit>=nDigitPos ) && nDigitPos>0 && (nDigitPos % 3 == 0) ) + StrAppendChar( sReturnStrg,cThousandSep ); + } + else + { + short nTempDigit; +#ifdef _with_sprintf + AppendDigit( sReturnStrg,nTempDigit = GetDigitAtPosScan( nDigitPos,bFoundFirstDigit ) ); +#else + AppendDigit( sReturnStrg,nTempDigit = GetDigitAtPos( dNumber,nDigitPos,dNumber,bFoundFirstDigit ) ); +#endif + // wurde wirklich eine Ziffer eingefuegt ? + if( nTempDigit!=_NO_DIGIT ) + // jetzt wurde wirklich eine Ziffer ausgegeben, Flag setzen + bFirstDigit = FALSE; + // muss ggf. ein Tausender-Trennzeichen erzeugt werden? + if( bGenerateThousandSeparator && ( c=='0' || nMaxDigit>=nDigitPos ) && nDigitPos>0 && (nDigitPos % 3 == 0) ) + StrAppendChar( sReturnStrg,cThousandSep ); + } + // und Position aktualisieren + nDigitPos--; + } + else + { + // Behandlung des Exponenten + if( bFirstExponentDigit ) + { + // Vorzeichen wurde schon bei e/E ausgegeben + bFirstExponentDigit = FALSE; + if( nMaxExponentDigit>nExponentPos ) + // hier jetzt "uberz"ahlige Stellen ausgeben, + // d.h. vom Format-String nicht erfasste Stellen + { + for( short j=nMaxExponentDigit; j>nExponentPos; j-- ) + { +#ifdef _with_sprintf + AppendDigit( sReturnStrg,GetDigitAtPosExpScan( dExponent,j,bFoundFirstDigit ) ); +#else + AppendDigit( sReturnStrg,GetDigitAtPos( dExponent,j,dExponent,bFoundFirstDigit ) ); +#endif + } + } + } + // muss f"ur eine leere Stelle eventuell eine 0 ausgegeben werden ? + if( nMaxExponentDigit<nExponentPos && c=='0' ) + AppendDigit( sReturnStrg,0 ); // Ja + else +#ifdef _with_sprintf + AppendDigit( sReturnStrg,GetDigitAtPosExpScan( dExponent,nExponentPos,bFoundFirstDigit ) ); +#else + AppendDigit( sReturnStrg,GetDigitAtPos( dExponent,nExponentPos,dExponent,bFoundFirstDigit ) ); +#endif + nExponentPos--; + } + break; + case '.': + if( bDigitPosNegative ) // #i13821: If no digits before . + { + bDigitPosNegative = false; + nDigitPos = 0; + cForce = '#'; + i-=2; + break; + } + // gebe Komma aus + StrAppendChar( sReturnStrg,cDecPoint ); + break; + case '%': + // ggf. "uberf"ussige 0en l"oschen, z.B. 4.500e4 in 0.0##e-00 + ParseBack( sReturnStrg,sFormatStrg,i-1 ); + // gebe Prozent-Zeichen aus + sReturnStrg.Insert('%'); + break; + case 'e': + case 'E': + // muss Mantisse noch gerundet werden, bevor der Exponent angezeigt wird ? + { + // gibt es ueberhaupt eine Mantisse ? + if( bFirstDigit ) + { + // anscheinend nicht, d.h. ungueltiger Format String, z.B. E000.00 + // d.h. ignoriere diese e bzw. E Zeichen + // ggf. einen Fehler (wie Visual Basic) ausgeben ? + + // #i13821: VB 6 behaviour + StrAppendChar( sReturnStrg,c ); + break; + } + + BOOL bOverflow = FALSE; +#ifdef _with_sprintf + short nNextDigit = GetDigitAtPosScan( nDigitPos,bFoundFirstDigit ); +#else + short nNextDigit = GetDigitAtPos( dNumber,nDigitPos,dNumber,bFoundFirstDigit ); +#endif + if( nNextDigit>=5 ) + StrRoundDigit( sReturnStrg,sReturnStrg.Len()-1,bOverflow ); + if( bOverflow ) + { + // es wurde eine f"uhrende 9 gerundet, d.h. + // verschiebe den Dezimal-Punkt um eine Stelle nach links + LeftShiftDecimalPoint( sReturnStrg ); + // und l"osche die letzte Ziffer, diese wird + // duch die f"uhrende 1 ersetzt: + sReturnStrg.SetChar( sReturnStrg.Len()-1 , 0 ); + // der Exponent muss um 1 erh"oht werden, + // da der Dezimalpunkt verschoben wurde + dExponent += 1.0; + } + // ggf. "uberf"ussige 0en l"oschen, z.B. 4.500e4 in 0.0##e-00 + ParseBack( sReturnStrg,sFormatStrg,i-1 ); + } + // "andere Zustand des Scanners + nState++; + // gebe Exponent-Zeichen aus + StrAppendChar( sReturnStrg,c ); + // i++; // MANIPULATION der Schleifen-Variable ! + c = sFormatStrg.GetChar( ++i ); + // und gebe Vorzeichen / Exponent aus + if( c!=0 ) + { + if( c=='-' ) + { + // falls Exponent < 0 gebe - aus + if( dExponent<0.0 ) + StrAppendChar( sReturnStrg,'-' ); + } + else if( c=='+' ) + { + // gebe auf jeden Fall das Vorzeichen des Exponenten aus ! + if( dExponent<0.0 ) + StrAppendChar( sReturnStrg,'-' ); + else + StrAppendChar( sReturnStrg,'+' ); + } + //else + // ShowError( "operator e/E did not find + or -" ); + } + //else + // ShowError( "operator e/E ended with 0" ); + break; + case ',': + // ACHTUNG: nur falls Zahl bisher ausgegeben wurde + // das Zeichen ausgeben + ////--> Siehe Kommentar vom 11.7. in AnalyseFormatString() + ////if( !bFirstDigit ) + //// // gebe Tausender-Trennzeichen aus + //// StrAppendChar( sReturnStrg,cThousandSep ); + break; + case ';': + break; + case '(': + case ')': + // ggf. "uberf"ussige 0en l"oschen, z.B. 4.500e4 in 0.0##e-00 + ParseBack( sReturnStrg,sFormatStrg,i-1 ); + if( bIsNegative ) + StrAppendChar( sReturnStrg,c ); + break; + case '$': + // den String fuer die Waehrung dranhengen: + sReturnStrg += sCurrencyStrg; + break; + case ' ': + case '-': + case '+': + // ggf. "uberf"ussige 0en l"oschen, z.B. 4.500e4 in 0.0##e-00 + ParseBack( sReturnStrg,sFormatStrg,i-1 ); + // gebe das jeweilige Zeichen direkt aus + StrAppendChar( sReturnStrg,c ); + break; + case '\\': + // ggf. "uberf"ussige 0en l"oschen, z.B. 4.500e4 in 0.0##e-00 + // falls Sonderzeichen am Ende oder mitten in + // Format-String vorkommen + ParseBack( sReturnStrg,sFormatStrg,i-1 ); + // Sonderzeichen gefunden, gebe N"ACHSTES + // Zeichen direkt aus (falls es existiert) + // i++; + c = sFormatStrg.GetChar( ++i ); + if( c!=0 ) + StrAppendChar( sReturnStrg,c ); + //else + // ShowError( "operator \\ ended with 0" ); + break; + case CREATE_1000SEP_CHAR: + // hier ignorieren, Aktion wurde schon in + // AnalyseFormatString durchgef"uhrt + break; + default: + // auch die Zeichen und Ziffern ausgeben (wie in Visual-Basic) + if( ( c>='a' && c<='z' ) || + ( c>='A' && c<='Z' ) || + ( c>='1' && c<='9' ) ) + StrAppendChar( sReturnStrg,c ); + // else + // ignorieren ! + // ehemals: ShowError( "bad character in format-string" ); + } + } + // Format-String wurde vollst"andig gescanned, + // muss die letzte Stelle nun gerundet werden ? + // Dies hier ist jedoch NUR notwendig, falls das + // Zahlenformat NICHT Scientific-Format ist ! + if( !bScientific ) + { +#ifdef _with_sprintf + short nNextDigit = GetDigitAtPosScan( nDigitPos,bFoundFirstDigit ); +#else + short nNextDigit = GetDigitAtPos( dNumber,nDigitPos,dNumber,bFoundFirstDigit ); +#endif + if( nNextDigit>=5 ) + StrRoundDigit( sReturnStrg,sReturnStrg.Len()-1 ); + } + // und ganz zum Schluss: + // ggf. "uberf"ussige 0en l"oschen, z.B. 4.500e4 in 0.0##e-00#, + // ABER nur Stellen nach dem Dezimal-Punkt k"onnen gel"oscht werden + if( nNoOfDigitsRight>0 ) + ParseBack( sReturnStrg,sFormatStrg,sFormatStrg.Len()-1 ); + } +} + +String SbxBasicFormater::BasicFormatNull( String sFormatStrg ) +{ + BOOL bNullFormatFound; + String sNullFormatStrg = GetNullFormatString( sFormatStrg,bNullFormatFound ); + + if( bNullFormatFound ) + return sNullFormatStrg; + String aRetStr; + aRetStr.AssignAscii( "null" ); + return aRetStr; +} + +String SbxBasicFormater::BasicFormat( double dNumber, String sFormatStrg ) +{ + BOOL bPosFormatFound,bNegFormatFound,b0FormatFound; + + // analysiere Format-String auf vordefinierte Formate: + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_GENERALNUMBER ) ) + sFormatStrg.AssignAscii( GENERALNUMBER_FORMAT ); + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_CURRENCY ) ) + sFormatStrg = sCurrencyFormatStrg; // old: CURRENCY_FORMAT; + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_FIXED ) ) + sFormatStrg.AssignAscii( FIXED_FORMAT ); + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_STANDARD ) ) + sFormatStrg.AssignAscii( STANDARD_FORMAT ); + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_PERCENT ) ) + sFormatStrg.AssignAscii( PERCENT_FORMAT ); + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_SCIENTIFIC ) ) + sFormatStrg.AssignAscii( SCIENTIFIC_FORMAT ); + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_YESNO ) ) + return ( dNumber==0.0 ) ? sNoStrg : sYesStrg ; + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_TRUEFALSE ) ) + return ( dNumber==0.0 ) ? sFalseStrg : sTrueStrg ; + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_ONOFF ) ) + return ( dNumber==0.0 ) ? sOffStrg : sOnStrg ; + + // analysiere Format-String auf ';', d.h. Format-Strings f"ur + // positive-, negative- und 0-Werte + String sPosFormatStrg = GetPosFormatString( sFormatStrg, bPosFormatFound ); + String sNegFormatStrg = GetNegFormatString( sFormatStrg, bNegFormatFound ); + String s0FormatStrg = Get0FormatString( sFormatStrg, b0FormatFound ); + //String sNullFormatStrg = GetNullFormatString( sFormatStrg, bNullFormatFound ); + + String sReturnStrg; + String sTempStrg; + + if( dNumber==0.0 ) + { + sTempStrg = sFormatStrg; + if( b0FormatFound ) + { + // wurde ggf. Leer-String uebergeben ? + if( s0FormatStrg.Len() == 0 && bPosFormatFound ) + // --> Ja, dann verwende String fuer positive Werte + sTempStrg = sPosFormatStrg; + else + sTempStrg = s0FormatStrg; + } + else if( bPosFormatFound ) + { + // verwende String fuer positive Werte + sTempStrg = sPosFormatStrg; + } + ScanFormatString( dNumber, sTempStrg, sReturnStrg,/*bCreateSign=*/FALSE ); + } + else + { + if( dNumber<0.0 ) + { + if( bNegFormatFound ) + { + // wurde ggf. Leer-String uebergeben ? + if( sNegFormatStrg.Len() == 0 && bPosFormatFound ) + { + // --> Ja, dann verwende String fuer positive Werte + // und setzte Minus-Zeichen davor ! + sTempStrg = String::CreateFromAscii("-"); + sTempStrg += sPosFormatStrg; + } + else + sTempStrg = sNegFormatStrg; + } + else + sTempStrg = sFormatStrg; + // falls KEIN Format-String speziell f"ur negative Werte angegeben + // wurde, so soll das Vorzeichen ausgegeben werden + ScanFormatString( dNumber, sTempStrg, sReturnStrg,/*bCreateSign=*/bNegFormatFound/*sNegFormatStrg!=EMPTYFORMATSTRING*/ ); + } + else // if( dNumber>0.0 ) + { + ScanFormatString( dNumber, + (/*sPosFormatStrg!=EMPTYFORMATSTRING*/bPosFormatFound ? sPosFormatStrg : sFormatStrg), + sReturnStrg,/*bCreateSign=*/FALSE ); + } + } + return sReturnStrg; +} + +BOOL SbxBasicFormater::isBasicFormat( String sFormatStrg ) +{ + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_GENERALNUMBER ) ) + return TRUE; + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_CURRENCY ) ) + return TRUE; + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_FIXED ) ) + return TRUE; + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_STANDARD ) ) + return TRUE; + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_PERCENT ) ) + return TRUE; + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_SCIENTIFIC ) ) + return TRUE; + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_YESNO ) ) + return TRUE; + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_TRUEFALSE ) ) + return TRUE; + if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_ONOFF ) ) + return TRUE; + return FALSE; +} + |