/* -*- 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 <stdlib.h>
 
#include <sbxform.hxx>
#include <rtl/ustrbuf.hxx>
 
#include <rtl/character.hxx>
#include <o3tl/sprintf.hxx>
#include <o3tl/string_view.hxx>
#include <string_view>
#include <utility>
 
/*
TODO: are there any Star-Basic characteristics unconsidered?
 
        what means: * as placeholder
 
COMMENT: Visual-Basic treats the following (invalid) format-strings
      as shown:
 
        ##0##.##0##     --> ##000.000##
 
      (this class behaves the same way)
*/
 
#include <stdio.h>
#include <float.h>
#include <math.h>
 
#define NO_DIGIT_                   -1
 
#define MAX_NO_OF_DIGITS            DBL_DIG
#define MAX_DOUBLE_BUFFER_LENGTH    MAX_NO_OF_DIGITS + 9
                    // +1 for leading sign
                    // +1 for digit before the decimal point
                    // +1 for decimal point
                    // +2 for exponent E and exp. leading sign
                    // +3 for the exponent's value
                    // +1 for closing 0
 
#define CREATE_1000SEP_CHAR         '@'
 
#define FORMAT_SEPARATOR            ';'
 
// predefined formats for the Format$()-command:
constexpr std::u16string_view BASICFORMAT_GENERALNUMBER = u"General Number";
constexpr std::u16string_view BASICFORMAT_CURRENCY = u"Currency";
constexpr std::u16string_view BASICFORMAT_FIXED = u"Fixed";
constexpr std::u16string_view BASICFORMAT_STANDARD = u"Standard";
constexpr std::u16string_view BASICFORMAT_PERCENT = u"Percent";
constexpr std::u16string_view BASICFORMAT_SCIENTIFIC = u"Scientific";
constexpr std::u16string_view BASICFORMAT_YESNO = u"Yes/No";
constexpr std::u16string_view BASICFORMAT_TRUEFALSE = u"True/False";
constexpr std::u16string_view BASICFORMAT_ONOFF = u"On/Off";
 
// Comment: Visual-Basic has a maximum of 12 positions after the
//          decimal point for floating-point-numbers.
// all format-strings are compatible to Visual-Basic:
constexpr OUString GENERALNUMBER_FORMAT = u"0.############"_ustr;
constexpr OUString FIXED_FORMAT = u"0.00"_ustr;
constexpr OUString STANDARD_FORMAT = u"@0.00"_ustr;
constexpr OUString PERCENT_FORMAT = u"0.00%"_ustr;
constexpr OUString SCIENTIFIC_FORMAT = u"#.00E+00"_ustr;
// Comment: the character @ means that thousand-separators shall
//          be generated. That's a StarBasic 'extension'.
 
 
static double get_number_of_digits( double dNumber )
//double floor_log10_fabs( double dNumber )
{
    if( dNumber==0.0 )
        return 0.0; // used to be 1.0, now 0.0 because of #40025;
    else
        return floor( log10( fabs( dNumber ) ) );
}
 
 
SbxBasicFormater::SbxBasicFormater( sal_Unicode _cDecPoint, sal_Unicode _cThousandSep,
                      OUString _sOnStrg,
                      OUString _sOffStrg,
                      OUString _sYesStrg,
                      OUString _sNoStrg,
                      OUString _sTrueStrg,
                      OUString _sFalseStrg,
                      OUString _sCurrencyStrg,
                      OUString _sCurrencyFormatStrg )
    : cDecPoint(_cDecPoint)
    , cThousandSep(_cThousandSep)
    , sOnStrg(std::move(_sOnStrg))
    , sOffStrg(std::move(_sOffStrg))
    , sYesStrg(std::move(_sYesStrg))
    , sNoStrg(std::move(_sNoStrg))
    , sTrueStrg(std::move(_sTrueStrg))
    , sFalseStrg(std::move(_sFalseStrg))
    , sCurrencyStrg(std::move(_sCurrencyStrg))
    , sCurrencyFormatStrg(std::move(_sCurrencyFormatStrg))
    , dNum(0.0)
    , nNumExp(0)
    , nExpExp(0)
{
}
 
// function to output an error-text (for debugging)
// displaces all characters of the string, starting from nStartPos
// for one position to larger indexes, i. e. place for a new
// character (which is to be inserted) is created.
// ATTENTION: the string MUST be long enough!
inline void SbxBasicFormater::ShiftString( OUStringBuffer& sStrg, sal_uInt16 nStartPos )
{
    sStrg.remove(nStartPos,1);
}
 
void SbxBasicFormater::AppendDigit( OUStringBuffer& sStrg, short nDigit )
{
    if( nDigit>=0 && nDigit<=9 )
    {
        sStrg.append(static_cast<sal_Unicode>(nDigit+'0'));
    }
}
 
void SbxBasicFormater::LeftShiftDecimalPoint( OUStringBuffer& sStrg )
{
    sal_Int32 nPos = -1;
 
    for(sal_Int32 i = 0; i < sStrg.getLength(); i++)
    {
        if(sStrg[i] == cDecPoint)
        {
            nPos = i;
            break;
        }
    }
    if( nPos >= 0 )
    {
        sStrg[nPos] = sStrg[nPos - 1];
        sStrg[nPos - 1] = cDecPoint;
    }
}
 
// returns a flag if rounding a 9
void SbxBasicFormater::StrRoundDigit( OUStringBuffer& sStrg, short nPos, bool& bOverflow )
{
    if( nPos<0 )
    {
        return;
    }
    bOverflow = false;
    sal_Unicode c = sStrg[nPos];
    if( nPos > 0 && (c == cDecPoint || c == cThousandSep) )
    {
        StrRoundDigit( sStrg, nPos - 1, bOverflow );
        // CHANGE from 9.3.1997: end the method immediately after recursive call!
        return;
    }
    // skip non-digits:
    // COMMENT:
    // in a valid format-string the number's output should be done
    // in one piece, i. e. special characters should ONLY be in
    // front OR behind the number and not right in the middle of
    // the format information for the number
    while( nPos >= 0 && ! rtl::isAsciiDigit(sStrg[nPos]))
    {
        nPos--;
    }
    if( nPos==-1 )
    {
        ShiftString( sStrg, 0 );
        sStrg[0] = '1';
        bOverflow = true;
    }
    else
    {
        sal_Unicode c2 = sStrg[nPos];
        if( rtl::isAsciiDigit(c2) )
        {
            if( c2 == '9' )
            {
                sStrg[nPos] = '0';
                StrRoundDigit( sStrg, nPos - 1, bOverflow );
            }
            else
            {
                sStrg[nPos] = c2 + 1;
            }
        }
        else
        {
            ShiftString( sStrg,nPos+1 );
            sStrg[nPos + 1] = '1';
            bOverflow = true;
        }
    }
}
 
void SbxBasicFormater::StrRoundDigit( OUStringBuffer& sStrg, short nPos )
{
    bool bOverflow;
 
    StrRoundDigit( sStrg, nPos, bOverflow );
}
 
void SbxBasicFormater::ParseBack( OUStringBuffer& sStrg, std::u16string_view sFormatStrg,
                                  short nFormatPos )
{
    for( sal_Int32 i = nFormatPos;
         i>0 && sFormatStrg[ i ]  == '#' && sStrg[sStrg.getLength() - 1] == '0';
         i-- )
    {
        sStrg.setLength(sStrg.getLength() - 1 );
    }
}
 
void SbxBasicFormater::InitScan( double _dNum )
{
    char sBuffer[ MAX_DOUBLE_BUFFER_LENGTH ];
 
    dNum = _dNum;
    InitExp( get_number_of_digits( dNum ) );
    // maximum of 15 positions behind the decimal point, example: -1.234000000000000E-001
    /*int nCount =*/ o3tl::sprintf( sBuffer,"%+22.15lE",dNum );
    sSciNumStrg = OUString::createFromAscii( sBuffer );
}
 
 
void SbxBasicFormater::InitExp( double _dNewExp )
{
    nNumExp = static_cast<short>(_dNewExp);
    sNumExpStrg = (nNumExp >= 0 ? std::u16string_view(u"+") : std::u16string_view(u""))
        + OUString::number(nNumExp);
    nExpExp = static_cast<short>(get_number_of_digits( static_cast<double>(nNumExp) ));
}
 
 
short SbxBasicFormater::GetDigitAtPosScan( short nPos, bool& bFoundFirstDigit )
{
    // trying to read a higher digit,
    // e. g. position 4 in 1.234,
    // or to read a digit outside of the
    // number's dissolution (double)
    if( nPos>nNumExp || abs(nNumExp-nPos)>MAX_NO_OF_DIGITS )
    {
        return NO_DIGIT_;
    }
    // determine the index of the position in the number-string:
    // skip the leading sign
    sal_uInt16 no = 1;
    // skip the decimal point if necessary
    if( nPos<nNumExp )
        no++;
    no += nNumExp-nPos;
    // query of the number's first valid digit --> set flag
    if( nPos==nNumExp )
        bFoundFirstDigit = true;
    return static_cast<short>(sSciNumStrg[ no ] - '0');
}
 
short SbxBasicFormater::GetDigitAtPosExpScan( short nPos, bool& bFoundFirstDigit )
{
    if( nPos>nExpExp )
        return -1;
 
    sal_uInt16 no = 1;
    no += nExpExp-nPos;
 
    if( nPos==nExpExp )
        bFoundFirstDigit = true;
    return static_cast<short>(sNumExpStrg[ no ] - '0');
}
 
// a value for the exponent can be given because the number maybe shall
// not be displayed in a normed way (e. g. 1.2345e-03) but maybe 123.345e-3 !
short SbxBasicFormater::GetDigitAtPosExpScan( double dNewExponent, short nPos,
                                              bool& bFoundFirstDigit )
{
    InitExp( dNewExponent );
 
    return GetDigitAtPosExpScan( nPos,bFoundFirstDigit );
}
 
// Copies the respective part of the format-string, if existing, and returns it.
// So a new string is created, which has to be freed by the caller later.
OUString SbxBasicFormater::GetPosFormatString( std::u16string_view sFormatStrg, bool & bFound )
{
    bFound = false;     // default...
    size_t nPos = sFormatStrg.find( FORMAT_SEPARATOR );
 
    if( nPos != std::u16string_view::npos )
    {
        bFound = true;
        // the format-string for positive numbers is
        // everything before the first ';'
        return OUString(sFormatStrg.substr( 0,nPos ));
    }
 
    return OUString();
}
 
// see also GetPosFormatString()
OUString SbxBasicFormater::GetNegFormatString( std::u16string_view sFormatStrg, bool & bFound )
{
    bFound = false;     // default...
    size_t nPos = sFormatStrg.find( FORMAT_SEPARATOR );
 
    if( nPos != std::u16string_view::npos)
    {
        // the format-string for negative numbers is
        // everything between the first and the second ';'
        std::u16string_view sTempStrg = sFormatStrg.substr( nPos+1 );
        nPos = sTempStrg.find( FORMAT_SEPARATOR );
        bFound = true;
        if( nPos == std::u16string_view::npos )
        {
            return OUString(sTempStrg);
        }
        else
        {
            return OUString(sTempStrg.substr( 0,nPos ));
        }
    }
    return OUString();
}
 
// see also GetPosFormatString()
OUString SbxBasicFormater::Get0FormatString( std::u16string_view sFormatStrg, bool & bFound )
{
    bFound = false;     // default...
    size_t nPos = sFormatStrg.find( FORMAT_SEPARATOR );
 
    if( nPos != std::u16string_view::npos )
    {
        // the format string for the zero is
        // everything after the second ';'
        std::u16string_view sTempStrg = sFormatStrg.substr( nPos+1 );
        nPos = sTempStrg.find( FORMAT_SEPARATOR );
        if( nPos != std::u16string_view::npos )
        {
            bFound = true;
            sTempStrg = sTempStrg.substr( nPos+1 );
            nPos = sTempStrg.find( FORMAT_SEPARATOR );
            if( nPos == std::u16string_view::npos )
            {
                return OUString(sTempStrg);
            }
            else
            {
                return OUString(sTempStrg.substr( 0,nPos ));
            }
        }
    }
 
    return OUString();
}
 
// see also GetPosFormatString()
OUString SbxBasicFormater::GetNullFormatString( std::u16string_view sFormatStrg, bool & bFound )
{
    bFound = false;     // default...
    size_t nPos = sFormatStrg.find( FORMAT_SEPARATOR );
 
    if( nPos != std::u16string_view::npos )
    {
        // the format-string for the Null is
        // everything after the third ';'
        std::u16string_view sTempStrg = sFormatStrg.substr( nPos+1 );
        nPos = sTempStrg.find( FORMAT_SEPARATOR );
        if( nPos != std::u16string_view::npos )
        {
            sTempStrg = sTempStrg.substr( nPos+1 );
            nPos = sTempStrg.find( FORMAT_SEPARATOR );
            if( nPos != std::u16string_view::npos )
            {
                bFound = true;
                return OUString(sTempStrg.substr( nPos+1 ));
            }
        }
    }
 
    return OUString();
}
 
// returns value <> 0 in case of an error
void SbxBasicFormater::AnalyseFormatString( std::u16string_view sFormatStrg,
                short& nNoOfDigitsLeft, short& nNoOfDigitsRight,
                short& nNoOfOptionalDigitsLeft,
                short& nNoOfExponentDigits, short& nNoOfOptionalExponentDigits,
                bool& bPercent, bool& bCurrency, bool& bScientific,
                bool& bGenerateThousandSeparator,
                short& nMultipleThousandSeparators )
{
    sal_Int32 nLen;
    short nState = 0;
 
    nLen = sFormatStrg.size();
    nNoOfDigitsLeft = 0;
    nNoOfDigitsRight = 0;
    nNoOfOptionalDigitsLeft = 0;
    nNoOfExponentDigits = 0;
    nNoOfOptionalExponentDigits = 0;
    bPercent = false;
    bCurrency = false;
    bScientific = false;
    // from 11.7.97: as soon as a comma (point?) is found in the format string,
    // all three decimal powers are marked (i. e. thousand, million, ...)
    bGenerateThousandSeparator = sFormatStrg.find( ',' ) != std::u16string_view::npos;
    nMultipleThousandSeparators = 0;
 
    for( sal_Int32 i = 0; i < nLen; i++ )
    {
        sal_Unicode c = sFormatStrg[ i ];
        switch( c )
        {
        case '#':
        case '0':
            if( nState==0 )
            {
                nNoOfDigitsLeft++;
// TODO  here maybe better error inspection of the mantissa for valid syntax (see grammar)h
                // ATTENTION: 'undefined' behaviour if # and 0 are combined!
                // REMARK: #-placeholders are actually useless for
                // scientific display before the decimal point!
                if( c=='#' )
                {
                    nNoOfOptionalDigitsLeft++;
                }
            }
            else if( nState==1 )
            {
                nNoOfDigitsRight++;
            }
            else if( nState==-1 )   // search 0 in the exponent
            {
                if( c=='#' )    // # switches on the condition
                {
                    nNoOfOptionalExponentDigits++;
                    nState = -2;
                }
                nNoOfExponentDigits++;
            }
            else if( nState==-2 )   // search # in the exponent
            {
                if( c=='0' )
                {
                    // ERROR: 0 after # in the exponent is NOT allowed!!
                    return;
                }
                nNoOfOptionalExponentDigits++;
                nNoOfExponentDigits++;
            }
            break;
        case '.':
            nState++;
            if( nState>1 )
            {
                return;  // ERROR: too many decimal points
            }
            break;
        case '%':
            bPercent = true;
            break;
        case '(':
            bCurrency = true;
            break;
        case ',':
            {
                sal_Unicode ch = sFormatStrg[ i+1 ];
 
                if( ch!=0 && (ch==',' || ch=='.') )
                {
                    nMultipleThousandSeparators++;
                }
            }
            break;
        case 'e':
        case 'E':
            // #i13821 not when no digits before
            if( nNoOfDigitsLeft > 0 || nNoOfDigitsRight > 0 )
            {
                nState = -1;   // abort counting digits
                bScientific = true;
            }
            break;
            // OWN command-character which turns on
            // the creation of thousand-separators
        case '\\':
            // Ignore next char
            i++;
            break;
        case CREATE_1000SEP_CHAR:
            bGenerateThousandSeparator = true;
            break;
        }
    }
}
 
// the flag bCreateSign says that at the mantissa a leading sign
// shall be created
void SbxBasicFormater::ScanFormatString( double dNumber,
                                         std::u16string_view sFormatStrg, OUString& sReturnStrgFinal,
                                         bool bCreateSign )
{
    short   /*nErr,*/nNoOfDigitsLeft,nNoOfDigitsRight,nNoOfOptionalDigitsLeft,
        nNoOfExponentDigits,nNoOfOptionalExponentDigits,
        nMultipleThousandSeparators;
    bool    bPercent,bCurrency,bScientific,bGenerateThousandSeparator;
 
    OUStringBuffer sReturnStrg(32);
 
    // analyse the format-string, i. e. determine the following values:
    /*
            - number of digits before decimal point
            - number of digits after decimal point
            - optional digits before decimal point
            - number of digits in the exponent
            - optional digits in the exponent
            - percent-character found?
            - () for negative leading sign?
            - exponential-notation?
            - shall thousand-separators be generated?
            - is a percent-character being found? --> dNumber *= 100.0;
            - are there thousand-separators in a row?
                ,, or ,. --> dNumber /= 1000.0;
            - other errors? multiple decimal points, E's, etc.
        --> errors are simply ignored at the moment
    */
    AnalyseFormatString( sFormatStrg, nNoOfDigitsLeft, nNoOfDigitsRight,
                         nNoOfOptionalDigitsLeft, nNoOfExponentDigits,
                         nNoOfOptionalExponentDigits,
                         bPercent, bCurrency, bScientific,
                         bGenerateThousandSeparator, nMultipleThousandSeparators );
    // special handling for special characters
    if( bPercent )
    {
        dNumber *= 100.0;
    }
// TODO: this condition (,, or ,.) is NOT Visual-Basic compatible!
        // Question: shall this stay here (requirements)?
    if( nMultipleThousandSeparators )
    {
        dNumber /= 1000.0;
    }
    double dExponent;
    short i,nLen;
    short nState,nDigitPos,nExponentPos,nMaxDigit,nMaxExponentDigit;
    bool bFirstDigit,bFirstExponentDigit,bFoundFirstDigit,
        bIsNegative,bZeroSpaceOn, bSignHappend,bDigitPosNegative;
 
    bSignHappend = false;
    bFoundFirstDigit = false;
    bIsNegative = dNumber < 0.0;
    nLen = sFormatStrg.size();
    dExponent = get_number_of_digits( dNumber );
    nExponentPos = 0;
    nMaxExponentDigit = 0;
    nMaxDigit = static_cast<short>(dExponent);
    bDigitPosNegative = false;
    if( bScientific )
    {
        dExponent = dExponent - static_cast<double>(nNoOfDigitsLeft-1);
        nDigitPos = nMaxDigit;
        nMaxExponentDigit = static_cast<short>(get_number_of_digits( dExponent ));
        nExponentPos = nNoOfExponentDigits - 1 - nNoOfOptionalExponentDigits;
    }
    else
    {
        nDigitPos = nNoOfDigitsLeft - 1; // counting starts at 0, 10^0
        // no exponent-data is needed here!
        bDigitPosNegative = (nDigitPos < 0);
    }
    bFirstDigit = true;
    bFirstExponentDigit = true;
    nState = 0; // 0 --> mantissa; 1 --> exponent
    bZeroSpaceOn = false;
 
 
    InitScan( dNumber );
    // scanning the format-string:
    sal_Unicode cForce = 0;
    for( i = 0; i < nLen; i++ )
    {
        sal_Unicode c;
        if( cForce )
        {
            c = cForce;
            cForce = 0;
        }
        else
        {
            c = sFormatStrg[ i ];
        }
        switch( c )
        {
        case '0':
        case '#':
            if( nState==0 )
            {
                // handling of the mantissa
                if( bFirstDigit )
                {
                    // remark: at bCurrency the negative
                    //         leading sign shall be shown with ()
                    if( bIsNegative && !bCreateSign && !bSignHappend )
                    {
                        bSignHappend = true;
                        sReturnStrg.append('-');
                    }
                    // output redundant positions, i. e. those which
                    // are undocumented by the format-string
                    if( nMaxDigit > nDigitPos )
                    {
                        for( short j = nMaxDigit; j > nDigitPos; j-- )
                        {
                            short nTempDigit = GetDigitAtPosScan( j, bFoundFirstDigit );
                            AppendDigit( sReturnStrg, nTempDigit );
                            if( nTempDigit != NO_DIGIT_ )
                            {
                                bFirstDigit = false;
                            }
                            if( bGenerateThousandSeparator && c=='0' && j > 0 && (j % 3 == 0) )
                            {
                                sReturnStrg.append(cThousandSep );
                            }
                        }
                    }
                }
 
                if( nMaxDigit<nDigitPos && ( c=='0' || bZeroSpaceOn ) )
                {
                    AppendDigit( sReturnStrg, 0 );
                    bFirstDigit = false;
                    bZeroSpaceOn = true;
                    // Remark: in Visual-Basic the first 0 turns on the 0 for
                    //         all the following # (up to the decimal point),
                    //         this behaviour is simulated here with the flag.
                    if (bGenerateThousandSeparator && c == '0' && nDigitPos > 0 && (nDigitPos % 3 == 0))
                    {
                        sReturnStrg.append(cThousandSep);
                    }
                }
                else
                {
                    short nTempDigit = GetDigitAtPosScan( nDigitPos, bFoundFirstDigit ) ;
                    AppendDigit( sReturnStrg, nTempDigit );
 
                    if( nTempDigit != NO_DIGIT_ )
                    {
                        bFirstDigit = false;
                    }
                    if( bGenerateThousandSeparator && ( c=='0' || nMaxDigit>=nDigitPos ) && nDigitPos>0 && (nDigitPos % 3 == 0) )
                    {
                        sReturnStrg.append(cThousandSep);
                    }
                }
                nDigitPos--;
            }
            else
            {
                // handling the exponent
                if( bFirstExponentDigit )
                {
                    // leading sign has been given out at e/E already
                    bFirstExponentDigit = false;
                    if( nMaxExponentDigit > nExponentPos )
                        // output redundant positions, i. e. those which
                        // are undocumented by the format-string
                    {
                        for( short j = nMaxExponentDigit; j > nExponentPos; j-- )
                        {
                            AppendDigit( sReturnStrg, GetDigitAtPosExpScan( dExponent, j, bFoundFirstDigit ) );
                        }
                    }
                }
 
                if( nMaxExponentDigit < nExponentPos && c=='0' )
                {
                    AppendDigit( sReturnStrg, 0 );
                }
                else
                {
                    AppendDigit( sReturnStrg, GetDigitAtPosExpScan( dExponent, nExponentPos, bFoundFirstDigit ) );
                }
                nExponentPos--;
            }
            break;
        case '.':
            if( bDigitPosNegative ) // #i13821: If no digits before .
            {
                bDigitPosNegative = false;
                nDigitPos = 0;
                cForce = '#';
                i-=2;
                break;
            }
            sReturnStrg.append(cDecPoint);
            break;
        case '%':
            // maybe remove redundant 0s, e. g. 4.500e4 in 0.0##e-00
            ParseBack( sReturnStrg, sFormatStrg, i-1 );
            sReturnStrg.append('%');
            break;
        case 'e':
        case 'E':
            // does mantissa have to be rounded, before the exponent is displayed?
            {
                // is there a mantissa at all?
                if( bFirstDigit )
                {
                    // apparently not, i. e. invalid format string, e. g. E000.00
                    // so ignore these e and E characters
                    // maybe output an error (like in Visual Basic)?
 
                    // #i13821: VB 6 behaviour
                    sReturnStrg.append(c);
                    break;
                }
 
                bool bOverflow = false;
                short nNextDigit = GetDigitAtPosScan( nDigitPos, bFoundFirstDigit );
                if( nNextDigit>=5 )
                {
                    StrRoundDigit( sReturnStrg, sReturnStrg.getLength() - 1, bOverflow );
                }
                if( bOverflow )
                {
                    // a leading 9 has been rounded
                    LeftShiftDecimalPoint( sReturnStrg );
                    sReturnStrg[sReturnStrg.getLength() - 1] = 0;
                    dExponent += 1.0;
                }
                // maybe remove redundant 0s, e. g. 4.500e4 in 0.0##e-00
                ParseBack( sReturnStrg, sFormatStrg, i-1 );
            }
            // change the scanner's condition
            nState++;
            // output exponent character
            sReturnStrg.append(c);
            // i++; // MANIPULATION of the loop-variable!
            c = sFormatStrg[ ++i ];
            // output leading sign / exponent
            if( c != 0 )
            {
                if( c == '-' )
                {
                    if( dExponent < 0.0 )
                    {
                        sReturnStrg.append('-');
                    }
                }
                else if( c == '+' )
                {
                    if( dExponent < 0.0 )
                    {
                        sReturnStrg.append('-');
                    }
                    else
                    {
                        sReturnStrg.append('+');
                    }
                }
            }
            break;
        case ',':
            break;
        case ';':
            break;
        case '(':
        case ')':
            // maybe remove redundant 0s, e. g. 4.500e4 in 0.0##e-00
            ParseBack( sReturnStrg, sFormatStrg, i-1 );
            if( bIsNegative )
            {
                sReturnStrg.append(c);
            }
            break;
        case '$':
            // append the string for the currency:
            sReturnStrg.append(sCurrencyStrg);
            break;
        case ' ':
        case '-':
        case '+':
            ParseBack( sReturnStrg, sFormatStrg, i-1 );
            sReturnStrg.append(c);
            break;
        case '\\':
            ParseBack( sReturnStrg, sFormatStrg, i-1 );
            // special character found, output next
            // character directly (if existing)
            c = sFormatStrg[ ++i ];
            if( c!=0 )
            {
                sReturnStrg.append(c);
            }
            break;
        case CREATE_1000SEP_CHAR:
            // ignore here, action has already been
            // executed in AnalyseFormatString
            break;
        default:
            // output characters and digits, too (like in Visual-Basic)
            if( ( c>='a' && c<='z' ) ||
                ( c>='A' && c<='Z' ) ||
                ( c>='1' && c<='9' ) )
            {
                sReturnStrg.append(c);
            }
        }
    }
 
    // scan completed - rounding necessary?
    if( !bScientific )
    {
        short nNextDigit = GetDigitAtPosScan( nDigitPos, bFoundFirstDigit );
        if( nNextDigit>=5 )
        {
            StrRoundDigit( sReturnStrg, sReturnStrg.getLength() - 1 );
        }
    }
 
    if( nNoOfDigitsRight>0 )
    {
        ParseBack( sReturnStrg, sFormatStrg, sFormatStrg.size()-1 );
    }
    sReturnStrgFinal = sReturnStrg.makeStringAndClear();
}
 
OUString SbxBasicFormater::BasicFormatNull( std::u16string_view sFormatStrg )
{
    bool bNullFormatFound;
    OUString sNullFormatStrg = GetNullFormatString( sFormatStrg, bNullFormatFound );
 
    if( bNullFormatFound )
    {
        return sNullFormatStrg;
    }
    return u"null"_ustr;
}
 
OUString SbxBasicFormater::BasicFormat( double dNumber, const OUString& _sFormatStrg )
{
    bool bPosFormatFound,bNegFormatFound,b0FormatFound;
    OUString sFormatStrg = _sFormatStrg;
 
    // analyse format-string concerning predefined formats:
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_GENERALNUMBER ) )
    {
        sFormatStrg = GENERALNUMBER_FORMAT;
    }
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_CURRENCY ) )
    {
        sFormatStrg = sCurrencyFormatStrg;
    }
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_FIXED ) )
    {
        sFormatStrg = FIXED_FORMAT;
    }
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_STANDARD ) )
    {
        sFormatStrg = STANDARD_FORMAT;
    }
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_PERCENT ) )
    {
        sFormatStrg = PERCENT_FORMAT;
    }
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_SCIENTIFIC ) )
    {
        sFormatStrg = SCIENTIFIC_FORMAT;
    }
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_YESNO ) )
    {
        return ( dNumber==0.0 ) ? sNoStrg : sYesStrg ;
    }
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_TRUEFALSE ) )
    {
        return ( dNumber==0.0 ) ? sFalseStrg : sTrueStrg ;
    }
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_ONOFF ) )
    {
        return ( dNumber==0.0 ) ? sOffStrg : sOnStrg ;
    }
 
    // analyse format-string concerning ';', i. e. format-strings for
    // positive-, negative- and 0-values
    OUString sPosFormatStrg = GetPosFormatString( sFormatStrg, bPosFormatFound );
    OUString sNegFormatStrg = GetNegFormatString( sFormatStrg, bNegFormatFound );
    OUString s0FormatStrg = Get0FormatString( sFormatStrg, b0FormatFound );
 
    OUString sReturnStrg;
    OUString sTempStrg;
 
    if( dNumber==0.0 )
    {
        sTempStrg = sFormatStrg;
        if( b0FormatFound )
        {
            if( s0FormatStrg.isEmpty() && bPosFormatFound )
            {
                sTempStrg = sPosFormatStrg;
            }
            else
            {
                sTempStrg = s0FormatStrg;
            }
        }
        else if( bPosFormatFound )
        {
            sTempStrg = sPosFormatStrg;
        }
        ScanFormatString( dNumber, sTempStrg, sReturnStrg,/*bCreateSign=*/false );
    }
    else
    {
        if( dNumber<0.0 )
        {
            if( bNegFormatFound )
            {
                if( sNegFormatStrg.isEmpty() && bPosFormatFound )
                {
                    sTempStrg = "-" + sPosFormatStrg;
                }
                else
                {
                    sTempStrg = sNegFormatStrg;
                }
            }
            else
            {
                sTempStrg = sFormatStrg;
            }
            // if NO format-string especially for negative
            // values is given, output the leading sign
            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( std::u16string_view sFormatStrg )
{
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_GENERALNUMBER ) )
    {
        return true;
    }
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_CURRENCY ) )
    {
        return true;
    }
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_FIXED ) )
    {
        return true;
    }
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_STANDARD ) )
    {
        return true;
    }
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_PERCENT ) )
    {
        return true;
    }
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_SCIENTIFIC ) )
    {
        return true;
    }
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_YESNO ) )
    {
        return true;
    }
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_TRUEFALSE ) )
    {
        return true;
    }
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_ONOFF ) )
    {
        return true;
    }
    return false;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'remove' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.