summaryrefslogtreecommitdiffstats
path: root/basic/source/sbx/sbxscan.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /basic/source/sbx/sbxscan.cxx
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'basic/source/sbx/sbxscan.cxx')
-rw-r--r--basic/source/sbx/sbxscan.cxx900
1 files changed, 900 insertions, 0 deletions
diff --git a/basic/source/sbx/sbxscan.cxx b/basic/source/sbx/sbxscan.cxx
new file mode 100644
index 000000000..24e3dba5e
--- /dev/null
+++ b/basic/source/sbx/sbxscan.cxx
@@ -0,0 +1,900 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_features.h>
+
+#include <vcl/errcode.hxx>
+#include <unotools/resmgr.hxx>
+#include "sbxconv.hxx"
+
+#include <unotools/syslocale.hxx>
+#include <unotools/charclass.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+#include <math.h>
+
+#include <sbxbase.hxx>
+#include <sbintern.hxx>
+#include <sbxform.hxx>
+
+#include <date.hxx>
+#include <runtime.hxx>
+#include <strings.hrc>
+
+#include <rtl/character.hxx>
+#include <sal/log.hxx>
+#include <svl/zforlist.hxx>
+
+
+void ImpGetIntntlSep( sal_Unicode& rcDecimalSep, sal_Unicode& rcThousandSep, sal_Unicode& rcDecimalSepAlt )
+{
+ SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
+ rcDecimalSep = rData.getNumDecimalSep()[0];
+ rcThousandSep = rData.getNumThousandSep()[0];
+ rcDecimalSepAlt = rData.getNumDecimalSepAlt().toChar();
+}
+
+
+/** NOTE: slightly differs from strchr() in that it does not consider the
+ terminating NULL character to be part of the string and returns bool
+ instead of pointer, if character is 0 returns false.
+ */
+static bool ImpStrChr( const sal_Unicode* p, sal_Unicode c )
+{
+ if (!c)
+ return false;
+ while (*p)
+ {
+ if (*p++ == c)
+ return true;
+ }
+ return false;
+}
+
+
+// scanning a string according to BASIC-conventions
+// but exponent may also be a D, so data type is SbxDOUBLE
+// conversion error if data type is fixed and it doesn't fit
+
+ErrCode ImpScan( const OUString& rWSrc, double& nVal, SbxDataType& rType,
+ sal_uInt16* pLen, bool bOnlyIntntl )
+{
+ sal_Unicode cIntntlDecSep, cIntntlGrpSep, cIntntlDecSepAlt;
+ sal_Unicode cNonIntntlDecSep = '.';
+ if( bOnlyIntntl )
+ {
+ ImpGetIntntlSep( cIntntlDecSep, cIntntlGrpSep, cIntntlDecSepAlt );
+ cNonIntntlDecSep = cIntntlDecSep;
+ // Ensure that the decimal separator alternative is really one.
+ if (cIntntlDecSepAlt && cIntntlDecSepAlt == cNonIntntlDecSep)
+ cIntntlDecSepAlt = 0;
+ }
+ else
+ {
+ cIntntlDecSep = cNonIntntlDecSep;
+ cIntntlGrpSep = 0; // no group separator accepted in non-i18n
+ cIntntlDecSepAlt = 0;
+ }
+
+ const sal_Unicode* const pStart = rWSrc.getStr();
+ const sal_Unicode* p = pStart;
+ OUStringBuffer aBuf( rWSrc.getLength());
+ bool bRes = true;
+ bool bMinus = false;
+ nVal = 0;
+ SbxDataType eScanType = SbxSINGLE;
+ while( *p == ' ' || *p == '\t' )
+ p++;
+ if( *p == '-' )
+ {
+ p++;
+ bMinus = true;
+ }
+ if( rtl::isAsciiDigit( *p ) || ((*p == cNonIntntlDecSep || *p == cIntntlDecSep ||
+ (cIntntlDecSep && *p == cIntntlGrpSep) || (cIntntlDecSepAlt && *p == cIntntlDecSepAlt)) &&
+ rtl::isAsciiDigit( *(p+1) )))
+ {
+ // tdf#118442: Whitespace and minus are skipped; store the position to calculate index
+ const sal_Unicode* const pDigitsStart = p;
+ short exp = 0;
+ short decsep = 0;
+ short ndig = 0;
+ short ncdig = 0; // number of digits after decimal point
+ OUStringBuffer aSearchStr("0123456789DEde");
+ aSearchStr.append(cNonIntntlDecSep);
+ if( cIntntlDecSep != cNonIntntlDecSep )
+ aSearchStr.append(cIntntlDecSep);
+ if( cIntntlDecSepAlt && cIntntlDecSepAlt != cNonIntntlDecSep )
+ aSearchStr.append(cIntntlDecSepAlt);
+ if( bOnlyIntntl )
+ aSearchStr.append(cIntntlGrpSep);
+ const sal_Unicode* const pSearchStr = aSearchStr.getStr();
+ const sal_Unicode pDdEe[] = { 'D', 'd', 'E', 'e', 0 };
+ while( ImpStrChr( pSearchStr, *p ) )
+ {
+ aBuf.append( *p );
+ if( bOnlyIntntl && *p == cIntntlGrpSep )
+ {
+ p++;
+ continue;
+ }
+ if( *p == cNonIntntlDecSep || *p == cIntntlDecSep || (cIntntlDecSepAlt && *p == cIntntlDecSepAlt) )
+ {
+ // Use the separator that is passed to stringToDouble()
+ aBuf[p - pDigitsStart] = cIntntlDecSep;
+ p++;
+ if( ++decsep > 1 )
+ continue;
+ }
+ else if( ImpStrChr( pDdEe, *p ) )
+ {
+ if( ++exp > 1 )
+ {
+ p++;
+ continue;
+ }
+ if( *p == 'D' || *p == 'd' )
+ eScanType = SbxDOUBLE;
+ aBuf[p - pDigitsStart] = 'E';
+ p++;
+ if (*p == '+')
+ ++p;
+ else if (*p == '-')
+ {
+ aBuf.append('-');
+ ++p;
+ }
+ }
+ else
+ {
+ p++;
+ if( decsep && !exp )
+ ncdig++;
+ }
+ if( !exp )
+ ndig++;
+ }
+
+ if( decsep > 1 || exp > 1 )
+ bRes = false;
+
+ OUString aBufStr( aBuf.makeStringAndClear());
+ rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
+ sal_Int32 nParseEnd = 0;
+ nVal = rtl::math::stringToDouble( aBufStr, cIntntlDecSep, cIntntlGrpSep, &eStatus, &nParseEnd );
+ if( eStatus != rtl_math_ConversionStatus_Ok || nParseEnd != aBufStr.getLength() )
+ bRes = false;
+
+ if( !decsep && !exp )
+ {
+ if( nVal >= SbxMININT && nVal <= SbxMAXINT )
+ eScanType = SbxINTEGER;
+ else if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
+ eScanType = SbxLONG;
+ }
+
+ ndig = ndig - decsep;
+ // too many numbers for SINGLE?
+ if( ndig > 15 || ncdig > 6 )
+ eScanType = SbxDOUBLE;
+
+ // type detection?
+ const sal_Unicode pTypes[] = { '%', '!', '&', '#', 0 };
+ if( ImpStrChr( pTypes, *p ) )
+ p++;
+ }
+ // hex/octal number? read in and convert:
+ else if( *p == '&' )
+ {
+ p++;
+ eScanType = SbxLONG;
+ OUString aCmp( "0123456789ABCDEFabcdef" );
+ char base = 16;
+ char ndig = 8;
+ switch( *p++ )
+ {
+ case 'O':
+ case 'o':
+ aCmp = "01234567";
+ base = 8;
+ ndig = 11;
+ break;
+ case 'H':
+ case 'h':
+ break;
+ default :
+ bRes = false;
+ }
+ const sal_Unicode* const pCmp = aCmp.getStr();
+ while( rtl::isAsciiAlphanumeric( *p ) ) /* XXX: really munge all alnum also when error? */
+ {
+ sal_Unicode ch = *p;
+ if( ImpStrChr( pCmp, ch ) )
+ {
+ if (ch > 0x60)
+ ch -= 0x20; // convert ASCII lower to upper case
+ aBuf.append( ch );
+ }
+ else
+ bRes = false;
+ p++;
+ }
+ OUString aBufStr( aBuf.makeStringAndClear());
+ sal_Int32 l = 0;
+ for( const sal_Unicode* q = aBufStr.getStr(); bRes && *q; q++ )
+ {
+ int i = *q - '0';
+ if( i > 9 )
+ i -= 7; // 'A'-'0' = 17 => 10, ...
+ l = ( l * base ) + i;
+ if( !ndig-- )
+ bRes = false;
+ }
+ if( *p == '&' )
+ p++;
+ nVal = static_cast<double>(l);
+ if( l >= SbxMININT && l <= SbxMAXINT )
+ eScanType = SbxINTEGER;
+ }
+#if HAVE_FEATURE_SCRIPTING
+ else if ( SbiRuntime::isVBAEnabled() )
+ {
+ return ERRCODE_BASIC_CONVERSION;
+ }
+#endif
+ if( pLen )
+ *pLen = static_cast<sal_uInt16>( p - pStart );
+ if( !bRes )
+ return ERRCODE_BASIC_CONVERSION;
+ if( bMinus )
+ nVal = -nVal;
+ rType = eScanType;
+ return ERRCODE_NONE;
+}
+
+// port for CDbl in the Basic
+ErrCode SbxValue::ScanNumIntnl( const OUString& rSrc, double& nVal, bool bSingle )
+{
+ SbxDataType t;
+ sal_uInt16 nLen = 0;
+ ErrCode nRetError = ImpScan( rSrc, nVal, t, &nLen,
+ /*bOnlyIntntl*/true );
+ // read completely?
+ if( nRetError == ERRCODE_NONE && nLen != rSrc.getLength() )
+ {
+ nRetError = ERRCODE_BASIC_CONVERSION;
+ }
+ if( bSingle )
+ {
+ SbxValues aValues( nVal );
+ nVal = static_cast<double>(ImpGetSingle( &aValues )); // here error at overflow
+ }
+ return nRetError;
+}
+
+
+static const 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, bool, bool )
+|*
+|* description: conversion double --> ASCII
+|* parameters: double the number
+|* char * target buffer
+|* short number of positions after decimal point
+|* short range of the exponent ( 0=no E )
+|* bool true: with 1000-separators
+|* bool true: output without formatting
+|*
+ */
+
+static void myftoa( double nNum, char * pBuf, short nPrec, short nExpWidth )
+{
+
+ short nExp = 0;
+ short nDig = nPrec + 1;
+ short nDec; // number of positions before decimal point
+ int i;
+
+ sal_Unicode cDecimalSep, cThousandSep, cDecimalSepAlt;
+ ImpGetIntntlSep( cDecimalSep, cThousandSep, cDecimalSepAlt );
+
+ // compute exponent
+ nExp = 0;
+ if( nNum > 0.0 )
+ {
+ while( nNum < 1.0 )
+ {
+ nNum *= 10.0;
+ nExp--;
+ }
+ while( nNum >= 10.0 )
+ {
+ nNum /= 10.0;
+ nExp++;
+ }
+ }
+ if( !nPrec )
+ nDig = nExp + 1;
+
+ // round number
+ if( (nNum += roundArray [std::min<short>( nDig, 16 )] ) >= 10.0 )
+ {
+ nNum = 1.0;
+ ++nExp;
+ if( !nExpWidth ) ++nDig;
+ }
+
+ // determine positions before decimal point
+ if( !nExpWidth )
+ {
+ if( nExp < 0 )
+ {
+ // #41691: also a 0 at bFix
+ *pBuf++ = '0';
+ if( nPrec ) *pBuf++ = static_cast<char>(cDecimalSep);
+ i = -nExp - 1;
+ if( nDig <= 0 ) i = nPrec;
+ while( i-- ) *pBuf++ = '0';
+ nDec = 0;
+ }
+ else
+ nDec = nExp+1;
+ }
+ else
+ nDec = 1;
+
+ // output number
+ if( nDig > 0 )
+ {
+ int digit;
+ for( i = 0 ; ; ++i )
+ {
+ if( i < 16 )
+ {
+ digit = static_cast<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++ = static_cast<char>(cDecimalSep);
+ }
+ }
+ }
+
+ // output exponent
+ if( nExpWidth )
+ {
+ if( nExpWidth < 3 ) nExpWidth = 3;
+ nExpWidth -= 2;
+ *pBuf++ = 'E';
+ if ( nExp < 0 )
+ {
+ nExp = -nExp;
+ *pBuf++ = '-';
+ }
+ else
+ *pBuf++ = '+';
+ 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;
+}
+
+// The number is prepared unformattedly with the given number of
+// NK-positions. A leading minus is added if applicable.
+// This routine is public because it's also used by the Put-functions
+// in the class SbxImpSTRING.
+
+void ImpCvtNum( double nNum, short nPrec, OUString& rRes, bool bCoreString )
+{
+ char *q;
+ char cBuf[ 40 ], *p = cBuf;
+
+ sal_Unicode cDecimalSep, cThousandSep, cDecimalSepAlt;
+ ImpGetIntntlSep( cDecimalSep, cThousandSep, cDecimalSepAlt );
+ 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 );
+ // remove trailing zeros
+ 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 = OUString::createFromAscii( cBuf );
+}
+
+bool ImpConvStringExt( OUString& rSrc, SbxDataType eTargetType )
+{
+ bool bChanged = false;
+ OUString aNewString;
+
+ // only special cases are handled, nothing on default
+ switch( eTargetType )
+ {
+ // consider international for floating point
+ case SbxSINGLE:
+ case SbxDOUBLE:
+ case SbxCURRENCY:
+ {
+ sal_Unicode cDecimalSep, cThousandSep, cDecimalSepAlt;
+ ImpGetIntntlSep( cDecimalSep, cThousandSep, cDecimalSepAlt );
+ aNewString = rSrc;
+
+ if( cDecimalSep != '.' || (cDecimalSepAlt && cDecimalSepAlt != '.') )
+ {
+ sal_Int32 nPos = aNewString.indexOf( cDecimalSep );
+ if( nPos == -1 && cDecimalSepAlt )
+ nPos = aNewString.indexOf( cDecimalSepAlt );
+ if( nPos != -1 )
+ {
+ sal_Unicode* pStr = const_cast<sal_Unicode*>(aNewString.getStr());
+ pStr[nPos] = '.';
+ bChanged = true;
+ }
+ }
+ break;
+ }
+
+ // check as string in case of sal_Bool sal_True and sal_False
+ case SbxBOOL:
+ {
+ if( rSrc.equalsIgnoreAsciiCase("true") )
+ {
+ aNewString = OUString::number( SbxTRUE );
+ bChanged = true;
+ }
+ else if( rSrc.equalsIgnoreAsciiCase("false") )
+ {
+ aNewString = OUString::number( SbxFALSE );
+ bChanged = true;
+ }
+ break;
+ }
+ default: break;
+ }
+
+ if( bChanged )
+ rSrc = aNewString;
+ return bChanged;
+}
+
+
+// formatted number output
+// the return value is the number of characters used
+// from the format
+
+static sal_uInt16 printfmtstr( const OUString& rStr, OUString& rRes, const OUString& rFmt )
+{
+ OUStringBuffer aTemp;
+ const sal_Unicode* pStr = rStr.getStr();
+ const sal_Unicode* pFmtStart = rFmt.getStr();
+ const sal_Unicode* pFmt = pFmtStart;
+
+ switch( *pFmt )
+ {
+ case '!':
+ aTemp.append(*pStr++);
+ pFmt++;
+ break;
+ case '\\':
+ do
+ {
+ aTemp.append( *pStr ? *pStr++ : u' ');
+ pFmt++;
+ }
+ while( *pFmt && *pFmt != '\\' );
+ aTemp.append(*pStr ? *pStr++ : u' ');
+ pFmt++; break;
+ case '&':
+ aTemp = rStr;
+ pFmt++; break;
+ default:
+ aTemp = rStr;
+ break;
+ }
+ rRes = aTemp.makeStringAndClear();
+ return static_cast<sal_uInt16>( pFmt - pFmtStart );
+}
+
+
+bool SbxValue::Scan( const OUString& rSrc, sal_uInt16* pLen )
+{
+ ErrCode eRes = ERRCODE_NONE;
+ if( !CanWrite() )
+ {
+ eRes = ERRCODE_BASIC_PROP_READONLY;
+ }
+ else
+ {
+ double n;
+ SbxDataType t;
+ eRes = ImpScan( rSrc, n, t, pLen, true );
+ if( eRes == ERRCODE_NONE )
+ {
+ if( !IsFixed() )
+ {
+ SetType( t );
+ }
+ PutDouble( n );
+ }
+ }
+ if( eRes )
+ {
+ SetError( eRes );
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+std::locale BasResLocale()
+{
+ return Translate::Create("sb");
+}
+
+OUString BasResId(const char *pId)
+{
+ return Translate::get(pId, BasResLocale());
+}
+
+namespace
+{
+
+enum class VbaFormatType
+{
+ Offset, // standard number format
+ UserDefined, // user defined number format
+ Null
+};
+
+struct VbaFormatInfo
+{
+ VbaFormatType meType;
+ OUStringLiteral mpVbaFormat; // Format string in vba
+ NfIndexTableOffset meOffset; // SvNumberFormatter format index, if meType = VbaFormatType::Offset
+ const char* mpOOoFormat; // if meType = VbaFormatType::UserDefined
+};
+
+#if HAVE_FEATURE_SCRIPTING
+const VbaFormatInfo pFormatInfoTable[] =
+{
+ { VbaFormatType::Offset, OUStringLiteral("Long Date"), NF_DATE_SYSTEM_LONG, nullptr },
+ { VbaFormatType::UserDefined, OUStringLiteral("Medium Date"), NF_NUMBER_STANDARD, "DD-MMM-YY" },
+ { VbaFormatType::Offset, OUStringLiteral("Short Date"), NF_DATE_SYSTEM_SHORT, nullptr },
+ { VbaFormatType::UserDefined, OUStringLiteral("Long Time"), NF_NUMBER_STANDARD, "H:MM:SS AM/PM" },
+ { VbaFormatType::Offset, OUStringLiteral("Medium Time"), NF_TIME_HHMMAMPM, nullptr },
+ { VbaFormatType::Offset, OUStringLiteral("Short Time"), NF_TIME_HHMM, nullptr },
+ { VbaFormatType::Offset, OUStringLiteral("ddddd"), NF_DATE_SYSTEM_SHORT, nullptr },
+ { VbaFormatType::Offset, OUStringLiteral("dddddd"), NF_DATE_SYSTEM_LONG, nullptr },
+ { VbaFormatType::UserDefined, OUStringLiteral("ttttt"), NF_NUMBER_STANDARD, "H:MM:SS AM/PM" },
+ { VbaFormatType::Offset, OUStringLiteral("ww"), NF_DATE_WW, nullptr },
+ { VbaFormatType::Null, OUStringLiteral(""), NF_INDEX_TABLE_ENTRIES, nullptr }
+};
+
+const VbaFormatInfo* getFormatInfo( const OUString& rFmt )
+{
+ const VbaFormatInfo* pInfo = pFormatInfoTable;
+ while( pInfo->meType != VbaFormatType::Null )
+ {
+ if( rFmt.equalsIgnoreAsciiCase( pInfo->mpVbaFormat ) )
+ break;
+ ++pInfo;
+ }
+ return pInfo;
+}
+#endif
+
+} // namespace
+
+#if HAVE_FEATURE_SCRIPTING
+#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 ">"
+#endif
+
+void SbxValue::Format( OUString& rRes, const OUString* 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 HAVE_FEATURE_SCRIPTING
+ if( pFmt && !SbxBasicFormater::isBasicFormat( *pFmt ) )
+ {
+ OUString aStr = GetOUString();
+
+ SvtSysLocale aSysLocale;
+ const CharClass& rCharClass = aSysLocale.GetCharClass();
+
+ if( pFmt->equalsIgnoreAsciiCase( VBAFORMAT_LOWERCASE ) )
+ {
+ rRes = rCharClass.lowercase( aStr );
+ return;
+ }
+ if( pFmt->equalsIgnoreAsciiCase( VBAFORMAT_UPPERCASE ) )
+ {
+ rRes = rCharClass.uppercase( aStr );
+ return;
+ }
+
+ LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
+ std::shared_ptr<SvNumberFormatter> pFormatter;
+ if (GetSbData()->pInst)
+ {
+ pFormatter = GetSbData()->pInst->GetNumberFormatter();
+ }
+ else
+ {
+ sal_uInt32 n; // Dummy
+ pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n );
+ }
+
+ // Passing an index of a locale switches IsNumberFormat() to use that
+ // locale in case the formatter wasn't default created with it.
+ sal_uInt32 nIndex = pFormatter->GetStandardIndex( eLangType);
+ double nNumber;
+ Color* pCol;
+
+ bool bSuccess = pFormatter->IsNumberFormat( aStr, nIndex, nNumber );
+
+ // number format, use SvNumberFormatter to handle it.
+ if( bSuccess )
+ {
+ sal_Int32 nCheckPos = 0;
+ SvNumFormatType nType;
+ OUString aFmtStr = *pFmt;
+ const VbaFormatInfo* pInfo = getFormatInfo( aFmtStr );
+ if( pInfo->meType != VbaFormatType::Null )
+ {
+ if( pInfo->meType == VbaFormatType::Offset )
+ {
+ nIndex = pFormatter->GetFormatIndex( pInfo->meOffset, eLangType );
+ }
+ else
+ {
+ aFmtStr = OUString::createFromAscii(pInfo->mpOOoFormat);
+ pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType, true);
+ }
+ pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
+ }
+ else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_GENERALDATE )
+ || aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_C ))
+ {
+ if( nNumber <=-1.0 || nNumber >= 1.0 )
+ {
+ // short date
+ nIndex = pFormatter->GetFormatIndex( NF_DATE_SYSTEM_SHORT, eLangType );
+ pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
+
+ // long time
+ if( floor( nNumber ) != nNumber )
+ {
+ aFmtStr = "H:MM:SS AM/PM";
+ pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType, true);
+ OUString aTime;
+ pFormatter->GetOutputString( nNumber, nIndex, aTime, &pCol );
+ rRes += " " + aTime;
+ }
+ }
+ else
+ {
+ // long time only
+ aFmtStr = "H:MM:SS AM/PM";
+ pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType, true);
+ pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
+ }
+ }
+ else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_N ) ||
+ aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_NN ))
+ {
+ sal_Int32 nMin = implGetMinute( nNumber );
+ if( nMin < 10 && aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_NN ))
+ {
+ // Minute in two digits
+ sal_Unicode aBuf[2];
+ aBuf[0] = '0';
+ aBuf[1] = '0' + nMin;
+ rRes = OUString(aBuf, SAL_N_ELEMENTS(aBuf));
+ }
+ else
+ {
+ rRes = OUString::number(nMin);
+ }
+ }
+ else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_W ))
+ {
+ sal_Int32 nWeekDay = implGetWeekDay( nNumber );
+ rRes = OUString::number(nWeekDay);
+ }
+ else if( aFmtStr.equalsIgnoreAsciiCase( VBAFORMAT_Y ))
+ {
+ sal_Int16 nYear = implGetDateYear( nNumber );
+ double dBaseDate;
+ implDateSerial( nYear, 1, 1, true, SbDateCorrection::None, dBaseDate );
+ sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate );
+ rRes = OUString::number(nYear32);
+ }
+ else
+ {
+ pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH, eLangType, true);
+ pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
+ }
+
+ return;
+ }
+ }
+#endif
+
+ 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 with a little cheating
+ nComma = 0; goto cvt;
+ case SbxSINGLE:
+ nComma = 6; goto cvt;
+ case SbxDOUBLE:
+ nComma = 14;
+
+ cvt:
+ if( eType != SbxNULL )
+ {
+ d = GetDouble();
+ }
+ // #45355 another point to jump in for isnumeric-String
+ cvt2:
+ if( pFmt )
+ {
+ SbxAppData& rAppData = GetSbxData_Impl();
+
+ LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
+ if( rAppData.pBasicFormater )
+ {
+ if( rAppData.eBasicFormaterLangType != eLangType )
+ {
+ rAppData.pBasicFormater.reset();
+ }
+ }
+ rAppData.eBasicFormaterLangType = eLangType;
+
+
+ if( !rAppData.pBasicFormater )
+ {
+ SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
+ sal_Unicode cComma = rData.getNumDecimalSep()[0];
+ sal_Unicode c1000 = rData.getNumThousandSep()[0];
+ const OUString& aCurrencyStrg = rData.getCurrSymbol();
+
+ // initialize the Basic-formater help object:
+ // get resources for predefined output
+ // of the Format()-command, e. g. for "On/Off"
+ OUString aOnStrg = BasResId(STR_BASICKEY_FORMAT_ON);
+ OUString aOffStrg = BasResId(STR_BASICKEY_FORMAT_OFF);
+ OUString aYesStrg = BasResId(STR_BASICKEY_FORMAT_YES);
+ OUString aNoStrg = BasResId(STR_BASICKEY_FORMAT_NO);
+ OUString aTrueStrg = BasResId(STR_BASICKEY_FORMAT_TRUE);
+ OUString aFalseStrg = BasResId(STR_BASICKEY_FORMAT_FALSE);
+ OUString aCurrencyFormatStrg = BasResId(STR_BASICKEY_FORMAT_CURRENCY);
+
+ rAppData.pBasicFormater = std::make_unique<SbxBasicFormater>(
+ cComma,c1000,aOnStrg,aOffStrg,
+ aYesStrg,aNoStrg,aTrueStrg,aFalseStrg,
+ aCurrencyStrg,aCurrencyFormatStrg );
+ }
+ // Remark: For performance reasons there's only ONE BasicFormater-
+ // object created and 'stored', so that the expensive resource-
+ // loading is saved (for country-specific predefined outputs,
+ // e. g. "On/Off") and the continuous string-creation
+ // operations, too.
+ // BUT: therefore this code is NOT multithreading capable!
+
+ // here are problems with ;;;Null because this method is only
+ // called, if SbxValue is a number!!!
+ // in addition rAppData.pBasicFormater->BasicFormatNull( *pFmt ); could be called!
+ if( eType != SbxNULL )
+ {
+ rRes = rAppData.pBasicFormater->BasicFormat( d ,*pFmt );
+ }
+ else
+ {
+ rRes = SbxBasicFormater::BasicFormatNull( *pFmt );
+ }
+
+ }
+ else
+ ImpCvtNum( GetDouble(), nComma, rRes );
+ break;
+ case SbxSTRING:
+ if( pFmt )
+ {
+ // #45355 converting if numeric
+ if( IsNumericRTL() )
+ {
+ ScanNumIntnl( GetOUString(), d );
+ goto cvt2;
+ }
+ else
+ {
+ printfmtstr( GetOUString(), rRes, *pFmt );
+ }
+ }
+ else
+ {
+ rRes = GetOUString();
+ }
+ break;
+ default:
+ rRes = GetOUString();
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */