/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svl.hxx"

#include <tools/debug.hxx>
#include <unotools/charclass.hxx>
#include <i18npool/mslangid.hxx>
#include <unotools/localedatawrapper.hxx>
#include <unotools/numberformatcodewrapper.hxx>
#include <unotools/calendarwrapper.hxx>
#include <com/sun/star/i18n/KNumberFormatUsage.hpp>
#include <com/sun/star/i18n/KNumberFormatType.hpp>
#include <comphelper/processfactory.hxx>
#include <unotools/misccfg.hxx>

#define _SVSTDARR_USHORTS
#include <svl/svstdarr.hxx>

#define _ZFORLIST_CXX
#include <osl/mutex.hxx>
#include <svl/zforlist.hxx>
#undef _ZFORLIST_CXX

#include "zforscan.hxx"
#include "zforfind.hxx"
#include <svl/zformat.hxx>
#include "numhead.hxx"

#include <unotools/syslocaleoptions.hxx>
#include <unotools/digitgroupingiterator.hxx>
#include <rtl/logfile.hxx>
#include <rtl/instance.hxx>

#include <math.h>
#include <limits>

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::i18n;
using namespace ::com::sun::star::lang;

using ::rtl::OUString;


// Constants for type offsets per Country/Language (CL)
#define ZF_STANDARD              0
#define ZF_STANDARD_PERCENT     10
#define ZF_STANDARD_CURRENCY    20
#define ZF_STANDARD_DATE        30
#define ZF_STANDARD_TIME        40
#define ZF_STANDARD_DATETIME    50
#define ZF_STANDARD_SCIENTIFIC  60
#define ZF_STANDARD_FRACTION    70
#define ZF_STANDARD_NEWEXTENDED	75
#define ZF_STANDARD_NEWEXTENDEDMAX	SV_MAX_ANZ_STANDARD_FORMATE-2	// 98
#define ZF_STANDARD_LOGICAL     SV_MAX_ANZ_STANDARD_FORMATE-1 //  99
#define ZF_STANDARD_TEXT        SV_MAX_ANZ_STANDARD_FORMATE   // 100

/* Locale that is set if an unknown locale (from another system) is loaded of
 * legacy documents. Can not be SYSTEM because else, for example, a German "DM"
 * (old currency) is recognized as a date (#53155#). */
#define UNKNOWN_SUBSTITUTE		LANGUAGE_ENGLISH_US

static sal_Bool bIndexTableInitialized = sal_False;
static sal_uInt32 theIndexTable[NF_INDEX_TABLE_ENTRIES];


// ====================================================================

/**
    instead of every number formatter being a listener we have a registry which
    also handles one instance of the SysLocale options
 */

class SvNumberFormatterRegistry_Impl : public utl::ConfigurationListener
{
    List                    aFormatters;
    SvtSysLocaleOptions     aSysLocaleOptions;
    LanguageType            eSysLanguage;

public:
                            SvNumberFormatterRegistry_Impl();
    virtual                 ~SvNumberFormatterRegistry_Impl();

            void            Insert( SvNumberFormatter* pThis )
                                { aFormatters.Insert( pThis, LIST_APPEND ); }
            SvNumberFormatter*  Remove( SvNumberFormatter* pThis )
                                    { return (SvNumberFormatter*)aFormatters.Remove( pThis ); }
            sal_uInt32           Count()
                                { return aFormatters.Count(); }

            virtual void ConfigurationChanged( utl::ConfigurationBroadcaster*, sal_uInt32 );
};


SvNumberFormatterRegistry_Impl::SvNumberFormatterRegistry_Impl()
{
    eSysLanguage = MsLangId::getRealLanguage( LANGUAGE_SYSTEM );
    aSysLocaleOptions.AddListener( this );
}


SvNumberFormatterRegistry_Impl::~SvNumberFormatterRegistry_Impl()
{
    aSysLocaleOptions.RemoveListener( this );
}


void SvNumberFormatterRegistry_Impl::ConfigurationChanged( utl::ConfigurationBroadcaster*, sal_uInt32 nHint )
{
        if ( nHint & SYSLOCALEOPTIONS_HINT_LOCALE )
        {
            ::osl::MutexGuard aGuard( SvNumberFormatter::GetMutex() );
            for ( SvNumberFormatter* p = (SvNumberFormatter*)aFormatters.First();
                    p; p = (SvNumberFormatter*)aFormatters.Next() )
            {
                p->ReplaceSystemCL( eSysLanguage );
            }
            eSysLanguage = MsLangId::getRealLanguage( LANGUAGE_SYSTEM );
        }
        if ( nHint & SYSLOCALEOPTIONS_HINT_CURRENCY )
        {
            ::osl::MutexGuard aGuard( SvNumberFormatter::GetMutex() );
            for ( SvNumberFormatter* p = (SvNumberFormatter*)aFormatters.First();
                    p; p = (SvNumberFormatter*)aFormatters.Next() )
            {
                p->ResetDefaultSystemCurrency();
            }
        }
}


// ====================================================================

SvNumberFormatterRegistry_Impl* SvNumberFormatter::pFormatterRegistry = NULL;
sal_Bool SvNumberFormatter::bCurrencyTableInitialized = sal_False;
namespace 
{
    struct theCurrencyTable : 
        public rtl::Static< NfCurrencyTable, theCurrencyTable > {}; 

    struct theLegacyOnlyCurrencyTable : 
        public rtl::Static< NfCurrencyTable, theLegacyOnlyCurrencyTable > {}; 
}
sal_uInt16 SvNumberFormatter::nSystemCurrencyPosition = 0;
SV_IMPL_PTRARR( NfCurrencyTable, NfCurrencyEntry* );
SV_IMPL_PTRARR( NfWSStringsDtor, String* );

// ob das BankSymbol immer am Ende ist (1 $;-1 $) oder sprachabhaengig
#define NF_BANKSYMBOL_FIX_POSITION 1


/***********************Funktionen SvNumberFormatter**************************/

const sal_uInt16 SvNumberFormatter::UNLIMITED_PRECISION   = ::std::numeric_limits<sal_uInt16>::max();
const sal_uInt16 SvNumberFormatter::INPUTSTRING_PRECISION = ::std::numeric_limits<sal_uInt16>::max()-1;
NfInstalledLocales SvNumberFormatter::theInstalledLocales;

SvNumberFormatter::SvNumberFormatter(
            const Reference< XMultiServiceFactory >& xSMgr,
            LanguageType eLang )
        :
        xServiceManager( xSMgr )
{
    ImpConstruct( eLang );
}


SvNumberFormatter::SvNumberFormatter( LanguageType eLang )
{
    ImpConstruct( eLang );
}


SvNumberFormatter::~SvNumberFormatter()
{
    {
        ::osl::MutexGuard aGuard( GetMutex() );
        pFormatterRegistry->Remove( this );
        if ( !pFormatterRegistry->Count() )
        {
            delete pFormatterRegistry;
            pFormatterRegistry = NULL;
        }
    }

    SvNumberformat* pEntry = aFTable.First();
    while (pEntry)
    {
        delete pEntry;
        pEntry = aFTable.Next();
    }
    delete pFormatTable;
    delete pCharClass;
    delete pStringScanner;
    delete pFormatScanner;
    ClearMergeTable();
    delete pMergeTable;
}


void SvNumberFormatter::ImpConstruct( LanguageType eLang )
{
    RTL_LOGFILE_CONTEXT_AUTHOR( aTimeLog, "svl", "er93726", "SvNumberFormatter::ImpConstruct" );

    if ( eLang == LANGUAGE_DONTKNOW )
        eLang = UNKNOWN_SUBSTITUTE;
    IniLnge = eLang;
    ActLnge = eLang;
    eEvalDateFormat = NF_EVALDATEFORMAT_INTL;
    nDefaultSystemCurrencyFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;

    aLocale = MsLangId::convertLanguageToLocale( eLang );
    pCharClass = new CharClass( xServiceManager, aLocale );
    xLocaleData.init( xServiceManager, aLocale, eLang );
    xCalendar.init( xServiceManager, aLocale );
    xTransliteration.init( xServiceManager, eLang,
        ::com::sun::star::i18n::TransliterationModules_IGNORE_CASE );
    xNatNum.init( xServiceManager );

    // cached locale data items
    const LocaleDataWrapper* pLoc = GetLocaleData();
    aDecimalSep = pLoc->getNumDecimalSep();
    aThousandSep = pLoc->getNumThousandSep();
    aDateSep = pLoc->getDateSep();

    pStringScanner = new ImpSvNumberInputScan( this );
    pFormatScanner = new ImpSvNumberformatScan( this );
    pFormatTable = NULL;
    MaxCLOffset = 0;
    ImpGenerateFormats( 0, sal_False );     // 0 .. 999 for initialized language formats
    pMergeTable = NULL;
    bNoZero = sal_False;

    ::osl::MutexGuard aGuard( GetMutex() );
    GetFormatterRegistry().Insert( this );
}


void SvNumberFormatter::ChangeIntl(LanguageType eLnge)
{
    if (ActLnge != eLnge)
    {
        ActLnge = eLnge;

        aLocale = MsLangId::convertLanguageToLocale( eLnge );
        pCharClass->setLocale( aLocale );
        xLocaleData.changeLocale( aLocale, eLnge );
        xCalendar.changeLocale( aLocale );
        xTransliteration.changeLocale( eLnge );

        // cached locale data items, initialize BEFORE calling ChangeIntl below
        const LocaleDataWrapper* pLoc = GetLocaleData();
        aDecimalSep = pLoc->getNumDecimalSep();
        aThousandSep = pLoc->getNumThousandSep();
        aDateSep = pLoc->getDateSep();

        pFormatScanner->ChangeIntl();
        pStringScanner->ChangeIntl();
    }
}


// static
::osl::Mutex& SvNumberFormatter::GetMutex()
{
    static ::osl::Mutex* pMutex = NULL;
    if( !pMutex )
    {
        ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
        if( !pMutex )
        {
            // #i77768# Due to a static reference in the toolkit lib
            // we need a mutex that lives longer than the svl library.
            // Otherwise the dtor would use a destructed mutex!!
            pMutex = new ::osl::Mutex;
        }
    }
    return *pMutex;
}


// static
SvNumberFormatterRegistry_Impl& SvNumberFormatter::GetFormatterRegistry()
{
    ::osl::MutexGuard aGuard( GetMutex() );
    if ( !pFormatterRegistry )
        pFormatterRegistry = new SvNumberFormatterRegistry_Impl;
    return *pFormatterRegistry;
}


Color* SvNumberFormatter::GetUserDefColor(sal_uInt16 nIndex)
{
    if( aColorLink.IsSet() )
        return (Color*) ( aColorLink.Call( (void*) &nIndex ));
    else
        return NULL;
}

void SvNumberFormatter::ChangeNullDate(sal_uInt16 nDay,
                                       sal_uInt16 nMonth,
                                       sal_uInt16 nYear)
{
    pFormatScanner->ChangeNullDate(nDay, nMonth, nYear);
    pStringScanner->ChangeNullDate(nDay, nMonth, nYear);
}

Date* SvNumberFormatter::GetNullDate()
{
    return pFormatScanner->GetNullDate();
}

void SvNumberFormatter::ChangeStandardPrec(short nPrec)
{
    pFormatScanner->ChangeStandardPrec(nPrec);
}

sal_uInt16 SvNumberFormatter::GetStandardPrec()
{
    return pFormatScanner->GetStandardPrec();
}

void SvNumberFormatter::ImpChangeSysCL( LanguageType eLnge, sal_Bool bLoadingSO5 )
{
    if (eLnge == LANGUAGE_DONTKNOW)
        eLnge = UNKNOWN_SUBSTITUTE;
    if (eLnge != IniLnge)
    {
        IniLnge = eLnge;
        ChangeIntl(eLnge);
        SvNumberformat* pEntry = aFTable.First();
        while (pEntry)							// delete old formats
        {
            pEntry = (SvNumberformat*) aFTable.Remove(aFTable.GetCurKey());
            delete pEntry;
            pEntry = (SvNumberformat*) aFTable.First();
        }
        ImpGenerateFormats( 0, bLoadingSO5 );	// new standard formats
    }
    else if ( bLoadingSO5 )
    {	// delete additional standard formats
        sal_uInt32 nKey;
        aFTable.Seek( SV_MAX_ANZ_STANDARD_FORMATE + 1 );
        while ( (nKey = aFTable.GetCurKey()) > SV_MAX_ANZ_STANDARD_FORMATE &&
                nKey < SV_COUNTRY_LANGUAGE_OFFSET )
        {
            SvNumberformat* pEntry = (SvNumberformat*) aFTable.Remove( nKey );
            delete pEntry;
        }
    }
}


void SvNumberFormatter::ReplaceSystemCL( LanguageType eOldLanguage )
{
    sal_uInt32 nCLOffset = ImpGetCLOffset( LANGUAGE_SYSTEM );
    if ( nCLOffset > MaxCLOffset )
        return ;    // no SYSTEM entries to replace

    const sal_uInt32 nMaxBuiltin = nCLOffset + SV_MAX_ANZ_STANDARD_FORMATE;
    const sal_uInt32 nNextCL = nCLOffset + SV_COUNTRY_LANGUAGE_OFFSET;
    sal_uInt32 nKey;

    // remove old builtin formats
    aFTable.Seek( nCLOffset );
    while ( (nKey = aFTable.GetCurKey()) >= nCLOffset && nKey <= nMaxBuiltin && aFTable.Count() )
    {
        SvNumberformat* pEntry = (SvNumberformat*) aFTable.Remove( nKey );
        delete pEntry;
    }

    // move additional and user defined to temporary table
    Table aOldTable;
    while ( (nKey = aFTable.GetCurKey()) >= nCLOffset && nKey < nNextCL && aFTable.Count() )
    {
        SvNumberformat* pEntry = (SvNumberformat*) aFTable.Remove( nKey );
        aOldTable.Insert( nKey, pEntry );
    }

    // generate new old builtin formats
    // reset ActLnge otherwise ChangeIntl() wouldn't switch if already LANGUAGE_SYSTEM
    ActLnge = LANGUAGE_DONTKNOW;
    ChangeIntl( LANGUAGE_SYSTEM );
    ImpGenerateFormats( nCLOffset, sal_True );

    // convert additional and user defined from old system to new system
    SvNumberformat* pStdFormat = (SvNumberformat*) aFTable.Get( nCLOffset + ZF_STANDARD );
    sal_uInt32 nLastKey = nMaxBuiltin;
    pFormatScanner->SetConvertMode( eOldLanguage, LANGUAGE_SYSTEM, sal_True );
    aOldTable.First();
    while ( aOldTable.Count() )
    {
        nKey = aOldTable.GetCurKey();
        if ( nLastKey < nKey )
            nLastKey = nKey;
        SvNumberformat* pOldEntry = (SvNumberformat*) aOldTable.Remove( nKey );
        String aString( pOldEntry->GetFormatstring() );
        xub_StrLen nCheckPos = STRING_NOTFOUND;

        // Same as PutEntry() but assures key position even if format code is
        // a duplicate. Also won't mix up any LastInsertKey.
        ChangeIntl( eOldLanguage );
        LanguageType eLge = eOldLanguage;   // ConvertMode changes this
        bool bCheck = false;
        SvNumberformat* pNewEntry = new SvNumberformat( aString, pFormatScanner,
            pStringScanner, nCheckPos, eLge );
        if ( nCheckPos != 0 )
            delete pNewEntry;
        else
        {
            short eCheckType = pNewEntry->GetType();
            if ( eCheckType != NUMBERFORMAT_UNDEFINED )
                pNewEntry->SetType( eCheckType | NUMBERFORMAT_DEFINED );
            else
                pNewEntry->SetType( NUMBERFORMAT_DEFINED );

            if ( !aFTable.Insert( nKey, pNewEntry ) )
                delete pNewEntry;
            else
                bCheck = true;
        }
        DBG_ASSERT( bCheck, "SvNumberFormatter::ReplaceSystemCL: couldn't convert" );
        (void)bCheck;

        delete pOldEntry;
    }
    pFormatScanner->SetConvertMode(sal_False);
    pStdFormat->SetLastInsertKey( sal_uInt16(nLastKey - nCLOffset) );

    // append new system additional formats
    NumberFormatCodeWrapper aNumberFormatCode( xServiceManager, GetLocale() );
    ImpGenerateAdditionalFormats( nCLOffset, aNumberFormatCode, sal_True );
}


sal_Bool SvNumberFormatter::IsTextFormat(sal_uInt32 F_Index) const
{
    SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(F_Index);
    if (!pFormat)
        return sal_False;
    else
        return pFormat->IsTextFormat();
}

sal_Bool SvNumberFormatter::HasTextFormat(sal_uInt32 F_Index) const
{
    SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(F_Index);
    if (!pFormat)
        return sal_False;
    else
        return pFormat->HasTextFormat();
}

sal_Bool SvNumberFormatter::PutEntry(String& rString,
                                 xub_StrLen& nCheckPos,
                                 short& nType,
                                 sal_uInt32& nKey,			// Formatnummer
                                 LanguageType eLnge)
{
    nKey = 0;
    if (rString.Len() == 0) 							// keinen Leerstring
    {
        nCheckPos = 1;									// -> Fehler
        return sal_False;
    }
    if (eLnge == LANGUAGE_DONTKNOW)
        eLnge = IniLnge;

    ChangeIntl(eLnge);									// ggfs. austauschen
    LanguageType eLge = eLnge;                          // Umgehung const fuer ConvertMode
    sal_Bool bCheck = sal_False;
    SvNumberformat* p_Entry = new SvNumberformat(rString,
                                                 pFormatScanner,
                                                 pStringScanner,
                                                 nCheckPos,
                                                 eLge);
    if (nCheckPos == 0)							// Format ok
    {											// Typvergleich:
        short eCheckType = p_Entry->GetType();
        if ( eCheckType != NUMBERFORMAT_UNDEFINED)
        {
            p_Entry->SetType(eCheckType | NUMBERFORMAT_DEFINED);
            nType = eCheckType;
        }
        else
        {
            p_Entry->SetType(NUMBERFORMAT_DEFINED);
            nType = NUMBERFORMAT_DEFINED;
        }
        sal_uInt32 CLOffset = ImpGenerateCL(eLge);				// ggfs. neu Standard-
                                                        // formate anlegen
        nKey = ImpIsEntry(p_Entry->GetFormatstring(),CLOffset, eLge);
        if (nKey != NUMBERFORMAT_ENTRY_NOT_FOUND)				// schon vorhanden
            delete p_Entry;
        else
        {
            SvNumberformat* pStdFormat =
                     (SvNumberformat*) aFTable.Get(CLOffset + ZF_STANDARD);
            sal_uInt32 nPos = CLOffset + pStdFormat->GetLastInsertKey();
            if (nPos - CLOffset >= SV_COUNTRY_LANGUAGE_OFFSET)
            {
                OSL_FAIL("SvNumberFormatter:: Zu viele Formate pro CL");
                delete p_Entry;
            }
            else if (!aFTable.Insert(nPos+1,p_Entry))
                delete p_Entry;
            else
            {
                bCheck = sal_True;
                nKey = nPos+1;
                pStdFormat->SetLastInsertKey((sal_uInt16) (nKey-CLOffset));
            }
        }
    }
    else
        delete p_Entry;
    return bCheck;
}

bool SvNumberFormatter::PutEntry(
    OUString& rString, xub_StrLen& nCheckPos, short& nType, sal_uInt32& nKey,
    LanguageType eLnge)
{
    // Wrapper to allow rtl::OUString to be used.
    String aStr(rString);
    bool bRet = PutEntry(aStr, nCheckPos, nType, nKey, eLnge);
    rString = aStr;
    return bRet;
}

bool SvNumberFormatter::PutandConvertEntry(String& rString,
                                           xub_StrLen& nCheckPos,
                                           short& nType,
                                           sal_uInt32& nKey,
                                           LanguageType eLnge,
                                           LanguageType eNewLnge)
{
    bool bRes;
    if (eNewLnge == LANGUAGE_DONTKNOW)
        eNewLnge = IniLnge;

    pFormatScanner->SetConvertMode(eLnge, eNewLnge);
    bRes = PutEntry(rString, nCheckPos, nType, nKey, eLnge);
    pFormatScanner->SetConvertMode(false);
    return bRes;
}


bool SvNumberFormatter::PutandConvertEntrySystem(String& rString,
                                           xub_StrLen& nCheckPos,
                                           short& nType,
                                           sal_uInt32& nKey,
                                           LanguageType eLnge,
                                           LanguageType eNewLnge)
{
    sal_Bool bRes;
    if (eNewLnge == LANGUAGE_DONTKNOW)
        eNewLnge = IniLnge;

    pFormatScanner->SetConvertMode(eLnge, eNewLnge, sal_True);
    bRes = PutEntry(rString, nCheckPos, nType, nKey, eLnge);
    pFormatScanner->SetConvertMode(sal_False);
    return bRes;
}


sal_uInt32 SvNumberFormatter::GetIndexPuttingAndConverting( String & rString,
        LanguageType eLnge, LanguageType eSysLnge, short & rType,
        sal_Bool & rNewInserted, xub_StrLen & rCheckPos )
{
    sal_uInt32 nKey = NUMBERFORMAT_ENTRY_NOT_FOUND;
    rNewInserted = sal_False;
    rCheckPos = 0;

    // #62389# empty format string (of Writer) => General standard format
    if (!rString.Len())
        ;   // nothing
    else if (eLnge == LANGUAGE_SYSTEM && eSysLnge != SvtSysLocale().GetLanguage())
    {
        sal_uInt32 nOrig = GetEntryKey( rString, eSysLnge );
        if (nOrig == NUMBERFORMAT_ENTRY_NOT_FOUND)
            nKey = nOrig;   // none avaliable, maybe user-defined
        else
            nKey = GetFormatForLanguageIfBuiltIn( nOrig, SvtSysLocale().GetLanguage() );
         
        if (nKey == nOrig)
        {
            // Not a builtin format, convert.
            // The format code string may get modified and adapted to the real
            // language and wouldn't match eSysLnge anymore, do that on a copy.
            String aTmp( rString);    
            rNewInserted = PutandConvertEntrySystem( aTmp, rCheckPos, rType,
                    nKey, eLnge, SvtSysLocale().GetLanguage());
            if (rCheckPos > 0)
            {
                DBG_ERRORFILE("SvNumberFormatter::GetIndexPuttingAndConverting: bad format code string for current locale");
                nKey = NUMBERFORMAT_ENTRY_NOT_FOUND;
            }
        }
    }
    else
    {
        nKey = GetEntryKey( rString, eLnge);
        if (nKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
        {
            rNewInserted = PutEntry( rString, rCheckPos, rType, nKey, eLnge);
            if (rCheckPos > 0)
            {
                DBG_ERRORFILE("SvNumberFormatter::GetIndexPuttingAndConverting: bad format code string for specified locale");
                nKey = NUMBERFORMAT_ENTRY_NOT_FOUND;
            }
        }
    }
    if (nKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
        nKey = GetStandardIndex( eLnge);
    rType = GetType( nKey);
    // Convert any (!) old "automatic" currency format to new fixed currency
    // default format.
    if ((rType & NUMBERFORMAT_CURRENCY) != 0)
    {
        const SvNumberformat* pFormat = GetEntry( nKey);
        if (!pFormat->HasNewCurrency())
        {
            if (rNewInserted)
            {
                DeleteEntry( nKey);     // don't leave trails of rubbish
                rNewInserted = sal_False;
            }
            nKey = GetStandardFormat( NUMBERFORMAT_CURRENCY, eLnge);
        }
    }
    return nKey;
}


void SvNumberFormatter::DeleteEntry(sal_uInt32 nKey)
{
    SvNumberformat* pEntry = aFTable.Remove(nKey);
    delete pEntry;
}

void SvNumberFormatter::PrepareSave()
{
     SvNumberformat* pFormat = aFTable.First();
     while (pFormat)
     {
        pFormat->SetUsed(sal_False);
        pFormat = aFTable.Next();
     }
}

void SvNumberFormatter::SetFormatUsed(sal_uInt32 nFIndex)
{
    SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(nFIndex);
    if (pFormat)
        pFormat->SetUsed(sal_True);
}

sal_Bool SvNumberFormatter::Load( SvStream& rStream )
{
    LanguageType eSysLang = SvtSysLocale().GetLanguage();
    SvNumberFormatter* pConverter = NULL;

    ImpSvNumMultipleReadHeader aHdr( rStream );
    sal_uInt16 nVersion;
    rStream >> nVersion;
    SvNumberformat* pEntry;
    sal_uInt32 nPos;
    LanguageType eSaveSysLang, eLoadSysLang;
    sal_uInt16 nSysOnStore, eLge, eDummy; 			// Dummy fuer kompatibles Format
    rStream >> nSysOnStore >> eLge;				// Systemeinstellung aus
                                                // Dokument
    eSaveSysLang = (nVersion < SV_NUMBERFORMATTER_VERSION_SYSTORE ?
        LANGUAGE_SYSTEM : (LanguageType) nSysOnStore);
    LanguageType eLnge = (LanguageType) eLge;
    ImpChangeSysCL( eLnge, sal_True );

    rStream >> nPos;
    while (nPos != NUMBERFORMAT_ENTRY_NOT_FOUND)
    {
        rStream >> eDummy >> eLge;
        eLnge = (LanguageType) eLge;
        ImpGenerateCL( eLnge, sal_True );			// ggfs. neue Standardformate anlegen

        sal_uInt32 nOffset = nPos % SV_COUNTRY_LANGUAGE_OFFSET;		// relativIndex
        sal_Bool bUserDefined = (nOffset > SV_MAX_ANZ_STANDARD_FORMATE);
        //! HACK! ER 29.07.97 15:15
        // SaveLang wurde bei SYSTEM nicht gespeichert sondern war auch SYSTEM,
        // erst ab 364i Unterscheidung moeglich
        sal_Bool bConversionHack;
        if ( eLnge == LANGUAGE_SYSTEM )
        {
            if ( nVersion < SV_NUMBERFORMATTER_VERSION_SYSTORE )
            {
                bConversionHack = bUserDefined;
                eLoadSysLang = eSaveSysLang;
            }
            else
            {
                bConversionHack = sal_False;
                eLoadSysLang = eSysLang;
            }
        }
        else
        {
            bConversionHack = sal_False;
            eLoadSysLang = eSaveSysLang;
        }

        pEntry = new SvNumberformat(*pFormatScanner, eLnge);
        if ( bConversionHack )
        {	// SYSTEM
            // nVersion < SV_NUMBERFORMATTER_VERSION_SYSTORE
            // nVersion < SV_NUMBERFORMATTER_VERSION_KEYWORDS
            if ( !pConverter )
                pConverter = new SvNumberFormatter( xServiceManager, eSysLang );
            NfHackConversion eHackConversion = pEntry->Load(
                rStream, aHdr, pConverter, *pStringScanner );
            switch ( eHackConversion )
            {
                case NF_CONVERT_GERMAN_ENGLISH :
                    pEntry->ConvertLanguage( *pConverter,
                        LANGUAGE_ENGLISH_US, eSysLang, sal_True );
                break;
                case NF_CONVERT_ENGLISH_GERMAN :
                    switch ( eSysLang )
                    {
                        case LANGUAGE_GERMAN:
                        case LANGUAGE_GERMAN_SWISS:
                        case LANGUAGE_GERMAN_AUSTRIAN:
                        case LANGUAGE_GERMAN_LUXEMBOURG:
                        case LANGUAGE_GERMAN_LIECHTENSTEIN:
                            // alles beim alten
                        break;
                        default:
                            pEntry->ConvertLanguage( *pConverter,
                                LANGUAGE_GERMAN, eSysLang, sal_True );
                    }
                break;
                case NF_CONVERT_NONE :
                break;  // -Wall not handled.
            }

        }
        else
        {
            pEntry->Load( rStream, aHdr, NULL, *pStringScanner );
            if ( !bUserDefined )
                bUserDefined = (pEntry->GetNewStandardDefined() > SV_NUMBERFORMATTER_VERSION);
            if ( bUserDefined )
            {
                if ( eSaveSysLang != eLoadSysLang )
                {	// SYSTEM verschieden
                    if ( !pConverter )
                        pConverter = new SvNumberFormatter( xServiceManager, eSysLang );
                    if ( nVersion < SV_NUMBERFORMATTER_VERSION_KEYWORDS )
                    {
                        switch ( eSaveSysLang )
                        {
                            case LANGUAGE_GERMAN:
                            case LANGUAGE_GERMAN_SWISS:
                            case LANGUAGE_GERMAN_AUSTRIAN:
                            case LANGUAGE_GERMAN_LUXEMBOURG:
                            case LANGUAGE_GERMAN_LIECHTENSTEIN:
                                // alles beim alten
                                pEntry->ConvertLanguage( *pConverter,
                                    eSaveSysLang, eLoadSysLang, sal_True );
                            break;
                            default:
                                // alte english nach neuem anderen
                                pEntry->ConvertLanguage( *pConverter,
                                    LANGUAGE_ENGLISH_US, eLoadSysLang, sal_True );
                        }
                    }
                    else
                        pEntry->ConvertLanguage( *pConverter,
                            eSaveSysLang, eLoadSysLang, sal_True );
                }
                else
                {	// nicht SYSTEM oder gleiches SYSTEM
                    if ( nVersion < SV_NUMBERFORMATTER_VERSION_KEYWORDS )
                    {
                        LanguageType eLoadLang;
                        sal_Bool bSystem;
                        if ( eLnge == LANGUAGE_SYSTEM )
                        {
                            eLoadLang = eSysLang;
                            bSystem = sal_True;
                        }
                        else
                        {
                            eLoadLang = eLnge;
                            bSystem = sal_False;
                        }
                        switch ( eLoadLang )
                        {
                            case LANGUAGE_GERMAN:
                            case LANGUAGE_GERMAN_SWISS:
                            case LANGUAGE_GERMAN_AUSTRIAN:
                            case LANGUAGE_GERMAN_LUXEMBOURG:
                            case LANGUAGE_GERMAN_LIECHTENSTEIN:
                                // alles beim alten
                            break;
                            default:
                                // alte english nach neuem anderen
                                if ( !pConverter )
                                    pConverter = new SvNumberFormatter( xServiceManager, eSysLang );
                                pEntry->ConvertLanguage( *pConverter,
                                    LANGUAGE_ENGLISH_US, eLoadLang, bSystem );
                        }
                    }
                }
            }
        }
        if ( nOffset == 0 )		// StandardFormat
        {
            SvNumberformat* pEnt = aFTable.Get(nPos);
            if (pEnt)
                pEnt->SetLastInsertKey(pEntry->GetLastInsertKey());
        }
        if (!aFTable.Insert(nPos, pEntry))
            delete pEntry;
        rStream >> nPos;
    }

    // ab SV_NUMBERFORMATTER_VERSION_YEAR2000
    if ( nVersion >= SV_NUMBERFORMATTER_VERSION_YEAR2000 )
    {
        aHdr.StartEntry();
        if ( aHdr.BytesLeft() >= sizeof(sal_uInt16) )
        {
            sal_uInt16 nY2k;
            rStream >> nY2k;
            if ( nVersion < SV_NUMBERFORMATTER_VERSION_TWODIGITYEAR && nY2k < 100 )
                nY2k += 1901;		// war vor src513e: 29, jetzt: 1930
            SetYear2000( nY2k );
        }
        aHdr.EndEntry();
    }

    if ( pConverter )
        delete pConverter;

    // generate additional i18n standard formats for all used locales
    LanguageType eOldLanguage = ActLnge;
    NumberFormatCodeWrapper aNumberFormatCode( xServiceManager, GetLocale() );
    SvUShorts aList;
    GetUsedLanguages( aList );
    sal_uInt16 nCount = aList.Count();
    for ( sal_uInt16 j=0; j<nCount; j++ )
    {
        LanguageType eLang = aList[j];
        ChangeIntl( eLang );
        sal_uInt32 CLOffset = ImpGetCLOffset( eLang );
        ImpGenerateAdditionalFormats( CLOffset, aNumberFormatCode, sal_True );
    }
    ChangeIntl( eOldLanguage );

    if (rStream.GetError())
        return sal_False;
    else
        return sal_True;
}

sal_Bool SvNumberFormatter::Save( SvStream& rStream ) const
{
    ImpSvNumMultipleWriteHeader aHdr( rStream );
    // ab 364i wird gespeichert was SYSTEM wirklich war, vorher hart LANGUAGE_SYSTEM
    rStream << (sal_uInt16) SV_NUMBERFORMATTER_VERSION;
    rStream << (sal_uInt16) SvtSysLocale().GetLanguage() << (sal_uInt16) IniLnge;
    SvNumberFormatTable* pTable = (SvNumberFormatTable*) &aFTable;
    SvNumberformat* pEntry = (SvNumberformat*) pTable->First();
    while (pEntry)
    {
        // Gespeichert werden alle markierten, benutzerdefinierten Formate und
        // jeweils das Standardformat zu allen angewaehlten CL-Kombinationen
        // sowie NewStandardDefined
        if ( pEntry->GetUsed() || (pEntry->GetType() & NUMBERFORMAT_DEFINED) ||
                pEntry->GetNewStandardDefined() ||
                (pTable->GetCurKey() % SV_COUNTRY_LANGUAGE_OFFSET == 0) )
        {
            rStream << static_cast<sal_uInt32>(pTable->GetCurKey())
                    << (sal_uInt16) LANGUAGE_SYSTEM
                    << (sal_uInt16) pEntry->GetLanguage();
            pEntry->Save(rStream, aHdr);
        }
        pEntry = (SvNumberformat*) pTable->Next();
    }
    rStream << NUMBERFORMAT_ENTRY_NOT_FOUND;				// EndeKennung

    // ab SV_NUMBERFORMATTER_VERSION_YEAR2000
    aHdr.StartEntry();
    rStream << (sal_uInt16) GetYear2000();
    aHdr.EndEntry();

    if (rStream.GetError())
        return sal_False;
    else
        return sal_True;
}

void SvNumberFormatter::GetUsedLanguages( SvUShorts& rList )
{
    rList.Remove( 0, rList.Count() );

    sal_uInt32 nOffset = 0;
    while (nOffset <= MaxCLOffset)
    {
        SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(nOffset);
        if (pFormat)
            rList.Insert( pFormat->GetLanguage(), rList.Count() );
        nOffset += SV_COUNTRY_LANGUAGE_OFFSET;
    }
}


void SvNumberFormatter::FillKeywordTable( NfKeywordTable& rKeywords,
        LanguageType eLang )
{
    ChangeIntl( eLang );
    const String* pTable = pFormatScanner->GetKeywords();
    for ( sal_uInt16 i = 0; i < NF_KEYWORD_ENTRIES_COUNT; ++i )
    {
        rKeywords[i] = pTable[i];
    }
}


String SvNumberFormatter::GetKeyword( LanguageType eLnge, sal_uInt16 nIndex )
{
    ChangeIntl(eLnge);
    const String* pTable = pFormatScanner->GetKeywords();
    if ( pTable && nIndex < NF_KEYWORD_ENTRIES_COUNT )
        return pTable[nIndex];

    OSL_FAIL("GetKeyword: invalid index");
    return String();
}


String SvNumberFormatter::GetStandardName( LanguageType eLnge )
{
    ChangeIntl( eLnge );
    return pFormatScanner->GetStandardName();
}


sal_uInt32 SvNumberFormatter::ImpGetCLOffset(LanguageType eLnge) const
{
    SvNumberformat* pFormat;
    sal_uInt32 nOffset = 0;
    while (nOffset <= MaxCLOffset)
    {
        pFormat = (SvNumberformat*) aFTable.Get(nOffset);
        if (pFormat && pFormat->GetLanguage() == eLnge)
            return nOffset;
        nOffset += SV_COUNTRY_LANGUAGE_OFFSET;
    }
    return nOffset;
}

sal_uInt32 SvNumberFormatter::ImpIsEntry(const String& rString,
                                       sal_uInt32 nCLOffset,
                                       LanguageType eLnge)
{
#ifndef NF_COMMENT_IN_FORMATSTRING
#error NF_COMMENT_IN_FORMATSTRING not defined (zformat.hxx)
#endif
#if NF_COMMENT_IN_FORMATSTRING
    String aStr( rString );
    SvNumberformat::EraseComment( aStr );
#endif
    sal_uInt32 res = NUMBERFORMAT_ENTRY_NOT_FOUND;
    SvNumberformat* pEntry;
    pEntry = (SvNumberformat*) aFTable.Seek(nCLOffset);
    while ( res == NUMBERFORMAT_ENTRY_NOT_FOUND &&
            pEntry && pEntry->GetLanguage() == eLnge )
    {
#if NF_COMMENT_IN_FORMATSTRING
        if ( pEntry->GetComment().Len() )
        {
            String aFormat( pEntry->GetFormatstring() );
            SvNumberformat::EraseComment( aFormat );
            if ( aStr == aFormat )
                res = aFTable.GetCurKey();
            else
                pEntry = (SvNumberformat*) aFTable.Next();
        }
        else
        {
            if ( aStr == pEntry->GetFormatstring() )
                res = aFTable.GetCurKey();
            else
                pEntry = (SvNumberformat*) aFTable.Next();
        }
#else
        if ( rString == pEntry->GetFormatstring() )
            res = aFTable.GetCurKey();
        else
            pEntry = (SvNumberformat*) aFTable.Next();
#endif
    }
    return res;
}


SvNumberFormatTable& SvNumberFormatter::GetFirstEntryTable(
                                                      short& eType,
                                                      sal_uInt32& FIndex,
                                                      LanguageType& rLnge)
{
    short eTypetmp = eType;
    if (eType == NUMBERFORMAT_ALL) 					// Leere Zelle oder don't care
        rLnge = IniLnge;
    else
    {
        SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(FIndex);
        if (!pFormat)
        {
//			OSL_FAIL("SvNumberFormatter:: Unbekanntes altes Zahlformat (1)");
            rLnge = IniLnge;
            eType = NUMBERFORMAT_ALL;
            eTypetmp = eType;
        }
        else
        {
            rLnge = pFormat->GetLanguage();
            eType = pFormat->GetType()&~NUMBERFORMAT_DEFINED;
            if (eType == 0)
            {
                eType = NUMBERFORMAT_DEFINED;
                eTypetmp = eType;
            }
            else if (eType == NUMBERFORMAT_DATETIME)
            {
                eTypetmp = eType;
                eType = NUMBERFORMAT_DATE;
            }
            else
                eTypetmp = eType;
        }
    }
    ChangeIntl(rLnge);
    return GetEntryTable(eTypetmp, FIndex, rLnge);
}

sal_uInt32 SvNumberFormatter::ImpGenerateCL( LanguageType eLnge, sal_Bool bLoadingSO5 )
{
    ChangeIntl(eLnge);
    sal_uInt32 CLOffset = ImpGetCLOffset(ActLnge);
    if (CLOffset > MaxCLOffset)
    {	// new CL combination
        if (LocaleDataWrapper::areChecksEnabled())
        {
            Locale aLoadedLocale = xLocaleData->getLoadedLocale();
            if ( aLoadedLocale.Language != aLocale.Language ||
                    aLoadedLocale.Country != aLocale.Country )
            {
                String aMsg( RTL_CONSTASCII_USTRINGPARAM(
                            "SvNumerFormatter::ImpGenerateCL: locales don't match:"));
                LocaleDataWrapper::outputCheckMessage(
                        xLocaleData->appendLocaleInfo( aMsg ));
            }
            // test XML locale data FormatElement entries
            {
                uno::Sequence< i18n::FormatElement > xSeq =
                    xLocaleData->getAllFormats();
                // A test for completeness of formatindex="0" ...
                // formatindex="47" is not needed here since it is done in
                // ImpGenerateFormats().

                // Test for dupes of formatindex="..."
                for ( sal_Int32 j = 0; j < xSeq.getLength(); j++ )
                {
                    sal_Int16 nIdx = xSeq[j].formatIndex;
                    String aDupes;
                    for ( sal_Int32 i = 0; i < xSeq.getLength(); i++ )
                    {
                        if ( i != j && xSeq[i].formatIndex == nIdx )
                        {
                            aDupes += String::CreateFromInt32( i );
                            aDupes += '(';
                            aDupes += String( xSeq[i].formatKey );
                            aDupes += ')';
                            aDupes += ' ';
                        }
                    }
                    if ( aDupes.Len() )
                    {
                        String aMsg( RTL_CONSTASCII_USTRINGPARAM(
                                    "XML locale data FormatElement formatindex dupe: "));
                        aMsg += String::CreateFromInt32( nIdx );
                        aMsg.AppendAscii( RTL_CONSTASCII_STRINGPARAM(
                                    "\nFormatElements: "));
                        aMsg += String::CreateFromInt32( j );
                        aMsg += '(';
                        aMsg += String( xSeq[j].formatKey );
                        aMsg += ')';
                        aMsg += ' ';
                        aMsg += aDupes;
                        LocaleDataWrapper::outputCheckMessage(
                                xLocaleData->appendLocaleInfo( aMsg ));
                    }
                }
            }
        }

        MaxCLOffset += SV_COUNTRY_LANGUAGE_OFFSET;
        ImpGenerateFormats( MaxCLOffset, bLoadingSO5 );
        CLOffset = MaxCLOffset;
    }
    return CLOffset;
}

SvNumberFormatTable& SvNumberFormatter::ChangeCL(short eType,
                                                 sal_uInt32& FIndex,
                                                 LanguageType eLnge)
{
    ImpGenerateCL(eLnge);
    return GetEntryTable(eType, FIndex, ActLnge);
}

SvNumberFormatTable& SvNumberFormatter::GetEntryTable(
                                                    short eType,
                                                    sal_uInt32& FIndex,
                                                    LanguageType eLnge)
{
    if ( pFormatTable )
        pFormatTable->Clear();
    else
        pFormatTable = new SvNumberFormatTable;
    ChangeIntl(eLnge);
    sal_uInt32 CLOffset = ImpGetCLOffset(ActLnge);

    // Might generate and insert a default format for the given type
    // (e.g. currency) => has to be done before collecting formats.
    sal_uInt32 nDefaultIndex = GetStandardFormat( eType, ActLnge );

    SvNumberformat* pEntry;
    pEntry = (SvNumberformat*) aFTable.Seek(CLOffset);

    if (eType == NUMBERFORMAT_ALL)
    {
        while (pEntry && pEntry->GetLanguage() == ActLnge)
        {   // copy all entries to output table
            pFormatTable->Insert( aFTable.GetCurKey(), pEntry );
            pEntry = (SvNumberformat*) aFTable.Next();
        }
    }
    else
    {
        while (pEntry && pEntry->GetLanguage() == ActLnge)
        {   // copy entries of queried type to output table
            if ((pEntry->GetType()) & eType)
                pFormatTable->Insert(aFTable.GetCurKey(),pEntry);
            pEntry = (SvNumberformat*) aFTable.Next();
        }
    }
    if ( pFormatTable->Count() > 0 )
    {   // select default if queried format doesn't exist or queried type or
        // language differ from existing format
        pEntry = aFTable.Get(FIndex);
        if ( !pEntry || !(pEntry->GetType() & eType) || pEntry->GetLanguage() != ActLnge )
            FIndex = nDefaultIndex;
    }
    return *pFormatTable;
}

sal_Bool SvNumberFormatter::IsNumberFormat(const String& sString,
                                       sal_uInt32& F_Index,
                                       double& fOutNumber)
{
    short FType;
    const SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(F_Index);
    if (!pFormat)
    {
//		OSL_FAIL("SvNumberFormatter:: Unbekanntes altes Zahlformat (2)");
        ChangeIntl(IniLnge);
        FType = NUMBERFORMAT_NUMBER;
    }
    else
    {
        FType = pFormat->GetType() &~NUMBERFORMAT_DEFINED;
        if (FType == 0)
            FType = NUMBERFORMAT_DEFINED;
        ChangeIntl(pFormat->GetLanguage());
    }
    sal_Bool res;
    short RType = FType;
                                                        // Ergebnistyp
                                                        // ohne def-Kennung
    if (RType == NUMBERFORMAT_TEXT)							// Zahlzelle ->Stringz.
        res = sal_False;
    else
        res = pStringScanner->IsNumberFormat(sString, RType, fOutNumber, pFormat);

    if (res && !IsCompatible(FType, RType))		// unpassender Typ
    {
        switch ( RType )
        {
            case NUMBERFORMAT_TIME :
            {
                if ( pStringScanner->GetDecPos() )
                {	// 100stel Sekunden
                    if ( pStringScanner->GetAnzNums() > 3 || fOutNumber < 0.0 )
                        F_Index = GetFormatIndex( NF_TIME_HH_MMSS00, ActLnge );
                    else
                        F_Index = GetFormatIndex( NF_TIME_MMSS00, ActLnge );
                }
                else if ( fOutNumber >= 1.0 || fOutNumber < 0.0 )
                    F_Index = GetFormatIndex( NF_TIME_HH_MMSS, ActLnge );
                else
                    F_Index = GetStandardFormat( RType, ActLnge );
            }
            break;
            default:
                F_Index = GetStandardFormat( RType, ActLnge );
        }
    }
    return res;
}

sal_Bool SvNumberFormatter::IsCompatible(short eOldType,
                                     short eNewType)
{
    if (eOldType == eNewType)
        return sal_True;
    else if (eOldType == NUMBERFORMAT_DEFINED)
        return sal_True;
    else
    {
        switch (eNewType)
        {
            case NUMBERFORMAT_NUMBER:
            {
                switch (eOldType)
                {
                    case NUMBERFORMAT_PERCENT:
                    case NUMBERFORMAT_CURRENCY:
                    case NUMBERFORMAT_SCIENTIFIC:
                    case NUMBERFORMAT_FRACTION:
//					case NUMBERFORMAT_LOGICAL:
                    case NUMBERFORMAT_DEFINED:
                        return sal_True;
                    default:
                        return sal_False;
                }
            }
            break;
            case NUMBERFORMAT_DATE:
            {
                switch (eOldType)
                {
                    case NUMBERFORMAT_DATETIME:
                        return sal_True;
                    default:
                        return sal_False;
                }
            }
            break;
            case NUMBERFORMAT_TIME:
            {
                switch (eOldType)
                {
                    case NUMBERFORMAT_DATETIME:
                        return sal_True;
                    default:
                        return sal_False;
                }
            }
            break;
            case NUMBERFORMAT_DATETIME:
            {
                switch (eOldType)
                {
                    case NUMBERFORMAT_TIME:
                    case NUMBERFORMAT_DATE:
                        return sal_True;
                    default:
                        return sal_False;
                }
            }
            break;
            default:
            return sal_False;
        }
        return sal_False;
    }
}


sal_uInt32 SvNumberFormatter::ImpGetDefaultFormat( short nType )
{
    sal_uInt32 CLOffset = ImpGetCLOffset( ActLnge );
    sal_uInt32 nSearch;
    switch( nType )
    {
        case NUMBERFORMAT_DATE		:
            nSearch = CLOffset + ZF_STANDARD_DATE;
        break;
        case NUMBERFORMAT_TIME      :
            nSearch = CLOffset + ZF_STANDARD_TIME;
        break;
        case NUMBERFORMAT_DATETIME  :
            nSearch = CLOffset + ZF_STANDARD_DATETIME;
        break;
        case NUMBERFORMAT_PERCENT   :
            nSearch = CLOffset + ZF_STANDARD_PERCENT;
        break;
        case NUMBERFORMAT_SCIENTIFIC:
            nSearch = CLOffset + ZF_STANDARD_SCIENTIFIC;
        break;
        default:
            nSearch = CLOffset + ZF_STANDARD;
    }
    sal_uInt32 nDefaultFormat = (sal_uInt32)(sal_uLong) aDefaultFormatKeys.Get( nSearch );
    if ( !nDefaultFormat )
        nDefaultFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
    if ( nDefaultFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
    {	// look for a defined standard
        sal_uInt32 nStopKey = CLOffset + SV_COUNTRY_LANGUAGE_OFFSET;
        sal_uInt32 nKey;
        aFTable.Seek( CLOffset );
        while ( (nKey = aFTable.GetCurKey()) >= CLOffset && nKey < nStopKey )
        {
            const SvNumberformat* pEntry =
                (const SvNumberformat*) aFTable.GetCurObject();
            if ( pEntry->IsStandard() && ((pEntry->GetType() &
                            ~NUMBERFORMAT_DEFINED) == nType) )
            {
                nDefaultFormat = nKey;
                break;	// while
            }
            aFTable.Next();
        }

        if ( nDefaultFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
        {	// none found, use old fixed standards
            switch( nType )
            {
                case NUMBERFORMAT_DATE		:
                    nDefaultFormat = CLOffset + ZF_STANDARD_DATE;
                break;
                case NUMBERFORMAT_TIME      :
                    nDefaultFormat = CLOffset + ZF_STANDARD_TIME+1;
                break;
                case NUMBERFORMAT_DATETIME  :
                    nDefaultFormat = CLOffset + ZF_STANDARD_DATETIME;
                break;
                case NUMBERFORMAT_PERCENT   :
                    nDefaultFormat = CLOffset + ZF_STANDARD_PERCENT+1;
                break;
                case NUMBERFORMAT_SCIENTIFIC:
                    nDefaultFormat = CLOffset + ZF_STANDARD_SCIENTIFIC;
                break;
                default:
                    nDefaultFormat = CLOffset + ZF_STANDARD;
            }
        }
        sal_uIntPtr nFormat = nDefaultFormat;
        aDefaultFormatKeys.Insert( nSearch, (void*) nFormat );
    }
    return nDefaultFormat;
}


sal_uInt32 SvNumberFormatter::GetStandardFormat( short eType, LanguageType eLnge )
{
    if (eLnge == LANGUAGE_DONTKNOW)
        eLnge = IniLnge;

    sal_uInt32 CLOffset = ImpGenerateCL(eLnge);
    switch(eType)
    {
        case NUMBERFORMAT_CURRENCY  :
        {
            if ( eLnge == LANGUAGE_SYSTEM )
                return ImpGetDefaultSystemCurrencyFormat();
            else
                return ImpGetDefaultCurrencyFormat();
        }
        case NUMBERFORMAT_DATE		:
        case NUMBERFORMAT_TIME      :
        case NUMBERFORMAT_DATETIME  :
        case NUMBERFORMAT_PERCENT   :
        case NUMBERFORMAT_SCIENTIFIC:
            return ImpGetDefaultFormat( eType );

        case NUMBERFORMAT_FRACTION  : return CLOffset + ZF_STANDARD_FRACTION;
        case NUMBERFORMAT_LOGICAL   : return CLOffset + ZF_STANDARD_LOGICAL;
        case NUMBERFORMAT_TEXT		: return CLOffset + ZF_STANDARD_TEXT;
        case NUMBERFORMAT_ALL       :
        case NUMBERFORMAT_DEFINED   :
        case NUMBERFORMAT_NUMBER    :
        case NUMBERFORMAT_UNDEFINED :
        default               : return CLOffset + ZF_STANDARD;
    }
}

sal_Bool SvNumberFormatter::IsSpecialStandardFormat( sal_uInt32 nFIndex,
        LanguageType eLnge )
{
    return
        nFIndex == GetFormatIndex( NF_TIME_MMSS00, eLnge ) ||
        nFIndex == GetFormatIndex( NF_TIME_HH_MMSS00, eLnge ) ||
        nFIndex == GetFormatIndex( NF_TIME_HH_MMSS, eLnge )
        ;
}

sal_uInt32 SvNumberFormatter::GetStandardFormat( sal_uInt32 nFIndex, short eType,
        LanguageType eLnge )
{
    if ( IsSpecialStandardFormat( nFIndex, eLnge ) )
        return nFIndex;
    else
        return GetStandardFormat( eType, eLnge );
}

sal_uInt32 SvNumberFormatter::GetStandardFormat( double fNumber, sal_uInt32 nFIndex,
        short eType, LanguageType eLnge )
{
    if ( IsSpecialStandardFormat( nFIndex, eLnge ) )
        return nFIndex;

    switch( eType )
    {
        case NUMBERFORMAT_TIME :
        {
            sal_Bool bSign;
            if ( fNumber < 0.0 )
            {
                bSign = sal_True;
                fNumber = -fNumber;
            }
            else
                bSign = sal_False;
            double fSeconds = fNumber * 86400;
            if ( floor( fSeconds + 0.5 ) * 100 != floor( fSeconds * 100 + 0.5 ) )
            {	// mit 100stel Sekunden
                if ( bSign || fSeconds >= 3600 )
                    return GetFormatIndex( NF_TIME_HH_MMSS00, eLnge );
                else
                    return GetFormatIndex( NF_TIME_MMSS00, eLnge );
            }
            else
            {
                if ( bSign || fNumber >= 1.0 )
                    return GetFormatIndex( NF_TIME_HH_MMSS, eLnge );
                else
                    return GetStandardFormat( eType, eLnge );
            }
        }
        default:
            return GetStandardFormat( eType, eLnge );
    }
}

void SvNumberFormatter::GetInputLineString(const double& fOutNumber,
                                           sal_uInt32 nFIndex,
                                           String& sOutString)
{
    SvNumberformat* pFormat;
    Color* pColor;
    pFormat = (SvNumberformat*) aFTable.Get(nFIndex);
    if (!pFormat)
        pFormat = aFTable.Get(ZF_STANDARD);
    LanguageType eLang = pFormat->GetLanguage();
    ChangeIntl( eLang );
    short eType = pFormat->GetType() & ~NUMBERFORMAT_DEFINED;
    if (eType == 0)
        eType = NUMBERFORMAT_DEFINED;
    sal_uInt16 nOldPrec = pFormatScanner->GetStandardPrec();
    bool bPrecChanged = false;
    if (eType == NUMBERFORMAT_NUMBER || eType == NUMBERFORMAT_PERCENT
                                     || eType == NUMBERFORMAT_CURRENCY
                                     || eType == NUMBERFORMAT_SCIENTIFIC
                                     || eType == NUMBERFORMAT_FRACTION)
    {
        if (eType != NUMBERFORMAT_PERCENT)	// spaeter Sonderbehandlung %
            eType = NUMBERFORMAT_NUMBER;
        ChangeStandardPrec(INPUTSTRING_PRECISION);
        bPrecChanged = true;
    }
    sal_uInt32 nKey = nFIndex;
    switch ( eType )
    {	// #61619# immer vierstelliges Jahr editieren
        case NUMBERFORMAT_DATE :
            nKey = GetFormatIndex( NF_DATE_SYS_DDMMYYYY, eLang );
        break;
        case NUMBERFORMAT_DATETIME :
            nKey = GetFormatIndex( NF_DATETIME_SYS_DDMMYYYY_HHMMSS, eLang );
        break;
        default:
            nKey = GetStandardFormat( fOutNumber, nFIndex, eType, eLang );
    }
    if ( nKey != nFIndex )
        pFormat = (SvNumberformat*) aFTable.Get( nKey );
    if (pFormat)
    {
        if ( eType == NUMBERFORMAT_TIME && pFormat->GetFormatPrecision() )
        {
            ChangeStandardPrec(INPUTSTRING_PRECISION);
            bPrecChanged = true;
        }
        pFormat->GetOutputString(fOutNumber, sOutString, &pColor);
    }
    if (bPrecChanged)
        ChangeStandardPrec(nOldPrec);
}

void SvNumberFormatter::GetOutputString(const double& fOutNumber,
                                        sal_uInt32 nFIndex,
                                        String& sOutString,
                                        Color** ppColor)
{
    if (bNoZero && fOutNumber == 0.0)
    {
        sOutString.Erase();
        return;
    }
    SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(nFIndex);
    if (!pFormat)
        pFormat = aFTable.Get(ZF_STANDARD);
    ChangeIntl(pFormat->GetLanguage());
    pFormat->GetOutputString(fOutNumber, sOutString, ppColor);
}

void SvNumberFormatter::GetOutputString(String& sString,
                                        sal_uInt32 nFIndex,
                                        String& sOutString,
                                        Color** ppColor)
{
    SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(nFIndex);
    if (!pFormat)
        pFormat = aFTable.Get(ZF_STANDARD_TEXT);
    if (!pFormat->IsTextFormat() && !pFormat->HasTextFormat())
    {
        *ppColor = NULL;
        sOutString = sString;
    }
    else
    {
        ChangeIntl(pFormat->GetLanguage());
        pFormat->GetOutputString(sString, sOutString, ppColor);
    }
}

sal_Bool SvNumberFormatter::GetPreviewString(const String& sFormatString,
                                         double fPreviewNumber,
                                         String& sOutString,
                                         Color** ppColor,
                                         LanguageType eLnge)
{
    if (sFormatString.Len() == 0) 						// keinen Leerstring
        return sal_False;

    xub_StrLen nCheckPos = STRING_NOTFOUND;
    sal_uInt32 nKey;
    if (eLnge == LANGUAGE_DONTKNOW)
        eLnge = IniLnge;
    ChangeIntl(eLnge);							// ggfs. austauschen
    eLnge = ActLnge;
    String sTmpString = sFormatString;
    SvNumberformat* p_Entry = new SvNumberformat(sTmpString,
                                                 pFormatScanner,
                                                 pStringScanner,
                                                 nCheckPos,
                                                 eLnge);
    if (nCheckPos == 0)									// String ok
    {
        sal_uInt32 CLOffset = ImpGenerateCL(eLnge);				// ggfs. neu Standard-
                                                        // formate anlegen
        nKey = ImpIsEntry(p_Entry->GetFormatstring(),CLOffset, eLnge);
        if (nKey != NUMBERFORMAT_ENTRY_NOT_FOUND)				// schon vorhanden
            GetOutputString(fPreviewNumber,nKey,sOutString,ppColor);
        else
            p_Entry->GetOutputString(fPreviewNumber,sOutString, ppColor);
        delete p_Entry;
        return sal_True;
    }
    else
    {
        delete p_Entry;
        return sal_False;
    }
}

sal_Bool SvNumberFormatter::GetPreviewStringGuess( const String& sFormatString,
                                         double fPreviewNumber,
                                         String& sOutString,
                                         Color** ppColor,
                                         LanguageType eLnge )
{
    if (sFormatString.Len() == 0) 						// keinen Leerstring
        return sal_False;

    if (eLnge == LANGUAGE_DONTKNOW)
        eLnge = IniLnge;

    ChangeIntl( eLnge );
    eLnge = ActLnge;
    sal_Bool bEnglish = (eLnge == LANGUAGE_ENGLISH_US);

    String aFormatStringUpper( pCharClass->upper( sFormatString ) );
    sal_uInt32 nCLOffset = ImpGenerateCL( eLnge );
    sal_uInt32 nKey = ImpIsEntry( aFormatStringUpper, nCLOffset, eLnge );
    if ( nKey != NUMBERFORMAT_ENTRY_NOT_FOUND )
    {	// Zielformat vorhanden
        GetOutputString( fPreviewNumber, nKey, sOutString, ppColor );
        return sal_True;
    }

    SvNumberformat *pEntry = NULL;
    xub_StrLen nCheckPos = STRING_NOTFOUND;
    String sTmpString;

    if ( bEnglish )
    {
        sTmpString = sFormatString;
        pEntry = new SvNumberformat( sTmpString, pFormatScanner,
            pStringScanner, nCheckPos, eLnge );
    }
    else
    {
        nCLOffset = ImpGenerateCL( LANGUAGE_ENGLISH_US );
        nKey = ImpIsEntry( aFormatStringUpper, nCLOffset, LANGUAGE_ENGLISH_US );
        sal_Bool bEnglishFormat = (nKey != NUMBERFORMAT_ENTRY_NOT_FOUND);

        // try english --> other bzw. english nach other konvertieren
        LanguageType eFormatLang = LANGUAGE_ENGLISH_US;
        pFormatScanner->SetConvertMode( LANGUAGE_ENGLISH_US, eLnge );
        sTmpString = sFormatString;
        pEntry = new SvNumberformat( sTmpString, pFormatScanner,
            pStringScanner, nCheckPos, eFormatLang );
        pFormatScanner->SetConvertMode( sal_False );
        ChangeIntl( eLnge );

        if ( !bEnglishFormat )
        {
            if ( nCheckPos > 0 || xTransliteration->isEqual( sFormatString,
                    pEntry->GetFormatstring() ) )
            {	// other Format
                delete pEntry;
                sTmpString = sFormatString;
                pEntry = new SvNumberformat( sTmpString, pFormatScanner,
                    pStringScanner, nCheckPos, eLnge );
            }
            else
            {	// verify english
                xub_StrLen nCheckPos2 = STRING_NOTFOUND;
                // try other --> english
                eFormatLang = eLnge;
                pFormatScanner->SetConvertMode( eLnge, LANGUAGE_ENGLISH_US );
                sTmpString = sFormatString;
                SvNumberformat* pEntry2 = new SvNumberformat( sTmpString, pFormatScanner,
                    pStringScanner, nCheckPos2, eFormatLang );
                pFormatScanner->SetConvertMode( sal_False );
                ChangeIntl( eLnge );
                if ( nCheckPos2 == 0 && !xTransliteration->isEqual( sFormatString,
                        pEntry2->GetFormatstring() ) )
                {	// other Format
                    delete pEntry;
                    sTmpString = sFormatString;
                    pEntry = new SvNumberformat( sTmpString, pFormatScanner,
                        pStringScanner, nCheckPos, eLnge );
                }
                delete pEntry2;
            }
        }
    }

    if (nCheckPos == 0)									// String ok
    {
        ImpGenerateCL( eLnge );		// ggfs. neu Standardformate anlegen
        pEntry->GetOutputString( fPreviewNumber, sOutString, ppColor );
        delete pEntry;
        return sal_True;
    }
    delete pEntry;
    return sal_False;
}

sal_Bool SvNumberFormatter::GetPreviewString( const String& sFormatString,
                                          const String& sPreviewString,
                                          String& sOutString,
                                          Color** ppColor,
                                          LanguageType eLnge )
{
    if (sFormatString.Len() == 0)               // no empty string
        return sal_False;

    xub_StrLen nCheckPos = STRING_NOTFOUND;
    sal_uInt32 nKey;
    if (eLnge == LANGUAGE_DONTKNOW)
        eLnge = IniLnge;
    ChangeIntl(eLnge);                          // switch if needed
    eLnge = ActLnge;
    String sTmpString = sFormatString;
    SvNumberformat* p_Entry = new SvNumberformat( sTmpString,
                                                  pFormatScanner,
                                                  pStringScanner,
                                                  nCheckPos,
                                                  eLnge);
    if (nCheckPos == 0)                          // String ok
    {
        String aNonConstPreview( sPreviewString);
        // May have to create standard formats for this locale.
        sal_uInt32 CLOffset = ImpGenerateCL(eLnge);
        nKey = ImpIsEntry( p_Entry->GetFormatstring(), CLOffset, eLnge);
        if (nKey != NUMBERFORMAT_ENTRY_NOT_FOUND)       // already present
            GetOutputString( aNonConstPreview, nKey, sOutString, ppColor);
        else
        {
            // If the format is valid but not a text format and does not 
            // include a text subformat, an empty string would result. Same as 
            // in SvNumberFormatter::GetOutputString()
            if (p_Entry->IsTextFormat() || p_Entry->HasTextFormat())
                p_Entry->GetOutputString( aNonConstPreview, sOutString, ppColor);
            else
            {
                *ppColor = NULL;
                sOutString = sPreviewString;
            }
        }
        delete p_Entry;
        return sal_True;
    }
    else
    {
        delete p_Entry;
        return sal_False;
    }
}

sal_uInt32 SvNumberFormatter::TestNewString(const String& sFormatString,
                                      LanguageType eLnge)
{
    if (sFormatString.Len() == 0) 						// keinen Leerstring
        return NUMBERFORMAT_ENTRY_NOT_FOUND;

    xub_StrLen nCheckPos = STRING_NOTFOUND;
    if (eLnge == LANGUAGE_DONTKNOW)
        eLnge = IniLnge;
    ChangeIntl(eLnge);									// ggfs. austauschen
    eLnge = ActLnge;
    sal_uInt32 nRes;
    String sTmpString = sFormatString;
    SvNumberformat* pEntry = new SvNumberformat(sTmpString,
                                                pFormatScanner,
                                                pStringScanner,
                                                nCheckPos,
                                                eLnge);
    if (nCheckPos == 0)									// String ok
    {
        sal_uInt32 CLOffset = ImpGenerateCL(eLnge);				// ggfs. neu Standard-
                                                        // formate anlegen
        nRes = ImpIsEntry(pEntry->GetFormatstring(),CLOffset, eLnge);
                                                        // schon vorhanden ?
    }
    else
        nRes = NUMBERFORMAT_ENTRY_NOT_FOUND;
    delete pEntry;
    return nRes;
}

SvNumberformat* SvNumberFormatter::ImpInsertFormat(
            const ::com::sun::star::i18n::NumberFormatCode& rCode,
            sal_uInt32 nPos, sal_Bool bAfterLoadingSO5, sal_Int16 nOrgIndex )
{
    String aCodeStr( rCode.Code );
    if ( rCode.Index < NF_INDEX_TABLE_ENTRIES &&
            rCode.Usage == ::com::sun::star::i18n::KNumberFormatUsage::CURRENCY &&
            rCode.Index != NF_CURRENCY_1000DEC2_CCC )
    {	// strip surrounding [$...] on automatic currency
        if ( aCodeStr.SearchAscii( "[$" ) != STRING_NOTFOUND )
            aCodeStr = SvNumberformat::StripNewCurrencyDelimiters( aCodeStr, sal_False );
        else
        {
            if (LocaleDataWrapper::areChecksEnabled() &&
                    rCode.Index != NF_CURRENCY_1000DEC2_CCC )
            {
                String aMsg( RTL_CONSTASCII_USTRINGPARAM(
                            "SvNumberFormatter::ImpInsertFormat: no [$...] on currency format code, index "));
                aMsg += String::CreateFromInt32( rCode.Index );
                aMsg.AppendAscii( RTL_CONSTASCII_STRINGPARAM( ":\n"));
                aMsg += String( rCode.Code );
                LocaleDataWrapper::outputCheckMessage(
                        xLocaleData->appendLocaleInfo( aMsg));
            }
        }
    }
    xub_StrLen nCheckPos = 0;
    SvNumberformat* pFormat = new SvNumberformat(aCodeStr,
                                                 pFormatScanner,
                                                 pStringScanner,
                                                 nCheckPos,
                                                 ActLnge);
    if ( !pFormat || nCheckPos > 0 )
    {
        if (LocaleDataWrapper::areChecksEnabled())
        {
            String aMsg( RTL_CONSTASCII_USTRINGPARAM(
                        "SvNumberFormatter::ImpInsertFormat: bad format code, index "));
            aMsg += String::CreateFromInt32( rCode.Index );
            aMsg += '\n';
            aMsg += String( rCode.Code );
            LocaleDataWrapper::outputCheckMessage(
                    xLocaleData->appendLocaleInfo( aMsg));
        }
        delete pFormat;
        return NULL;
    }
    if ( rCode.Index >= NF_INDEX_TABLE_ENTRIES )
    {
        sal_uInt32 nCLOffset = nPos - (nPos % SV_COUNTRY_LANGUAGE_OFFSET);
        sal_uInt32 nKey = ImpIsEntry( aCodeStr, nCLOffset, ActLnge );
        if ( nKey != NUMBERFORMAT_ENTRY_NOT_FOUND )
        {
            if (LocaleDataWrapper::areChecksEnabled())
            {
                switch ( nOrgIndex )
                {
                    // These may be dupes of integer versions for locales where
                    // currencies have no decimals like Italian Lira.
                    case NF_CURRENCY_1000DEC2 :			// NF_CURRENCY_1000INT
                    case NF_CURRENCY_1000DEC2_RED :		// NF_CURRENCY_1000INT_RED
                    case NF_CURRENCY_1000DEC2_DASHED :	// NF_CURRENCY_1000INT_RED
                    break;
                    default:
                        if ( !bAfterLoadingSO5 )
                        {	// If bAfterLoadingSO5 there will definitely be some dupes,
                            // don't cry. But we need this test for verification of locale
                            // data if not loading old SO5 documents.
                            String aMsg( RTL_CONSTASCII_USTRINGPARAM(
                                        "SvNumberFormatter::ImpInsertFormat: dup format code, index "));
                            aMsg += String::CreateFromInt32( rCode.Index );
                            aMsg += '\n';
                            aMsg += String( rCode.Code );
                            LocaleDataWrapper::outputCheckMessage(
                                    xLocaleData->appendLocaleInfo( aMsg));
                        }
                }
            }
            delete pFormat;
            return NULL;
        }
        else if ( nPos - nCLOffset >= SV_COUNTRY_LANGUAGE_OFFSET )
        {
            if (LocaleDataWrapper::areChecksEnabled())
            {
                String aMsg( RTL_CONSTASCII_USTRINGPARAM(
                            "SvNumberFormatter::ImpInsertFormat: too many format codes, index "));
                aMsg += String::CreateFromInt32( rCode.Index );
                aMsg += '\n';
                aMsg += String( rCode.Code );
                LocaleDataWrapper::outputCheckMessage(
                        xLocaleData->appendLocaleInfo( aMsg));
            }
            delete pFormat;
            return NULL;
        }
    }
    if ( !aFTable.Insert( nPos, pFormat ) )
    {
        if (LocaleDataWrapper::areChecksEnabled())
        {
            String aMsg( RTL_CONSTASCII_USTRINGPARAM(
                        "ImpInsertFormat: can't insert number format key pos: "));
            aMsg += String::CreateFromInt32( nPos );
            aMsg.AppendAscii( RTL_CONSTASCII_STRINGPARAM( ", code index "));
            aMsg += String::CreateFromInt32( rCode.Index );
            aMsg += '\n';
            aMsg += String( rCode.Code );
            LocaleDataWrapper::outputCheckMessage(
                    xLocaleData->appendLocaleInfo( aMsg));
        }
        delete pFormat;
        return NULL;
    }
    if ( rCode.Default )
        pFormat->SetStandard();
    if ( rCode.DefaultName.getLength() )
        pFormat->SetComment( rCode.DefaultName );
    return pFormat;
}

SvNumberformat* SvNumberFormatter::ImpInsertNewStandardFormat(
            const ::com::sun::star::i18n::NumberFormatCode& rCode,
            sal_uInt32 nPos, sal_uInt16 nVersion, sal_Bool bAfterLoadingSO5,
            sal_Int16 nOrgIndex )
{
    SvNumberformat* pNewFormat = ImpInsertFormat( rCode, nPos,
        bAfterLoadingSO5, nOrgIndex );
    if (pNewFormat)
        pNewFormat->SetNewStandardDefined( nVersion );
        // so that it gets saved, displayed properly, and converted by old versions
    return pNewFormat;
}

void SvNumberFormatter::GetFormatSpecialInfo(sal_uInt32 nFormat,
                                             sal_Bool& bThousand,
                                             sal_Bool& IsRed,
                                             sal_uInt16& nPrecision,
                                             sal_uInt16& nAnzLeading)

{
    const SvNumberformat* pFormat = aFTable.Get(nFormat);
    if (pFormat)
        pFormat->GetFormatSpecialInfo(bThousand, IsRed,
                                      nPrecision, nAnzLeading);
    else
    {
        bThousand = sal_False;
        IsRed = sal_False;
        nPrecision = pFormatScanner->GetStandardPrec();
        nAnzLeading = 0;
    }
}

sal_uInt16 SvNumberFormatter::GetFormatPrecision( sal_uInt32 nFormat ) const
{
    const SvNumberformat* pFormat = aFTable.Get( nFormat );
    if ( pFormat )
        return pFormat->GetFormatPrecision();
    else
        return pFormatScanner->GetStandardPrec();
}


String SvNumberFormatter::GetFormatDecimalSep( sal_uInt32 nFormat ) const
{
    const SvNumberformat* pFormat = aFTable.Get( nFormat );
    if ( !pFormat || pFormat->GetLanguage() == ActLnge )
        return GetNumDecimalSep();

    String aRet;
    LanguageType eSaveLang = xLocaleData.getCurrentLanguage();
    if ( pFormat->GetLanguage() == eSaveLang )
        aRet = xLocaleData->getNumDecimalSep();
    else
    {
        ::com::sun::star::lang::Locale aSaveLocale( xLocaleData->getLocale() );
        ::com::sun::star::lang::Locale aTmpLocale(MsLangId::convertLanguageToLocale(pFormat->GetLanguage()));
        ((SvNumberFormatter*)this)->xLocaleData.changeLocale(aTmpLocale, pFormat->GetLanguage() );
        aRet = xLocaleData->getNumDecimalSep();
        ((SvNumberFormatter*)this)->xLocaleData.changeLocale( aSaveLocale, eSaveLang );
    }
    return aRet;
}


sal_uInt32 SvNumberFormatter::GetFormatSpecialInfo( const String& rFormatString,
            sal_Bool& bThousand, sal_Bool& IsRed, sal_uInt16& nPrecision,
            sal_uInt16& nAnzLeading, LanguageType eLnge )

{
    xub_StrLen nCheckPos = 0;
    if (eLnge == LANGUAGE_DONTKNOW)
        eLnge = IniLnge;
    ChangeIntl(eLnge);									// ggfs. austauschen
    eLnge = ActLnge;
    String aTmpStr( rFormatString );
    SvNumberformat* pFormat = new SvNumberformat( aTmpStr,
        pFormatScanner, pStringScanner, nCheckPos, eLnge );
    if ( nCheckPos == 0 )
        pFormat->GetFormatSpecialInfo( bThousand, IsRed, nPrecision, nAnzLeading );
    else
    {
        bThousand = sal_False;
        IsRed = sal_False;
        nPrecision = pFormatScanner->GetStandardPrec();
        nAnzLeading = 0;
    }
    delete pFormat;
    return nCheckPos;
}


inline sal_uInt32 SetIndexTable( NfIndexTableOffset nTabOff, sal_uInt32 nIndOff )
{
    if ( !bIndexTableInitialized )
    {
        DBG_ASSERT( theIndexTable[nTabOff] == NUMBERFORMAT_ENTRY_NOT_FOUND,
            "SetIndexTable: theIndexTable[nTabOff] already occupied" );
        theIndexTable[nTabOff] = nIndOff;
    }
    return nIndOff;
}


sal_Int32 SvNumberFormatter::ImpGetFormatCodeIndex(
            ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::NumberFormatCode >& rSeq,
            const NfIndexTableOffset nTabOff )
{
    const sal_Int32 nLen = rSeq.getLength();
    for ( sal_Int32 j=0; j<nLen; j++ )
    {
        if ( rSeq[j].Index == nTabOff )
            return j;
    }
    if (LocaleDataWrapper::areChecksEnabled() && (nTabOff < NF_CURRENCY_START
                || NF_CURRENCY_END < nTabOff || nTabOff == NF_CURRENCY_1000INT
                || nTabOff == NF_CURRENCY_1000INT_RED
                || nTabOff == NF_CURRENCY_1000DEC2_CCC))
    {	// currency entries with decimals might not exist, e.g. Italian Lira
        String aMsg( RTL_CONSTASCII_USTRINGPARAM(
                    "SvNumberFormatter::ImpGetFormatCodeIndex: not found: "));
        aMsg += String::CreateFromInt32( nTabOff );
        LocaleDataWrapper::outputCheckMessage( xLocaleData->appendLocaleInfo(
                    aMsg));
    }
    if ( nLen )
    {
        sal_Int32 j;
        // look for a preset default
        for ( j=0; j<nLen; j++ )
        {
            if ( rSeq[j].Default )
                return j;
        }
        // currencies are special, not all format codes must exist, but all
        // builtin number format key index positions must have a format assigned
        if ( NF_CURRENCY_START <= nTabOff && nTabOff <= NF_CURRENCY_END )
        {
            // look for a format with decimals
            for ( j=0; j<nLen; j++ )
            {
                if ( rSeq[j].Index == NF_CURRENCY_1000DEC2 )
                    return j;
            }
            // last resort: look for a format without decimals
            for ( j=0; j<nLen; j++ )
            {
                if ( rSeq[j].Index == NF_CURRENCY_1000INT )
                    return j;
            }
        }
    }
    else
    {	// we need at least _some_ format
        rSeq.realloc(1);
        rSeq[0] = ::com::sun::star::i18n::NumberFormatCode();
        String aTmp( '0' );
        aTmp += GetNumDecimalSep();
        aTmp.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "############" ) );
        rSeq[0].Code = aTmp;
    }
    return 0;
}


sal_Int32 SvNumberFormatter::ImpAdjustFormatCodeDefault(
        ::com::sun::star::i18n::NumberFormatCode * pFormatArr,
        sal_Int32 nCnt, sal_Bool bCheckCorrectness )
{
    using namespace ::com::sun::star;

    if ( !nCnt )
        return -1;
    if (bCheckCorrectness && LocaleDataWrapper::areChecksEnabled())
    {   // check the locale data for correctness
        ByteString aMsg;
        sal_Int32 nElem, nShort, nMedium, nLong, nShortDef, nMediumDef, nLongDef;
        nShort = nMedium = nLong = nShortDef = nMediumDef = nLongDef = -1;
        for ( nElem = 0; nElem < nCnt; nElem++ )
        {
            switch ( pFormatArr[nElem].Type )
            {
                case i18n::KNumberFormatType::SHORT :
                    nShort = nElem;
                break;
                case i18n::KNumberFormatType::MEDIUM :
                    nMedium = nElem;
                break;
                case i18n::KNumberFormatType::LONG :
                    nLong = nElem;
                break;
                default:
                    aMsg = "unknown type";
            }
            if ( pFormatArr[nElem].Default )
            {
                switch ( pFormatArr[nElem].Type )
                {
                    case i18n::KNumberFormatType::SHORT :
                        if ( nShortDef != -1 )
                            aMsg = "dupe short type default";
                        nShortDef = nElem;
                    break;
                    case i18n::KNumberFormatType::MEDIUM :
                        if ( nMediumDef != -1 )
                            aMsg = "dupe medium type default";
                        nMediumDef = nElem;
                    break;
                    case i18n::KNumberFormatType::LONG :
                        if ( nLongDef != -1 )
                            aMsg = "dupe long type default";
                        nLongDef = nElem;
                    break;
                }
            }
            if ( aMsg.Len() )
            {
                aMsg.Insert( "SvNumberFormatter::ImpAdjustFormatCodeDefault: ", 0 );
                aMsg += "\nXML locale data FormatElement formatindex: ";
                aMsg += ByteString::CreateFromInt32( pFormatArr[nElem].Index );
                String aUMsg( aMsg, RTL_TEXTENCODING_ASCII_US);
                LocaleDataWrapper::outputCheckMessage(
                        xLocaleData->appendLocaleInfo( aUMsg));
                aMsg.Erase();
            }
        }
        if ( nShort != -1 && nShortDef == -1 )
            aMsg += "no short type default  ";
        if ( nMedium != -1 && nMediumDef == -1 )
            aMsg += "no medium type default  ";
        if ( nLong != -1 && nLongDef == -1 )
            aMsg += "no long type default  ";
        if ( aMsg.Len() )
        {
            aMsg.Insert( "SvNumberFormatter::ImpAdjustFormatCodeDefault: ", 0 );
            aMsg += "\nXML locale data FormatElement group of: ";
            String aUMsg( aMsg, RTL_TEXTENCODING_ASCII_US);
            aUMsg += String( pFormatArr[0].NameID );
            LocaleDataWrapper::outputCheckMessage(
                    xLocaleData->appendLocaleInfo( aUMsg));
            aMsg.Erase();
        }
    }
    // find the default (medium preferred, then long) and reset all other defaults
    sal_Int32 nElem, nDef, nMedium;
    nDef = nMedium = -1;
    for ( nElem = 0; nElem < nCnt; nElem++ )
    {
        if ( pFormatArr[nElem].Default )
        {
            switch ( pFormatArr[nElem].Type )
            {
                case i18n::KNumberFormatType::MEDIUM :
                    nDef = nMedium = nElem;
                break;
                case i18n::KNumberFormatType::LONG :
                    if ( nMedium == -1 )
                        nDef = nElem;
                // fallthru
                default:
                    if ( nDef == -1 )
                        nDef = nElem;
                    pFormatArr[nElem].Default = sal_False;
            }
        }
    }
    if ( nDef == -1 )
        nDef = 0;
    pFormatArr[nDef].Default = sal_True;
    return nDef;
}


void SvNumberFormatter::ImpGenerateFormats( sal_uInt32 CLOffset, sal_Bool bLoadingSO5 )
{
    using namespace ::com::sun::star;

    if ( !bIndexTableInitialized )
    {
        for ( sal_uInt16 j=0; j<NF_INDEX_TABLE_ENTRIES; j++ )
        {
            theIndexTable[j] = NUMBERFORMAT_ENTRY_NOT_FOUND;
        }
    }
    sal_Bool bOldConvertMode = pFormatScanner->GetConvertMode();
    if (bOldConvertMode)
        pFormatScanner->SetConvertMode(sal_False);		// switch off for this function

    NumberFormatCodeWrapper aNumberFormatCode( xServiceManager, GetLocale() );

    xub_StrLen nCheckPos = 0;
    SvNumberformat* pNewFormat = NULL;
    String aFormatCode;
    sal_Int32 nIdx;
    sal_Bool bDefault;

    // Counter for additional builtin formats not fitting into the first 10
    // of a category (TLOT:=The Legacy Of Templin), altogether about 20 formats.
    // Has to be incremented on each ImpInsertNewStandardformat, new formats
    // must be appended, not inserted!
    sal_uInt16 nNewExtended = ZF_STANDARD_NEWEXTENDED;

    // Number
    uno::Sequence< i18n::NumberFormatCode > aFormatSeq
        = aNumberFormatCode.getAllFormatCode( i18n::KNumberFormatUsage::FIXED_NUMBER );
    ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() );

    // General
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_STANDARD );
    SvNumberformat* pStdFormat = ImpInsertFormat( aFormatSeq[nIdx],
            CLOffset + SetIndexTable( NF_NUMBER_STANDARD, ZF_STANDARD ));
    if (pStdFormat)
    {
        // This is _the_ standard format.
        if (LocaleDataWrapper::areChecksEnabled() &&
                pStdFormat->GetType() != NUMBERFORMAT_NUMBER)
        {
            String aMsg( RTL_CONSTASCII_USTRINGPARAM(
                        "SvNumberFormatter::ImpGenerateFormats: General format not NUMBER"));
            LocaleDataWrapper::outputCheckMessage(
                    xLocaleData->appendLocaleInfo( aMsg));
        }
        pStdFormat->SetType( NUMBERFORMAT_NUMBER );
        pStdFormat->SetStandard();
        pStdFormat->SetLastInsertKey( SV_MAX_ANZ_STANDARD_FORMATE );
    }
    else
    {
        if (LocaleDataWrapper::areChecksEnabled())
        {
            String aMsg( RTL_CONSTASCII_USTRINGPARAM(
                        "SvNumberFormatter::ImpGenerateFormats: General format not insertable, nothing will work"));
            LocaleDataWrapper::outputCheckMessage(
                    xLocaleData->appendLocaleInfo( aMsg));
        }
    }

    // Boolean
    aFormatCode = pFormatScanner->GetBooleanString();
    pNewFormat = new SvNumberformat( aFormatCode,
        pFormatScanner, pStringScanner,	nCheckPos, ActLnge );
    pNewFormat->SetType(NUMBERFORMAT_LOGICAL);
    pNewFormat->SetStandard();
    if ( !aFTable.Insert(
            CLOffset + SetIndexTable( NF_BOOLEAN, ZF_STANDARD_LOGICAL ),
            pNewFormat))
        delete pNewFormat;

    // Text
    aFormatCode = '@';
    pNewFormat = new SvNumberformat( aFormatCode,
        pFormatScanner, pStringScanner, nCheckPos, ActLnge );
    pNewFormat->SetType(NUMBERFORMAT_TEXT);
    pNewFormat->SetStandard();
    if ( !aFTable.Insert(
            CLOffset + SetIndexTable( NF_TEXT, ZF_STANDARD_TEXT ),
            pNewFormat))
        delete pNewFormat;



    // 0
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_INT );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_NUMBER_INT, ZF_STANDARD+1 ));

    // 0.00
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_DEC2 );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_NUMBER_DEC2, ZF_STANDARD+2 ));

    // #,##0
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_1000INT );
    ImpInsertFormat( aFormatSeq[nIdx],
            CLOffset + SetIndexTable( NF_NUMBER_1000INT, ZF_STANDARD+3 ));

    // #,##0.00
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_1000DEC2 );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_NUMBER_1000DEC2, ZF_STANDARD+4 ));

    // #.##0,00 System country/language dependent   since number formatter version 6
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_SYSTEM );
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_NUMBER_SYSTEM, ZF_STANDARD+5 ),
        SV_NUMBERFORMATTER_VERSION_NEWSTANDARD );


    // Percent number
    aFormatSeq = aNumberFormatCode.getAllFormatCode( i18n::KNumberFormatUsage::PERCENT_NUMBER );
    ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() );

    // 0%
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_PERCENT_INT );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_PERCENT_INT, ZF_STANDARD_PERCENT ));

    // 0.00%
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_PERCENT_DEC2 );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_PERCENT_DEC2, ZF_STANDARD_PERCENT+1 ));



    // Currency. NO default standard option! Default is determined of locale
    // data default currency and format is generated if needed.
    aFormatSeq = aNumberFormatCode.getAllFormatCode( i18n::KNumberFormatUsage::CURRENCY );
    if (LocaleDataWrapper::areChecksEnabled())
    {
        // though no default desired here, test for correctness of locale data
        ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() );
    }

    // #,##0
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000INT );
    bDefault = aFormatSeq[nIdx].Default;
    aFormatSeq[nIdx].Default = sal_False;
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_CURRENCY_1000INT, ZF_STANDARD_CURRENCY ));
    aFormatSeq[nIdx].Default = bDefault;

    // #,##0.00
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000DEC2 );
    bDefault = aFormatSeq[nIdx].Default;
    aFormatSeq[nIdx].Default = sal_False;
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_CURRENCY_1000DEC2, ZF_STANDARD_CURRENCY+1 ));
    aFormatSeq[nIdx].Default = bDefault;

    // #,##0 negative red
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000INT_RED );
    bDefault = aFormatSeq[nIdx].Default;
    aFormatSeq[nIdx].Default = sal_False;
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_CURRENCY_1000INT_RED, ZF_STANDARD_CURRENCY+2 ));
    aFormatSeq[nIdx].Default = bDefault;

    // #,##0.00 negative red
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000DEC2_RED );
    bDefault = aFormatSeq[nIdx].Default;
    aFormatSeq[nIdx].Default = sal_False;
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_CURRENCY_1000DEC2_RED, ZF_STANDARD_CURRENCY+3 ));
    aFormatSeq[nIdx].Default = bDefault;

    // #,##0.00 USD   since number formatter version 3
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000DEC2_CCC );
    bDefault = aFormatSeq[nIdx].Default;
    aFormatSeq[nIdx].Default = sal_False;
    pNewFormat = ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_CURRENCY_1000DEC2_CCC, ZF_STANDARD_CURRENCY+4 ));
    if ( pNewFormat )
        pNewFormat->SetUsed(sal_True);		// must be saved for older versions
    aFormatSeq[nIdx].Default = bDefault;

    // #.##0,--   since number formatter version 6
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000DEC2_DASHED );
    bDefault = aFormatSeq[nIdx].Default;
    aFormatSeq[nIdx].Default = sal_False;
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_CURRENCY_1000DEC2_DASHED, ZF_STANDARD_CURRENCY+5 ),
        SV_NUMBERFORMATTER_VERSION_NEWSTANDARD );
    aFormatSeq[nIdx].Default = bDefault;



    // Date
    aFormatSeq = aNumberFormatCode.getAllFormatCode( i18n::KNumberFormatUsage::DATE );
    ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() );

    // DD.MM.YY   System
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYSTEM_SHORT );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_SYSTEM_SHORT, ZF_STANDARD_DATE ));

    // NN DD.MMM YY
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DEF_NNDDMMMYY );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_DEF_NNDDMMMYY, ZF_STANDARD_DATE+1 ));

    // DD.MM.YY   def/System
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_MMYY );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_SYS_MMYY, ZF_STANDARD_DATE+2 ));

    // DD MMM
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DDMMM );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_SYS_DDMMM, ZF_STANDARD_DATE+3 ));

    // MMMM
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_MMMM );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_MMMM, ZF_STANDARD_DATE+4 ));

    // QQ YY
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_QQJJ );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_QQJJ, ZF_STANDARD_DATE+5 ));

    // DD.MM.YYYY   since number formatter version 2, was DD.MM.[YY]YY
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DDMMYYYY );
    pNewFormat = ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_SYS_DDMMYYYY, ZF_STANDARD_DATE+6 ));
    if ( pNewFormat )
        pNewFormat->SetUsed(sal_True);		// must be saved for older versions

    // DD.MM.YY   def/System, since number formatter version 6
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DDMMYY );
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_SYS_DDMMYY, ZF_STANDARD_DATE+7 ),
        SV_NUMBERFORMATTER_VERSION_NEWSTANDARD );

    // NNN, D. MMMM YYYY   System
    // Long day of week: "NNNN" instead of "NNN," because of compatibility
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYSTEM_LONG );
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_SYSTEM_LONG, ZF_STANDARD_DATE+8 ),
        SV_NUMBERFORMATTER_VERSION_NEWSTANDARD );

    // Hard coded but system (regional settings) delimiters dependent long date formats
    // since numberformatter version 6

    // D. MMM YY   def/System
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DMMMYY );
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_SYS_DMMMYY, ZF_STANDARD_DATE+9 ),
        SV_NUMBERFORMATTER_VERSION_NEWSTANDARD );

    //! Unfortunally TLOT intended only 10 builtin formats per category, more
    //! would overwrite the next category (ZF_STANDARD_TIME) :-((
    //! Therefore they are inserted with nNewExtended++ (which is also limited)

    // D. MMM YYYY   def/System
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DMMMYYYY );
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_SYS_DMMMYYYY, nNewExtended++ ),
        SV_NUMBERFORMATTER_VERSION_NEWSTANDARD );

    // D. MMMM YYYY   def/System
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DMMMMYYYY );
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_SYS_DMMMMYYYY, nNewExtended++ ),
        SV_NUMBERFORMATTER_VERSION_NEWSTANDARD );

    // NN, D. MMM YY   def/System
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_NNDMMMYY );
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_SYS_NNDMMMYY, nNewExtended++ ),
        SV_NUMBERFORMATTER_VERSION_NEWSTANDARD );

    // NN, D. MMMM YYYY   def/System
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_NNDMMMMYYYY );
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_SYS_NNDMMMMYYYY, nNewExtended++ ),
        SV_NUMBERFORMATTER_VERSION_NEWSTANDARD );

    // NNN, D. MMMM YYYY   def/System
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_NNNNDMMMMYYYY );
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_SYS_NNNNDMMMMYYYY, nNewExtended++ ),
        SV_NUMBERFORMATTER_VERSION_NEWSTANDARD );

    // Hard coded DIN (Deutsche Industrie Norm) and EN (European Norm) date formats

    // D. MMM. YYYY   DIN/EN
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DIN_DMMMYYYY );
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_DIN_DMMMYYYY, nNewExtended++ ),
        SV_NUMBERFORMATTER_VERSION_NEWSTANDARD );

    // D. MMMM YYYY   DIN/EN
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DIN_DMMMMYYYY );
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_DIN_DMMMMYYYY, nNewExtended++ ),
        SV_NUMBERFORMATTER_VERSION_NEWSTANDARD );

    // MM-DD   DIN/EN
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DIN_MMDD );
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_DIN_MMDD, nNewExtended++ ),
        SV_NUMBERFORMATTER_VERSION_NEWSTANDARD );

    // YY-MM-DD   DIN/EN
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DIN_YYMMDD );
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_DIN_YYMMDD, nNewExtended++ ),
        SV_NUMBERFORMATTER_VERSION_NEWSTANDARD );

    // YYYY-MM-DD   DIN/EN
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DIN_YYYYMMDD );
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATE_DIN_YYYYMMDD, nNewExtended++ ),
        SV_NUMBERFORMATTER_VERSION_NEWSTANDARD );



    // Time
    aFormatSeq = aNumberFormatCode.getAllFormatCode( i18n::KNumberFormatUsage::TIME );
    ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() );

    // HH:MM
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HHMM );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_TIME_HHMM, ZF_STANDARD_TIME ));

    // HH:MM:SS
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HHMMSS );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_TIME_HHMMSS, ZF_STANDARD_TIME+1 ));

    // HH:MM AM/PM
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HHMMAMPM );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_TIME_HHMMAMPM, ZF_STANDARD_TIME+2 ));

    // HH:MM:SS AM/PM
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HHMMSSAMPM );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_TIME_HHMMSSAMPM, ZF_STANDARD_TIME+3 ));

    // [HH]:MM:SS
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HH_MMSS );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_TIME_HH_MMSS, ZF_STANDARD_TIME+4 ));

    // MM:SS,00
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_MMSS00 );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_TIME_MMSS00, ZF_STANDARD_TIME+5 ));

    // [HH]:MM:SS,00
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HH_MMSS00 );
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_TIME_HH_MMSS00, ZF_STANDARD_TIME+6 ),
        SV_NUMBERFORMATTER_VERSION_NF_TIME_HH_MMSS00 );



    // DateTime
    aFormatSeq = aNumberFormatCode.getAllFormatCode( i18n::KNumberFormatUsage::DATE_TIME );
    ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() );

    // DD.MM.YY HH:MM   System
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATETIME_SYSTEM_SHORT_HHMM );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATETIME_SYSTEM_SHORT_HHMM, ZF_STANDARD_DATETIME ));

    // DD.MM.YYYY HH:MM:SS   System
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATETIME_SYS_DDMMYYYY_HHMMSS );
    ImpInsertNewStandardFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_DATETIME_SYS_DDMMYYYY_HHMMSS, ZF_STANDARD_DATETIME+1 ),
        SV_NUMBERFORMATTER_VERSION_NF_DATETIME_SYS_DDMMYYYY_HHMMSS );



    // Scientific number
    aFormatSeq = aNumberFormatCode.getAllFormatCode( i18n::KNumberFormatUsage::SCIENTIFIC_NUMBER );
    ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() );

    // 0.00E+000
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_SCIENTIFIC_000E000 );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_SCIENTIFIC_000E000, ZF_STANDARD_SCIENTIFIC ));

    // 0.00E+00
    nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_SCIENTIFIC_000E00 );
    ImpInsertFormat( aFormatSeq[nIdx],
        CLOffset + SetIndexTable( NF_SCIENTIFIC_000E00, ZF_STANDARD_SCIENTIFIC+1 ));



    // Fraction number (no default option)
    i18n::NumberFormatCode aSingleFormatCode;

     // # ?/?
    aSingleFormatCode.Code = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "# ?/?" ) );
    ImpInsertFormat( aSingleFormatCode,
        CLOffset + SetIndexTable( NF_FRACTION_1, ZF_STANDARD_FRACTION ));

    // # ??/??
    //! "??/" would be interpreted by the compiler as a trigraph for '\'
    aSingleFormatCode.Code = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "# ?\?/?\?" ) );
    ImpInsertFormat( aSingleFormatCode,
        CLOffset + SetIndexTable( NF_FRACTION_2, ZF_STANDARD_FRACTION+1 ));

    // Week of year   must be appended here because of nNewExtended
    const String* pKeyword = pFormatScanner->GetKeywords();
    aSingleFormatCode.Code = pKeyword[NF_KEY_WW];
    ImpInsertNewStandardFormat( aSingleFormatCode,
        CLOffset + SetIndexTable( NF_DATE_WW, nNewExtended++ ),
        SV_NUMBERFORMATTER_VERSION_NF_DATE_WW );



    bIndexTableInitialized = sal_True;
    DBG_ASSERT( nNewExtended <= ZF_STANDARD_NEWEXTENDEDMAX,
        "ImpGenerateFormats: overflow of nNewExtended standard formats" );

    // Now all additional format codes provided by I18N, but only if not
    // loading from old SO5 file format, then they are appended last.
    if ( !bLoadingSO5 )
        ImpGenerateAdditionalFormats( CLOffset, aNumberFormatCode, sal_False );

    sal_uInt32 nPos = CLOffset + pStdFormat->GetLastInsertKey();

    aSingleFormatCode.Code = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "# ?/4" ) );
    ImpInsertNewStandardFormat( aSingleFormatCode, nPos+1, SV_NUMBERFORMATTER_VERSION_ADDITIONAL_I18N_FORMATS );
    nPos++;

    aSingleFormatCode.Code = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "# ?\?/100" ) );
    ImpInsertNewStandardFormat( aSingleFormatCode, nPos+1, SV_NUMBERFORMATTER_VERSION_ADDITIONAL_I18N_FORMATS );
    nPos++;

    pStdFormat->SetLastInsertKey( (sal_uInt16)(nPos - CLOffset) );

    if (bOldConvertMode)
        pFormatScanner->SetConvertMode(sal_True);
}


void SvNumberFormatter::ImpGenerateAdditionalFormats( sal_uInt32 CLOffset,
            NumberFormatCodeWrapper& rNumberFormatCode, sal_Bool bAfterLoadingSO5 )
{
    using namespace ::com::sun::star;

    SvNumberformat* pStdFormat =
        (SvNumberformat*) aFTable.Get( CLOffset + ZF_STANDARD );
    if ( !pStdFormat )
    {
        DBG_ERRORFILE( "ImpGenerateAdditionalFormats: no GENERAL format" );
        return ;
    }
    sal_uInt32 nPos = CLOffset + pStdFormat->GetLastInsertKey();
    rNumberFormatCode.setLocale( GetLocale() );
    sal_Int32 j;

    // All currencies, this time with [$...] which was stripped in
    // ImpGenerateFormats for old "automatic" currency formats.
    uno::Sequence< i18n::NumberFormatCode > aFormatSeq =
        rNumberFormatCode.getAllFormatCode( i18n::KNumberFormatUsage::CURRENCY );
    i18n::NumberFormatCode * pFormatArr = aFormatSeq.getArray();
    sal_Int32 nCodes = aFormatSeq.getLength();
    ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), nCodes );
    for ( j = 0; j < nCodes; j++ )
    {
        if ( nPos - CLOffset >= SV_COUNTRY_LANGUAGE_OFFSET )
        {
            DBG_ERRORFILE( "ImpGenerateAdditionalFormats: too many formats" );
            break;	// for
        }
        if ( pFormatArr[j].Index < NF_INDEX_TABLE_ENTRIES &&
                pFormatArr[j].Index != NF_CURRENCY_1000DEC2_CCC )
        {	// Insert only if not already inserted, but internal index must be
            // above so ImpInsertFormat can distinguish it.
            sal_Int16 nOrgIndex = pFormatArr[j].Index;
            pFormatArr[j].Index = sal::static_int_cast< sal_Int16 >(
                pFormatArr[j].Index + nCodes + NF_INDEX_TABLE_ENTRIES);
            //! no default on currency
            sal_Bool bDefault = aFormatSeq[j].Default;
            aFormatSeq[j].Default = sal_False;
            if ( ImpInsertNewStandardFormat( pFormatArr[j], nPos+1,
                    SV_NUMBERFORMATTER_VERSION_ADDITIONAL_I18N_FORMATS,
                    bAfterLoadingSO5, nOrgIndex ) )
                nPos++;
            pFormatArr[j].Index = nOrgIndex;
            aFormatSeq[j].Default = bDefault;
        }
    }

    // all additional format codes provided by I18N that are not old standard index
    aFormatSeq = rNumberFormatCode.getAllFormatCodes();
    nCodes = aFormatSeq.getLength();
    if ( nCodes )
    {
        pFormatArr = aFormatSeq.getArray();
        // don't check ALL
        sal_Int32 nDef = ImpAdjustFormatCodeDefault( pFormatArr, nCodes, sal_False);
        // don't have any defaults here
        pFormatArr[nDef].Default = sal_False;
        for ( j = 0; j < nCodes; j++ )
        {
            if ( nPos - CLOffset >= SV_COUNTRY_LANGUAGE_OFFSET )
            {
                DBG_ERRORFILE( "ImpGenerateAdditionalFormats: too many formats" );
                break;  // for
            }
            if ( pFormatArr[j].Index >= NF_INDEX_TABLE_ENTRIES )
                if ( ImpInsertNewStandardFormat( pFormatArr[j], nPos+1,
                        SV_NUMBERFORMATTER_VERSION_ADDITIONAL_I18N_FORMATS,
                        bAfterLoadingSO5 ) )
                    nPos++;
        }
    }

    pStdFormat->SetLastInsertKey( (sal_uInt16)(nPos - CLOffset) );
}


void SvNumberFormatter::ImpGetPosCurrFormat( String& sPosStr, const String& rCurrSymbol )
{
    NfCurrencyEntry::CompletePositiveFormatString( sPosStr,
        rCurrSymbol, xLocaleData->getCurrPositiveFormat() );
}

void SvNumberFormatter::ImpGetNegCurrFormat( String& sNegStr, const String& rCurrSymbol )
{
    NfCurrencyEntry::CompleteNegativeFormatString( sNegStr,
        rCurrSymbol, xLocaleData->getCurrNegativeFormat() );
}

void SvNumberFormatter::GenerateFormat(String& sString,
                                       sal_uInt32 nIndex,
                                       LanguageType eLnge,
                                       sal_Bool bThousand,
                                       sal_Bool IsRed,
                                       sal_uInt16 nPrecision,
                                       sal_uInt16 nAnzLeading)
{
    if (eLnge == LANGUAGE_DONTKNOW)
        eLnge = IniLnge;
    short eType = GetType(nIndex);
    sal_uInt16 i;
    ImpGenerateCL(eLnge);				// ggfs. neu Standard-
                                    // formate anlegen
    sString.Erase();

    utl::DigitGroupingIterator aGrouping( xLocaleData->getDigitGrouping());
    const xub_StrLen nDigitsInFirstGroup = static_cast<xub_StrLen>(aGrouping.get());
    const String& rThSep = GetNumThousandSep();

    SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(nIndex);

    if (nAnzLeading == 0)
    {
        if (!bThousand)
            sString += '#';
        else
        {
            sString += '#';
            sString += rThSep;
            sString.Expand( sString.Len() + nDigitsInFirstGroup, '#' );
        }
    }
    else
    {
        for (i = 0; i < nAnzLeading; i++)
        {
            if (bThousand && i > 0 && i == aGrouping.getPos())
            {
                sString.Insert( rThSep, 0 );
                aGrouping.advance();
            }
            sString.Insert('0',0);
        }
        if (bThousand && nAnzLeading < nDigitsInFirstGroup + 1)
        {
            for (i = nAnzLeading; i < nDigitsInFirstGroup + 1; i++)
            {
                if (bThousand && i % nDigitsInFirstGroup == 0)
                    sString.Insert( rThSep, 0 );
                sString.Insert('#',0);
            }
        }
    }
    if (nPrecision > 0)
    {
        sString += GetNumDecimalSep();
        sString.Expand( sString.Len() + nPrecision, '0' );
    }
    if (eType == NUMBERFORMAT_PERCENT)
        sString += '%';
    else if (eType == NUMBERFORMAT_CURRENCY)
    {
        String sNegStr = sString;
        String aCurr;
        const NfCurrencyEntry* pEntry;
        sal_Bool bBank;
        if ( GetNewCurrencySymbolString( nIndex, aCurr, &pEntry, &bBank ) )
        {
            if ( pEntry )
            {
                sal_uInt16 nPosiForm = NfCurrencyEntry::GetEffectivePositiveFormat(
                    xLocaleData->getCurrPositiveFormat(),
                    pEntry->GetPositiveFormat(), bBank );
                sal_uInt16 nNegaForm = NfCurrencyEntry::GetEffectiveNegativeFormat(
                    xLocaleData->getCurrNegativeFormat(),
                    pEntry->GetNegativeFormat(), bBank );
                pEntry->CompletePositiveFormatString( sString, bBank,
                    nPosiForm );
                pEntry->CompleteNegativeFormatString( sNegStr, bBank,
                    nNegaForm );
            }
            else
            {   // assume currency abbreviation (AKA banking symbol), not symbol
                sal_uInt16 nPosiForm = NfCurrencyEntry::GetEffectivePositiveFormat(
                    xLocaleData->getCurrPositiveFormat(),
                    xLocaleData->getCurrPositiveFormat(), sal_True );
                sal_uInt16 nNegaForm = NfCurrencyEntry::GetEffectiveNegativeFormat(
                    xLocaleData->getCurrNegativeFormat(),
                    xLocaleData->getCurrNegativeFormat(), sal_True );
                NfCurrencyEntry::CompletePositiveFormatString( sString, aCurr,
                    nPosiForm );
                NfCurrencyEntry::CompleteNegativeFormatString( sNegStr, aCurr,
                    nNegaForm );
            }
        }
        else
        {   // "automatic" old style
            String aSymbol, aAbbrev;
            GetCompatibilityCurrency( aSymbol, aAbbrev );
            ImpGetPosCurrFormat( sString, aSymbol );
            ImpGetNegCurrFormat( sNegStr, aSymbol );
        }
        if (IsRed)
        {
            sString += ';';
            sString += '[';
            sString += pFormatScanner->GetRedString();
            sString += ']';
        }
        else
            sString += ';';
        sString += sNegStr;
    }
    if (eType != NUMBERFORMAT_CURRENCY)
    {
        bool insertBrackets = false;
        if ( eType != NUMBERFORMAT_UNDEFINED)
            insertBrackets = pFormat->IsNegativeInBracket();
        if (IsRed || insertBrackets)
        {
            String sTmpStr = sString;

            if ( pFormat->HasPositiveBracketPlaceholder() )
            {
                 sTmpStr += '_';
                 sTmpStr += ')';
            }
            sTmpStr += ';';

            if (IsRed)
            {
                sTmpStr += '[';
                sTmpStr += pFormatScanner->GetRedString();
                sTmpStr += ']';
            }

            if (insertBrackets)
            {
                sTmpStr += '(';
                sTmpStr += sString;
                sTmpStr += ')';
            }
            else
            {
                sTmpStr += '-';
                sTmpStr +=sString;
            }
            sString = sTmpStr;
            }
    }
}

sal_Bool SvNumberFormatter::IsUserDefined(const String& sStr,
                                      LanguageType eLnge)
{
    if (eLnge == LANGUAGE_DONTKNOW)
        eLnge = IniLnge;
    sal_uInt32 CLOffset = ImpGenerateCL(eLnge);				// ggfs. neu Standard-
                                                    // formate anlegen
    eLnge = ActLnge;
    sal_uInt32 nKey = ImpIsEntry(sStr, CLOffset, eLnge);
    if (nKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
        return sal_True;
    SvNumberformat* pEntry = aFTable.Get(nKey);
    if ( pEntry && ((pEntry->GetType() & NUMBERFORMAT_DEFINED) != 0) )
        return sal_True;
    return sal_False;
}

sal_uInt32 SvNumberFormatter::GetEntryKey(const String& sStr,
                                     LanguageType eLnge)
{
    if (eLnge == LANGUAGE_DONTKNOW)
        eLnge = IniLnge;
    sal_uInt32 CLOffset = ImpGenerateCL(eLnge);				// ggfs. neu Standard-
                                                    // formate anlegen
    return ImpIsEntry(sStr, CLOffset, eLnge);
}

sal_uInt32 SvNumberFormatter::GetStandardIndex(LanguageType eLnge)
{
    if (eLnge == LANGUAGE_DONTKNOW)
        eLnge = IniLnge;
    return GetStandardFormat(NUMBERFORMAT_NUMBER, eLnge);
}

short SvNumberFormatter::GetType(sal_uInt32 nFIndex)
{
    short eType;
    SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get(nFIndex);
    if (!pFormat)
        eType = NUMBERFORMAT_UNDEFINED;
    else
    {
        eType = pFormat->GetType() &~NUMBERFORMAT_DEFINED;
        if (eType == 0)
            eType = NUMBERFORMAT_DEFINED;
    }
    return eType;
}

void SvNumberFormatter::ClearMergeTable()
{
    if ( pMergeTable )
    {
        sal_uInt32* pIndex = (sal_uInt32*) pMergeTable->First();
        while (pIndex)
        {
            delete pIndex;
            pIndex = pMergeTable->Next();
        }
        pMergeTable->Clear();
    }
}

SvNumberFormatterIndexTable* SvNumberFormatter::MergeFormatter(SvNumberFormatter& rTable)
{
    if ( pMergeTable )
        ClearMergeTable();
    else
        pMergeTable = new SvNumberFormatterIndexTable;
    sal_uInt32 nCLOffset = 0;
    sal_uInt32 nOldKey, nOffset, nNewKey;
    sal_uInt32* pNewIndex;
    SvNumberformat* pNewEntry;
    SvNumberformat* pFormat = rTable.aFTable.First();
    while (pFormat)
    {
        nOldKey = rTable.aFTable.GetCurKey();
        nOffset = nOldKey % SV_COUNTRY_LANGUAGE_OFFSET;		// relativIndex
        if (nOffset == 0)									// 1. Format von CL
            nCLOffset = ImpGenerateCL(pFormat->GetLanguage());

        if (nOffset <= SV_MAX_ANZ_STANDARD_FORMATE)		// Std.form.
        {
            nNewKey = nCLOffset + nOffset;
            if (!aFTable.Get(nNewKey))					// noch nicht da
            {
//				pNewEntry = new SvNumberformat(*pFormat);	// Copy reicht nicht !!!
                pNewEntry = new SvNumberformat( *pFormat, *pFormatScanner );
                if (!aFTable.Insert(nNewKey, pNewEntry))
                    delete pNewEntry;
            }
            if (nNewKey != nOldKey)						// neuer Index
            {
                pNewIndex = new sal_uInt32(nNewKey);
                if (!pMergeTable->Insert(nOldKey,pNewIndex))
                    delete pNewIndex;
            }
        }
        else											// benutzerdef.
        {
//			pNewEntry = new SvNumberformat(*pFormat);	// Copy reicht nicht !!!
            pNewEntry = new SvNumberformat( *pFormat, *pFormatScanner );
            nNewKey = ImpIsEntry(pNewEntry->GetFormatstring(),
                              nCLOffset,
                              pFormat->GetLanguage());
            if (nNewKey != NUMBERFORMAT_ENTRY_NOT_FOUND) // schon vorhanden
                delete pNewEntry;
            else
            {
                SvNumberformat* pStdFormat =
                        (SvNumberformat*) aFTable.Get(nCLOffset + ZF_STANDARD);
                sal_uInt32 nPos = nCLOffset + pStdFormat->GetLastInsertKey();
                nNewKey = nPos+1;
                if (nPos - nCLOffset >= SV_COUNTRY_LANGUAGE_OFFSET)
                {
                    OSL_FAIL(
                        "SvNumberFormatter:: Zu viele Formate pro CL");
                    delete pNewEntry;
                }
                else if (!aFTable.Insert(nNewKey, pNewEntry))
                        delete pNewEntry;
                else
                    pStdFormat->SetLastInsertKey((sal_uInt16) (nNewKey - nCLOffset));
            }
            if (nNewKey != nOldKey)						// neuer Index
            {
                pNewIndex = new sal_uInt32(nNewKey);
                if (!pMergeTable->Insert(nOldKey,pNewIndex))
                    delete pNewIndex;
            }
        }
        pFormat = rTable.aFTable.Next();
    }
    return pMergeTable;
}


SvNumberFormatterMergeMap SvNumberFormatter::ConvertMergeTableToMap()
{
    if (!HasMergeFmtTbl())
        return SvNumberFormatterMergeMap();

    SvNumberFormatterMergeMap aMap;
    for (sal_uInt32* pIndex = pMergeTable->First(); pIndex; pIndex = pMergeTable->Next())
    {
        sal_uInt32 nOldKey = pMergeTable->GetCurKey();
        aMap.insert( SvNumberFormatterMergeMap::value_type( nOldKey, *pIndex));
    }
    ClearMergeTable();
    return aMap;
}


sal_uInt32 SvNumberFormatter::GetFormatForLanguageIfBuiltIn( sal_uInt32 nFormat,
        LanguageType eLnge )
{
    if ( eLnge == LANGUAGE_DONTKNOW )
        eLnge = IniLnge;
    if ( nFormat < SV_COUNTRY_LANGUAGE_OFFSET && eLnge == IniLnge )
        return nFormat;		// es bleibt wie es ist
    sal_uInt32 nOffset = nFormat % SV_COUNTRY_LANGUAGE_OFFSET;		// relativIndex
    if ( nOffset > SV_MAX_ANZ_STANDARD_FORMATE )
        return nFormat;					// kein eingebautes Format
    sal_uInt32 nCLOffset = ImpGenerateCL(eLnge);		// ggbf. generieren
    return nCLOffset + nOffset;
}


sal_uInt32 SvNumberFormatter::GetFormatIndex( NfIndexTableOffset nTabOff,
        LanguageType eLnge )
{
    if ( nTabOff >= NF_INDEX_TABLE_ENTRIES
            || theIndexTable[nTabOff] == NUMBERFORMAT_ENTRY_NOT_FOUND )
        return NUMBERFORMAT_ENTRY_NOT_FOUND;
    if ( eLnge == LANGUAGE_DONTKNOW )
        eLnge = IniLnge;
    sal_uInt32 nCLOffset = ImpGenerateCL(eLnge);		// ggbf. generieren
    return nCLOffset + theIndexTable[nTabOff];
}


NfIndexTableOffset SvNumberFormatter::GetIndexTableOffset( sal_uInt32 nFormat ) const
{
    sal_uInt32 nOffset = nFormat % SV_COUNTRY_LANGUAGE_OFFSET;		// relativIndex
    if ( nOffset > SV_MAX_ANZ_STANDARD_FORMATE )
        return NF_INDEX_TABLE_ENTRIES;		// kein eingebautes Format
    for ( sal_uInt16 j = 0; j < NF_INDEX_TABLE_ENTRIES; j++ )
    {
        if ( theIndexTable[j] == nOffset )
            return (NfIndexTableOffset) j;
    }
    return NF_INDEX_TABLE_ENTRIES;		// bad luck
}


void SvNumberFormatter::SetYear2000( sal_uInt16 nVal )
{
    pStringScanner->SetYear2000( nVal );
}


sal_uInt16 SvNumberFormatter::GetYear2000() const
{
    return pStringScanner->GetYear2000();
}


sal_uInt16 SvNumberFormatter::ExpandTwoDigitYear( sal_uInt16 nYear ) const
{
    if ( nYear < 100 )
        return SvNumberFormatter::ExpandTwoDigitYear( nYear,
            pStringScanner->GetYear2000() );
    return nYear;
}


// static
sal_uInt16 SvNumberFormatter::GetYear2000Default()
{
    return (sal_uInt16) ::utl::MiscCfg().GetYear2000();
}


// static
const NfCurrencyTable& SvNumberFormatter::GetTheCurrencyTable()
{
    ::osl::MutexGuard aGuard( GetMutex() );
    while ( !bCurrencyTableInitialized )
        ImpInitCurrencyTable();
    return theCurrencyTable::get();
}


// static
const NfCurrencyEntry* SvNumberFormatter::MatchSystemCurrency()
{
    // MUST call GetTheCurrencyTable() before accessing nSystemCurrencyPosition
    const NfCurrencyTable& rTable = GetTheCurrencyTable();
    return nSystemCurrencyPosition ? rTable[nSystemCurrencyPosition] : NULL;
}


// static
const NfCurrencyEntry& SvNumberFormatter::GetCurrencyEntry( LanguageType eLang )
{
    if ( eLang == LANGUAGE_SYSTEM )
    {
        const NfCurrencyEntry* pCurr = MatchSystemCurrency();
        return pCurr ? *pCurr : *(GetTheCurrencyTable()[0]);
    }
    else
    {
        eLang = MsLangId::getRealLanguage( eLang );
        const NfCurrencyTable& rTable = GetTheCurrencyTable();
        sal_uInt16 nCount = rTable.Count();
        const NfCurrencyEntryPtr* ppData = rTable.GetData();
        for ( sal_uInt16 j = 0; j < nCount; j++, ppData++ )
        {
            if ( (*ppData)->GetLanguage() == eLang )
                return **ppData;
        }
        return *(rTable[0]);
    }
}


// static
const NfCurrencyEntry* SvNumberFormatter::GetCurrencyEntry(
        const String& rAbbrev, LanguageType eLang )
{
    eLang = MsLangId::getRealLanguage( eLang );
    const NfCurrencyTable& rTable = GetTheCurrencyTable();
    sal_uInt16 nCount = rTable.Count();
    const NfCurrencyEntryPtr* ppData = rTable.GetData();
    for ( sal_uInt16 j = 0; j < nCount; j++, ppData++ )
    {
        if ( (*ppData)->GetLanguage() == eLang &&
                (*ppData)->GetBankSymbol() == rAbbrev )
            return *ppData;
    }
    return NULL;
}


// static
const NfCurrencyEntry* SvNumberFormatter::GetLegacyOnlyCurrencyEntry(
        const String& rSymbol, const String& rAbbrev )
{
    if (!bCurrencyTableInitialized)
        GetTheCurrencyTable();      // just for initialization
    const NfCurrencyTable& rTable = theLegacyOnlyCurrencyTable::get();
    sal_uInt16 nCount = rTable.Count();
    const NfCurrencyEntryPtr* ppData = rTable.GetData();
    for ( sal_uInt16 j = 0; j < nCount; j++, ppData++ )
    {
        if ( (*ppData)->GetSymbol() == rSymbol &&
                (*ppData)->GetBankSymbol() == rAbbrev )
            return *ppData;
    }
    return NULL;
}


// static
IMPL_STATIC_LINK_NOINSTANCE( SvNumberFormatter, CurrencyChangeLink, void*, EMPTYARG )
{
    ::osl::MutexGuard aGuard( GetMutex() );
    String aAbbrev;
    LanguageType eLang = LANGUAGE_SYSTEM;
    SvtSysLocaleOptions().GetCurrencyAbbrevAndLanguage( aAbbrev, eLang );
    SetDefaultSystemCurrency( aAbbrev, eLang );
    return 0;
}


// static
void SvNumberFormatter::SetDefaultSystemCurrency( const String& rAbbrev, LanguageType eLang )
{
    ::osl::MutexGuard aGuard( GetMutex() );
    if ( eLang == LANGUAGE_SYSTEM )
        eLang = SvtSysLocale().GetLanguage();
    const NfCurrencyTable& rTable = GetTheCurrencyTable();
    sal_uInt16 nCount = rTable.Count();
    const NfCurrencyEntryPtr* ppData = rTable.GetData();
    if ( rAbbrev.Len() )
    {
        for ( sal_uInt16 j = 0; j < nCount; j++, ppData++ )
        {
            if ( (*ppData)->GetLanguage() == eLang && (*ppData)->GetBankSymbol() == rAbbrev )
            {
                nSystemCurrencyPosition = j;
                return ;
            }
        }
    }
    else
    {
        for ( sal_uInt16 j = 0; j < nCount; j++, ppData++ )
        {
            if ( (*ppData)->GetLanguage() == eLang )
            {
                nSystemCurrencyPosition = j;
                return ;
            }
        }
    }
    nSystemCurrencyPosition = 0;    // not found => simple SYSTEM
}


void SvNumberFormatter::ResetDefaultSystemCurrency()
{
    nDefaultSystemCurrencyFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
}


sal_uInt32 SvNumberFormatter::ImpGetDefaultSystemCurrencyFormat()
{
    if ( nDefaultSystemCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
    {
        xub_StrLen nCheck;
        short nType;
        NfWSStringsDtor aCurrList;
        sal_uInt16 nDefault = GetCurrencyFormatStrings( aCurrList,
            GetCurrencyEntry( LANGUAGE_SYSTEM ), sal_False );
        DBG_ASSERT( aCurrList.Count(), "where is the NewCurrency System standard format?!?" );
        // if already loaded or user defined nDefaultSystemCurrencyFormat
        // will be set to the right value
        PutEntry( *aCurrList.GetObject( nDefault ), nCheck, nType,
            nDefaultSystemCurrencyFormat, LANGUAGE_SYSTEM );
        DBG_ASSERT( nCheck == 0, "NewCurrency CheckError" );
        DBG_ASSERT( nDefaultSystemCurrencyFormat != NUMBERFORMAT_ENTRY_NOT_FOUND,
            "nDefaultSystemCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND" );
    }
    return nDefaultSystemCurrencyFormat;
}


sal_uInt32 SvNumberFormatter::ImpGetDefaultCurrencyFormat()
{
    sal_uInt32 CLOffset = ImpGetCLOffset( ActLnge );
    sal_uInt32 nDefaultCurrencyFormat =
        (sal_uInt32)(sal_uLong) aDefaultFormatKeys.Get( CLOffset + ZF_STANDARD_CURRENCY );
    if ( !nDefaultCurrencyFormat )
        nDefaultCurrencyFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
    if ( nDefaultCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
    {
        // look for a defined standard
        sal_uInt32 nStopKey = CLOffset + SV_COUNTRY_LANGUAGE_OFFSET;
        sal_uInt32 nKey;
        aFTable.Seek( CLOffset );
        while ( (nKey = aFTable.GetCurKey()) >= CLOffset && nKey < nStopKey )
        {
            const SvNumberformat* pEntry =
                (const SvNumberformat*) aFTable.GetCurObject();
            if ( pEntry->IsStandard() && (pEntry->GetType() & NUMBERFORMAT_CURRENCY) )
            {
                nDefaultCurrencyFormat = nKey;
                break;	// while
            }
            aFTable.Next();
        }

        if ( nDefaultCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
        {	// none found, create one
            xub_StrLen nCheck;
            short nType;
            NfWSStringsDtor aCurrList;
            sal_uInt16 nDefault = GetCurrencyFormatStrings( aCurrList,
                GetCurrencyEntry( ActLnge ), sal_False );
            DBG_ASSERT( aCurrList.Count(), "where is the NewCurrency standard format?" );
            if ( aCurrList.Count() )
            {
                // if already loaded or user defined nDefaultSystemCurrencyFormat
                // will be set to the right value
                PutEntry( *aCurrList.GetObject( nDefault ), nCheck, nType,
                    nDefaultCurrencyFormat, ActLnge );
                DBG_ASSERT( nCheck == 0, "NewCurrency CheckError" );
                DBG_ASSERT( nDefaultCurrencyFormat != NUMBERFORMAT_ENTRY_NOT_FOUND,
                    "nDefaultCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND" );
            }
            // old automatic currency format as a last resort
            if ( nDefaultCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
                nDefaultCurrencyFormat = CLOffset + ZF_STANDARD_CURRENCY+3;
            else
            {	// mark as standard so that it is found next time
                SvNumberformat* pEntry = aFTable.Get( nDefaultCurrencyFormat );
                if ( pEntry )
                    pEntry->SetStandard();
            }
        }
        sal_uIntPtr nFormat = nDefaultCurrencyFormat;
        aDefaultFormatKeys.Insert( CLOffset + ZF_STANDARD_CURRENCY,
            (void*) nFormat );
    }
    return nDefaultCurrencyFormat;
}


// static
// try to make it inline if possible since this a loop body
// sal_True: continue; sal_False: break loop, if pFoundEntry==NULL dupe found
#ifndef DBG_UTIL
inline
#endif
    sal_Bool SvNumberFormatter::ImpLookupCurrencyEntryLoopBody(
        const NfCurrencyEntry*& pFoundEntry, sal_Bool& bFoundBank,
        const NfCurrencyEntry* pData, sal_uInt16 nPos, const String& rSymbol )
{
    sal_Bool bFound;
    if ( pData->GetSymbol() == rSymbol )
    {
        bFound = sal_True;
        bFoundBank = sal_False;
    }
    else if ( pData->GetBankSymbol() == rSymbol )
    {
        bFound = sal_True;
        bFoundBank = sal_True;
    }
    else
        bFound = sal_False;
    if ( bFound )
    {
        if ( pFoundEntry && pFoundEntry != pData )
        {
            pFoundEntry = NULL;
            return sal_False;	// break loop, not unique
        }
        if ( nPos == 0 )
        {	// first entry is SYSTEM
            pFoundEntry = MatchSystemCurrency();
            if ( pFoundEntry )
                return sal_False;	// break loop
                // even if there are more matching entries
                // this one is propably the one we are looking for
            else
                pFoundEntry = pData;
        }
        else
            pFoundEntry = pData;
    }
    return sal_True;
}


sal_Bool SvNumberFormatter::GetNewCurrencySymbolString( sal_uInt32 nFormat,
            String& rStr, const NfCurrencyEntry** ppEntry /* = NULL */,
            sal_Bool* pBank /* = NULL */ ) const
{
    rStr.Erase();
    if ( ppEntry )
        *ppEntry = NULL;
    if ( pBank )
        *pBank = sal_False;
    SvNumberformat* pFormat = (SvNumberformat*) aFTable.Get( nFormat );
    if ( pFormat )
    {
        String aSymbol, aExtension;
        if ( pFormat->GetNewCurrencySymbol( aSymbol, aExtension ) )
        {
            if ( ppEntry )
            {
                sal_Bool bFoundBank = sal_False;
                // we definiteley need an entry matching the format code string
                const NfCurrencyEntry* pFoundEntry = GetCurrencyEntry(
                    bFoundBank, aSymbol, aExtension, pFormat->GetLanguage(),
                    sal_True );
                if ( pFoundEntry )
                {
                    *ppEntry = pFoundEntry;
                    if ( pBank )
                        *pBank = bFoundBank;
                    pFoundEntry->BuildSymbolString( rStr, bFoundBank );
                }
            }
            if ( !rStr.Len() )
            {	// analog zu BuildSymbolString
                rStr  = '[';
                rStr += '$';
                if ( aSymbol.Search( '-' ) != STRING_NOTFOUND ||
                        aSymbol.Search( ']' ) != STRING_NOTFOUND )
                {
                    rStr += '"';
                    rStr += aSymbol;
                    rStr += '"';
                }
                else
                    rStr += aSymbol;
                if ( aExtension.Len() )
                    rStr += aExtension;
                rStr += ']';
            }
            return sal_True;
        }
    }
    return sal_False;
}


// static
const NfCurrencyEntry* SvNumberFormatter::GetCurrencyEntry( sal_Bool & bFoundBank,
            const String& rSymbol, const String& rExtension,
            LanguageType eFormatLanguage, sal_Bool bOnlyStringLanguage )
{
    xub_StrLen nExtLen = rExtension.Len();
    LanguageType eExtLang;
    if ( nExtLen )
    {
        sal_Int32 nExtLang = ::rtl::OUString( rExtension ).toInt32( 16 );
        if ( !nExtLang )
            eExtLang = LANGUAGE_DONTKNOW;
        else
            eExtLang = (LanguageType) ((nExtLang < 0) ?
                -nExtLang : nExtLang);
    }
    else
        eExtLang = LANGUAGE_DONTKNOW;
    const NfCurrencyEntry* pFoundEntry = NULL;
    const NfCurrencyTable& rTable = GetTheCurrencyTable();
    sal_uInt16 nCount = rTable.Count();
    sal_Bool bCont = sal_True;

    // first try with given extension language/country
    if ( nExtLen )
    {
        const NfCurrencyEntryPtr* ppData = rTable.GetData();
        for ( sal_uInt16 j = 0; j < nCount && bCont; j++, ppData++ )
        {
            LanguageType eLang = (*ppData)->GetLanguage();
            if ( eLang == eExtLang ||
                    ((eExtLang == LANGUAGE_DONTKNOW) &&
                    (eLang == LANGUAGE_SYSTEM))
                )
            {
                bCont = ImpLookupCurrencyEntryLoopBody( pFoundEntry, bFoundBank,
                    *ppData, j, rSymbol );
            }
        }
    }

    // ok?
    if ( pFoundEntry || !bCont || (bOnlyStringLanguage && nExtLen) )
        return pFoundEntry;

    if ( !bOnlyStringLanguage )
    {
        // now try the language/country of the number format
        const NfCurrencyEntryPtr* ppData = rTable.GetData();
        for ( sal_uInt16 j = 0; j < nCount && bCont; j++, ppData++ )
        {
            LanguageType eLang = (*ppData)->GetLanguage();
            if ( eLang == eFormatLanguage ||
                    ((eFormatLanguage == LANGUAGE_DONTKNOW) &&
                    (eLang == LANGUAGE_SYSTEM))
                )
            {
                bCont = ImpLookupCurrencyEntryLoopBody( pFoundEntry, bFoundBank,
                    *ppData, j, rSymbol );
            }
        }

        // ok?
        if ( pFoundEntry || !bCont )
            return pFoundEntry;
    }

    // then try without language/country if no extension specified
    if ( !nExtLen )
    {
        const NfCurrencyEntryPtr* ppData = rTable.GetData();
        for ( sal_uInt16 j = 0; j < nCount && bCont; j++, ppData++ )
        {
            bCont = ImpLookupCurrencyEntryLoopBody( pFoundEntry, bFoundBank,
                *ppData, j, rSymbol );
        }
    }

    return pFoundEntry;
}


void SvNumberFormatter::GetCompatibilityCurrency( String& rSymbol, String& rAbbrev ) const
{
    ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Currency2 >
        xCurrencies( xLocaleData->getAllCurrencies() );

    const ::com::sun::star::i18n::Currency2 *pCurrencies = xCurrencies.getConstArray();
    sal_Int32 nCurrencies = xCurrencies.getLength();

    sal_Int32 j;
    for ( j=0; j < nCurrencies; ++j )
    {
        if ( pCurrencies[j].UsedInCompatibleFormatCodes )
        {
            rSymbol = pCurrencies[j].Symbol;
            rAbbrev = pCurrencies[j].BankSymbol;
            break;
        }
    }
    if ( j >= nCurrencies )
    {
        if (LocaleDataWrapper::areChecksEnabled())
        {
            String aMsg( RTL_CONSTASCII_USTRINGPARAM(
                        "GetCompatibilityCurrency: none?"));
            LocaleDataWrapper::outputCheckMessage(
                    xLocaleData->appendLocaleInfo( aMsg));
        }
        rSymbol = xLocaleData->getCurrSymbol();
        rAbbrev = xLocaleData->getCurrBankSymbol();
    }
}


void lcl_CheckCurrencySymbolPosition( const NfCurrencyEntry& rCurr )
{
    short nPos = -1;		// -1:=unknown, 0:=vorne, 1:=hinten
    short nNeg = -1;
    switch ( rCurr.GetPositiveFormat() )
    {
        case 0:                                        	// $1
            nPos = 0;
        break;
        case 1:											// 1$
            nPos = 1;
        break;
        case 2:											// $ 1
            nPos = 0;
        break;
        case 3:											// 1 $
            nPos = 1;
        break;
        default:
            LocaleDataWrapper::outputCheckMessage(
                    "lcl_CheckCurrencySymbolPosition: unknown PositiveFormat");
        break;
    }
    switch ( rCurr.GetNegativeFormat() )
    {
        case 0:                                        	// ($1)
            nNeg = 0;
        break;
        case 1:                                        	// -$1
            nNeg = 0;
        break;
        case 2:                                        	// $-1
            nNeg = 0;
        break;
        case 3:                                        	// $1-
            nNeg = 0;
        break;
        case 4:                                        	// (1$)
            nNeg = 1;
        break;
        case 5:                                        	// -1$
            nNeg = 1;
        break;
        case 6:                                        	// 1-$
            nNeg = 1;
        break;
        case 7:                                        	// 1$-
            nNeg = 1;
        break;
        case 8:                                        	// -1 $
            nNeg = 1;
        break;
        case 9:                                        	// -$ 1
            nNeg = 0;
        break;
        case 10:                                        // 1 $-
            nNeg = 1;
        break;
        case 11:                                        // $ -1
            nNeg = 0;
        break;
        case 12 : 										// $ 1-
            nNeg = 0;
        break;
        case 13 : 										// 1- $
            nNeg = 1;
        break;
        case 14 : 										// ($ 1)
            nNeg = 0;
        break;
        case 15 :										// (1 $)
            nNeg = 1;
        break;
        default:
            LocaleDataWrapper::outputCheckMessage(
                    "lcl_CheckCurrencySymbolPosition: unknown NegativeFormat");
        break;
    }
    if ( nPos >= 0 && nNeg >= 0 && nPos != nNeg )
    {
        ByteString aStr( "positions of currency symbols differ\nLanguage: " );
        aStr += ByteString::CreateFromInt32( rCurr.GetLanguage() );
        aStr += " <";
        aStr += ByteString( rCurr.GetSymbol(), RTL_TEXTENCODING_UTF8 );
        aStr += "> positive: ";
        aStr += ByteString::CreateFromInt32( rCurr.GetPositiveFormat() );
        aStr += ( nPos ? " (postfix)" : " (prefix)" );
        aStr += ", negative: ";
        aStr += ByteString::CreateFromInt32( rCurr.GetNegativeFormat() );
        aStr += ( nNeg ? " (postfix)" : " (prefix)" );
#if 0
// seems that there really are some currencies which differ, e.g. YugoDinar
        DBG_ERRORFILE( aStr.GetBuffer() );
#endif
    }
}


// static
void SvNumberFormatter::ImpInitCurrencyTable()
{
    // racing condition possible:
    // ::osl::MutexGuard aGuard( GetMutex() );
    // while ( !bCurrencyTableInitialized )
    // 		ImpInitCurrencyTable();
    static sal_Bool bInitializing = sal_False;
    if ( bCurrencyTableInitialized || bInitializing )
        return ;
    bInitializing = sal_True;

    RTL_LOGFILE_CONTEXT_AUTHOR( aTimeLog, "svl", "er93726", "SvNumberFormatter::ImpInitCurrencyTable" );

    LanguageType eSysLang = SvtSysLocale().GetLanguage();
    LocaleDataWrapper* pLocaleData = new LocaleDataWrapper(
        ::comphelper::getProcessServiceFactory(),
        MsLangId::convertLanguageToLocale( eSysLang ) );
    // get user configured currency
    String aConfiguredCurrencyAbbrev;
    LanguageType eConfiguredCurrencyLanguage = LANGUAGE_SYSTEM;
    SvtSysLocaleOptions().GetCurrencyAbbrevAndLanguage(
        aConfiguredCurrencyAbbrev, eConfiguredCurrencyLanguage );
    sal_uInt16 nSecondarySystemCurrencyPosition = 0;
    sal_uInt16 nMatchingSystemCurrencyPosition = 0;
    NfCurrencyEntryPtr pEntry;

    // first entry is SYSTEM
    pEntry = new NfCurrencyEntry( *pLocaleData, LANGUAGE_SYSTEM );
    theCurrencyTable::get().Insert( pEntry, 0 );
    sal_uInt16 nCurrencyPos = 1;

    ::com::sun::star::uno::Sequence< ::com::sun::star::lang::Locale > xLoc =
        LocaleDataWrapper::getInstalledLocaleNames();
    sal_Int32 nLocaleCount = xLoc.getLength();
    RTL_LOGFILE_CONTEXT_TRACE1( aTimeLog, "number of locales: %ld", nLocaleCount );
    Locale const * const pLocales = xLoc.getConstArray();
    NfCurrencyTable &rCurrencyTable = theCurrencyTable::get();
    NfCurrencyTable &rLegacyOnlyCurrencyTable = theLegacyOnlyCurrencyTable::get();
    sal_uInt16 nLegacyOnlyCurrencyPos = 0;
    for ( sal_Int32 nLocale = 0; nLocale < nLocaleCount; nLocale++ )
    {
        LanguageType eLang = MsLangId::convertLocaleToLanguage(
                pLocales[nLocale]);
        theInstalledLocales.insert( eLang);
        pLocaleData->setLocale( pLocales[nLocale] );
        Sequence< Currency2 > aCurrSeq = pLocaleData->getAllCurrencies();
        sal_Int32 nCurrencyCount = aCurrSeq.getLength();
        Currency2 const * const pCurrencies = aCurrSeq.getConstArray();

        // one default currency for each locale, insert first so it is found first
        sal_Int32 nDefault;
        for ( nDefault = 0; nDefault < nCurrencyCount; nDefault++ )
        {
            if ( pCurrencies[nDefault].Default )
                break;
        }
        if ( nDefault < nCurrencyCount )
            pEntry = new NfCurrencyEntry( pCurrencies[nDefault], *pLocaleData, eLang );
        else
            pEntry = new NfCurrencyEntry( *pLocaleData, eLang );    // first or ShellsAndPebbles

        if (LocaleDataWrapper::areChecksEnabled())
            lcl_CheckCurrencySymbolPosition( *pEntry );

        rCurrencyTable.Insert( pEntry, nCurrencyPos++ );
        if ( !nSystemCurrencyPosition && (aConfiguredCurrencyAbbrev.Len() ?
                pEntry->GetBankSymbol() == aConfiguredCurrencyAbbrev &&
                pEntry->GetLanguage() == eConfiguredCurrencyLanguage : sal_False) )
            nSystemCurrencyPosition = nCurrencyPos-1;
        if ( !nMatchingSystemCurrencyPosition &&
                pEntry->GetLanguage() == eSysLang )
            nMatchingSystemCurrencyPosition = nCurrencyPos-1;

        // all remaining currencies for each locale
        if ( nCurrencyCount > 1 )
        {
            sal_Int32 nCurrency;
            for ( nCurrency = 0; nCurrency < nCurrencyCount; nCurrency++ )
            {
                if (pCurrencies[nCurrency].LegacyOnly)
                {
                    pEntry = new NfCurrencyEntry( pCurrencies[nCurrency], *pLocaleData, eLang );
                    rLegacyOnlyCurrencyTable.Insert( pEntry, nLegacyOnlyCurrencyPos++ );
                }
                else if ( nCurrency != nDefault )
                {
                    pEntry = new NfCurrencyEntry( pCurrencies[nCurrency], *pLocaleData, eLang );
                    // no dupes
                    sal_Bool bInsert = sal_True;
                    NfCurrencyEntry const * const * pData = rCurrencyTable.GetData();
                    sal_uInt16 n = rCurrencyTable.Count();
                    pData++;        // skip first SYSTEM entry
                    for ( sal_uInt16 j=1; j<n; j++ )
                    {
                        if ( *(*pData++) == *pEntry )
                        {
                            bInsert = sal_False;
                            break;  // for
                        }
                    }
                    if ( !bInsert )
                        delete pEntry;
                    else
                    {
                        rCurrencyTable.Insert( pEntry, nCurrencyPos++ );
                        if ( !nSecondarySystemCurrencyPosition &&
                                (aConfiguredCurrencyAbbrev.Len() ?
                                pEntry->GetBankSymbol() == aConfiguredCurrencyAbbrev :
                                pEntry->GetLanguage() == eConfiguredCurrencyLanguage) )
                            nSecondarySystemCurrencyPosition = nCurrencyPos-1;
                        if ( !nMatchingSystemCurrencyPosition &&
                                pEntry->GetLanguage() ==  eSysLang )
                            nMatchingSystemCurrencyPosition = nCurrencyPos-1;
                    }
                }
            }
        }
    }
    if ( !nSystemCurrencyPosition )
        nSystemCurrencyPosition = nSecondarySystemCurrencyPosition;
    if ((aConfiguredCurrencyAbbrev.Len() && !nSystemCurrencyPosition) &&
            LocaleDataWrapper::areChecksEnabled())
        LocaleDataWrapper::outputCheckMessage(
                "SvNumberFormatter::ImpInitCurrencyTable: configured currency not in I18N locale data.");
    // match SYSTEM if no configured currency found
    if ( !nSystemCurrencyPosition )
        nSystemCurrencyPosition = nMatchingSystemCurrencyPosition;
    if ((!aConfiguredCurrencyAbbrev.Len() && !nSystemCurrencyPosition) &&
            LocaleDataWrapper::areChecksEnabled())
        LocaleDataWrapper::outputCheckMessage(
                "SvNumberFormatter::ImpInitCurrencyTable: system currency not in I18N locale data.");
    delete pLocaleData;
    SvtSysLocaleOptions::SetCurrencyChangeLink(
        STATIC_LINK( NULL, SvNumberFormatter, CurrencyChangeLink ) );
    bInitializing = sal_False;
    bCurrencyTableInitialized = sal_True;
}


sal_uInt16 SvNumberFormatter::GetCurrencyFormatStrings( NfWSStringsDtor& rStrArr,
            const NfCurrencyEntry& rCurr, sal_Bool bBank ) const
{
    sal_uInt16 nDefault = 0;
    if ( bBank )
    {	// nur Bankensymbole
        String aPositiveBank, aNegativeBank;
        rCurr.BuildPositiveFormatString( aPositiveBank, sal_True, *xLocaleData, 1 );
        rCurr.BuildNegativeFormatString( aNegativeBank, sal_True, *xLocaleData, 1 );

        WSStringPtr pFormat1 = new String( aPositiveBank );
        *pFormat1 += ';';
        WSStringPtr pFormat2 = new String( *pFormat1 );

        String aRed( '[' );
        aRed += pFormatScanner->GetRedString();
        aRed += ']';

        *pFormat2 += aRed;

        *pFormat1 += aNegativeBank;
        *pFormat2 += aNegativeBank;

        rStrArr.Insert( pFormat1, rStrArr.Count() );
        rStrArr.Insert( pFormat2, rStrArr.Count() );
        nDefault = rStrArr.Count() - 1;
    }
    else
    {	// gemischte Formate wie in SvNumberFormatter::ImpGenerateFormats
        // aber keine doppelten, wenn keine Nachkommastellen in Waehrung
        String aPositive, aNegative, aPositiveNoDec, aNegativeNoDec,
            aPositiveDashed, aNegativeDashed;
        WSStringPtr pFormat1, pFormat2, pFormat3, pFormat4, pFormat5;

        String aRed( '[' );
        aRed += pFormatScanner->GetRedString();
        aRed += ']';

        rCurr.BuildPositiveFormatString( aPositive, sal_False, *xLocaleData, 1 );
        rCurr.BuildNegativeFormatString( aNegative, sal_False, *xLocaleData, 1 );
        if ( rCurr.GetDigits() )
        {
            rCurr.BuildPositiveFormatString( aPositiveNoDec, sal_False, *xLocaleData, 0 );
            rCurr.BuildNegativeFormatString( aNegativeNoDec, sal_False, *xLocaleData, 0 );
            rCurr.BuildPositiveFormatString( aPositiveDashed, sal_False, *xLocaleData, 2 );
            rCurr.BuildNegativeFormatString( aNegativeDashed, sal_False, *xLocaleData, 2 );

            pFormat1 = new String( aPositiveNoDec );
            *pFormat1 += ';';
            pFormat3 = new String( *pFormat1 );
            pFormat5 = new String( aPositiveDashed );
            *pFormat5 += ';';

            *pFormat1 += aNegativeNoDec;

            *pFormat3 += aRed;
            *pFormat5 += aRed;

            *pFormat3 += aNegativeNoDec;
            *pFormat5 += aNegativeDashed;
        }
        else
        {
            pFormat1 = NULL;
            pFormat3 = NULL;
            pFormat5 = NULL;
        }

        pFormat2 = new String( aPositive );
        *pFormat2 += ';';
        pFormat4 = new String( *pFormat2 );

        *pFormat2 += aNegative;

        *pFormat4 += aRed;
        *pFormat4 += aNegative;

        if ( pFormat1 )
            rStrArr.Insert( pFormat1, rStrArr.Count() );
        rStrArr.Insert( pFormat2, rStrArr.Count() );
        if ( pFormat3 )
            rStrArr.Insert( pFormat3, rStrArr.Count() );
        rStrArr.Insert( pFormat4, rStrArr.Count() );
        nDefault = rStrArr.Count() - 1;
        if ( pFormat5 )
            rStrArr.Insert( pFormat5, rStrArr.Count() );
    }
    return nDefault;
}


//--- NfCurrencyEntry ----------------------------------------------------

NfCurrencyEntry::NfCurrencyEntry()
    :	eLanguage( LANGUAGE_DONTKNOW ),
        nPositiveFormat(3),
        nNegativeFormat(8),
        nDigits(2),
        cZeroChar('0')
{
}


NfCurrencyEntry::NfCurrencyEntry( const LocaleDataWrapper& rLocaleData, LanguageType eLang )
{
    aSymbol			= rLocaleData.getCurrSymbol();
    aBankSymbol		= rLocaleData.getCurrBankSymbol();
    eLanguage		= eLang;
    nPositiveFormat	= rLocaleData.getCurrPositiveFormat();
    nNegativeFormat	= rLocaleData.getCurrNegativeFormat();
    nDigits			= rLocaleData.getCurrDigits();
    cZeroChar		= rLocaleData.getCurrZeroChar();
}


NfCurrencyEntry::NfCurrencyEntry( const ::com::sun::star::i18n::Currency & rCurr,
            const LocaleDataWrapper& rLocaleData, LanguageType eLang )
{
    aSymbol			= rCurr.Symbol;
    aBankSymbol		= rCurr.BankSymbol;
    eLanguage		= eLang;
    nPositiveFormat	= rLocaleData.getCurrPositiveFormat();
    nNegativeFormat	= rLocaleData.getCurrNegativeFormat();
    nDigits         = rCurr.DecimalPlaces;
    cZeroChar		= rLocaleData.getCurrZeroChar();
}


sal_Bool NfCurrencyEntry::operator==( const NfCurrencyEntry& r ) const
{
    return aSymbol		== r.aSymbol
        && aBankSymbol	== r.aBankSymbol
        && eLanguage	== r.eLanguage
        ;
}


void NfCurrencyEntry::SetEuro()
{
    aSymbol = NfCurrencyEntry::GetEuroSymbol();
    aBankSymbol.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "EUR" ) );
    eLanguage		= LANGUAGE_DONTKNOW;
    nPositiveFormat	= 3;
    nNegativeFormat	= 8;
    nDigits			= 2;
    cZeroChar		= '0';
}


sal_Bool NfCurrencyEntry::IsEuro() const
{
    if ( aBankSymbol.EqualsAscii( "EUR" ) )
        return sal_True;
    String aEuro( NfCurrencyEntry::GetEuroSymbol() );
    return aSymbol == aEuro;
}


void NfCurrencyEntry::ApplyVariableInformation( const NfCurrencyEntry& r )
{
    nPositiveFormat	= r.nPositiveFormat;
    nNegativeFormat	= r.nNegativeFormat;
    cZeroChar		= r.cZeroChar;
}


void NfCurrencyEntry::BuildSymbolString( String& rStr, sal_Bool bBank,
            sal_Bool bWithoutExtension ) const
{
    rStr  = '[';
    rStr += '$';
    if ( bBank )
        rStr += aBankSymbol;
    else
    {
        if ( aSymbol.Search( '-' ) != STRING_NOTFOUND || aSymbol.Search( ']' ) != STRING_NOTFOUND )
        {
            rStr += '"';
            rStr += aSymbol;
            rStr += '"';
        }
        else
            rStr += aSymbol;
        if ( !bWithoutExtension && eLanguage != LANGUAGE_DONTKNOW && eLanguage != LANGUAGE_SYSTEM )
        {
            rStr += '-';
            rStr += String::CreateFromInt32( sal_Int32( eLanguage ), 16 ).ToUpperAscii();
        }
    }
    rStr += ']';
}


void NfCurrencyEntry::Impl_BuildFormatStringNumChars( String& rStr,
            const LocaleDataWrapper& rLoc, sal_uInt16 nDecimalFormat ) const
{
    rStr.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "###0" ) );
    rStr.Insert( rLoc.getNumThousandSep(), 1 );
    if ( nDecimalFormat && nDigits )
    {
        rStr += rLoc.getNumDecimalSep();
        rStr.Expand( rStr.Len() + nDigits, (nDecimalFormat == 2 ? '-' : cZeroChar) );
    }
}


void NfCurrencyEntry::BuildPositiveFormatString( String& rStr, sal_Bool bBank,
            const LocaleDataWrapper& rLoc, sal_uInt16 nDecimalFormat ) const
{
    Impl_BuildFormatStringNumChars( rStr, rLoc, nDecimalFormat );
    sal_uInt16 nPosiForm = NfCurrencyEntry::GetEffectivePositiveFormat(
        rLoc.getCurrPositiveFormat(), nPositiveFormat, bBank );
    CompletePositiveFormatString( rStr, bBank, nPosiForm );
}


void NfCurrencyEntry::BuildNegativeFormatString( String& rStr, sal_Bool bBank,
            const LocaleDataWrapper& rLoc, sal_uInt16 nDecimalFormat ) const
{
    Impl_BuildFormatStringNumChars( rStr, rLoc, nDecimalFormat );
    sal_uInt16 nNegaForm = NfCurrencyEntry::GetEffectiveNegativeFormat(
        rLoc.getCurrNegativeFormat(), nNegativeFormat, bBank );
    CompleteNegativeFormatString( rStr, bBank, nNegaForm );
}


void NfCurrencyEntry::CompletePositiveFormatString( String& rStr, sal_Bool bBank,
            sal_uInt16 nPosiForm ) const
{
    String aSymStr;
    BuildSymbolString( aSymStr, bBank );
    NfCurrencyEntry::CompletePositiveFormatString( rStr, aSymStr, nPosiForm );
}


void NfCurrencyEntry::CompleteNegativeFormatString( String& rStr, sal_Bool bBank,
            sal_uInt16 nNegaForm ) const
{
    String aSymStr;
    BuildSymbolString( aSymStr, bBank );
    NfCurrencyEntry::CompleteNegativeFormatString( rStr, aSymStr, nNegaForm );
}


// static
void NfCurrencyEntry::CompletePositiveFormatString( String& rStr,
        const String& rSymStr, sal_uInt16 nPositiveFormat )
{
    switch( nPositiveFormat )
    {
        case 0:                                        	// $1
            rStr.Insert( rSymStr , 0 );
        break;
        case 1:											// 1$
            rStr += rSymStr;
        break;
        case 2:											// $ 1
        {
            rStr.Insert( ' ', 0 );
            rStr.Insert( rSymStr, 0 );
        }
        break;
        case 3:                                         // 1 $
        {
            rStr += ' ';
            rStr += rSymStr;
        }
        break;
        default:
            OSL_FAIL("NfCurrencyEntry::CompletePositiveFormatString: unknown option");
        break;
    }
}


// static
void NfCurrencyEntry::CompleteNegativeFormatString( String& rStr,
        const String& rSymStr, sal_uInt16 nNegativeFormat )
{
    switch( nNegativeFormat )
    {
        case 0:                                        	// ($1)
        {
            rStr.Insert( rSymStr, 0);
            rStr.Insert('(',0);
            rStr += ')';
        }
        break;
        case 1:                                        	// -$1
        {
            rStr.Insert( rSymStr, 0);
            rStr.Insert('-',0);
        }
        break;
        case 2:                                        	// $-1
        {
            rStr.Insert('-',0);
            rStr.Insert( rSymStr, 0);
        }
        break;
        case 3:                                        	// $1-
        {
            rStr.Insert( rSymStr, 0);
            rStr += '-';
        }
        break;
        case 4:                                        	// (1$)
        {
            rStr.Insert('(',0);
            rStr += rSymStr;
            rStr += ')';
        }
        break;
        case 5:                                        	// -1$
        {
            rStr += rSymStr;
            rStr.Insert('-',0);
        }
        break;
        case 6:                                        	// 1-$
        {
            rStr += '-';
            rStr += rSymStr;
        }
        break;
        case 7:                                        	// 1$-
        {
            rStr += rSymStr;
            rStr += '-';
        }
        break;
        case 8:                                        	// -1 $
        {
            rStr += ' ';
            rStr += rSymStr;
            rStr.Insert('-',0);
        }
        break;
        case 9:                                        	// -$ 1
        {
            rStr.Insert(' ',0);
            rStr.Insert( rSymStr, 0);
            rStr.Insert('-',0);
        }
        break;
        case 10:                                        // 1 $-
        {
            rStr += ' ';
            rStr += rSymStr;
            rStr += '-';
        }
        break;
        case 11:                                        // $ -1
        {
            String aTmp( rSymStr );
            aTmp += ' ';
            aTmp += '-';
            rStr.Insert( aTmp, 0 );
        }
        break;
        case 12 : 										// $ 1-
        {
            rStr.Insert(' ', 0);
            rStr.Insert( rSymStr, 0);
            rStr += '-';
        }
        break;
        case 13 : 										// 1- $
        {
            rStr += '-';
            rStr += ' ';
            rStr += rSymStr;
        }
        break;
        case 14 : 										// ($ 1)
        {
            rStr.Insert(' ',0);
            rStr.Insert( rSymStr, 0);
            rStr.Insert('(',0);
            rStr += ')';
        }
        break;
        case 15 :										// (1 $)
        {
            rStr.Insert('(',0);
            rStr += ' ';
            rStr += rSymStr;
            rStr += ')';
        }
        break;
        default:
            OSL_FAIL("NfCurrencyEntry::CompleteNegativeFormatString: unknown option");
        break;
    }
}


// static
sal_uInt16 NfCurrencyEntry::GetEffectivePositiveFormat( sal_uInt16
#if ! NF_BANKSYMBOL_FIX_POSITION
            nIntlFormat
#endif
            , sal_uInt16 nCurrFormat, sal_Bool bBank )
{
    if ( bBank )
    {
#if NF_BANKSYMBOL_FIX_POSITION
        return 3;
#else
        switch ( nIntlFormat )
        {
            case 0:                                        	// $1
                nIntlFormat = 2;                            // $ 1
            break;
            case 1:											// 1$
                nIntlFormat = 3;                            // 1 $
            break;
            case 2:											// $ 1
            break;
            case 3:                                         // 1 $
            break;
            default:
                OSL_FAIL("NfCurrencyEntry::GetEffectivePositiveFormat: unknown option");
            break;
        }
        return nIntlFormat;
#endif
    }
    else
        return nCurrFormat;
}


// nur aufrufen, wenn nCurrFormat wirklich mit Klammern ist
sal_uInt16 lcl_MergeNegativeParenthesisFormat( sal_uInt16 nIntlFormat, sal_uInt16 nCurrFormat )
{
    short nSign = 0;		// -1:=Klammer 0:=links, 1:=mitte, 2:=rechts
    switch ( nIntlFormat )
    {
        case 0:                                        	// ($1)
        case 4:                                        	// (1$)
        case 14 : 										// ($ 1)
        case 15 :										// (1 $)
            return nCurrFormat;
        case 1:                                        	// -$1
        case 5:                                        	// -1$
        case 8:                                        	// -1 $
        case 9:                                        	// -$ 1
            nSign = 0;
        break;
        case 2:                                        	// $-1
        case 6:                                        	// 1-$
        case 11 : 										// $ -1
        case 13 : 										// 1- $
            nSign = 1;
        break;
        case 3:                                        	// $1-
        case 7:                                        	// 1$-
        case 10:                                        // 1 $-
        case 12 : 										// $ 1-
            nSign = 2;
        break;
        default:
            OSL_FAIL("lcl_MergeNegativeParenthesisFormat: unknown option");
        break;
    }

    switch ( nCurrFormat )
    {
        case 0:                                        	// ($1)
            switch ( nSign )
            {
                case 0:
                    return 1;                           // -$1
                case 1:
                    return 2;                           // $-1
                case 2:
                    return 3;                           // $1-
            }
        break;
        case 4:                                        	// (1$)
            switch ( nSign )
            {
                case 0:
                    return 5;                           // -1$
                case 1:
                    return 6;                           // 1-$
                case 2:
                    return 7;                           // 1$-
            }
        break;
        case 14 : 										// ($ 1)
            switch ( nSign )
            {
                case 0:
                    return 9;                           // -$ 1
                case 1:
                    return 11;                          // $ -1
                case 2:
                    return 12;                          // $ 1-
            }
        break;
        case 15 :										// (1 $)
            switch ( nSign )
            {
                case 0:
                    return 8;                           // -1 $
                case 1:
                    return 13;                          // 1- $
                case 2:
                    return 10;                          // 1 $-
            }
        break;
    }
    return nCurrFormat;
}


// static
sal_uInt16 NfCurrencyEntry::GetEffectiveNegativeFormat( sal_uInt16 nIntlFormat,
            sal_uInt16 nCurrFormat, sal_Bool bBank )
{
    if ( bBank )
    {
#if NF_BANKSYMBOL_FIX_POSITION
        return 8;
#else
        switch ( nIntlFormat )
        {
            case 0:                                        	// ($1)
//				nIntlFormat = 14;                           // ($ 1)
                nIntlFormat = 9;                            // -$ 1
            break;
            case 1:                                        	// -$1
                nIntlFormat = 9;                            // -$ 1
            break;
            case 2:                                        	// $-1
                nIntlFormat = 11;                           // $ -1
            break;
            case 3:                                        	// $1-
                nIntlFormat = 12;                           // $ 1-
            break;
            case 4:                                        	// (1$)
//				nIntlFormat = 15;                           // (1 $)
                nIntlFormat = 8;                            // -1 $
            break;
            case 5:                                        	// -1$
                nIntlFormat = 8;                            // -1 $
            break;
            case 6:                                        	// 1-$
                nIntlFormat = 13;                           // 1- $
            break;
            case 7:                                        	// 1$-
                nIntlFormat = 10;                           // 1 $-
            break;
            case 8:                                        	// -1 $
            break;
            case 9:                                        	// -$ 1
            break;
            case 10:                                        // 1 $-
            break;
            case 11:                                        // $ -1
            break;
            case 12 : 										// $ 1-
            break;
            case 13 : 										// 1- $
            break;
            case 14 : 										// ($ 1)
//				nIntlFormat = 14;                           // ($ 1)
                nIntlFormat = 9;                            // -$ 1
            break;
            case 15 :										// (1 $)
//				nIntlFormat = 15;                           // (1 $)
                nIntlFormat = 8;                            // -1 $
            break;
            default:
                OSL_FAIL("NfCurrencyEntry::GetEffectiveNegativeFormat: unknown option");
            break;
        }
#endif
    }
    else if ( nIntlFormat != nCurrFormat )
    {
        switch ( nCurrFormat )
        {
            case 0:                                        	// ($1)
                nIntlFormat = lcl_MergeNegativeParenthesisFormat(
                    nIntlFormat, nCurrFormat );
            break;
            case 1:                                        	// -$1
                nIntlFormat = nCurrFormat;
            break;
            case 2:                                        	// $-1
                nIntlFormat = nCurrFormat;
            break;
            case 3:                                        	// $1-
                nIntlFormat = nCurrFormat;
            break;
            case 4:                                        	// (1$)
                nIntlFormat = lcl_MergeNegativeParenthesisFormat(
                    nIntlFormat, nCurrFormat );
            break;
            case 5:                                        	// -1$
                nIntlFormat = nCurrFormat;
            break;
            case 6:                                        	// 1-$
                nIntlFormat = nCurrFormat;
            break;
            case 7:                                        	// 1$-
                nIntlFormat = nCurrFormat;
            break;
            case 8:                                        	// -1 $
                nIntlFormat = nCurrFormat;
            break;
            case 9:                                        	// -$ 1
                nIntlFormat = nCurrFormat;
            break;
            case 10:                                        // 1 $-
                nIntlFormat = nCurrFormat;
            break;
            case 11:                                        // $ -1
                nIntlFormat = nCurrFormat;
            break;
            case 12 : 										// $ 1-
                nIntlFormat = nCurrFormat;
            break;
            case 13 : 										// 1- $
                nIntlFormat = nCurrFormat;
            break;
            case 14 : 										// ($ 1)
                nIntlFormat = lcl_MergeNegativeParenthesisFormat(
                    nIntlFormat, nCurrFormat );
            break;
            case 15 :										// (1 $)
                nIntlFormat = lcl_MergeNegativeParenthesisFormat(
                    nIntlFormat, nCurrFormat );
            break;
            default:
                OSL_FAIL("NfCurrencyEntry::GetEffectiveNegativeFormat: unknown option");
            break;
        }
    }
    return nIntlFormat;
}


// we only support default encodings here
// static
sal_Char NfCurrencyEntry::GetEuroSymbol( rtl_TextEncoding eTextEncoding )
{
    switch ( eTextEncoding )
    {
        case RTL_TEXTENCODING_MS_1252 :			// WNT Ansi
        case RTL_TEXTENCODING_ISO_8859_1 :		// UNX for use with TrueType fonts
            return '\x80';
        case RTL_TEXTENCODING_ISO_8859_15 :		// UNX real
            return '\xA4';
        case RTL_TEXTENCODING_IBM_850 :			// OS2
            return '\xD5';
        case RTL_TEXTENCODING_APPLE_ROMAN :		// MAC
            return '\xDB';
        default:								// default system
#if WNT
            return '\x80';
#elif OS2
            return '\xD5';
#elif UNX
//			return '\xA4';		// #56121# 0xA4 waere korrekt fuer iso-8859-15
            return '\x80';		// aber Windoze-Code fuer die konvertierten TrueType-Fonts
#else
#error EuroSymbol is what?
            return '\x80';
#endif
    }
    return '\x80';
}



/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
