blob: 4530678bd889e2dc372c518bb6e575e7aa627762 [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 <tools/errcode.hxx>
#include <basic/sbx.hxx>
#include "sbxconv.hxx"
#include "unotools/syslocale.hxx"
#if defined ( UNX )
#include <stdlib.h>
#endif
#ifndef _APP_HXX //autogen
#include <vcl/svapp.hxx>
#endif
#include <math.h>
#include <string.h>
#include <ctype.h>
#include "sbxres.hxx"
#include <basic/sbxbase.hxx>
#include <basic/sbxform.hxx>
#include <svtools/svtools.hrc>
#include "basrid.hxx"
#include "runtime.hxx"
#include <svl/zforlist.hxx>
#include <comphelper/processfactory.hxx>
void ImpGetIntntlSep( sal_Unicode& rcDecimalSep, sal_Unicode& rcThousandSep )
{
SvtSysLocale aSysLocale;
const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
rcDecimalSep = rData.getNumDecimalSep().GetBuffer()[0];
rcThousandSep = rData.getNumThousandSep().GetBuffer()[0];
}
// Scannen eines Strings nach BASIC-Konventionen
// Dies entspricht den ueblichen Konventionen, nur dass der Exponent
// auch ein D sein darf, was den Datentyp auf SbxDOUBLE festlegt.
// Die Routine versucht, den Datentyp so klein wie moeglich zu gestalten.
// Das ganze gibt auch noch einen Konversionsfehler, wenn der Datentyp
// Fixed ist und das ganze nicht hineinpasst!
SbxError ImpScan( const ::rtl::OUString& rWSrc, double& nVal, SbxDataType& rType,
sal_uInt16* pLen, sal_Bool bAllowIntntl, sal_Bool bOnlyIntntl )
{
::rtl::OString aBStr( ::rtl::OUStringToOString( rWSrc, RTL_TEXTENCODING_ASCII_US ) );
// Bei International Komma besorgen
char cIntntlComma, cIntntl1000;
char cNonIntntlComma = '.';
sal_Unicode cDecimalSep, cThousandSep = 0;
if( bAllowIntntl || bOnlyIntntl )
{
ImpGetIntntlSep( cDecimalSep, cThousandSep );
cIntntlComma = (char)cDecimalSep;
cIntntl1000 = (char)cThousandSep;
}
// Sonst einfach auch auf . setzen
else
{
cIntntlComma = cNonIntntlComma;
cIntntl1000 = cNonIntntlComma; // Unschaedlich machen
}
// Nur International -> IntnlComma uebernehmen
if( bOnlyIntntl )
{
cNonIntntlComma = cIntntlComma;
cIntntl1000 = (char)cThousandSep;
}
const char* pStart = aBStr.getStr();
const char* p = pStart;
char buf[ 80 ], *q = buf;
sal_Bool bRes = sal_True;
sal_Bool bMinus = sal_False;
nVal = 0;
SbxDataType eScanType = SbxSINGLE;
// Whitespace wech
while( *p &&( *p == ' ' || *p == '\t' ) ) p++;
// Zahl? Dann einlesen und konvertieren.
if( *p == '-' )
p++, bMinus = sal_True;
if( isdigit( *p ) ||( (*p == cNonIntntlComma || *p == cIntntlComma ||
*p == cIntntl1000) && isdigit( *(p+1 ) ) ) )
{
short exp = 0; // >0: Exponentteil
short comma = 0; // >0: Nachkomma
short ndig = 0; // Anzahl Ziffern
short ncdig = 0; // Anzahl Ziffern nach Komma
ByteString aSearchStr( "0123456789DEde" );
// Kommas ergaenzen
aSearchStr += cNonIntntlComma;
if( cIntntlComma != cNonIntntlComma )
aSearchStr += cIntntlComma;
if( bOnlyIntntl )
aSearchStr += cIntntl1000;
const char* pSearchStr = aSearchStr.GetBuffer();
while( strchr( pSearchStr, *p ) && *p )
{
// 1000er-Trenner ueberlesen
if( bOnlyIntntl && *p == cIntntl1000 )
{
p++;
continue;
}
// Komma oder Exponent?
if( *p == cNonIntntlComma || *p == cIntntlComma )
{
// Immer '.' einfuegen, damit atof funktioniert
p++;
if( ++comma > 1 )
continue;
else
*q++ = '.';
}
else if( strchr( "DdEe", *p ) )
{
if( ++exp > 1 )
{
p++; continue;
}
if( toupper( *p ) == 'D' )
eScanType = SbxDOUBLE;
*q++ = 'E'; p++;
// Vorzeichen hinter Exponent?
if( *p == '+' )
p++;
else
if( *p == '-' )
*q++ = *p++;
}
else
{
*q++ = *p++;
if( comma && !exp ) ncdig++;
}
if( !exp ) ndig++;
}
*q = 0;
// Komma, Exponent mehrfach vorhanden?
if( comma > 1 || exp > 1 )
bRes = sal_False;
// Kann auf Integer gefaltet werden?
if( !comma && !exp )
{
if( nVal >= SbxMININT && nVal <= SbxMAXINT )
eScanType = SbxINTEGER;
else if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
eScanType = SbxLONG;
}
nVal = atof( buf );
ndig = ndig - comma;
// zu viele Zahlen fuer SINGLE?
if( ndig > 15 || ncdig > 6 )
eScanType = SbxDOUBLE;
// Typkennung?
if( strchr( "%!&#", *p ) && *p ) p++;
}
// Hex/Oktalzahl? Einlesen und konvertieren:
else if( *p == '&' )
{
p++;
eScanType = SbxLONG;
const char *cmp = "0123456789ABCDEF";
char base = 16;
char ndig = 8;
char xch = *p++;
switch( toupper( xch ) )
{
case 'O': cmp = "01234567"; base = 8; ndig = 11; break;
case 'H': break;
default : bRes = sal_False;
}
long l = 0;
int i;
while( isalnum( *p ) )
{
char ch = sal::static_int_cast< char >( toupper( *p ) );
p++;
if( strchr( cmp, ch ) ) *q++ = ch;
else bRes = sal_False;
}
*q = 0;
for( q = buf; *q; q++ )
{
i =( *q & 0xFF ) - '0';
if( i > 9 ) i -= 7;
l =( l * base ) + i;
if( !ndig-- )
bRes = sal_False;
}
if( *p == '&' ) p++;
nVal = (double) l;
if( l >= SbxMININT && l <= SbxMAXINT )
eScanType = SbxINTEGER;
}
else if ( SbiRuntime::isVBAEnabled() )
{
OSL_TRACE("Reporting error converting");
return SbxERR_CONVERSION;
}
if( pLen )
*pLen = (sal_uInt16) ( p - pStart );
if( !bRes )
return SbxERR_CONVERSION;
if( bMinus )
nVal = -nVal;
rType = eScanType;
return SbxERR_OK;
}
// Schnittstelle fuer CDbl im Basic
SbxError SbxValue::ScanNumIntnl( const String& rSrc, double& nVal, sal_Bool bSingle )
{
SbxDataType t;
sal_uInt16 nLen = 0;
SbxError nRetError = ImpScan( rSrc, nVal, t, &nLen,
/*bAllowIntntl*/sal_False, /*bOnlyIntntl*/sal_True );
// Komplett gelesen?
if( nRetError == SbxERR_OK && nLen != rSrc.Len() )
nRetError = SbxERR_CONVERSION;
if( bSingle )
{
SbxValues aValues( nVal );
nVal = (double)ImpGetSingle( &aValues ); // Hier Error bei Overflow
}
return nRetError;
}
////////////////////////////////////////////////////////////////////////////
static double roundArray[] = {
5.0e+0, 0.5e+0, 0.5e-1, 0.5e-2, 0.5e-3, 0.5e-4, 0.5e-5, 0.5e-6, 0.5e-7,
0.5e-8, 0.5e-9, 0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14,0.5e-15 };
/***************************************************************************
|*
|* void myftoa( double, char *, short, short, sal_Bool, sal_Bool )
|*
|* Beschreibung: Konversion double --> ASCII
|* Parameter: double die Zahl.
|* char * der Zielpuffer
|* short Anzahl Nachkommastellen
|* short Weite des Exponenten( 0=kein E )
|* sal_Bool sal_True: mit 1000er Punkten
|* sal_Bool sal_True: formatfreie Ausgabe
|*
***************************************************************************/
static void myftoa( double nNum, char * pBuf, short nPrec, short nExpWidth,
sal_Bool bPt, sal_Bool bFix, sal_Unicode cForceThousandSep = 0 )
{
short nExp = 0; // Exponent
short nDig = nPrec + 1; // Anzahl Digits in Zahl
short nDec; // Anzahl Vorkommastellen
register int i, digit;
// Komma besorgen
sal_Unicode cDecimalSep, cThousandSep;
ImpGetIntntlSep( cDecimalSep, cThousandSep );
if( cForceThousandSep )
cThousandSep = cForceThousandSep;
// Exponentberechnung:
nExp = 0;
if( nNum > 0.0 )
{
while( nNum < 1.0 ) nNum *= 10.0, nExp--;
while( nNum >= 10.0 ) nNum /= 10.0, nExp++;
}
if( !bFix && !nExpWidth )
nDig = nDig + nExp;
else if( bFix && !nPrec )
nDig = nExp + 1;
// Zahl runden:
if( (nNum += roundArray [( nDig > 16 ) ? 16 : nDig] ) >= 10.0 )
{
nNum = 1.0;
++nExp;
if( !nExpWidth ) ++nDig;
}
// Bestimmung der Vorkommastellen:
if( !nExpWidth )
{
if( nExp < 0 )
{
// #41691: Auch bei bFix eine 0 spendieren
*pBuf++ = '0';
if( nPrec ) *pBuf++ = (char)cDecimalSep;
i = -nExp - 1;
if( nDig <= 0 ) i = nPrec;
while( i-- ) *pBuf++ = '0';
nDec = 0;
}
else
nDec = nExp+1;
}
else
nDec = 1;
// Zahl ausgeben:
if( nDig > 0 )
{
for( i = 0 ; ; ++i )
{
if( i < 16 )
{
digit = (int) nNum;
*pBuf++ = sal::static_int_cast< char >(digit + '0');
nNum =( nNum - digit ) * 10.0;
} else
*pBuf++ = '0';
if( --nDig == 0 ) break;
if( nDec )
{
nDec--;
if( !nDec )
*pBuf++ = (char)cDecimalSep;
else if( !(nDec % 3 ) && bPt )
*pBuf++ = (char)cThousandSep;
}
}
}
// Exponent ausgeben:
if( nExpWidth )
{
if( nExpWidth < 3 ) nExpWidth = 3;
nExpWidth -= 2;
*pBuf++ = 'E';
*pBuf++ =( nExp < 0 ) ?( (nExp = -nExp ), '-' ) : '+';
while( nExpWidth > 3 ) *pBuf++ = '0', nExpWidth--;
if( nExp >= 100 || nExpWidth == 3 )
{
*pBuf++ = sal::static_int_cast< char >(nExp/100 + '0');
nExp %= 100;
}
if( nExp/10 || nExpWidth >= 2 )
*pBuf++ = sal::static_int_cast< char >(nExp/10 + '0');
*pBuf++ = sal::static_int_cast< char >(nExp%10 + '0');
}
*pBuf = 0;
}
// Die Zahl wird unformatiert mit der angegebenen Anzahl NK-Stellen
// aufbereitet. Evtl. wird ein Minus vorangestellt.
// Diese Routine ist public, weil sie auch von den Put-Funktionen
// der Klasse SbxImpSTRING verwendet wird.
#ifdef _MSC_VER
#pragma optimize( "", off )
#pragma warning(disable: 4748) // "... because optimizations are disabled ..."
#endif
void ImpCvtNum( double nNum, short nPrec, ::rtl::OUString& rRes, sal_Bool bCoreString )
{
char *q;
char cBuf[ 40 ], *p = cBuf;
sal_Unicode cDecimalSep, cThousandSep;
ImpGetIntntlSep( cDecimalSep, cThousandSep );
if( bCoreString )
cDecimalSep = '.';
if( nNum < 0.0 ) {
nNum = -nNum;
*p++ = '-';
}
double dMaxNumWithoutExp = (nPrec == 6) ? 1E6 : 1E14;
myftoa( nNum, p, nPrec,( nNum &&( nNum < 1E-1 || nNum >= dMaxNumWithoutExp ) ) ? 4:0,
sal_False, sal_True, cDecimalSep );
// Trailing Zeroes weg:
for( p = cBuf; *p &&( *p != 'E' ); p++ ) {}
q = p; p--;
while( nPrec && *p == '0' ) nPrec--, p--;
if( *p == cDecimalSep ) p--;
while( *q ) *++p = *q++;
*++p = 0;
rRes = ::rtl::OUString::createFromAscii( cBuf );
}
#ifdef _MSC_VER
#pragma optimize( "", on )
#endif
sal_Bool ImpConvStringExt( ::rtl::OUString& rSrc, SbxDataType eTargetType )
{
// Merken, ob ueberhaupt was geaendert wurde
sal_Bool bChanged = sal_False;
::rtl::OUString aNewString;
// Nur Spezial-Fälle behandeln, als Default tun wir nichts
switch( eTargetType )
{
// Bei Fliesskomma International beruecksichtigen
case SbxSINGLE:
case SbxDOUBLE:
case SbxCURRENCY:
{
::rtl::OString aBStr( ::rtl::OUStringToOString( rSrc, RTL_TEXTENCODING_ASCII_US ) );
// Komma besorgen
sal_Unicode cDecimalSep, cThousandSep;
ImpGetIntntlSep( cDecimalSep, cThousandSep );
aNewString = rSrc;
// Ersetzen, wenn DecimalSep kein '.' (nur den ersten)
if( cDecimalSep != (sal_Unicode)'.' )
{
sal_Int32 nPos = aNewString.indexOf( cDecimalSep );
if( nPos != -1 )
{
sal_Unicode* pStr = (sal_Unicode*)aNewString.getStr();
pStr[nPos] = (sal_Unicode)'.';
bChanged = sal_True;
}
}
break;
}
// Bei sal_Bool sal_True und sal_False als String pruefen
case SbxBOOL:
{
if( rSrc.equalsIgnoreAsciiCaseAscii( "true" ) )
{
aNewString = ::rtl::OUString::valueOf( (sal_Int32)SbxTRUE );
bChanged = sal_True;
}
else
if( rSrc.equalsIgnoreAsciiCaseAscii( "false" ) )
{
aNewString = ::rtl::OUString::valueOf( (sal_Int32)SbxFALSE );
bChanged = sal_True;
}
break;
}
default: break;
}
// String bei Aenderung uebernehmen
if( bChanged )
rSrc = aNewString;
return bChanged;
}
// Formatierte Zahlenausgabe
// Der Returnwert ist die Anzahl Zeichen, die aus dem
// Format verwendt wurden.
#ifdef _old_format_code_
// lasse diesen Code vorl"aufig drin, zum 'abgucken'
// der bisherigen Implementation
static sal_uInt16 printfmtnum( double nNum, XubString& rRes, const XubString& rWFmt )
{
const String& rFmt = rWFmt;
char cFill = ' '; // Fuellzeichen
char cPre = 0; // Startzeichen( evtl. "$" )
short nExpDig= 0; // Anzahl Exponentstellen
short nPrec = 0; // Anzahl Nachkommastellen
short nWidth = 0; // Zahlenweite gesamnt
short nLen; // Laenge konvertierte Zahl
sal_Bool bPoint = sal_False; // sal_True: mit 1000er Kommas
sal_Bool bTrail = sal_False; // sal_True, wenn folgendes Minus
sal_Bool bSign = sal_False; // sal_True: immer mit Vorzeichen
sal_Bool bNeg = sal_False; // sal_True: Zahl ist negativ
char cBuf [1024]; // Zahlenpuffer
char * p;
const char* pFmt = rFmt;
rRes.Erase();
// $$ und ** abfangen. Einfach wird als Zeichen ausgegeben.
if( *pFmt == '$' )
if( *++pFmt != '$' ) rRes += '$';
if( *pFmt == '*' )
if( *++pFmt != '*' ) rRes += '*';
switch( *pFmt++ )
{
case 0:
break;
case '+':
bSign = sal_True; nWidth++; break;
case '*':
nWidth++; cFill = '*';
if( *pFmt == '$' ) nWidth++, pFmt++, cPre = '$';
break;
case '$':
nWidth++; cPre = '$'; break;
case '#':
case '.':
case ',':
pFmt--; break;
}
// Vorkomma:
for( ;; )
{
while( *pFmt == '#' ) pFmt++, nWidth++;
// 1000er Kommas?
if( *pFmt == ',' )
{
nWidth++; pFmt++; bPoint = sal_True;
} else break;
}
// Nachkomma:
if( *pFmt == '.' )
{
while( *++pFmt == '#' ) nPrec++;
nWidth += nPrec + 1;
}
// Exponent:
while( *pFmt == '^' )
pFmt++, nExpDig++, nWidth++;
// Folgendes Minus:
if( !bSign && *pFmt == '-' )
pFmt++, bTrail = sal_True;
// Zahl konvertieren:
if( nPrec > 15 ) nPrec = 15;
if( nNum < 0.0 ) nNum = -nNum, bNeg = sal_True;
p = cBuf;
if( bSign ) *p++ = bNeg ? '-' : '+';
myftoa( nNum, p, nPrec, nExpDig, bPoint, sal_False );
nLen = strlen( cBuf );
// Ueberlauf?
if( cPre ) nLen++;
if( nLen > nWidth ) rRes += '%';
else {
nWidth -= nLen;
while( nWidth-- ) rRes += (xub_Unicode)cFill;
if( cPre ) rRes += (xub_Unicode)cPre;
}
rRes += (xub_Unicode*)&(cBuf[0]);
if( bTrail )
rRes += bNeg ? '-' : ' ';
return (sal_uInt16) ( pFmt - (const char*) rFmt );
}
#endif //_old_format_code_
static sal_uInt16 printfmtstr( const XubString& rStr, XubString& rRes, const XubString& rFmt )
{
const xub_Unicode* pStr = rStr.GetBuffer();
const xub_Unicode* pFmtStart = rFmt.GetBuffer();
const xub_Unicode* pFmt = pFmtStart;
rRes.Erase();
switch( *pFmt )
{
case '!':
rRes += *pStr++; pFmt++; break;
case '\\':
do
{
rRes += *pStr ? *pStr++ : static_cast< xub_Unicode >(' ');
pFmt++;
} while( *pFmt != '\\' );
rRes += *pStr ? *pStr++ : static_cast< xub_Unicode >(' ');
pFmt++; break;
case '&':
rRes = rStr;
pFmt++; break;
default:
rRes = rStr;
break;
}
return (sal_uInt16) ( pFmt - pFmtStart );
}
/////////////////////////////////////////////////////////////////////////
sal_Bool SbxValue::Scan( const XubString& rSrc, sal_uInt16* pLen )
{
SbxError eRes = SbxERR_OK;
if( !CanWrite() )
eRes = SbxERR_PROP_READONLY;
else
{
double n;
SbxDataType t;
eRes = ImpScan( rSrc, n, t, pLen );
if( eRes == SbxERR_OK )
{
if( !IsFixed() )
SetType( t );
PutDouble( n );
}
}
if( eRes )
{
SetError( eRes ); return sal_False;
}
else
return sal_True;
}
ResMgr* implGetResMgr( void )
{
static ResMgr* pResMgr = NULL;
if( !pResMgr )
{
::com::sun::star::lang::Locale aLocale = Application::GetSettings().GetUILocale();
pResMgr = ResMgr::CreateResMgr(CREATEVERSIONRESMGR_NAME(sb), aLocale );
}
return pResMgr;
}
class SbxValueFormatResId : public ResId
{
public:
SbxValueFormatResId( sal_uInt16 nId )
: ResId( nId, *implGetResMgr() )
{}
};
enum VbaFormatType
{
VBA_FORMAT_TYPE_OFFSET, // standard number format
VBA_FORMAT_TYPE_USERDEFINED, // user defined number format
VBA_FORMAT_TYPE_NULL
};
struct VbaFormatInfo
{
VbaFormatType meType;
const char* mpVbaFormat; // Format string in vba
NfIndexTableOffset meOffset; // SvNumberFormatter format index, if meType = VBA_FORMAT_TYPE_OFFSET
const char* mpOOoFormat; // if meType = VBA_FORMAT_TYPE_USERDEFINED
};
#define VBA_FORMAT_OFFSET( pcUtf8, eOffset ) \
{ VBA_FORMAT_TYPE_OFFSET, pcUtf8, eOffset, 0 }
#define VBA_FORMAT_USERDEFINED( pcUtf8, pcDefinedUtf8 ) \
{ VBA_FORMAT_TYPE_USERDEFINED, pcUtf8, NF_NUMBER_STANDARD, pcDefinedUtf8 }
static VbaFormatInfo pFormatInfoTable[] =
{
VBA_FORMAT_OFFSET( "Long Date", NF_DATE_SYSTEM_LONG ),
VBA_FORMAT_USERDEFINED( "Medium Date", "DD-MMM-YY" ),
VBA_FORMAT_OFFSET( "Short Date", NF_DATE_SYSTEM_SHORT ),
VBA_FORMAT_USERDEFINED( "Long Time", "H:MM:SS AM/PM" ),
VBA_FORMAT_OFFSET( "Medium Time", NF_TIME_HHMMAMPM ),
VBA_FORMAT_OFFSET( "Short Time", NF_TIME_HHMM ),
VBA_FORMAT_OFFSET( "ddddd", NF_DATE_SYSTEM_SHORT ),
VBA_FORMAT_OFFSET( "dddddd", NF_DATE_SYSTEM_LONG ),
VBA_FORMAT_USERDEFINED( "ttttt", "H:MM:SS AM/PM" ),
VBA_FORMAT_OFFSET( "ww", NF_DATE_WW ),
{ VBA_FORMAT_TYPE_NULL, 0, NF_INDEX_TABLE_ENTRIES, 0 }
};
VbaFormatInfo* getFormatInfo( const String& rFmt )
{
VbaFormatInfo* pInfo = NULL;
sal_Int16 i = 0;
while( (pInfo = pFormatInfoTable + i )->mpVbaFormat != NULL )
{
if( rFmt.EqualsIgnoreCaseAscii( pInfo->mpVbaFormat ) )
break;
i++;
}
return pInfo;
}
#define VBAFORMAT_GENERALDATE "General Date"
#define VBAFORMAT_C "c"
#define VBAFORMAT_N "n"
#define VBAFORMAT_NN "nn"
#define VBAFORMAT_W "w"
#define VBAFORMAT_Y "y"
#define VBAFORMAT_LOWERCASE "<"
#define VBAFORMAT_UPPERCASE ">"
// From methods1.cxx
sal_Int16 implGetWeekDay( double aDate, bool bFirstDayParam = false, sal_Int16 nFirstDay = 0 );
// from methods.cxx
sal_Int16 implGetMinute( double dDate );
sal_Int16 implGetDateYear( double aDate );
sal_Bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, double& rdRet );
void SbxValue::Format( XubString& rRes, const XubString* pFmt ) const
{
short nComma = 0;
double d = 0;
// pflin, It is better to use SvNumberFormatter to handle the date/time/number format.
// the SvNumberFormatter output is mostly compatible with
// VBA output besides the OOo-basic output
if( pFmt && !SbxBasicFormater::isBasicFormat( *pFmt ) )
{
String aStr = GetString();
SvtSysLocale aSysLocale;
const CharClass& rCharClass = aSysLocale.GetCharClass();
if( pFmt->EqualsIgnoreCaseAscii( VBAFORMAT_LOWERCASE ) )
{
rCharClass.toLower( aStr );
rRes = aStr;
return;
}
if( pFmt->EqualsIgnoreCaseAscii( VBAFORMAT_UPPERCASE ) )
{
rCharClass.toUpper( aStr );
rRes = aStr;
return;
}
LanguageType eLangType = GetpApp()->GetSettings().GetLanguage();
com::sun::star::uno::Reference< com::sun::star::lang::XMultiServiceFactory >
xFactory = comphelper::getProcessServiceFactory();
SvNumberFormatter aFormatter( xFactory, eLangType );
sal_uInt32 nIndex;
xub_StrLen nCheckPos = 0;
short nType;
double nNumber;
Color* pCol;
sal_Bool bSuccess = aFormatter.IsNumberFormat( aStr, nIndex, nNumber );
// number format, use SvNumberFormatter to handle it.
if( bSuccess )
{
String aFmtStr = *pFmt;
VbaFormatInfo* pInfo = getFormatInfo( aFmtStr );
if( pInfo && pInfo->meType != VBA_FORMAT_TYPE_NULL )
{
if( pInfo->meType == VBA_FORMAT_TYPE_OFFSET )
{
nIndex = aFormatter.GetFormatIndex( pInfo->meOffset, eLangType );
}
else
{
aFmtStr.AssignAscii( pInfo->mpOOoFormat );
aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
}
aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
}
else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_GENERALDATE )
|| aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_C ))
{
if( nNumber <=-1.0 || nNumber >= 1.0 )
{
// short date
nIndex = aFormatter.GetFormatIndex( NF_DATE_SYSTEM_SHORT, eLangType );
aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
// long time
if( floor( nNumber ) != nNumber )
{
aFmtStr.AssignAscii( "H:MM:SS AM/PM" );
aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
String aTime;
aFormatter.GetOutputString( nNumber, nIndex, aTime, &pCol );
rRes.AppendAscii(" ");
rRes += aTime;
}
}
else
{
// long time only
aFmtStr.AssignAscii( "H:MM:SS AM/PM" );
aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
}
}
else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_N )
|| aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_NN ))
{
sal_Int32 nMin = implGetMinute( nNumber );
if( nMin < 10 && aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_NN ) )
{
// Minute in two digits
sal_Unicode* p = rRes.AllocBuffer( 2 );
*p++ = '0';
*p = sal_Unicode( '0' + nMin );
}
else
{
rRes = String::CreateFromInt32( nMin );
}
}
else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_W ))
{
sal_Int32 nWeekDay = implGetWeekDay( nNumber );
rRes = String::CreateFromInt32( nWeekDay );
}
else if( aFmtStr.EqualsIgnoreCaseAscii( VBAFORMAT_Y ))
{
sal_Int16 nYear = implGetDateYear( nNumber );
double dBaseDate;
implDateSerial( nYear, 1, 1, dBaseDate );
sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate );
rRes = String::CreateFromInt32( nYear32 );
}
else
{
aFormatter.PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType );
aFormatter.GetOutputString( nNumber, nIndex, rRes, &pCol );
}
return;
}
}
SbxDataType eType = GetType();
switch( eType )
{
case SbxCHAR:
case SbxBYTE:
case SbxINTEGER:
case SbxUSHORT:
case SbxLONG:
case SbxULONG:
case SbxINT:
case SbxUINT:
case SbxNULL: // #45929 NULL mit durchschummeln
nComma = 0; goto cvt;
case SbxSINGLE:
nComma = 6; goto cvt;
case SbxDOUBLE:
nComma = 14;
cvt:
if( eType != SbxNULL )
d = GetDouble();
// #45355 weiterer Einsprungpunkt fuer isnumeric-String
cvt2:
if( pFmt )
{
// hole die 'statischen' Daten f"ur Sbx
SbxAppData* pData = GetSbxData_Impl();
LanguageType eLangType = GetpApp()->GetSettings().GetLanguage();
if( pData->pBasicFormater )
{
if( pData->eBasicFormaterLangType != eLangType )
{
delete pData->pBasicFormater;
pData->pBasicFormater = NULL;
}
}
pData->eBasicFormaterLangType = eLangType;
// falls bisher noch kein BasicFormater-Objekt
// existiert, so erzeuge dieses
if( !pData->pBasicFormater )
{
SvtSysLocale aSysLocale;
const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
sal_Unicode cComma = rData.getNumDecimalSep().GetBuffer()[0];
sal_Unicode c1000 = rData.getNumThousandSep().GetBuffer()[0];
String aCurrencyStrg = rData.getCurrSymbol();
// Initialisierung des Basic-Formater-Hilfsobjekts:
// hole die Resourcen f"ur die vordefinierten Ausgaben
// des Format()-Befehls, z.B. f"ur "On/Off".
String aOnStrg = String( SbxValueFormatResId(
STR_BASICKEY_FORMAT_ON ) );
String aOffStrg = String( SbxValueFormatResId(
STR_BASICKEY_FORMAT_OFF) );
String aYesStrg = String( SbxValueFormatResId(
STR_BASICKEY_FORMAT_YES) );
String aNoStrg = String( SbxValueFormatResId(
STR_BASICKEY_FORMAT_NO) );
String aTrueStrg = String( SbxValueFormatResId(
STR_BASICKEY_FORMAT_TRUE) );
String aFalseStrg = String( SbxValueFormatResId(
STR_BASICKEY_FORMAT_FALSE) );
String aCurrencyFormatStrg = String( SbxValueFormatResId(
STR_BASICKEY_FORMAT_CURRENCY) );
// erzeuge das Basic-Formater-Objekt
pData->pBasicFormater
= new SbxBasicFormater( cComma,c1000,aOnStrg,aOffStrg,
aYesStrg,aNoStrg,aTrueStrg,aFalseStrg,
aCurrencyStrg,aCurrencyFormatStrg );
}
// Bem.: Aus Performance-Gr"unden wird nur EIN BasicFormater-
// Objekt erzeugt und 'gespeichert', dadurch erspart man
// sich das teure Resourcen-Laden (f"ur landesspezifische
// vordefinierte Ausgaben, z.B. "On/Off") und die st"andige
// String-Erzeugungs Operationen.
// ABER: dadurch ist dieser Code NICHT multithreading f"ahig !
// hier gibt es Probleme mit ;;;Null, da diese Methode nur aufgerufen
// wird, wenn der SbxValue eine Zahl ist !!!
// dazu koennte: pData->pBasicFormater->BasicFormatNull( *pFmt ); aufgerufen werden !
if( eType != SbxNULL )
{
rRes = pData->pBasicFormater->BasicFormat( d ,*pFmt );
}
else
{
rRes = pData->pBasicFormater->BasicFormatNull( *pFmt );
}
// Die alte Implementierung:
//old: printfmtnum( GetDouble(), rRes, *pFmt );
}
else
{
::rtl::OUString aTmpString( rRes );
ImpCvtNum( GetDouble(), nComma, aTmpString );
rRes = aTmpString;
}
break;
case SbxSTRING:
if( pFmt )
{
// #45355 wenn es numerisch ist, muss gewandelt werden
if( IsNumericRTL() )
{
ScanNumIntnl( GetString(), d, /*bSingle*/sal_False );
goto cvt2;
}
else
{
// Sonst String-Formatierung
printfmtstr( GetString(), rRes, *pFmt );
}
}
else
rRes = GetString();
break;
default:
rRes = GetString();
}
}