blob: c42fad5f55b6a36025b144538b9fa419f0b9a11d [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the 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, sal_uInt16 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 )
{
sal_uInt16 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, sal_Bool& bOverflow )
{
// wurde ggf ein falscher Index uebergeben --> Aufruf ignorieren
if( nPos<0 )
return;
bOverflow = sal_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 = sal_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 = sal_True;
}
}
}
// rundet in einem String die Ziffer an der angegebenen Stelle
void SbxBasicFormater::StrRoundDigit( String& sStrg, short nPos )
{
sal_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, sal_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
sal_uInt16 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 = sal_True;
return (short)(sSciNumStrg.GetChar( no ) - ASCII_0);
}
short SbxBasicFormater::GetDigitAtPosExpScan( short nPos, sal_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
sal_uInt16 no = 1;
no += nExpExp-nPos;
// Abfrage der ersten (g"ultigen) Ziffer der Zahl --> Flag setzen
if( nPos==nExpExp )
bFoundFirstDigit = sal_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,
sal_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, sal_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 = sal_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, sal_Bool & bFound )
{
bFound = sal_False; // default...
sal_uInt16 nPos = sFormatStrg.Search( FORMAT_SEPARATOR );
if( nPos!=STRING_NOTFOUND )
{
bFound = sal_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, sal_Bool & bFound )
{
bFound = sal_False; // default...
sal_uInt16 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 = sal_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, sal_Bool & bFound )
{
bFound = sal_False; // default...
sal_uInt16 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 = sal_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, sal_Bool & bFound )
{
bFound = sal_False; // default...
sal_uInt16 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 = sal_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,
sal_Bool& bPercent, sal_Bool& bCurrency, sal_Bool& bScientific,
sal_Bool& bGenerateThousandSeparator,
short& nMultipleThousandSeparators )
{
sal_uInt16 nLen;
short nState = 0;
nLen = sFormatStrg.Len();
// initialisiere alle Z"ahler und Flags
nNoOfDigitsLeft = 0;
nNoOfDigitsRight = 0;
nNoOfOptionalDigitsLeft = 0;
nNoOfExponentDigits = 0;
nNoOfOptionalExponentDigits = 0;
bPercent = sal_False;
bCurrency = sal_False;
bScientific = sal_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( sal_uInt16 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 = sal_True;
/* old:
bPercent++;
if( bPercent>1 )
return -2; // ERROR: zu viele Prozent-Zeichen
*/
break;
case '(':
bCurrency = sal_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 = sal_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 = sal_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,
sal_Bool bCreateSign )
{
short /*nErr,*/nNoOfDigitsLeft,nNoOfDigitsRight,nNoOfOptionalDigitsLeft,
nNoOfExponentDigits,nNoOfOptionalExponentDigits,
nMultipleThousandSeparators;
sal_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;
sal_Bool bFirstDigit,bFirstExponentDigit,bFoundFirstDigit,
bIsNegative,bZeroSpaceOn, bSignHappend,bDigitPosNegative;
// Initialisierung der Arbeits-Variablen
bSignHappend = sal_False;
bFoundFirstDigit = sal_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 = sal_True;
bFirstExponentDigit = sal_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 = sal_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 = sal_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 = sal_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 = sal_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 = sal_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 = sal_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;
}
sal_Bool bOverflow = sal_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 )
{
sal_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 )
{
sal_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=*/sal_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=*/sal_False );
}
}
return sReturnStrg;
}
sal_Bool SbxBasicFormater::isBasicFormat( String sFormatStrg )
{
if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_GENERALNUMBER ) )
return sal_True;
if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_CURRENCY ) )
return sal_True;
if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_FIXED ) )
return sal_True;
if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_STANDARD ) )
return sal_True;
if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_PERCENT ) )
return sal_True;
if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_SCIENTIFIC ) )
return sal_True;
if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_YESNO ) )
return sal_True;
if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_TRUEFALSE ) )
return sal_True;
if( sFormatStrg.EqualsIgnoreCaseAscii( BASICFORMAT_ONOFF ) )
return sal_True;
return sal_False;
}