Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/svl/source/numbers/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 129 kB image not shown  

Quelle  zforscan.cxx   Sprache: C

 
/* -*- 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 <comphelper/string.hxx>
#include <sal/log.hxx>
#include <tools/debug.hxx>
#include <i18nlangtag/mslangid.hxx>
#include <unotools/charclass.hxx>
#include <unotools/localedatawrapper.hxx>
#include <com/sun/star/i18n/NumberFormatCode.hpp>
#include <com/sun/star/i18n/NumberFormatMapper.hpp>

#include <svl/zforlist.hxx>
#include <svl/zformat.hxx>
#include <unotools/digitgroupingiterator.hxx>

#include "zforscan.hxx"

#include <svl/nfsymbol.hxx>
using namespace svt;

const sal_Unicode cNoBreakSpace = 0xA0;
const sal_Unicode cNarrowNoBreakSpace = 0x202F;

const int MaxCntPost = 20; //max dec places allow by rtl_math_round

const NfKeywordTable ImpSvNumberformatScan::sEnglishKeyword =
{             // Syntax keywords in English (USA)
    //! All keywords MUST be UPPERCASE! In same order as NfKeywordIndex
    u""_ustr,        // NF_KEY_NONE 0
    u"E"_ustr,       // NF_KEY_E Exponent
    u"AM/PM"_ustr,   // NF_KEY_AMPM AM/PM
    u"A/P"_ustr,     // NF_KEY_AP AM/PM short
    u"M"_ustr,       // NF_KEY_MI Minute
    u"MM"_ustr,      // NF_KEY_MMI Minute 02
    u"M"_ustr,       // NF_KEY_M month        (!)
    u"MM"_ustr,      // NF_KEY_MM month 02     (!)
    u"MMM"_ustr,     // NF_KEY_MMM month short name
    u"MMMM"_ustr,    // NF_KEY_MMMM month long name
    u"MMMMM"_ustr,   // NF_KEY_MMMMM first letter of month name
    u"H"_ustr,       // NF_KEY_H hour
    u"HH"_ustr,      // NF_KEY_HH hour 02
    u"S"_ustr,       // NF_KEY_S Second
    u"SS"_ustr,      // NF_KEY_SS Second 02
    u"Q"_ustr,       // NF_KEY_Q Quarter short 'Q'
    u"QQ"_ustr,      // NF_KEY_QQ Quarter long
    u"D"_ustr,       // NF_KEY_D day of month
    u"DD"_ustr,      // NF_KEY_DD day of month 02
    u"DDD"_ustr,     // NF_KEY_DDD day of week short
    u"DDDD"_ustr,    // NF_KEY_DDDD day of week long
    u"YY"_ustr,      // NF_KEY_YY year two digits
    u"YYYY"_ustr,    // NF_KEY_YYYY year four digits
    u"NN"_ustr,      // NF_KEY_NN Day of week short
    u"NNN"_ustr,     // NF_KEY_NNN Day of week long
    u"NNNN"_ustr,    // NF_KEY_NNNN Day of week long incl. separator
    u"AAA"_ustr,     // NF_KEY_AAA
    u"AAAA"_ustr,    // NF_KEY_AAAA
    u"E"_ustr,       // NF_KEY_EC
    u"EE"_ustr,      // NF_KEY_EEC
    u"G"_ustr,       // NF_KEY_G
    u"GG"_ustr,      // NF_KEY_GG
    u"GGG"_ustr,     // NF_KEY_GGG
    u"R"_ustr,       // NF_KEY_R
    u"RR"_ustr,      // NF_KEY_RR
    u"WW"_ustr,      // NF_KEY_WW Week of year
    u"t"_ustr,       // NF_KEY_THAI_T Thai T modifier, speciality of Thai Excel, only
                // used with Thai locale and converted to [NatNum1], only
                // exception as lowercase
    u"CCC"_ustr,     // NF_KEY_CCC Currency abbreviation
    u"BOOLEAN"_ustr, // NF_KEY_BOOLEAN boolean
    u"GENERAL"_ustr, // NF_KEY_GENERAL General / Standard

    // Reserved words translated and color names follow:
    u"TRUE"_ustr,    // NF_KEY_TRUE boolean true
    u"FALSE"_ustr,   // NF_KEY_FALSE boolean false
    u"COLOR"_ustr,   // NF_KEY_COLOR color
        // colours
    u"BLACK"_ustr,   // NF_KEY_BLACK
    u"BLUE"_ustr,    // NF_KEY_BLUE
    u"GREEN"_ustr,   // NF_KEY_GREEN
    u"CYAN"_ustr,    // NF_KEY_CYAN
    u"RED"_ustr,     // NF_KEY_RED
    u"MAGENTA"_ustr, // NF_KEY_MAGENTA
    u"BROWN"_ustr,   // NF_KEY_BROWN
    u"GREY"_ustr,    // NF_KEY_GREY
    u"YELLOW"_ustr,  // NF_KEY_YELLOW
    u"WHITE"_ustr    // NF_KEY_WHITE
};

const ::std::vector<Color> ImpSvNumberformatScan::StandardColor{
    COL_BLACK,        COL_LIGHTBLUE, COL_LIGHTGREEN, COL_LIGHTCYAN, COL_LIGHTRED,
    COL_LIGHTMAGENTA, COL_BROWN,     COL_GRAY,       COL_YELLOW,    COL_WHITE
};

// This vector will hold *only* the color names in German language.
static const std::u16string_view& GermanColorName(size_t i)
{
    static const std::u16string_view sGermanColorNames[]{ u"FARBE",   u"SCHWARZ", u"BLAU",
                                                          u"GRÜN",    u"CYAN",    u"ROT",
                                                          u"MAGENTA", u"BRAUN",   u"GRAU",
                                                          u"GELB",    u"WEISS" };
    assert(i < SAL_N_ELEMENTS(sGermanColorNames));
    return sGermanColorNames[i];
}

ImpSvNumberformatScan::ImpSvNumberformatScan(SvNFLanguageData& rCurrentLanguageData,
                                             const SvNumberFormatter& rColorCallback,
                                             const Date& aNullDate)
    : maNullDate(aNullDate)
    , mrCurrentLanguageData(rCurrentLanguageData)
    , mrColorCallback(rColorCallback)
    , eNewLnge(LANGUAGE_DONTKNOW)
    , eTmpLnge(LANGUAGE_DONTKNOW)
    , nCurrPos(-1)
    , meKeywordLocalization(KeywordLocalization::AllowEnglish)
{
    xNFC = css::i18n::NumberFormatMapper::create(rCurrentLanguageData.GetComponentContext());
    bConvertMode = false;
    mbConvertDateOrder = false;
    bConvertSystemToSystem = false;
    bKeywordsNeedInit = true;            // locale dependent and not locale dependent keywords
    bCompatCurNeedInit = true;           // locale dependent compatibility currency strings

    static_assert( NF_KEY_BLACK - NF_KEY_COLOR == 1,        "bad FARBE(COLOR), SCHWARZ(BLACK) sequence");
    static_assert( NF_KEY_FIRSTCOLOR - NF_KEY_COLOR == 1,   "bad color sequence");
    static_assert( NF_MAX_DEFAULT_COLORS + 1 == 11,         "bad color count");
    static_assert( NF_KEY_WHITE - NF_KEY_COLOR + 1 == 11,   "bad color sequence count");

    nStandardPrec = 2;

    Reset();
}

ImpSvNumberformatScan::~ImpSvNumberformatScan()
{
    Reset();
}

void ImpSvNumberformatScan::ChangeIntl( KeywordLocalization eKeywordLocalization )
{
    meKeywordLocalization = eKeywordLocalization;
    bKeywordsNeedInit = true;
    bCompatCurNeedInit = true;
    // may be initialized by InitSpecialKeyword()
    sKeyword[NF_KEY_TRUE].clear();
    sKeyword[NF_KEY_FALSE].clear();
}

void ImpSvNumberformatScan::InitSpecialKeyword( NfKeywordIndex eIdx ) const
{
    switch ( eIdx )
    {
    case NF_KEY_TRUE :
        const_cast<ImpSvNumberformatScan*>(this)->sKeyword[NF_KEY_TRUE] =
            mrCurrentLanguageData.GetCharClass()->uppercase( mrCurrentLanguageData.GetLocaleData()->getTrueWord() );
        if ( sKeyword[NF_KEY_TRUE].isEmpty() )
        {
            SAL_WARN( "svl.numbers""InitSpecialKeyword: TRUE_WORD?" );
            const_cast<ImpSvNumberformatScan*>(this)->sKeyword[NF_KEY_TRUE] = sEnglishKeyword[NF_KEY_TRUE];
        }
        break;
    case NF_KEY_FALSE :
        const_cast<ImpSvNumberformatScan*>(this)->sKeyword[NF_KEY_FALSE] =
            mrCurrentLanguageData.GetCharClass()->uppercase( mrCurrentLanguageData.GetLocaleData()->getFalseWord() );
        if ( sKeyword[NF_KEY_FALSE].isEmpty() )
        {
            SAL_WARN( "svl.numbers""InitSpecialKeyword: FALSE_WORD?" );
            const_cast<ImpSvNumberformatScan*>(this)->sKeyword[NF_KEY_FALSE] = sEnglishKeyword[NF_KEY_FALSE];
        }
        break;
    default:
        SAL_WARN( "svl.numbers""InitSpecialKeyword: unknown request" );
    }
}

void ImpSvNumberformatScan::InitCompatCur() const
{
    ImpSvNumberformatScan* pThis = const_cast<ImpSvNumberformatScan*>(this);
    // currency symbol for old style ("automatic") compatibility format codes
    mrCurrentLanguageData.GetCompatibilityCurrency( pThis->sCurSymbol, pThis->sCurAbbrev );
    // currency symbol upper case
    pThis->sCurString = mrCurrentLanguageData.GetCharClass()->uppercase( sCurSymbol );
    bCompatCurNeedInit = false;
}

void ImpSvNumberformatScan::InitKeywords() const
{
    if ( !bKeywordsNeedInit )
        return ;
    const_cast<ImpSvNumberformatScan*>(this)->SetDependentKeywords();
    bKeywordsNeedInit = false;
}

/** Extract the name of General, Standard, Whatever, ignoring leading modifiers
    such as [NatNum1]. */

static OUString lcl_extractStandardGeneralName( const OUString & rCode )
{
    OUString aStr;
    const sal_Unicode* p = rCode.getStr();
    const sal_Unicode* const pStop = p + rCode.getLength();
    const sal_Unicode* pBeg = p;    // name begins here
    bool bMod = false;
    bool bDone = false;
    while (p < pStop && !bDone)
    {
        switch (*p)
        {
        case '[':
            bMod = true;
            break;
        case ']':
            if (bMod)
            {
                bMod = false;
                pBeg = p+1;
            }
            // else: would be a locale data error, easily to be spotted in
            // UI dialog
            break;
        case ';':
            if (!bMod)
            {
                bDone = true;
                --p;    // put back, increment by one follows
            }
            break;
        }
        ++p;
        if (bMod)
        {
            pBeg = p;
        }
    }
    if (pBeg < p)
    {
        aStr = rCode.copy( pBeg - rCode.getStr(), p - pBeg);
    }
    return aStr;
}

void ImpSvNumberformatScan::SetDependentKeywords()
{
    using namespace ::com::sun::star;
    using namespace ::com::sun::star::uno;

    const CharClass* pCharClass = mrCurrentLanguageData.GetCharClass();
    const LocaleDataWrapper* pLocaleData = mrCurrentLanguageData.GetLocaleData();
    // #80023# be sure to generate keywords for the loaded Locale, not for the
    // requested Locale, otherwise number format codes might not match
    const LanguageTag aLoadedLocale = pLocaleData->getLoadedLanguageTag();
    LanguageType eLang = aLoadedLocale.getLanguageType( false);

    bool bL10n = (meKeywordLocalization != KeywordLocalization::EnglishOnly);
    if (bL10n)
    {
        // Check if this actually is a locale that uses any localized keywords,
        // if not then disable localized keywords completely.
        if ( !eLang.anyOf( LANGUAGE_GERMAN,
                    LANGUAGE_GERMAN_SWISS,
                    LANGUAGE_GERMAN_AUSTRIAN,
                    LANGUAGE_GERMAN_LUXEMBOURG,
                    LANGUAGE_GERMAN_LIECHTENSTEIN,
                    LANGUAGE_DUTCH,
                    LANGUAGE_DUTCH_BELGIAN,
                    LANGUAGE_FRENCH,
                    LANGUAGE_FRENCH_BELGIAN,
                    LANGUAGE_FRENCH_CANADIAN,
                    LANGUAGE_FRENCH_SWISS,
                    LANGUAGE_FRENCH_LUXEMBOURG,
                    LANGUAGE_FRENCH_MONACO,
                    LANGUAGE_FINNISH,
                    LANGUAGE_ITALIAN,
                    LANGUAGE_ITALIAN_SWISS,
                    LANGUAGE_DANISH,
                    LANGUAGE_NORWEGIAN,
                    LANGUAGE_NORWEGIAN_BOKMAL,
                    LANGUAGE_NORWEGIAN_NYNORSK,
                    LANGUAGE_SWEDISH,
                    LANGUAGE_SWEDISH_FINLAND,
                    LANGUAGE_PORTUGUESE,
                    LANGUAGE_PORTUGUESE_BRAZILIAN,
                    LANGUAGE_SPANISH_MODERN,
                    LANGUAGE_SPANISH_DATED,
                    LANGUAGE_SPANISH_MEXICAN,
                    LANGUAGE_SPANISH_GUATEMALA,
                    LANGUAGE_SPANISH_COSTARICA,
                    LANGUAGE_SPANISH_PANAMA,
                    LANGUAGE_SPANISH_DOMINICAN_REPUBLIC,
                    LANGUAGE_SPANISH_VENEZUELA,
                    LANGUAGE_SPANISH_COLOMBIA,
                    LANGUAGE_SPANISH_PERU,
                    LANGUAGE_SPANISH_ARGENTINA,
                    LANGUAGE_SPANISH_ECUADOR,
                    LANGUAGE_SPANISH_CHILE,
                    LANGUAGE_SPANISH_URUGUAY,
                    LANGUAGE_SPANISH_PARAGUAY,
                    LANGUAGE_SPANISH_BOLIVIA,
                    LANGUAGE_SPANISH_EL_SALVADOR,
                    LANGUAGE_SPANISH_HONDURAS,
                    LANGUAGE_SPANISH_NICARAGUA,
                    LANGUAGE_SPANISH_PUERTO_RICO ))
        {
            bL10n = false;
            meKeywordLocalization = KeywordLocalization::EnglishOnly;
        }
    }

    // Init the current NfKeywordTable with English keywords.
    sKeyword = sEnglishKeyword;

    // Set the uppercase localized General name, e.g. Standard -> STANDARD
    i18n::NumberFormatCode aFormat = xNFC->getFormatCode( NF_NUMBER_STANDARD, aLoadedLocale.getLocale() );
    sNameStandardFormat = lcl_extractStandardGeneralName( aFormat.Code );
    sKeyword[NF_KEY_GENERAL] = pCharClass->uppercase( sNameStandardFormat );

    // Thai T NatNum special. Other locale's small letter 't' results in upper
    // case comparison not matching but length does in conversion mode. Ugly.
    if (eLang == LANGUAGE_THAI)
    {
        sKeyword[NF_KEY_THAI_T] = "T";
    }
    else
    {
        sKeyword[NF_KEY_THAI_T] = sEnglishKeyword[NF_KEY_THAI_T];
    }

    // boolean keywords
    InitSpecialKeyword( NF_KEY_TRUE );
    InitSpecialKeyword( NF_KEY_FALSE );

    // Boolean equivalent format codes that are written to Excel files, may
    // have been written to ODF as well, specifically if such loaded Excel file
    // was saved as ODF, and shall result in proper Boolean again.
    // "TRUE";"TRUE";"FALSE"
    sBooleanEquivalent1 = "\"" + sKeyword[NF_KEY_TRUE] + "\";\"" +
        sKeyword[NF_KEY_TRUE] + "\";\"" + sKeyword[NF_KEY_FALSE] + "\"";
    // [>0]"TRUE";[<0]"TRUE";"FALSE"
    sBooleanEquivalent2 = "[>0]\"" + sKeyword[NF_KEY_TRUE] + "\";[<0]\"" +
        sKeyword[NF_KEY_TRUE] + "\";\"" + sKeyword[NF_KEY_FALSE] + "\"";

    // compatibility currency strings
    InitCompatCur();

    if (!bL10n)
        return;

    // All locale dependent keywords overrides follow.

    if ( eLang.anyOf(
            LANGUAGE_GERMAN,
            LANGUAGE_GERMAN_SWISS,
            LANGUAGE_GERMAN_AUSTRIAN,
            LANGUAGE_GERMAN_LUXEMBOURG,
            LANGUAGE_GERMAN_LIECHTENSTEIN))
    {
        //! all capital letters
        sKeyword[NF_KEY_M] =         "M";     // month 1
        sKeyword[NF_KEY_MM] =        "MM";    // month 01
        sKeyword[NF_KEY_MMM] =       "MMM";   // month Jan
        sKeyword[NF_KEY_MMMM] =      "MMMM";  // month Januar
        sKeyword[NF_KEY_MMMMM] =     "MMMMM"// month J
        sKeyword[NF_KEY_H] =         "H";     // hour 2
        sKeyword[NF_KEY_HH] =        "HH";    // hour 02
        sKeyword[NF_KEY_D] =         "T";
        sKeyword[NF_KEY_DD] =        "TT";
        sKeyword[NF_KEY_DDD] =       "TTT";
        sKeyword[NF_KEY_DDDD] =      "TTTT";
        sKeyword[NF_KEY_YY] =        "JJ";
        sKeyword[NF_KEY_YYYY] =      "JJJJ";
        sKeyword[NF_KEY_BOOLEAN] =   "LOGISCH";
        sKeyword[NF_KEY_COLOR] =     GermanColorName(NF_KEY_COLOR - NF_KEY_COLOR);
        sKeyword[NF_KEY_BLACK] =     GermanColorName(NF_KEY_BLACK - NF_KEY_COLOR);
        sKeyword[NF_KEY_BLUE] =      GermanColorName(NF_KEY_BLUE - NF_KEY_COLOR);
        sKeyword[NF_KEY_GREEN] =     GermanColorName(NF_KEY_GREEN - NF_KEY_COLOR);
        sKeyword[NF_KEY_CYAN] =      GermanColorName(NF_KEY_CYAN - NF_KEY_COLOR);
        sKeyword[NF_KEY_RED] =       GermanColorName(NF_KEY_RED - NF_KEY_COLOR);
        sKeyword[NF_KEY_MAGENTA] =   GermanColorName(NF_KEY_MAGENTA - NF_KEY_COLOR);
        sKeyword[NF_KEY_BROWN] =     GermanColorName(NF_KEY_BROWN - NF_KEY_COLOR);
        sKeyword[NF_KEY_GREY] =      GermanColorName(NF_KEY_GREY - NF_KEY_COLOR);
        sKeyword[NF_KEY_YELLOW] =    GermanColorName(NF_KEY_YELLOW - NF_KEY_COLOR);
        sKeyword[NF_KEY_WHITE] =     GermanColorName(NF_KEY_WHITE - NF_KEY_COLOR);
    }
    else
    {
        // day
        if ( eLang.anyOf(
                LANGUAGE_ITALIAN,
                LANGUAGE_ITALIAN_SWISS))
        {
            sKeyword[NF_KEY_D] = "G";
            sKeyword[NF_KEY_DD] = "GG";
            sKeyword[NF_KEY_DDD] = "GGG";
            sKeyword[NF_KEY_DDDD] = "GGGG";
            // must exchange the era code, same as Xcl
            sKeyword[NF_KEY_G] = "X";
            sKeyword[NF_KEY_GG] = "XX";
            sKeyword[NF_KEY_GGG] = "XXX";
        }
        else if ( eLang.anyOf(
                 LANGUAGE_FRENCH,
                 LANGUAGE_FRENCH_BELGIAN,
                 LANGUAGE_FRENCH_CANADIAN,
                 LANGUAGE_FRENCH_SWISS,
                 LANGUAGE_FRENCH_LUXEMBOURG,
                 LANGUAGE_FRENCH_MONACO))
        {
            sKeyword[NF_KEY_D] = "J";
            sKeyword[NF_KEY_DD] = "JJ";
            sKeyword[NF_KEY_DDD] = "JJJ";
            sKeyword[NF_KEY_DDDD] = "JJJJ";
        }
        else if ( eLang == LANGUAGE_FINNISH )
        {
            sKeyword[NF_KEY_D] = "P";
            sKeyword[NF_KEY_DD] = "PP";
            sKeyword[NF_KEY_DDD] = "PPP";
            sKeyword[NF_KEY_DDDD] = "PPPP";
        }

        // month
        if ( eLang == LANGUAGE_FINNISH )
        {
            sKeyword[NF_KEY_M] = "K";
            sKeyword[NF_KEY_MM] = "KK";
            sKeyword[NF_KEY_MMM] = "KKK";
            sKeyword[NF_KEY_MMMM] = "KKKK";
            sKeyword[NF_KEY_MMMMM] = "KKKKK";
        }

        // year
        if ( eLang.anyOf(
            LANGUAGE_ITALIAN,
            LANGUAGE_ITALIAN_SWISS,
            LANGUAGE_FRENCH,
            LANGUAGE_FRENCH_BELGIAN,
            LANGUAGE_FRENCH_CANADIAN,
            LANGUAGE_FRENCH_SWISS,
            LANGUAGE_FRENCH_LUXEMBOURG,
            LANGUAGE_FRENCH_MONACO,
            LANGUAGE_PORTUGUESE,
            LANGUAGE_PORTUGUESE_BRAZILIAN,
            LANGUAGE_SPANISH_MODERN,
            LANGUAGE_SPANISH_DATED,
            LANGUAGE_SPANISH_MEXICAN,
            LANGUAGE_SPANISH_GUATEMALA,
            LANGUAGE_SPANISH_COSTARICA,
            LANGUAGE_SPANISH_PANAMA,
            LANGUAGE_SPANISH_DOMINICAN_REPUBLIC,
            LANGUAGE_SPANISH_VENEZUELA,
            LANGUAGE_SPANISH_COLOMBIA,
            LANGUAGE_SPANISH_PERU,
            LANGUAGE_SPANISH_ARGENTINA,
            LANGUAGE_SPANISH_ECUADOR,
            LANGUAGE_SPANISH_CHILE,
            LANGUAGE_SPANISH_URUGUAY,
            LANGUAGE_SPANISH_PARAGUAY,
            LANGUAGE_SPANISH_BOLIVIA,
            LANGUAGE_SPANISH_EL_SALVADOR,
            LANGUAGE_SPANISH_HONDURAS,
            LANGUAGE_SPANISH_NICARAGUA,
            LANGUAGE_SPANISH_PUERTO_RICO))
        {
            sKeyword[NF_KEY_YY] = "AA";
            sKeyword[NF_KEY_YYYY] = "AAAA";
            // must exchange the day of week name code, same as Xcl
            sKeyword[NF_KEY_AAA] =   "OOO";
            sKeyword[NF_KEY_AAAA] =  "OOOO";
        }
        else if ( eLang.anyOf(
             LANGUAGE_DUTCH,
             LANGUAGE_DUTCH_BELGIAN))
        {
            sKeyword[NF_KEY_YY] = "JJ";
            sKeyword[NF_KEY_YYYY] = "JJJJ";
        }
        else if ( eLang == LANGUAGE_FINNISH )
        {
            sKeyword[NF_KEY_YY] = "VV";
            sKeyword[NF_KEY_YYYY] = "VVVV";
        }

        // hour
        if ( eLang.anyOf(
             LANGUAGE_DUTCH,
             LANGUAGE_DUTCH_BELGIAN))
        {
            sKeyword[NF_KEY_H] = "U";
            sKeyword[NF_KEY_HH] = "UU";
        }
        else if ( eLang.anyOf(
            LANGUAGE_FINNISH,
            LANGUAGE_SWEDISH,
            LANGUAGE_SWEDISH_FINLAND,
            LANGUAGE_DANISH,
            LANGUAGE_NORWEGIAN,
            LANGUAGE_NORWEGIAN_BOKMAL,
            LANGUAGE_NORWEGIAN_NYNORSK))
        {
            sKeyword[NF_KEY_H] = "T";
            sKeyword[NF_KEY_HH] = "TT";
        }
    }
}

void ImpSvNumberformatScan::ChangeNullDate(sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear)
{
    Date aDate(nDay, nMonth, nYear);
    if (!aDate.IsValidDate())
    {
        aDate.Normalize();
        SAL_WARN("svl.numbers","ImpSvNumberformatScan::ChangeNullDate - not valid"
                " d: " << nDay << " m: " << nMonth << " y: " << nYear << " normalized to"
                " d: " << aDate.GetDay() << " m: " << aDate.GetMonth() << " y: " << aDate.GetYear());
        // Slap the caller if really bad, like year 0.
        assert(aDate.IsValidDate());
    }
    if (aDate.IsValidDate())
        maNullDate = aDate;
}

void ImpSvNumberformatScan::ChangeStandardPrec(sal_uInt16 nPrec)
{
    nStandardPrec = nPrec;
}

const Color* ImpSvNumberformatScan::GetColor(OUString& sStr) const
{
    OUString sString = mrCurrentLanguageData.GetCharClass()->uppercase(sStr);
    const NfKeywordTable & rKeyword = GetKeywords();
    size_t i = 0;
    while (i < NF_MAX_DEFAULT_COLORS && sString != rKeyword[NF_KEY_FIRSTCOLOR+i] )
    {
        i++;
    }
    if (i >= NF_MAX_DEFAULT_COLORS && meKeywordLocalization == KeywordLocalization::AllowEnglish)
    {
        LanguageType eLang = mrCurrentLanguageData.GetLocaleData()->getLoadedLanguageTag().getLanguageType( false);
        if ( eLang.anyOf(
                    LANGUAGE_GERMAN,
                    LANGUAGE_GERMAN_SWISS,
                    LANGUAGE_GERMAN_AUSTRIAN,
                    LANGUAGE_GERMAN_LUXEMBOURG,
                    LANGUAGE_GERMAN_LIECHTENSTEIN )) // only German uses localized color names
        {
            size_t j = 0;
            while ( j < NF_MAX_DEFAULT_COLORS && sString != sEnglishKeyword[NF_KEY_FIRSTCOLOR + j] )
            {
                ++j;
            }
            if ( j < NF_MAX_DEFAULT_COLORS )
            {
                i = j;
            }
        }
    }

    enum ColorKeywordConversion
    {
        None,
        GermanToEnglish,
        EnglishToGerman
    } eColorKeywordConversion(None);

    if (bConvertMode)
    {
        const bool bFromGerman = eTmpLnge.anyOf(
                LANGUAGE_GERMAN,
                LANGUAGE_GERMAN_SWISS,
                LANGUAGE_GERMAN_AUSTRIAN,
                LANGUAGE_GERMAN_LUXEMBOURG,
                LANGUAGE_GERMAN_LIECHTENSTEIN);
        const bool bToGerman = eNewLnge.anyOf(
                LANGUAGE_GERMAN,
                LANGUAGE_GERMAN_SWISS,
                LANGUAGE_GERMAN_AUSTRIAN,
                LANGUAGE_GERMAN_LUXEMBOURG,
                LANGUAGE_GERMAN_LIECHTENSTEIN);
        if (bFromGerman && !bToGerman)
            eColorKeywordConversion = ColorKeywordConversion::GermanToEnglish;
        else if (!bFromGerman && bToGerman)
            eColorKeywordConversion = ColorKeywordConversion::EnglishToGerman;
    }

    const Color* pResult = nullptr;
    if (i >= NF_MAX_DEFAULT_COLORS)
    {
        const OUString& rColorWord = rKeyword[NF_KEY_COLOR];
        bool bL10n = true;
        if ((bL10n = sString.startsWith(rColorWord)) ||
                ((meKeywordLocalization == KeywordLocalization::AllowEnglish) &&
                 sString.startsWith(sEnglishKeyword[NF_KEY_COLOR])))
        {
            sal_Int32 nPos = (bL10n ? rColorWord.getLength() : sEnglishKeyword[NF_KEY_COLOR].getLength());
            sStr = sStr.copy(nPos);
            sStr = comphelper::string::strip(sStr, ' ');
            switch (eColorKeywordConversion)
            {
                case ColorKeywordConversion::None:
                    sStr = rColorWord + sStr;
                break;
                case ColorKeywordConversion::GermanToEnglish:
                    sStr = sEnglishKeyword[NF_KEY_COLOR] + sStr;                    // Farbe -> COLOR
                break;
                case ColorKeywordConversion::EnglishToGerman:
                    sStr = GermanColorName(NF_KEY_COLOR - NF_KEY_COLOR) + sStr;   // Color -> FARBE
                break;
            }
            sString = sString.copy(nPos);
            sString = comphelper::string::strip(sString, ' ');

            if ( CharClass::isAsciiNumeric( sString ) )
            {
                sal_Int32 nIndex = sString.toInt32();
                if (nIndex > 0 && nIndex <= 64)
                {
                    pResult = GetUserDefColor(static_cast<sal_uInt16>(nIndex)-1);
                }
            }
        }
    }
    else
    {
        sStr.clear();
        switch (eColorKeywordConversion)
        {
            case ColorKeywordConversion::None:
                sStr = rKeyword[NF_KEY_FIRSTCOLOR+i];
            break;
            case ColorKeywordConversion::GermanToEnglish:
                sStr = sEnglishKeyword[NF_KEY_FIRSTCOLOR + i];                  // Rot -> RED
            break;
            case ColorKeywordConversion::EnglishToGerman:
                sStr = GermanColorName(NF_KEY_FIRSTCOLOR - NF_KEY_COLOR + i); // Red -> ROT
            break;
        }
        pResult = &(StandardColor[i]);
    }
    return pResult;
}

short ImpSvNumberformatScan::GetKeyWord( const OUString& sSymbol, sal_Int32 nPos, bool& rbFoundEnglish ) const
{
    OUString sString = mrCurrentLanguageData.GetCharClass()->uppercase( sSymbol, nPos, sSymbol.getLength() - nPos );
    const NfKeywordTable & rKeyword = GetKeywords();
    // #77026# for the Xcl perverts: the GENERAL keyword is recognized anywhere
    if (sString.startsWith( rKeyword[NF_KEY_GENERAL] ))
    {
        return NF_KEY_GENERAL;
    }
    if ((meKeywordLocalization == KeywordLocalization::AllowEnglish) &&
            sString.startsWith( sEnglishKeyword[NF_KEY_GENERAL]))
    {
        rbFoundEnglish = true;
        return NF_KEY_GENERAL;
    }

    // MUST be a reverse search to find longer strings first,
    // new keywords take precedence over old keywords,
    // skip colors et al after keywords.
    short i = NF_KEY_LASTKEYWORD;
    while (i > 0 && !sString.startsWith( rKeyword[i]))
    {
        i--;
    }
    if (i == 0 && meKeywordLocalization == KeywordLocalization::AllowEnglish)
    {
        // No localized (if so) keyword, try English keywords if keywords
        // are localized. That was already checked in SetDependentKeywords().
        i = NF_KEY_LASTKEYWORD;
        while (i > 0 && !sString.startsWith( sEnglishKeyword[i]))
        {
            i--;
        }
    }

    // The Thai T NatNum modifier during Xcl import.
    if (i == 0 && bConvertMode &&
        sString[0] == 'T' &&
        eTmpLnge == LANGUAGE_ENGLISH_US &&
        MsLangId::getRealLanguage( eNewLnge) == LANGUAGE_THAI)
    {
        i = NF_KEY_THAI_T;
    }
    return i; // 0 => not found
}

/**
 * Next_Symbol
 *
 * Splits up the input for further processing (by the Turing machine).
 *
 * Starting state = SsStar
 *
 * ---------------+-------------------+---------------------------+---------------
 * Old state      | Character read    | Event                     | New state
 * ---------------+-------------------+---------------------------+---------------
 * SsStart        | Character         | Symbol = Character        | SsGetWord
 *                |    "              | Type = String             | SsGetString
 *                |    \              | Type = String             | SsGetChar
 *                |    *              | Type = Star               | SsGetStar
 *                |    _              | Type = Blank              | SsGetBlank
 *                | @ # 0 ? / . , % [ | Symbol = Character;       |
 *                | ] ' Blank         | Type = Control character  | SsStop
 *                | $ - + ( ) :       | Type  = String;           |
 *                | Else              | Symbol = Character        | SsStop
 * ---------------|-------------------+---------------------------+---------------
 * SsGetChar      | Else              | Symbol = Character        | SsStop
 * ---------------+-------------------+---------------------------+---------------
 * GetString      | "                 |                           | SsStop
 *                | Else              | Symbol += Character       | GetString
 * ---------------+-------------------+---------------------------+---------------
 * SsGetWord      | Character         | Symbol += Character       |
 *                | + -        (E+ E-)| Symbol += Character       | SsStop
 *                | /          (AM/PM)| Symbol += Character       |
 *                | Else              | Pos--, if Key Type = Word | SsStop
 * ---------------+-------------------+---------------------------+---------------
 * SsGetStar      | Else              | Symbol += Character       | SsStop
 *                |                   | Mark special case *       |
 * ---------------+-------------------+---------------------------+---------------
 * SsGetBlank     | Else              | Symbol + =Character       | SsStop
 *                |                   | Mark special case  _      |
 * ---------------------------------------------------------------+--------------
 *
 * If we recognize a keyword in the state SsGetWord (even as the symbol's start text)
 * we write back the rest of the characters!
 */


namespace {

enum ScanState
{
    SsStop      = 0,
    SsStart     = 1,
    SsGetChar   = 2,
    SsGetString = 3,
    SsGetWord   = 4,
    SsGetStar   = 5,
    SsGetBlank  = 6
};

}

short ImpSvNumberformatScan::Next_Symbol( const OUString& rStr,
                                          sal_Int32& nPos,
                                          OUString& sSymbol ) const
{
    InitKeywords();
    const CharClass* pChrCls = mrCurrentLanguageData.GetCharClass();
    const LocaleDataWrapper* pLoc = mrCurrentLanguageData.GetLocaleData();
    short eType = 0;
    ScanState eState = SsStart;
    OUStringBuffer sSymbolBuffer;
    while ( nPos < rStr.getLength() && eState != SsStop )
    {
        sal_Unicode cToken = rStr[nPos++];
        switch (eState)
        {
        case SsStart:
            // Fetch any currency longer than one character and don't get
            // confused later on by "E/" or other combinations of letters
            // and meaningful symbols. Necessary for old automatic currency.
            // #96158# But don't do it if we're starting a "[...]" section,
            // for example a "[$...]" new currency symbol to not parse away
            // "$U" (symbol) of "[$UYU]" (abbreviation).
            if ( nCurrPos >= 0 && sCurString.getLength() > 1 &&
                 nPos-1 + sCurString.getLength() <= rStr.getLength() &&
                 (nPos <= 1 || rStr[nPos-2] != '[') )
            {
                OUString aTest = pChrCls->uppercase( rStr.copy( nPos-1, sCurString.getLength() ) );
                if ( aTest == sCurString )
                {
                    sSymbol = rStr.copy( --nPos, sCurString.getLength() );
                    nPos = nPos + sSymbol.getLength();
                    eType = NF_SYMBOLTYPE_STRING;
                    return eType;
                }
            }
            switch (cToken)
            {
            case '#':
            case '0':
            case '?':
            case '%':
            case '@':
            case '[':
            case ']':
            case ',':
            case '.':
            case '/':
            case '\'':
            case ' ':
            case ':':
            case '-':
                eType = NF_SYMBOLTYPE_DEL;
                sSymbolBuffer.append(OUStringChar(cToken));
                eState = SsStop;
                break;
            case '*':
                eType = NF_SYMBOLTYPE_STAR;
                sSymbolBuffer.append(OUStringChar(cToken));
                eState = SsGetStar;
                break;
            case '_':
                eType = NF_SYMBOLTYPE_BLANK;
                sSymbolBuffer.append(OUStringChar(cToken));
                eState = SsGetBlank;
                break;
            case '"':
                eType = NF_SYMBOLTYPE_STRING;
                eState = SsGetString;
                sSymbolBuffer.append(OUStringChar(cToken));
                break;
            case '\\':
                eType = NF_SYMBOLTYPE_STRING;
                eState = SsGetChar;
                sSymbolBuffer.append(OUStringChar(cToken));
                break;
            case '$':
            case '+':
            case '(':
            case ')':
                eType = NF_SYMBOLTYPE_STRING;
                eState = SsStop;
                sSymbolBuffer.append(OUStringChar(cToken));
                break;
            default :
                if (StringEqualsChar( mrCurrentLanguageData.GetNumDecimalSep(), cToken) ||
                    StringEqualsChar( mrCurrentLanguageData.GetNumThousandSep(), cToken) ||
                    StringEqualsChar( mrCurrentLanguageData.GetDateSep(), cToken) ||
                    StringEqualsChar( pLoc->getTimeSep(), cToken) ||
                    StringEqualsChar( pLoc->getTime100SecSep(), cToken))
                {
                    // Another separator than pre-known ASCII
                    eType = NF_SYMBOLTYPE_DEL;
                    sSymbolBuffer.append(OUStringChar(cToken));
                    eState = SsStop;
                }
                else if ( pChrCls->isLetter( rStr, nPos-1 ) )
                {
                    bool bFoundEnglish = false;
                    short nTmpType = GetKeyWord( rStr, nPos-1, bFoundEnglish);
                    if ( nTmpType )
                    {
                        bool bCurrency = false;
                        // "Automatic" currency may start with keyword,
                        // like "R" (Rand) and 'R' (era)
                        if ( nCurrPos >= 0 &&
                             nPos-1 + sCurString.getLength() <= rStr.getLength() &&
                             sCurString.startsWith( bFoundEnglish ? sEnglishKeyword[nTmpType] : sKeyword[nTmpType]))
                        {
                            OUString aTest = pChrCls->uppercase( rStr.copy( nPos-1, sCurString.getLength() ) );
                            if ( aTest == sCurString )
                            {
                                bCurrency = true;
                            }
                        }
                        if ( bCurrency )
                        {
                            eState = SsGetWord;
                            sSymbolBuffer.append(OUStringChar(cToken));
                        }
                        else
                        {
                            eType = nTmpType;
                            // The code to be advanced is the detected keyword,
                            // not necessarily the locale's keyword, but the
                            // symbol is to be the locale's keyword.
                            sal_Int32 nLen;
                            if (bFoundEnglish)
                            {
                                nLen = sEnglishKeyword[eType].getLength();
                                // Use the locale's General keyword name, not uppercase.
                                sSymbolBuffer = (eType == NF_KEY_GENERAL ? sNameStandardFormat : sKeyword[eType]);
                            }
                            else
                            {
                                nLen = sKeyword[eType].getLength();
                                // Preserve a locale's keyword's case as entered.
                                sSymbolBuffer = rStr.subView( nPos-1, nLen);
                            }
                            if ((eType == NF_KEY_E || IsAmbiguousE(eType)) && nPos < rStr.getLength())
                            {
                                sal_Unicode cNext = rStr[nPos];
                                switch ( cNext )
                                {
                                case '+' :
                                case '-' :  // E+ E- combine to one symbol
                                    sSymbolBuffer.append(OUStringChar(cNext));
                                    eType = NF_KEY_E;
                                    nPos++;
                                    break;
                                case '0' :
                                case '#' :  // scientific E without sign
                                    eType = NF_KEY_E;
                                    break;
                                }
                            }
                            nPos--;
                            nPos = nPos + nLen;
                            eState = SsStop;
                        }
                    }
                    else
                    {
                        eState = SsGetWord;
                        sSymbolBuffer.append(OUStringChar(cToken));
                    }
                }
                else
                {
                    eType = NF_SYMBOLTYPE_STRING;
                    eState = SsStop;
                    sSymbolBuffer.append(OUStringChar(cToken));
                }
                break;
            }
            break;
        case SsGetChar:
            sSymbolBuffer.append(OUStringChar(cToken));
            eState = SsStop;
            break;
        case SsGetString:
            if (cToken == '"')
            {
                eState = SsStop;
            }
            sSymbolBuffer.append(OUStringChar(cToken));
            break;
        case SsGetWord:
            if ( pChrCls->isLetter( rStr, nPos-1 ) )
            {
                bool bFoundEnglish = false;
                short nTmpType = GetKeyWord( rStr, nPos-1, bFoundEnglish);
                if ( nTmpType )
                {
                    // beginning of keyword, stop scan and put back
                    eType = NF_SYMBOLTYPE_STRING;
                    eState = SsStop;
                    nPos--;
                }
                else
                {
                    sSymbolBuffer.append(OUStringChar(cToken));
                }
            }
            else
            {
                bool bDontStop = false;
                sal_Unicode cNext;
                switch (cToken)
                {
                case '/'// AM/PM, A/P
                    if (nPos < rStr.getLength())
                    {
                        cNext = rStr[nPos];
                        if ( cNext == 'P' || cNext == 'p' )
                        {
                            sal_Int32 nLen = sSymbolBuffer.getLength();
                            if ( 1 <= nLen &&
                                    (sSymbolBuffer[0] == 'A' || sSymbolBuffer[0] == 'a') &&
                                    (nLen == 1 ||
                                     (nLen == 2 && (sSymbolBuffer[1] == 'M' || sSymbolBuffer[1] == 'm')
                                      && (rStr[nPos + 1] == 'M' || rStr[nPos + 1] == 'm'))))
                            {
                                sSymbolBuffer.append(OUStringChar(cToken));
                                bDontStop = true;
                            }
                        }
                    }
                    break;
                }
                // anything not recognized will stop the scan
                if (!bDontStop)
                {
                    eState = SsStop;
                    nPos--;
                    eType = NF_SYMBOLTYPE_STRING;
                }
            }
            break;
        case SsGetStar:
        case SsGetBlank:
            eState = SsStop;
            sSymbolBuffer.append(OUStringChar(cToken));
            break;
        default:
            break;
        } // of switch
    } // of while
    if (eState == SsGetWord)
    {
        eType = NF_SYMBOLTYPE_STRING;
    }
    sSymbol = sSymbolBuffer.makeStringAndClear();
    return eType;
}

sal_Int32 ImpSvNumberformatScan::Symbol_Division(const OUString& rString)
{
    nCurrPos = -1;
    // Do we have some sort of currency?
    OUString sString = mrCurrentLanguageData.GetCharClass()->uppercase(rString);
    sal_Int32 nCPos = 0;
    while (nCPos >= 0 && nCPos < sString.getLength())
    {
        nCPos = sString.indexOf(GetCurString(),nCPos);
        if (nCPos >= 0)
        {
            // In Quotes?
            sal_Int32 nQ = SvNumberformat::GetQuoteEnd( sString, nCPos );
            if ( nQ < 0 )
            {
                sal_Unicode c;
                if ( nCPos == 0 ||
                    ((c = sString[nCPos-1]) != '"'
                            && c != '\\') ) // dm can be protected by "dm \d
                {
                    nCurrPos = nCPos;
                    nCPos = -1;
                }
                else
                {
                    nCPos++; // Continue search
                }
            }
            else
            {
                nCPos = nQ + 1; // Continue search
            }
        }
    }
    nStringsCnt = 0;
    bool bStar = false// Is set on detecting '*'
    Reset();

    sal_Int32 nPos = 0;
    const sal_Int32 nLen = rString.getLength();
    while (nPos < nLen && nStringsCnt < NF_MAX_FORMAT_SYMBOLS)
    {
        nTypeArray[nStringsCnt] = Next_Symbol(rString, nPos, sStrArray[nStringsCnt]);
        if (nTypeArray[nStringsCnt] == NF_SYMBOLTYPE_STAR)
        { // Monitoring the '*'
            if (bStar)
            {
                return nPos; // Error: double '*'
            }
            else
            {
                // Valid only if there is a character following, else we are
                // at the end of a code that does not have a fill character
                // (yet?).
                if (sStrArray[nStringsCnt].getLength() < 2)
                    return nPos;
                bStar = true;
            }
        }
        nStringsCnt++;
    }

    return 0; // 0 => ok
}

void ImpSvNumberformatScan::SkipStrings(sal_uInt16& i, sal_Int32& nPos) const
{
    while (i < nStringsCnt && (   nTypeArray[i] == NF_SYMBOLTYPE_STRING
                               || nTypeArray[i] == NF_SYMBOLTYPE_BLANK
                               || nTypeArray[i] == NF_SYMBOLTYPE_STAR) )
    {
        nPos = nPos + sStrArray[i].getLength();
        i++;
    }
}

sal_uInt16 ImpSvNumberformatScan::PreviousKeyword(sal_uInt16 i) const
{
    short res = 0;
    if (i > 0 && i < nStringsCnt)
    {
        i--;
        while (i > 0 && nTypeArray[i] <= 0)
        {
            i--;
        }
        if (nTypeArray[i] > 0)
        {
            res = nTypeArray[i];
        }
    }
    return res;
}

sal_uInt16 ImpSvNumberformatScan::NextKeyword(sal_uInt16 i) const
{
    short res = 0;
    if (i < nStringsCnt-1)
    {
        i++;
        while (i < nStringsCnt-1 && nTypeArray[i] <= 0)
        {
            i++;
        }
        if (nTypeArray[i] > 0)
        {
            res = nTypeArray[i];
        }
    }
    return res;
}

short ImpSvNumberformatScan::PreviousType( sal_uInt16 i ) const
{
    if ( i > 0 && i < nStringsCnt )
    {
        do
        {
            i--;
        }
        while ( i > 0 && nTypeArray[i] == NF_SYMBOLTYPE_EMPTY );
        return nTypeArray[i];
    }
    return 0;
}

sal_Unicode ImpSvNumberformatScan::PreviousChar(sal_uInt16 i) const
{
    sal_Unicode res = ' ';
    if (i > 0 && i < nStringsCnt)
    {
        i--;
        while (i > 0 &&
               ( nTypeArray[i] == NF_SYMBOLTYPE_EMPTY ||
                 nTypeArray[i] == NF_SYMBOLTYPE_STRING ||
                 nTypeArray[i] == NF_SYMBOLTYPE_STAR ||
                 nTypeArray[i] == NF_SYMBOLTYPE_BLANK ))
        {
            i--;
        }
        if (sStrArray[i].getLength() > 0)
        {
            res = sStrArray[i][sStrArray[i].getLength()-1];
        }
    }
    return res;
}

sal_Unicode ImpSvNumberformatScan::NextChar(sal_uInt16 i) const
{
    sal_Unicode res = ' ';
    if (i < nStringsCnt-1)
    {
        i++;
        while (i < nStringsCnt-1 &&
               ( nTypeArray[i] == NF_SYMBOLTYPE_EMPTY ||
                 nTypeArray[i] == NF_SYMBOLTYPE_STRING ||
                 nTypeArray[i] == NF_SYMBOLTYPE_STAR ||
                 nTypeArray[i] == NF_SYMBOLTYPE_BLANK))
        {
            i++;
        }
        if (sStrArray[i].getLength() > 0)
        {
            res = sStrArray[i][0];
        }
    }
    return res;
}

bool ImpSvNumberformatScan::IsLastBlankBeforeFrac(sal_uInt16 i) const
{
    bool res = true;
    if (i < nStringsCnt-1)
    {
        bool bStop = false;
        i++;
        while (i < nStringsCnt-1 && !bStop)
        {
            i++;
            if ( nTypeArray[i] == NF_SYMBOLTYPE_DEL &&
                 sStrArray[i][0] == '/')
            {
                bStop = true;
            }
            else if ( ( nTypeArray[i] == NF_SYMBOLTYPE_DEL  &&
                        sStrArray[i][0] == ' ')             ||
                        nTypeArray[i] == NF_SYMBOLTYPE_STRING ) // integer/fraction delimiter can also be a string
            {
                res = false;
            }
        }
        if (!bStop) // no '/'{
        {
            res = false;
        }
    }
    else
    {
        res = false// no '/' any more
    }
    return res;
}

void ImpSvNumberformatScan::Reset()
{
    nStringsCnt = 0;
    nResultStringsCnt = 0;
    eScannedType = SvNumFormatType::UNDEFINED;
    bExp = false;
    bThousand = false;
    nThousand = 0;
    bDecSep = false;
    nDecPos = sal_uInt16(-1);
    nExpPos = sal_uInt16(-1);
    nBlankPos = sal_uInt16(-1);
    nCntPre = 0;
    nCntPost = 0;
    nCntExp = 0;
    bFrac = false;
    bBlank = false;
    nNatNumModifier = 0;
}

bool ImpSvNumberformatScan::Is100SecZero( sal_uInt16 i, bool bHadDecSep ) const
{
    sal_uInt16 nIndexPre = PreviousKeyword( i );
    return (nIndexPre == NF_KEY_S || nIndexPre == NF_KEY_SS) &&
            (bHadDecSep ||
             ( i > 0 && nTypeArray[i-1] == NF_SYMBOLTYPE_STRING));
              // SS"any"00  take "any" as a valid decimal separator
}

sal_Int32 ImpSvNumberformatScan::ScanType()
{
    const LocaleDataWrapper* pLoc = mrCurrentLanguageData.GetLocaleData();

    sal_Int32 nPos = 0;
    sal_uInt16 i = 0;
    SvNumFormatType eNewType;
    bool bMatchBracket = false;
    bool bHaveGeneral = false// if General/Standard encountered
    bool bIsTimeDetected =false;   // hour or second found in format
    bool bHaveMinute = false;

    SkipStrings(i, nPos);
    while (i < nStringsCnt)
    {
        if (nTypeArray[i] > 0)
        {   // keyword
            sal_uInt16 nIndexPre;
            sal_uInt16 nIndexNex;

            switch (nTypeArray[i])
            {
            case NF_KEY_E:                          // E
                eNewType = SvNumFormatType::SCIENTIFIC;
                break;
            case NF_KEY_H:                          // H
            case NF_KEY_HH:                         // HH
                bIsTimeDetected = true;
                [[fallthrough]];
            case NF_KEY_S:                          // S
            case NF_KEY_SS:                         // SS
                if ( !bHaveMinute )
                    bIsTimeDetected = true;
                [[fallthrough]];
            case NF_KEY_AMPM:                       // AM,A,PM,P
            case NF_KEY_AP:
                eNewType = SvNumFormatType::TIME;
                break;
            case NF_KEY_M:                          // M
            case NF_KEY_MM:                         // MM
            case NF_KEY_MI:                         // M  minute detected in Finnish
            case NF_KEY_MMI:                        // MM
                /* Minute or month.
                   Minute if one of:
                   * preceded by time keyword H (ignoring separators)
                   * followed by time keyword S (ignoring separators)
                   * H or S was detected and this is the first M following
                   * preceded by '[' amount bracket
                   Else month.
                   That are the Excel rules. BUT, we break it because certainly
                   in something like {HH YYYY-MM-DD} the MM is NOT meant to be
                   minute, so not if MM is between YY and DD or DD and YY.
                   Actually not if any date specific keyword followed a time
                   setting keyword.
                */

                nIndexPre = PreviousKeyword(i);
                nIndexNex = NextKeyword(i);
                if (nIndexPre == NF_KEY_H   ||      // H
                    nIndexPre == NF_KEY_HH  ||      // HH
                    nIndexNex == NF_KEY_S   ||      // S
                    nIndexNex == NF_KEY_SS  ||      // SS
                    bIsTimeDetected         ||      // tdf#101147
                    PreviousChar(i) == '['  )       // [M
                {
                    eNewType = SvNumFormatType::TIME;
                    if ( nTypeArray[i] == NF_KEY_M || nTypeArray[i] == NF_KEY_MM )
                    {
                        nTypeArray[i] -= 2;             // 6 -> 4, 7 -> 5
                    }
                    bIsTimeDetected = false;        // next M should be month
                    bHaveMinute = true;
                }
                else
                {
                    eNewType = SvNumFormatType::DATE;
                    if ( nTypeArray[i] == NF_KEY_MI || nTypeArray[i] == NF_KEY_MMI )
                    {   // follow resolution of tdf#33689 for Finnish
                        nTypeArray[i] += 2;             // 4 -> 6, 5 -> 7
                    }
                }
                break;
            case NF_KEY_MMM:                        // MMM
            case NF_KEY_MMMM:                       // MMMM
            case NF_KEY_MMMMM:                      // MMMMM
            case NF_KEY_Q:                          // Q
            case NF_KEY_QQ:                         // QQ
            case NF_KEY_D:                          // D
            case NF_KEY_DD:                         // DD
            case NF_KEY_DDD:                        // DDD
            case NF_KEY_DDDD:                       // DDDD
            case NF_KEY_YY:                         // YY
            case NF_KEY_YYYY:                       // YYYY
            case NF_KEY_NN:                         // NN
            case NF_KEY_NNN:                        // NNN
            case NF_KEY_NNNN:                       // NNNN
            case NF_KEY_WW :                        // WW
            case NF_KEY_AAA :                       // AAA
            case NF_KEY_AAAA :                      // AAAA
            case NF_KEY_EC :                        // E
            case NF_KEY_EEC :                       // EE
            case NF_KEY_G :                         // G
            case NF_KEY_GG :                        // GG
            case NF_KEY_GGG :                       // GGG
            case NF_KEY_R :                         // R
            case NF_KEY_RR :                        // RR
                eNewType = SvNumFormatType::DATE;
                bIsTimeDetected = false;
                break;
            case NF_KEY_CCC:                        // CCC
                eNewType = SvNumFormatType::CURRENCY;
                break;
            case NF_KEY_BOOLEAN:                    // BOOLEAN
                eNewType = SvNumFormatType::LOGICAL;
                break;
            case NF_KEY_GENERAL:                    // General
                eNewType = SvNumFormatType::NUMBER;
                bHaveGeneral = true;
                break;
            default:
                eNewType = SvNumFormatType::UNDEFINED;
                break;
            }
        }
        else
        {                                           // control character
            switch ( sStrArray[i][0] )
            {
            case '#':
            case '?':
                eNewType = SvNumFormatType::NUMBER;
                break;
            case '0':
                if ( eScannedType & SvNumFormatType::TIME )
                {
                    if ( Is100SecZero( i, bDecSep ) )
                    {
                        bDecSep = true;                 // subsequent 0's
                        eNewType = SvNumFormatType::TIME;
                    }
                    else
                    {
                        return nPos;                    // Error
                    }
                }
                else
                {
                    eNewType = SvNumFormatType::NUMBER;
                }
                break;
            case '%':
                eNewType = SvNumFormatType::PERCENT;
                break;
            case '/':
                eNewType = SvNumFormatType::FRACTION;
                break;
            case '[':
                if ( i < nStringsCnt-1 &&
                     nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
                     sStrArray[i+1][0] == '$' )
                {
                    eNewType = SvNumFormatType::CURRENCY;
                    bMatchBracket = true;
                }
                else if ( i < nStringsCnt-1 &&
                          nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
                          sStrArray[i+1][0] == '~' )
                {
                    eNewType = SvNumFormatType::DATE;
                    bMatchBracket = true;
                }
                else
                {
                    sal_uInt16 nIndexNex = NextKeyword(i);
                    if (nIndexNex == NF_KEY_H   ||  // H
                        nIndexNex == NF_KEY_HH  ||  // HH
                        nIndexNex == NF_KEY_M   ||  // M
                        nIndexNex == NF_KEY_MM  ||  // MM
                        nIndexNex == NF_KEY_S   ||  // S
                        nIndexNex == NF_KEY_SS   )  // SS
                        eNewType = SvNumFormatType::TIME;
                    else
                    {
                        return nPos;                // Error
                    }
                }
                break;
            case '@':
                eNewType = SvNumFormatType::TEXT;
                break;
            default:
                // Separator for SS,0
                if ((eScannedType & SvNumFormatType::TIME)
                        && 0 < i && (nTypeArray[i-1] == NF_KEY_S || nTypeArray[i-1] == NF_KEY_SS))
                {
                    // For ISO 8601 only YYYY-MM-DD"T"HH:MM:SS,0  accept both
                    // ',' and '.' regardless of locale's separator, and only
                    // those.
                    // XXX NOTE: this catches only known separators of
                    // NF_SYMBOLTYPE_DEL as all NF_SYMBOLTYPE_STRING are
                    // skipped during the loop. Meant to error out if the
                    // Time100SecSep or decimal separator differ and were used.
                    if ((eScannedType & SvNumFormatType::DATE)
                            && 11 <= i && i < nStringsCnt-1
                            && (nTypeArray[i-6] == NF_SYMBOLTYPE_STRING
                                && (sStrArray[i-6] == "\"T\"" || sStrArray[i-6] == "\\T"
                                    || sStrArray[i-6] == "T"))
                            && (nTypeArray[i-11] == NF_KEY_YYYY)
                            && (nTypeArray[i-9] == NF_KEY_M || nTypeArray[i-9] == NF_KEY_MM)
                            && (nTypeArray[i-7] == NF_KEY_D || nTypeArray[i-7] == NF_KEY_DD)
                            && (nTypeArray[i-5] == NF_KEY_H || nTypeArray[i-5] == NF_KEY_HH)
                            && (nTypeArray[i-3] == NF_KEY_MI || nTypeArray[i-3] == NF_KEY_MMI)
                            && (nTypeArray[i+1] == NF_SYMBOLTYPE_DEL && sStrArray[i+1][0] == '0'))

                    {
                        if (sStrArray[i].getLength() == 1 && (sStrArray[i][0] == ',' || sStrArray[i][0] == '.'))
                            bDecSep = true;
                        else
                            return nPos;            // Error
                    }
                    else if (pLoc->getTime100SecSep() == sStrArray[i])
                        bDecSep = true;
                    else if ( sStrArray[i][0] == ']' && i < nStringsCnt - 1 && pLoc->getTime100SecSep() == sStrArray[i+1] )
                    {
                        bDecSep = true;
                        i++;
                    }
                }
                eNewType = SvNumFormatType::UNDEFINED;
                break;
            }
        }
        if (eScannedType == SvNumFormatType::UNDEFINED)
        {
            eScannedType = eNewType;
        }
        else if (eScannedType == SvNumFormatType::TEXT || eNewType == SvNumFormatType::TEXT)
        {
            eScannedType = SvNumFormatType::TEXT; // Text always remains text
        }
        else if (eNewType == SvNumFormatType::UNDEFINED)
        { // Remains as is
        }
        else if (eScannedType != eNewType)
        {
            switch (eScannedType)
            {
            case SvNumFormatType::DATE:
                switch (eNewType)
                {
                case SvNumFormatType::TIME:
                    eScannedType = SvNumFormatType::DATETIME;
                    break;
                case SvNumFormatType::FRACTION:         // DD/MM
                    break;
                default:
                    if (nCurrPos >= 0)
                    {
                        eScannedType = SvNumFormatType::UNDEFINED;
                    }
                    else if ( sStrArray[i] != mrCurrentLanguageData.GetDateSep() )
                    {
                        return nPos;
                    }
                }
                break;
            case SvNumFormatType::TIME:
                switch (eNewType)
                {
                case SvNumFormatType::DATE:
                    eScannedType = SvNumFormatType::DATETIME;
                    break;
                case SvNumFormatType::FRACTION:         // MM/SS
                    break;
                default:
                    if (nCurrPos >= 0)
                    {
                        eScannedType = SvNumFormatType::UNDEFINED;
                    }
                    else if (pLoc->getTimeSep() != sStrArray[i])
                    {
                        return nPos;
                    }
                    break;
                }
                break;
            case SvNumFormatType::DATETIME:
                switch (eNewType)
                {
                case SvNumFormatType::TIME:
                case SvNumFormatType::DATE:
                    break;
                case SvNumFormatType::FRACTION:         // DD/MM
                    break;
                default:
                    if (nCurrPos >= 0)
                    {
                        eScannedType = SvNumFormatType::UNDEFINED;
                    }
                    else if ( mrCurrentLanguageData.GetDateSep() != sStrArray[i] &&
                              pLoc->getTimeSep() != sStrArray[i] )
                    {
                        return nPos;
                    }
                }
                break;
            case SvNumFormatType::PERCENT:
            case SvNumFormatType::SCIENTIFIC:
            case SvNumFormatType::FRACTION:
                switch (eNewType)
                {
                case SvNumFormatType::NUMBER:
                    break;
                default:
                    return nPos;
                }
                break;
            case SvNumFormatType::NUMBER:
                switch (eNewType)
                {
                case SvNumFormatType::SCIENTIFIC:
                case SvNumFormatType::PERCENT:
                case SvNumFormatType::FRACTION:
                case SvNumFormatType::CURRENCY:
                    eScannedType = eNewType;
                    break;
                default:
                    if (nCurrPos >= 0)
                    {
                        eScannedType = SvNumFormatType::UNDEFINED;
                    }
                    else
                    {
                        return nPos;
                    }
                }
                break;
            default:
                break;
            }
        }
        nPos = nPos + sStrArray[i].getLength(); // Position of correction
        i++;
        if ( bMatchBracket )
        {   // no type detection inside of matching brackets if [$...], [~...]
            while ( bMatchBracket && i < nStringsCnt )
            {
                if ( nTypeArray[i] == NF_SYMBOLTYPE_DEL
                     && sStrArray[i][0] == ']' )
                {
                    bMatchBracket = false;
                }
                else
                {
                    nTypeArray[i] = NF_SYMBOLTYPE_STRING;
                }
                nPos = nPos + sStrArray[i].getLength();
                i++;
            }
            if ( bMatchBracket )
            {
                return nPos; // missing closing bracket at end of code
            }
        }
        SkipStrings(i, nPos);
    }

    if ((eScannedType == SvNumFormatType::NUMBER ||
         eScannedType == SvNumFormatType::UNDEFINED) &&
        nCurrPos >= 0 && !bHaveGeneral)
    {
        eScannedType = SvNumFormatType::CURRENCY; // old "automatic" currency
    }
    if (eScannedType == SvNumFormatType::UNDEFINED)
    {
        eScannedType = SvNumFormatType::DEFINED;
    }
    return 0; // All is fine
}

bool ImpSvNumberformatScan::InsertSymbol( sal_uInt16 & nPos, svt::NfSymbolType eType, const OUString& rStr )
{
    if (nStringsCnt >= NF_MAX_FORMAT_SYMBOLS || nPos > nStringsCnt)
    {
        return false;
    }
    if (nPos > 0 && nTypeArray[nPos-1] == NF_SYMBOLTYPE_EMPTY)
    {
        --nPos; // reuse position
    }
    else
    {
        if (nStringsCnt >= NF_MAX_FORMAT_SYMBOLS - 1)
        {
            return false;
        }
        ++nStringsCnt;
        sal_uInt16 i = nStringsCnt;
        while (i > nPos)
        {
            sal_uInt16 nexti = o3tl::sanitizing_dec(i);
            nTypeArray[i] = nTypeArray[nexti];
            sStrArray[i] = sStrArray[nexti];
            i = nexti;
        }
    }
    ++nResultStringsCnt;
    nTypeArray[nPos] = static_cast<short>(eType);
    sStrArray[nPos] = rStr;
    return true;
}

int ImpSvNumberformatScan::FinalScanGetCalendar( sal_Int32& nPos, sal_uInt16& ;i,
                                                 sal_uInt16& rResultStringsCnt )
{
    if ( i < nStringsCnt-1 &&
         sStrArray[i][0] == '[' &&
         nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
         sStrArray[i+1][0] == '~' )
    {
        // [~calendarID]
        nPos = nPos + sStrArray[i].getLength();           // [
        nTypeArray[i] = NF_SYMBOLTYPE_CALDEL;
        nPos = nPos + sStrArray[++i].getLength();         // ~
        sStrArray[i-1] += sStrArray[i];                   // [~
        nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
        rResultStringsCnt--;
        if ( ++i >= nStringsCnt )
        {
            return -1; // error
        }
        nPos = nPos + sStrArray[i].getLength();           // calendarID
        OUString& rStr = sStrArray[i];
        nTypeArray[i] = NF_SYMBOLTYPE_CALENDAR;          // convert
        i++;
        while ( i < nStringsCnt && sStrArray[i][0] != ']' )
        {
            nPos = nPos + sStrArray[i].getLength();
            rStr += sStrArray[i];
            nTypeArray[i] = NF_SYMBOLTYPE_EMPTY;
            rResultStringsCnt--;
            i++;
        }
        if ( rStr.getLength() && i < nStringsCnt &&
             sStrArray[i][0] == ']' )
        {
            nTypeArray[i] = NF_SYMBOLTYPE_CALDEL;
            nPos = nPos + sStrArray[i].getLength();
            i++;
        }
        else
        {
            return -1; // error
        }
        return 1;
    }
    return 0;
}

bool ImpSvNumberformatScan::IsDateFragment( size_t nPos1, size_t nPos2 ) const
{
    return nPos2 - nPos1 == 2 && nTypeArray[nPos1+1] == NF_SYMBOLTYPE_DATESEP;
}

void ImpSvNumberformatScan::SwapArrayElements( size_t nPos1, size_t nPos2 )
{
    std::swap( nTypeArray[nPos1], nTypeArray[nPos2]);
    std::swap( sStrArray[nPos1], sStrArray[nPos2]);
}

sal_Int32 ImpSvNumberformatScan::FinalScan( OUString& rString )
{
    const LocaleDataWrapper* pLoc = mrCurrentLanguageData.GetLocaleData();

    // save values for convert mode
    OUString sOldDecSep       = mrCurrentLanguageData.GetNumDecimalSep();
    OUString sOldThousandSep  = mrCurrentLanguageData.GetNumThousandSep();
    OUString sOldDateSep      = mrCurrentLanguageData.GetDateSep();
    OUString sOldTimeSep      = pLoc->getTimeSep();
    OUString sOldTime100SecSep= pLoc->getTime100SecSep();
    OUString sOldCurSymbol    = GetCurSymbol();
    OUString sOldCurString = GetCurString();
    sal_Unicode cOldKeyH    = sKeyword[NF_KEY_H][0];
    sal_Unicode cOldKeyMI   = sKeyword[NF_KEY_MI][0];
    sal_Unicode cOldKeyS    = sKeyword[NF_KEY_S][0];
    DateOrder eOldDateOrder = pLoc->getDateOrder();
    sal_uInt16 nDayPos, nMonthPos, nYearPos;
    nDayPos = nMonthPos = nYearPos = SAL_MAX_UINT16;

    // If the group separator is a No-Break Space (French) continue with a
    // normal space instead so queries on space work correctly.
    // The same for Narrow No-Break Space just in case some locale uses it.
    // The format string is adjusted to allow both.
    // For output of the format code string the LocaleData characters are used.
    if ( (sOldThousandSep[0] == cNoBreakSpace || sOldThousandSep[0] == cNarrowNoBreakSpace) &&
            sOldThousandSep.getLength() == 1 )
    {
        sOldThousandSep = " ";
    }
    bool bNewDateOrder = false;
    // change locale data et al
    if (bConvertMode)
    {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=91 G=91

¤ Dauer der Verarbeitung: 0.25 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.