/* -*- 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 .
 */
#ifndef INCLUDED_SVL_ZFORMAT_HXX
#define INCLUDED_SVL_ZFORMAT_HXX
 
#include <svl/svldllapi.h>
#include <i18nlangtag/mslangid.hxx>
#include <svl/zforlist.hxx>
#include <svl/nfkeytab.hxx>
#include <vector>
#include <com/sun/star/i18n/NativeNumberXmlAttributes2.hpp>
 
namespace utl {
    class DigitGroupingIterator;
}
 
class Color;
 
class ImpSvNumberformatScan;            // format code string scanner
class ImpSvNumberInputScan;             // input string scanner
class SvNumberFormatter;
 
enum SvNumberformatLimitOps
{
    NUMBERFORMAT_OP_NO  = 0,            // Undefined, no OP
    NUMBERFORMAT_OP_EQ  = 1,            // Operator =
    NUMBERFORMAT_OP_NE  = 2,            // Operator <>
    NUMBERFORMAT_OP_LT  = 3,            // Operator <
    NUMBERFORMAT_OP_LE  = 4,            // Operator <=
    NUMBERFORMAT_OP_GT  = 5,            // Operator >
    NUMBERFORMAT_OP_GE  = 6             // Operator >=
};
 
struct ImpSvNumberformatInfo            // Struct for FormatInfo
{
    std::vector<OUString> sStrArray;    // Array of symbols
    std::vector<short> nTypeArray;      // Array of infos
    sal_uInt16 nThousand;               // Count of group separator sequences
    sal_uInt16 nCntPre;                 // Count of digits before decimal point
    sal_uInt16 nCntPost;                // Count of digits after decimal point
    sal_uInt16 nCntExp;                 // Count of exponent digits, or AM/PM
    SvNumFormatType eScannedType;       // Type determined by scan
    bool bThousand;                     // Has group (AKA thousand) separator
 
    void Copy( const ImpSvNumberformatInfo& rNumFor, sal_uInt16 nAnz );
};
 
// NativeNumber, represent numbers using CJK or other digits if nNum>0,
// eLang specifies the Locale to use.
class SvNumberNatNum
{
    OUString sParams;               // For [NatNum12 ordinal-number]-like syntax
    LanguageType eLang;
    sal_uInt8            nNum;
    bool            bDBNum  :1;     // DBNum, to be converted to NatNum
    bool            bDate   :1;     // Used in date? (needed for DBNum/NatNum mapping)
    bool            bSet    :1;     // If set, since NatNum0 is possible
 
public:
 
    static  sal_uInt8    MapDBNumToNatNum( sal_uInt8 nDBNum, LanguageType eLang, bool bDate );
    static  sal_uInt8    MapNatNumToDBNum( sal_uInt8 nNatNum, LanguageType eLang, bool bDate );
 
                    SvNumberNatNum() : eLang( LANGUAGE_DONTKNOW ), nNum(0),
                                        bDBNum(false), bDate(false), bSet(false) {}
    bool            IsComplete() const  { return bSet && eLang != LANGUAGE_DONTKNOW; }
    sal_uInt8       GetNatNum() const   { return bDBNum ? MapDBNumToNatNum( nNum, eLang, bDate ) : nNum; }
    sal_uInt8       GetDBNum() const    { return bDBNum ? nNum : MapNatNumToDBNum( nNum, eLang, bDate ); }
    LanguageType    GetLang() const     { return eLang; }
    void            SetLang( LanguageType e ) { eLang = e; }
    void            SetNum( sal_uInt8 nNumber, bool bDBNumber )
                        {
                            nNum = nNumber;
                            bDBNum = bDBNumber;
                            bSet = true;
                        }
    bool            IsSet() const       { return bSet; }
    void            SetDate( bool bDateP )   { bDate = bDateP; }
    void            SetParams(const OUString& s) { sParams = s; }
    OUString const & GetParams() const { return sParams; }
};
 
class CharClass;
 
class ImpSvNumFor                       // One of four subformats of the format code string
{
public:
    ImpSvNumFor();                      // Ctor without filling the Info
    ~ImpSvNumFor();
 
    void Enlarge(sal_uInt16 nAnz);      // Init of arrays to the right size
 
    // if pSc is set, it is used to get the Color pointer
    void Copy( const ImpSvNumFor& rNumFor, ImpSvNumberformatScan* pSc );
 
    // Access to Info; call Enlarge before!
    ImpSvNumberformatInfo& Info() { return aI;}
    const ImpSvNumberformatInfo& Info() const { return aI; }
 
    // Get count of substrings (symbols)
    sal_uInt16 GetCount() const { return nStringsCnt;}
 
    Color* GetColor() const { return pColor; }
    void SetColor( Color* pCol, OUString const & rName )
     { pColor = pCol; sColorName = rName; }
    const OUString& GetColorName() const { return sColorName; }
 
    // new SYMBOLTYPE_CURRENCY in subformat?
    bool HasNewCurrency() const;
    bool GetNewCurrencySymbol( OUString& rSymbol, OUString& rExtension ) const;
 
    // [NatNum1], [NatNum2], ...
    void SetNatNumNum( sal_uInt8 nNum, bool bDBNum ) { aNatNum.SetNum( nNum, bDBNum ); }
    void SetNatNumLang( LanguageType eLang ) { aNatNum.SetLang( eLang ); }
    void SetNatNumDate( bool bDate ) { aNatNum.SetDate( bDate ); }
    void SetNatNumParams(const OUString& sParams) { aNatNum.SetParams(sParams); }
    const SvNumberNatNum& GetNatNum() const { return aNatNum; }
 
private:
    ImpSvNumberformatInfo aI;           // helper struct for remaining information
    OUString sColorName;                // color name
    Color* pColor;                      // pointer to color of subformat
    sal_uInt16 nStringsCnt;             // count of symbols
    SvNumberNatNum aNatNum;             // DoubleByteNumber
 
};
 
class SVL_DLLPUBLIC SvNumberformat
{
    struct LocaleType
    {
        enum class Substitute : sal_uInt8
        {
            NONE,
            TIME,
            LONGDATE
        };
 
        LanguageType meLanguage;
        LanguageType meLanguageWithoutLocaleData;
        Substitute meSubstitute;
        sal_uInt8 mnNumeralShape;
        sal_uInt8 mnCalendarType;
 
        OUString generateCode() const;
 
        LocaleType();
        LocaleType(sal_uInt32 nRawCode);
 
        bool isPlainLocale() const;
    };
 
public:
    // Normal ctor
    SvNumberformat( OUString& rString,
                   ImpSvNumberformatScan* pSc,
                   ImpSvNumberInputScan* pISc,
                   sal_Int32& nCheckPos,
                   LanguageType& eLan );
 
    // Copy ctor
    SvNumberformat( SvNumberformat const & rFormat );
 
    // Copy ctor with exchange of format code string scanner (used in merge)
    SvNumberformat( SvNumberformat const & rFormat, ImpSvNumberformatScan& rSc );
 
    ~SvNumberformat();
 
    /// Get type of format, may include css::util::NumberFormat::DEFINED bit
    SvNumFormatType GetType() const             { return eType; }
 
    /// Get type of format, does not include css::util::NumberFormat::DEFINED
    SvNumFormatType GetMaskedType() const       { return eType & ~SvNumFormatType::DEFINED; }
 
    void SetType(SvNumFormatType eSetType)      { eType = eSetType; }
    // Standard means the I18N defined standard format of this type
    void SetStandard()                          { bStandard = true; }
    bool IsStandard() const                     { return bStandard; }
 
    // If this format is an additional built-in format defined by i18n.
    void SetAdditionalBuiltin()                 { bAdditionalBuiltin = true; }
    bool IsAdditionalBuiltin() const            { return bAdditionalBuiltin; }
 
    LanguageType GetLanguage() const            { return maLocale.meLanguage;}
 
    /** If the format is a placeholder and needs to be substituted. */
    bool IsSubstituted() const
        {
            return maLocale.meSubstitute != LocaleType::Substitute::NONE;
        }
 
    /** If the format is a placeholder for the system time format and needs to
        be substituted during formatting time.
     */
    bool IsSystemTimeFormat() const
        {
            return maLocale.meSubstitute == LocaleType::Substitute::TIME && maLocale.meLanguage == LANGUAGE_SYSTEM;
        }
 
    /** If the format is a placeholder for the system long date format and needs
        to be substituted during formatting time.
     */
    bool IsSystemLongDateFormat() const
        {
            return maLocale.meSubstitute == LocaleType::Substitute::LONGDATE && maLocale.meLanguage == LANGUAGE_SYSTEM;
        }
 
    const OUString& GetFormatstring() const   { return sFormatstring; }
 
    // Build a format string of application defined keywords
    OUString GetMappedFormatstring( const NfKeywordTable& rKeywords,
                                    const LocaleDataWrapper& rLoc,
                                    LanguageType nOriginalLang = LANGUAGE_DONTKNOW,
                                    bool bSystemLanguage = false ) const;
 
    void SetStarFormatSupport( bool b )         { bStarFlag = b; }
 
    /**
     * Get output string from a numeric value that fits the number of
     * characters specified.
     */
    bool GetOutputString( double fNumber, sal_uInt16 nCharCount, OUString& rOutString ) const;
 
    bool GetOutputString( double fNumber, OUString& OutString, Color** ppColor );
    void GetOutputString( const OUString& sString, OUString& OutString, Color** ppColor );
 
    // True if type text
    bool IsTextFormat() const { return bool(eType & SvNumFormatType::TEXT); }
    // True if 4th subformat present
    bool HasTextFormat() const
        {
            return (NumFor[3].GetCount() > 0) ||
                (NumFor[3].Info().eScannedType == SvNumFormatType::TEXT);
        }
 
    void GetFormatSpecialInfo(bool& bThousand,
                              bool& IsRed,
                              sal_uInt16& nPrecision,
                              sal_uInt16& nLeadingCnt) const;
 
    /// Get index of subformat (0..3) according to conditions and fNumber value
    sal_uInt16 GetSubformatIndex( double fNumber ) const;
 
    /// Count of decimal precision
    sal_uInt16 GetFormatPrecision( sal_uInt16 nIx = 0 ) const
        { return NumFor[nIx].Info().nCntPost; }
 
    /// Count of integer digits
    sal_uInt16 GetFormatIntegerDigits( sal_uInt16 nIx = 0 ) const
        { return NumFor[nIx].Info().nCntPre; }
 
    /** Count of hidden integer digits with thousands divisor:
     * formats like "0," to show only thousands
     */
    sal_uInt16 GetThousandDivisorPrecision( sal_uInt16 nIx = 0 ) const
        { return NumFor[nIx].Info().nThousand * 3; }
 
    //! Read/write access on a special sal_uInt16 component, may only be used on the
    //! standard format 0, 10000, ... and only by the number formatter!
    struct FormatterPrivateAccess { friend SvNumberFormatter; private: FormatterPrivateAccess() {} };
    sal_uInt16 GetLastInsertKey( const FormatterPrivateAccess& ) const
        { return NumFor[0].Info().nThousand; }
    void SetLastInsertKey( sal_uInt16 nKey, const FormatterPrivateAccess& )
        { NumFor[0].Info().nThousand = nKey; }
 
    //! Only onLoad: convert from stored to current system language/country
    void ConvertLanguage( SvNumberFormatter& rConverter,
        LanguageType eConvertFrom, LanguageType eConvertTo );
 
    // Substring of a subformat code nNumFor (0..3)
    // nPos == 0xFFFF => last substring
    // bString==true: first/last SYMBOLTYPE_STRING or SYMBOLTYPE_CURRENCY
    const OUString* GetNumForString( sal_uInt16 nNumFor, sal_uInt16 nPos,
                                     bool bString = false ) const;
 
    // Subtype of a subformat code nNumFor (0..3)
    // nPos == 0xFFFF => last substring
    short GetNumForType( sal_uInt16 nNumFor, sal_uInt16 nPos ) const;
 
    OUString GetDenominatorString( sal_uInt16 nNumFor ) const;
    OUString GetNumeratorString( sal_uInt16 nNumFor ) const;
    OUString GetIntegerFractionDelimiterString( sal_uInt16 nNumFor ) const;
    /// Round fNumber to its fraction representation
    double GetRoundFractionValue ( double fNumber ) const;
 
    /** If the count of string elements (substrings, ignoring [modifiers] and
        so on) in a subformat code nNumFor (0..3) is equal to the given number.
        Used by ImpSvNumberInputScan::IsNumberFormatMain() to detect a matched
        format.  */
    bool IsNumForStringElementCountEqual( sal_uInt16 nNumFor, sal_uInt16 nAllCount,
            sal_uInt16 nNumCount ) const
        {
            if ( nNumFor < 4 )
            {
                // First try a simple approach. Note that this is called only
                // if all MidStrings did match so far, to verify that all
                // strings of the format were matched and not just the starting
                // sequence, so we don't have to check if GetnAnz() includes
                // [modifiers] or anything else if both counts are equal.
                sal_uInt16 nCnt = NumFor[nNumFor].GetCount();
                if ( nAllCount == nCnt )
                    return true;
                if ( nAllCount < nCnt ) // check ignoring [modifiers] and so on
                    return ImpGetNumForStringElementCount( nNumFor ) ==
                        (nAllCount - nNumCount);
            }
            return false;
        }
    /** Get the count of numbers among string elements **/
    sal_uInt16 GetNumForNumberElementCount( sal_uInt16 nNumFor ) const;
 
    /** Get the scanned type of the specified subformat. */
    SvNumFormatType GetNumForInfoScannedType( sal_uInt16 nNumFor ) const
    {
        return (nNumFor < 4) ? NumFor[nNumFor].Info().eScannedType : SvNumFormatType::UNDEFINED;
    }
 
    // Whether the second subformat code is really for negative numbers
    // or another limit set.
    bool IsSecondSubformatRealNegative() const
        {
            return fLimit1 == 0.0 && fLimit2 == 0.0 &&
            ( (eOp1 == NUMBERFORMAT_OP_GE && eOp2 == NUMBERFORMAT_OP_NO) ||
              (eOp1 == NUMBERFORMAT_OP_GT && eOp2 == NUMBERFORMAT_OP_LT) ||
              (eOp1 == NUMBERFORMAT_OP_NO && eOp2 == NUMBERFORMAT_OP_NO) );
        }
 
    // Whether the first subformat code is really for negative numbers
    // or another limit set.
    bool IsFirstSubformatRealNegative() const
        {
            return fLimit1 == 0.0 && fLimit2 == 0.0 &&
                ((eOp1 == NUMBERFORMAT_OP_LT &&
                  (eOp2 == NUMBERFORMAT_OP_GT || eOp2 == NUMBERFORMAT_OP_EQ ||
                   eOp2 == NUMBERFORMAT_OP_GE || eOp2 == NUMBERFORMAT_OP_NO)) ||
                 (eOp1 == NUMBERFORMAT_OP_LE &&
                  (eOp2 == NUMBERFORMAT_OP_NO || eOp2 == NUMBERFORMAT_OP_GT)));
        }
 
    // Whether the negative format is without a sign or not
    bool IsNegativeWithoutSign() const;
 
    bool IsNegativeInBracket() const;
 
    bool HasPositiveBracketPlaceholder() const;
 
    // Whether a new SYMBOLTYPE_CURRENCY is contained in the format
    bool HasNewCurrency() const;
 
    // strip [$-yyy] from all [$xxx-yyy] leaving only xxx's,
    static OUString StripNewCurrencyDelimiters( const OUString& rStr );
 
    // If a new SYMBOLTYPE_CURRENCY is contained if the format is of type
    // css::util::NumberFormat::CURRENCY, and if so the symbol xxx and the extension nnn
    // of [$xxx-nnn] are returned
    bool GetNewCurrencySymbol( OUString& rSymbol, OUString& rExtension ) const;
 
    static bool HasStringNegativeSign( const OUString& rStr );
 
    /**
        Whether a character at position nPos is somewhere between two matching
        cQuote or not.
        If nPos points to a cQuote, a true is returned on an opening cQuote,
        a false is returned on a closing cQuote.
        A cQuote between quotes may be escaped by a cEscIn, a cQuote outside of
        quotes may be escaped by a cEscOut.
        The default '\0' results in no escapement possible.
        Defaults are set right according to the "unlogic" of the Numberformatter
     */
    static bool IsInQuote( const OUString& rString, sal_Int32 nPos,
                           sal_Unicode cQuote = '"',
                           sal_Unicode cEscIn = '\0', sal_Unicode cEscOut = '\\' );
 
    /**
        Return the position of a matching closing cQuote if the character at
        position nPos is between two matching cQuote, otherwise return -1.
        If nPos points to an opening cQuote the position of the matching
        closing cQuote is returned.
        If nPos points to a closing cQuote nPos is returned.
        If nPos points into a part which starts with an opening cQuote but has
        no closing cQuote, rString.Len() is returned.
        Uses <method>IsInQuote</method> internally, so you don't have to call
        that prior to a call of this method.
     */
    static sal_Int32 GetQuoteEnd( const OUString& rString, sal_Int32 nPos,
                                  sal_Unicode cQuote = '"',
                                  sal_Unicode cEscIn = '\0' );
 
    void SetComment( const OUString& rStr )
        { sComment = rStr; }
    const OUString& GetComment() const { return sComment; }
 
    /** Insert the number of blanks into the string that is needed to simulate
        the width of character c for underscore formats */
    static sal_Int32 InsertBlanks( OUString& r, sal_Int32 nPos, sal_Unicode c )
    {
        sal_Int32 result;
        OUStringBuffer sBuff(r);
 
        result = InsertBlanks(sBuff, nPos, c);
        r = sBuff.makeStringAndClear();
 
        return result;
    }
 
    /** Insert the number of blanks into the string that is needed to simulate
        the width of character c for underscore formats */
    static sal_Int32 InsertBlanks( OUStringBuffer& r, sal_Int32 nPos, sal_Unicode c );
 
    /// One of YMD,DMY,MDY if date format
    DateOrder GetDateOrder() const;
 
    /** A coded value of the exact YMD combination used, if date format.
        For example: YYYY-MM-DD => ('Y' << 16) | ('M' << 8) | 'D'
        or: MM/YY => ('M' << 8) | 'Y'  */
    sal_uInt32 GetExactDateOrder() const;
 
    // used in XML export
    void GetConditions( SvNumberformatLimitOps& rOper1, double& rVal1,
                        SvNumberformatLimitOps& rOper2, double& rVal2 ) const;
    Color* GetColor( sal_uInt16 nNumFor ) const;
    void GetNumForInfo( sal_uInt16 nNumFor, SvNumFormatType& rScannedType,
                    bool& bThousand, sal_uInt16& nPrecision, sal_uInt16& nLeadingCnt ) const;
 
    // rAttr.Number not empty if NatNum attributes are to be stored
    void GetNatNumXml(
            css::i18n::NativeNumberXmlAttributes2& rAttr,
            sal_uInt16 nNumFor ) const;
 
    /** Switches to the first non-"gregorian" calendar, but only if the current
        calendar is "gregorian"; original calendar name and date/time returned,
        but only if calendar switched and rOrgCalendar was empty. */
    void SwitchToOtherCalendar( OUString& rOrgCalendar, double& fOrgDateTime ) const;
 
    /** Switches to the "gregorian" calendar, but only if the current calendar
        is non-"gregorian" and rOrgCalendar is not empty. Thus a preceding
        ImpSwitchToOtherCalendar() call should have been placed prior to
        calling this method. */
    void SwitchToGregorianCalendar( const OUString& rOrgCalendar, double fOrgDateTime ) const;
 
#ifdef THE_FUTURE
    /** Switches to the first specified calendar, if any, in subformat nNumFor
        (0..3). Original calendar name and date/time returned, but only if
        calendar switched and rOrgCalendar was empty.
 
        @return
            <TRUE/> if a calendar was specified and switched to,
            <FALSE/> else.
     */
    bool SwitchToSpecifiedCalendar( OUString& rOrgCalendar, double& fOrgDateTime,
            sal_uInt16 nNumFor ) const
        {
            if ( nNumFor < 4 )
                return ImpSwitchToSpecifiedCalendar( rOrgCalendar,
                        fOrgDateTime, NumFor[nNumFor] );
            return false;
        }
#endif
 
    /// Whether it's a (YY)YY-M(M)-D(D) format.
    bool IsIso8601( sal_uInt16 nNumFor ) const
        {
            if ( nNumFor < 4 )
                return ImpIsIso8601( NumFor[nNumFor]);
            return false;
        }
 
private:
    ImpSvNumFor NumFor[4];          // Array for the 4 subformats
    OUString sFormatstring;         // The format code string
    OUString sComment;                // Comment, since number formatter version 6
    double fLimit1;                 // Value for first condition
    double fLimit2;                 // Value for second condition
    ImpSvNumberformatScan& rScan;   // Format code scanner
    LocaleType maLocale;            // Language/country of the format, numeral shape and calendar type from Excel.
    SvNumberformatLimitOps eOp1;    // Operator for first condition
    SvNumberformatLimitOps eOp2;    // Operator for second condition
    SvNumFormatType eType;          // Type of format
    bool bAdditionalBuiltin;        // If this is an additional built-in format defined by i18n
    bool bStarFlag;                 // Take *n format as ESC n
    bool bStandard;                 // If this is a default standard format
    bool bIsUsed;                   // Flag as used for storing
 
    SVL_DLLPRIVATE sal_uInt16 ImpGetNumForStringElementCount( sal_uInt16 nNumFor ) const;
 
    SVL_DLLPRIVATE bool ImpIsOtherCalendar( const ImpSvNumFor& rNumFor ) const;
 
#ifdef THE_FUTURE
    SVL_DLLPRIVATE bool ImpSwitchToSpecifiedCalendar( OUString& rOrgCalendar,
                                                      double& fOrgDateTime,
                                                      const ImpSvNumFor& rNumFor ) const;
#endif
 
    /** Whether to use possessive genitive case month name, or partitive case
        month name, instead of nominative name (noun).
 
        @param io_nState
            0: execute check <br>
               set to 1 if nominative case is returned, <br>
               set to 2 if genitive case is returned, <br>
               set to 3 if partitive case is returned <br>
            1: don't execute check, return nominative case <br>
            2: don't execute check, return genitive case <br>
            3: don't execute check, return partitive case <br>
 
        @param eCodeType
            a NfKeywordIndex, must designate a month type code
 
        @returns one of css::i18n::CalendarDisplayCode values
            according to eCodeType and the check executed (or passed).
     */
    SVL_DLLPRIVATE static sal_Int32 ImpUseMonthCase( int & io_nState, const ImpSvNumFor& rNumFor, NfKeywordIndex eCodeType );
 
    /// Whether it's a (YY)YY-M(M)-D(D) format.
    SVL_DLLPRIVATE bool ImpIsIso8601( const ImpSvNumFor& rNumFor ) const;
 
    const CharClass& rChrCls() const;
    const LocaleDataWrapper& rLoc() const;
    CalendarWrapper& GetCal() const;
    const SvNumberFormatter& GetFormatter() const;
 
    // divide in substrings and color conditions
    SVL_DLLPRIVATE short ImpNextSymbol( OUStringBuffer& rString,
                                        sal_Int32& nPos,
                                        OUString& sSymbol ) const;
 
    // read string until ']' and strip blanks (after condition)
    SVL_DLLPRIVATE static sal_Int32 ImpGetNumber( OUStringBuffer& rString,
                                                  sal_Int32& nPos,
                                                  OUString& sSymbol );
 
    /**
     * Parse the content of '[$-xxx] or '[$-xxxxxxxx]' and extract the locale
     * type from it.  Given the string, start parsing at position specified by
     * nPos, and store the end position with nPos when the parsing is
     * complete.  The nPos should point to the '$' before the parsing, and to
     * the closing bracket after the parsing.  When the content is [$-xxx],
     * the xxx part represents the language type (aka LCID) in hex numerals.
     * When the content is [$-xxxxxxxx] the last 4 digits represent the LCID
     * (again in hex), the next 2 digits represent the calendar type, and the
     * 2 highest digits (if exists) is the numeral shape.
     *
     * @reference
     * http://office.microsoft.com/en-us/excel-help/creating-international-number-formats-HA001034635.aspx
     *
     * @param rString input string
     * @param nPos position (see above).
     *
     * @return struct containing numeral shape, calendar type, and LCID that
     *         specifies language type. See i18nlangtag/lang.h for a complete
     *         list of language types. These numbers also correspond with the
     *         numbers used by Microsoft Office.
     */
    SVL_DLLPRIVATE static LocaleType ImpGetLocaleType( const OUString& rString, sal_Int32& nPos );
 
    /** Obtain calendar and numerals from a LocaleType that was parsed from a
        LCID with ImpGetLocaleType().
 
        Inserts a NatNum modifier to rString at nPos if needed as determined
        from the numeral code.
 
        @ATTENTION: may modify <member>maLocale</member> to make it follow
        aTmpLocale, in which case also nLang is adapted.
 
        @returns a string with the calendar if one was determined from the
        calendar code, else an empty string. The calendar string needs to be
        inserted at a proper position to rString after all bracketed prefixes.
     */
    SVL_DLLPRIVATE OUString ImpObtainCalendarAndNumerals( OUStringBuffer & rString,
                                                          sal_Int32 nPos,
                                                          LanguageType & nLang,
                                                          const LocaleType & aTmpLocale );
 
    // standard number output
    SVL_DLLPRIVATE void ImpGetOutputStandard( double& fNumber, OUString& OutString ) const;
    SVL_DLLPRIVATE void ImpGetOutputStandard( double& fNumber, OUStringBuffer& OutString ) const;
    SVL_DLLPRIVATE void ImpGetOutputStdToPrecision( double& rNumber, OUString& rOutString, sal_uInt16 nPrecision ) const;
    // numbers in input line
    SVL_DLLPRIVATE void ImpGetOutputInputLine( double fNumber, OUString& OutString ) const;
 
    // check subcondition
    // OP undefined => -1
    // else 0 or 1
    SVL_DLLPRIVATE static short ImpCheckCondition(double fNumber,
                         double fLimit,
                         SvNumberformatLimitOps eOp);
 
    // Helper function for number strings
    // append string symbols, insert leading 0 or ' ', or ...
    SVL_DLLPRIVATE bool ImpNumberFill( OUStringBuffer& sStr,
                    double& rNumber,
                    sal_Int32& k,
                    sal_uInt16& j,
                    sal_uInt16 nIx,
                    short eSymbolType,
                    bool bInsertRightBlank = false );
 
    // Helper function to fill in the integer part and the group (AKA thousand) separators
    SVL_DLLPRIVATE bool ImpNumberFillWithThousands( OUStringBuffer& sStr,
                                 double& rNumber,
                                 sal_Int32 k,
                                 sal_uInt16 j,
                                 sal_uInt16 nIx,
                                 sal_Int32 nDigCnt,
                                 bool bAddDecSep = true );
 
    // Helper function to fill in the group (AKA thousand) separators
    // or to skip additional digits
    SVL_DLLPRIVATE void ImpDigitFill( OUStringBuffer& sStr,
                                      sal_Int32 nStart,
                                      sal_Int32& k,
                                      sal_uInt16 nIx,
                                      sal_Int32 & nDigitCount,
                                      utl::DigitGroupingIterator & );
 
    SVL_DLLPRIVATE bool ImpDecimalFill( OUStringBuffer& sStr,
                                 double& rNumber,
                                 sal_Int32 nDecPos,
                                 sal_uInt16 j,
                                 sal_uInt16 nIx,
                                 bool bInteger );
 
    /** Calculate each element of fraction:
     * integer part, numerator part, denominator part
     * @param fNumber value to be represented as fraction. Will contain absolute fractional part
     * @param nIx subformat number 0..3
     * @param fIntPart integral part of fraction
     * @param nFrac numerator of fraction
     * @param nDic denominator of fraction
     */
    SVL_DLLPRIVATE void ImpGetFractionElements( double& fNumber,
                                                sal_uInt16 nIx,
                                                double& fIntPart,
                                                sal_uInt64& nFrac,
                                                sal_uInt64& nDiv ) const;
    SVL_DLLPRIVATE bool ImpGetFractionOutput(double fNumber,
                                             sal_uInt16 nIx,
                                             OUStringBuffer& OutString);
    SVL_DLLPRIVATE bool ImpGetScientificOutput(double fNumber,
                                               sal_uInt16 nIx,
                                               OUStringBuffer& OutString);
 
    SVL_DLLPRIVATE bool ImpGetDateOutput( double fNumber,
                                          sal_uInt16 nIx,
                                          OUStringBuffer& OutString );
    SVL_DLLPRIVATE bool ImpGetTimeOutput( double fNumber,
                                          sal_uInt16 nIx,
                                          OUStringBuffer& OutString );
    SVL_DLLPRIVATE bool ImpGetDateTimeOutput( double fNumber,
                                              sal_uInt16 nIx,
                                              OUStringBuffer& OutString );
 
    // Switches to the "gregorian" calendar if the current calendar is
    // non-"gregorian" and the era is a "Dummy" era of a calendar which doesn't
    // know a "before" era (like zh_TW ROC or ja_JP Gengou). If switched and
    // rOrgCalendar was "gregorian" the string is emptied. If rOrgCalendar was
    // empty the previous calendar name and date/time are returned.
    SVL_DLLPRIVATE bool ImpFallBackToGregorianCalendar( OUString& rOrgCalendar, double& fOrgDateTime );
 
    // Append a "G" short era string of the given calendar. In the case of a
    // Gengou calendar this is a one character abbreviation, for other
    // calendars the XExtendedCalendar::getDisplayString() method is called.
    SVL_DLLPRIVATE static void ImpAppendEraG( OUStringBuffer& OutStringBuffer, const CalendarWrapper& rCal,
                                              sal_Int16 nNatNum );
 
    SVL_DLLPRIVATE bool ImpGetNumberOutput( double fNumber,
                                            sal_uInt16 nIx,
                                            OUStringBuffer& OutString );
 
    SVL_DLLPRIVATE void ImpCopyNumberformat( const SvNumberformat& rFormat );
 
    // normal digits or other digits, depending on ImpSvNumFor.aNatNum,
    // [NatNum1], [NatNum2], ...
    SVL_DLLPRIVATE OUString ImpGetNatNumString( const SvNumberNatNum& rNum, sal_Int32 nVal,
                                              sal_uInt16 nMinDigits  ) const;
 
    OUString ImpIntToString( sal_uInt16 nIx, sal_Int32 nVal, sal_uInt16 nMinDigits = 0 ) const
    {
        const SvNumberNatNum& rNum = NumFor[nIx].GetNatNum();
        if ( nMinDigits || rNum.IsComplete() )
        {
            return ImpGetNatNumString( rNum, nVal, nMinDigits );
        }
        return OUString::number(nVal);
    }
 
    // Obtain the string of the fraction of second, without leading "0.",
    // rounded to nFractionDecimals (or nFractionDecimals+1 if
    // bAddOneRoundingDecimal==true but then truncated at nFractionDecimals,
    // for use with the result of tools::Time::GetClock()) with the length of
    // nFractionDecimals, unless nMinimumInputLineDecimals>0 is given for input
    // line string where extra trailing "0" are discarded.
    SVL_DLLPRIVATE sal_uInt16 ImpGetFractionOfSecondString( OUStringBuffer& rBuf, double fFractionOfSecond,
            int nFractionDecimals, bool bAddOneRoundingDecimal, sal_uInt16 nIx, sal_uInt16 nMinimumInputLineDecimals );
 
    // transliterate according to NativeNumber
    SVL_DLLPRIVATE OUString impTransliterateImpl(const OUString& rStr, const SvNumberNatNum& rNum) const;
    SVL_DLLPRIVATE void impTransliterateImpl(OUStringBuffer& rStr, const SvNumberNatNum& rNum) const;
    SVL_DLLPRIVATE OUString impTransliterateImpl(const OUString& rStr, const SvNumberNatNum& rNum, sal_uInt16 nDateKey) const;
 
    OUString impTransliterate(const OUString& rStr, const SvNumberNatNum& rNum) const
    {
        return rNum.IsComplete() ? impTransliterateImpl(rStr, rNum) : rStr;
    }
 
    SVL_DLLPRIVATE void impTransliterate(OUStringBuffer& rStr, const SvNumberNatNum& rNum) const
    {
        if(rNum.IsComplete())
        {
            impTransliterateImpl(rStr, rNum);
        }
    }
 
    OUString impTransliterate(const OUString& rStr, const SvNumberNatNum& rNum, sal_uInt16 nDateKey) const
    {
        return rNum.IsComplete() ? impTransliterateImpl(rStr, rNum, nDateKey) : rStr;
    }
 
};
 
#endif // INCLUDED_SVL_ZFORMAT_HXX
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V690 The 'SvNumberformat' class implements a copy constructor, but lacks the '=' operator. It is dangerous to use such a class.