/*************************************************************************
 *
 *  $RCSfile: zforlist.hxx,v $
 *
 *  $Revision: 1.26 $
 *
 *  last change: $Author: er $ $Date: 2001/08/07 16:06:12 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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 for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _ZFORLIST_HXX
#define _ZFORLIST_HXX

#ifndef _STRING_HXX //autogen
#include <tools/string.hxx>
#endif
#ifndef _TABLE_HXX //autogen
#include <tools/table.hxx>
#endif
#ifndef _LANG_HXX //autogen
#include <tools/lang.hxx>
#endif
#ifndef _TOOLS_INTN_HXX //autogen
#include <tools/intn.hxx>
#endif
#ifndef _SVARRAY_HXX //autogen
#include <svtools/svarray.hxx>
#endif
#ifndef _COM_SUN_STAR_UNO_REFERENCE_HXX_
#include <com/sun/star/uno/Reference.hxx>
#endif
#ifndef _COM_SUN_STAR_LANG_LOCALE_HPP_
#include <com/sun/star/lang/Locale.hpp>
#endif
#ifndef _COM_SUN_STAR_I18N_NUMBERFORMATCODE_HPP_
#include <com/sun/star/i18n/NumberFormatCode.hpp>
#endif
#ifndef _UNOTOOLS_LOCALEDATAWRAPPER_HXX
#include <unotools/localedatawrapper.hxx>
#endif
#ifndef INCLUDED_SVTOOLS_ONDEMAND_HXX
#include <svtools/ondemand.hxx>
#endif
#ifndef _LINK_HXX
#include <tools/link.hxx>
#endif

class Date;
class SvStream;
class Color;
class SvUShorts;
class CharClass;
class CalendarWrapper;

class ImpSvNumberformatScan;
class ImpSvNumberInputScan;
class SvNumberformat;

namespace com { namespace sun { namespace star {
	namespace lang {
		class XMultiServiceFactory;
	}
}}}

#define SV_COUNTRY_LANGUAGE_OFFSET  5000	// Max count of formats per country/language
#define SV_MAX_ANZ_STANDARD_FORMATE  100	// Max count of builtin default formats per CL

// Format types
#ifndef NUMBERFORMAT_ALL
//	also defined in com/sun/star/util/NumberFormat.hpp
//!	=> put in single .idl file and include here
#define NUMBERFORMAT_ALL			 0x000	/// Just for Output of total list, not a real format type
#define NUMBERFORMAT_DEFINED		 0x001	/// Format defined by user
#define NUMBERFORMAT_DATE			 0x002	/// Number as date
#define NUMBERFORMAT_TIME			 0x004	/// Number as time
#define NUMBERFORMAT_CURRENCY		 0x008	/// Number as currency
#define NUMBERFORMAT_NUMBER			 0x010	/// Any "normal" number format
#define NUMBERFORMAT_SCIENTIFIC		 0x020	/// Number as scientific
#define NUMBERFORMAT_FRACTION		 0x040	/// Number as fraction
#define NUMBERFORMAT_PERCENT		 0x080	/// Number as percent
#define NUMBERFORMAT_TEXT			 0x100	/// Text format
#define NUMBERFORMAT_DATETIME		 0x006	/// Number as date and time
#define NUMBERFORMAT_LOGICAL		 0x400	/// Number as boolean value
#define NUMBERFORMAT_UNDEFINED		 0x800	/// Format undefined yet in analyzing
#endif
#define NUMBERFORMAT_ENTRY_NOT_FOUND 0xffffffff	/// MAX_ULONG

/** enum values for <method>SvNumberFormatter::GetFormatIndex</method>

	<p>
 	Builtin standard formats, order should be also the arrangement in the
	dialog list box representation.</p>

	<p>
 	Date specials:<ul>
	<li>SYSTEM: As set in System Regional Settings.
	<li>SYS: short/long defined, order and separators from System Regional Settings.
	<li>DEF: short/long and order defined, separators from System Regional Settings.
	<li>DIN: all settings hard coded as DIN (Deutsche Industrie Norm) and EN (European Norm) require.
	<li>all other: hard coded
	</ul>
 */
enum NfIndexTableOffset
{
	NF_NUMERIC_START = 0,

	NF_NUMBER_START = NF_NUMERIC_START,
	NF_NUMBER_STANDARD = NF_NUMBER_START,	// Standard/General
	NF_NUMBER_INT,							// 0
	NF_NUMBER_DEC2,							// 0.00
	NF_NUMBER_1000INT,						// #,##0
	NF_NUMBER_1000DEC2,						// #,##0.00
	NF_NUMBER_SYSTEM,						// #,##0.00 or whatever is set in System Regional Settings
	NF_NUMBER_END = NF_NUMBER_SYSTEM,

	NF_SCIENTIFIC_START,
	NF_SCIENTIFIC_000E000 = NF_SCIENTIFIC_START,	// 0.00E+000
	NF_SCIENTIFIC_000E00,							// 0.00E+00
	NF_SCIENTIFIC_END = NF_SCIENTIFIC_000E00,

	NF_PERCENT_START,
	NF_PERCENT_INT = NF_PERCENT_START,		// 0%
	NF_PERCENT_DEC2,						// 0.00%
	NF_PERCENT_END = NF_PERCENT_DEC2,

	NF_FRACTION_START,
	NF_FRACTION_1 = NF_FRACTION_START,		// # ?/?
	NF_FRACTION_2,							// # ??/??
	NF_FRACTION_END = NF_FRACTION_2,

	NF_NUMERIC_END = NF_FRACTION_END,

	NF_CURRENCY_START,
	NF_CURRENCY_1000INT = NF_CURRENCY_START,// #,##0 DM
	NF_CURRENCY_1000DEC2,					// #,##0.00 DM
	NF_CURRENCY_1000INT_RED,                // #,##0 DM			negative in red
	NF_CURRENCY_1000DEC2_RED,				// #,##0.00 DM		negative in red
	NF_CURRENCY_1000DEC2_CCC,				// #,##0.00 DEM		currency abbreviation
	NF_CURRENCY_1000DEC2_DASHED,			// #,##0.-- DM
	NF_CURRENCY_END = NF_CURRENCY_1000DEC2_DASHED,

	NF_DATE_START,
	NF_DATE_SYSTEM_SHORT = NF_DATE_START,	// 08.10.97
	NF_DATE_SYSTEM_LONG,					// Wednesday, 8. October 1997
	NF_DATE_SYS_DDMMYY,		   				// 08.10.97
	NF_DATE_SYS_DDMMYYYY,	   				// 08.10.1997
	NF_DATE_SYS_DMMMYY,	   					// 8. Oct 97
	NF_DATE_SYS_DMMMYYYY,	   				// 8. Oct 1997
	NF_DATE_DIN_DMMMYYYY,					// 8. Oct. 1997					DIN
	NF_DATE_SYS_DMMMMYYYY,					// 8. October 1997
	NF_DATE_DIN_DMMMMYYYY,					// 8. October 1997				DIN
	NF_DATE_SYS_NNDMMMYY,		 			// Wed, 8. Okt 97
	NF_DATE_DEF_NNDDMMMYY,		 			// Wed 08.Okt 97
	NF_DATE_SYS_NNDMMMMYYYY,	   			// Wed, 8. Oktober 1997
	NF_DATE_SYS_NNNNDMMMMYYYY,	   			// Wednesday, 8. Oktober 1997
	NF_DATE_DIN_MMDD,		 				// 10-08						DIN
	NF_DATE_DIN_YYMMDD,		 				// 97-10-08						DIN
	NF_DATE_DIN_YYYYMMDD,	 				// 1997-10-08					DIN
	NF_DATE_SYS_MMYY,		   				// 10.97
	NF_DATE_SYS_DDMMM,						// 08.Oct
	NF_DATE_MMMM,							// October
	NF_DATE_QQJJ,							// 4. Quarter 97
	NF_DATE_WW,								// week of year
	NF_DATE_END = NF_DATE_WW,

	NF_TIME_START,
	NF_TIME_HHMM = NF_TIME_START,			// HH:MM
	NF_TIME_HHMMSS,							// HH:MM:SS
	NF_TIME_HHMMAMPM,						// HH:MM AM/PM
	NF_TIME_HHMMSSAMPM,						// HH:MM:SS AM/PM
	NF_TIME_HH_MMSS,						// [HH]:MM:SS
	NF_TIME_MMSS00,							// MM:SS,00
	NF_TIME_HH_MMSS00,	  					// [HH]:MM:SS,00
	NF_TIME_END = NF_TIME_HH_MMSS00,

	NF_DATETIME_START,
	NF_DATETIME_SYSTEM_SHORT_HHMM = NF_DATETIME_START,	// 08.10.97 01:23
	NF_DATETIME_SYS_DDMMYYYY_HHMMSS,		// 08.10.1997 01:23:45
	NF_DATETIME_END = NF_DATETIME_SYS_DDMMYYYY_HHMMSS,

	NF_BOOLEAN,								// BOOLEAN
	NF_TEXT,								// @
	NF_INDEX_TABLE_ENTRIES
};

// #45717# IsNumberFormat( "98-10-24", 30, x ), YMD Format set with DMY
// International settings doesn't recognize the string as a date.
/** enum values for <method>SvNumberFormatter::SetEvalDateFormat</method>

	<p>How <method>ImpSvNumberInputScan::GetDateRef</method> shall take the
 	DateFormat order (YMD,DMY,MDY) into account, if called from IsNumberFormat
	with a date format to match against.
 */
enum NfEvalDateFormat
{
	/** DateFormat only from International, default. */
	NF_EVALDATEFORMAT_INTL,

	/** DateFormat only from date format passed to function (if any).
		If no date format is passed then the DateFormat is taken from International. */
	NF_EVALDATEFORMAT_FORMAT,

	/** First try the DateFormat from International. If it doesn't match a
	 	valid date try the DateFormat from the date format passed. */
	NF_EVALDATEFORMAT_INTL_FORMAT,

	/** First try the DateFormat from the date format passed. If it doesn't
		match a valid date try the DateFormat from International. */
	NF_EVALDATEFORMAT_FORMAT_INTL
};

#if 0 // _SOLAR__PRIVATE
#define _ZFORLIST_DECLARE_TABLE
#endif
#ifdef _ZFORLIST_DECLARE_TABLE
DECLARE_TABLE (SvNumberFormatTable, SvNumberformat*);
DECLARE_TABLE (SvULONGTable, ULONG*);
#else
typedef Table SvNumberFormatTable;
typedef Table SvULONGTable;
#endif

/** Language/country dependent currency entries
 */
class NfCurrencyEntry
{
	String   		aSymbol;			/// currency symbol
	String			aBankSymbol;		/// currency abbreviation
	LanguageType	eLanguage;			/// language/country value
	USHORT			nPositiveFormat;	/// position of symbol
	USHORT			nNegativeFormat;	/// position of symbol and type and position of negative sign
	USHORT			nDigits;			/// count of decimal digits
	sal_Unicode		cZeroChar;			/// which character is used for zeros as last decimal digits

						/// not implemented, prevent usage
						NfCurrencyEntry( const NfCurrencyEntry& );
						/// not implemented, prevent usage
	NfCurrencyEntry&	operator=( const NfCurrencyEntry& );

private:

#if 0 // _SOLAR__PRIVATE
						// nDecimalFormat := 0, 1, 2
						// #,##0 or #,##0.00 or #,##0.-- are assigned
	void				Impl_BuildFormatStringNumChars( String&,
							const LocaleDataWrapper&, USHORT nDecimalFormat ) const;
#endif	// __PRIVATE

public:

						NfCurrencyEntry();
						NfCurrencyEntry( const LocaleDataWrapper& rLocaleData,
							LanguageType eLang );
						NfCurrencyEntry(
							const ::com::sun::star::i18n::Currency & rCurr,
							const LocaleDataWrapper& rLocaleData,
							LanguageType eLang );
                        ~NfCurrencyEntry() {}

						/// Symbols and language identical
	BOOL				operator==( const NfCurrencyEntry& r ) const;

						/// Set this format to be the EURo entry, overwrite other settings
	void				SetEuro();
	BOOL				IsEuro() const;

						/** Apply format information (nPositiveFormat,
						 	nNegativeFormat, nDigits, cZeroChar) of another format. */
	void				ApplyVariableInformation( const NfCurrencyEntry& );

	const String&		GetSymbol() const			{ return aSymbol; }
	const String&		GetBankSymbol() const		{ return aBankSymbol; }
	LanguageType		GetLanguage() const			{ return eLanguage; }
	USHORT				GetPositiveFormat() const	{ return nPositiveFormat; }
	USHORT				GetNegativeFormat() const	{ return nNegativeFormat; }
	USHORT				GetDigits() const			{ return nDigits; }
	sal_Unicode			GetZeroChar() const			{ return cZeroChar; }

						/** [$DM-407] (bBank==FALSE) or [$DEM] (bBank==TRUE)
							is assigned to rStr, if bBank==FALSE and
							bWithoutExtension==TRUE only [$DM] */
	void				BuildSymbolString( String& rStr, BOOL bBank,
							BOOL bWithoutExtension = FALSE ) const;

						/** #,##0.00 [$DM-407] is assigned to rStr, separators
						 	 from rLoc,	incl. minus sign but without [RED] */
	void				BuildPositiveFormatString( String& rStr, BOOL bBank,
							const LocaleDataWrapper&, USHORT nDecimalFormat = 1 ) const;
	void				BuildNegativeFormatString( String& rStr, BOOL bBank,
							const LocaleDataWrapper&, USHORT nDecimalFormat = 1 ) const;

						/** [$DM-407] (or [$DEM] if bBank==TRUE)
							is appended/prepended to rStr, incl. minus sign */
	void				CompletePositiveFormatString( String& rStr, BOOL bBank,
							USHORT nPosiFormat ) const;
	void				CompleteNegativeFormatString( String& rStr, BOOL bBank,
							USHORT nNegaFormat ) const;

						/// rSymStr is appended/prepended to rStr, incl. minus sign
	static	void		CompletePositiveFormatString( String& rStr,
							const String& rSymStr, USHORT nPosiFormat );
	static	void		CompleteNegativeFormatString( String& rStr,
							const String& rSymStr, USHORT nNegaFormat );

						/** Representation of a currency (symbol position and
						 	negative sign) in other language settings */
	static	USHORT		GetEffectivePositiveFormat( USHORT nIntlFormat,
							USHORT nCurrFormat, BOOL bBank );
	static	USHORT		GetEffectiveNegativeFormat( USHORT nIntlFormat,
							USHORT nCurrFormat, BOOL bBank );

						/// General Unicode Euro symbol
	static inline sal_Unicode	GetEuroSymbol() { return sal_Unicode(0x20AC); }
						/** Platform and CharSet dependent Euro symbol,
						 	needed for import/export */
	static	sal_Char	GetEuroSymbol( rtl_TextEncoding eTextEncoding );
};

typedef NfCurrencyEntry* NfCurrencyEntryPtr;
SV_DECL_PTRARR_DEL( NfCurrencyTable, NfCurrencyEntryPtr, LANGUAGE_COUNT, 1 );
typedef String* WSStringPtr;
SV_DECL_PTRARR_DEL( NfWSStringsDtor, WSStringPtr, 8, 1 );

class SvNumberFormatterRegistry_Impl;

class SvNumberFormatter
{
public:

	/// Preferred ctor with service manager and language/country enum
	SvNumberFormatter(
		const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& xSMgr,
		LanguageType eLang
		);

	/// Depricated ctor without service manager
	SvNumberFormatter( LanguageType eLang );

	~SvNumberFormatter();

	/// Set CallBack to ColorTable
    void SetColorLink( const Link& rColorTableCallBack )    { aColorLink = rColorTableCallBack; }
	/// Do the CallBack to ColorTable
	Color* GetUserDefColor(USHORT nIndex);

	/// Change language/country, also input and format scanner
	void ChangeIntl( LanguageType eLnge );
	/// Change the reference null date
	void ChangeNullDate(USHORT nDay, USHORT nMonth, USHORT nYear);
	/// Change standard precision
	void ChangeStandardPrec(short nPrec);
	/// Set zero value suppression
	void SetNoZero(BOOL bNZ) { bNoZero = bNZ; }

    /** The language with which the formatter was initialized (system setting),
        NOT the current language after a ChangeIntl() */
    LanguageType GetLanguage() const { return IniLnge; }

	// Determine whether two format types are input compatible or not
	BOOL IsCompatible(short eOldType, short eNewType);

	/** Get table of formats of a specific type of a language/country.
		FIndex returns the default format of that type. */
	SvNumberFormatTable& GetEntryTable(short eType,
									   ULONG& FIndex,
									   LanguageType eLnge);

	/** Get table of formats of a specific type of a language/country.
		FIndex returns the default format of that type.
		If the language/country was never touched before new entries are generated */
	SvNumberFormatTable& ChangeCL(short eType,
								  ULONG& FIndex,
								  LanguageType eLnge);

	/** Get table of formats of the same type as FIndex; eType and rLnge are
		set accordingly. An unknown format is set to Standard/General */
	SvNumberFormatTable& GetFirstEntryTable(short& eType,
											ULONG& FIndex,
											LanguageType& rLnge);

	/// Delete an entry including the format it is refering to
	void DeleteEntry(ULONG nKey);

	/** Create new entry of a format code string for language/country.
		@return
			<TRUE/> if string new and ok and inserted.
	 		<FALSE/> if string already exists or an unresolvable parse error
	 		occured, in which case nCheckPos is the error position within rString.
			If the error occurs at position 0 or rString is empty nCheckPos
			will be 1, so an error in the string is always indicated by
	 		nCheckPos not being zero.
			The content of the rString variable can be changed and corrected
	 		by the method.
			nType contains the type of the format.
			nKey contains the index key of the format.
	 */
	BOOL PutEntry( String& rString, xub_StrLen& nCheckPos, short& nType, ULONG& nKey,
				  LanguageType eLnge = LANGUAGE_DONTKNOW );

	/** Same as <method>PutEntry</method> but the format code string is
	 	considered to be of language/country eLnge and is converted to
		language/country eNewLnge */
	BOOL PutandConvertEntry( String& rString, xub_StrLen& nCheckPos,
							 short& nType, ULONG& nKey,
							 LanguageType eLnge, LanguageType eNewLnge );

	/** Same as <method>PutandConvertEntry</method> but the format code string
	 	is considered to be of the System language/country eLnge and is
		converted to another System language/country eNewLnge. In this case
	 	the automatic currency is converted too. */
	BOOL PutandConvertEntrySystem( String& rString, xub_StrLen& nCheckPos,
							 short& nType, ULONG& nKey,
							 LanguageType eLnge, LanguageType eNewLnge );

	/** Create a format code string using format nIndex as a template and
		applying other settings (passed from the dialog) */
	void GenerateFormat( String& sString, ULONG nIndex,
						LanguageType eLnge = LANGUAGE_DONTKNOW,
						BOOL bThousand = FALSE, BOOL IsRed = FALSE,
						USHORT nPrecision = 0, USHORT nAnzLeading = 1 );

	/** Analyze an input string
		@return
			<TRUE/> if input is a number or is matching a format F_Index
				F_Index is set to a matching format if number, the value is
				returned in fOutNumber
			<FALSE/> if input is not a number
	 */
	BOOL IsNumberFormat( const String& sString, ULONG& F_Index, double& fOutNumber );

	/// Format a number according to a format index, return string and color
	void GetOutputString( const double& fOutNumber, ULONG nFIndex,
						  String& sOutString, Color** ppColor );

	/** Format a string according to a format index, return string and color.
		Formats only if the format code is of type text or the 4th subcode
		of a format code is specified, otherwise sOutString will be == "" */
	void GetOutputString( String& sString, ULONG nFIndex,
						  String& sOutString, Color** ppColor );

	/** Format a number according to the standard default format matching
		the given format index */
	void GetInputLineString( const double& fOutNumber,
							ULONG nFIndex, String& sOutString );

	/** Format a number according to a format code string to be scanned.
		@return
			<FALSE/> if format code contains an error
			<TRUE/> else, in which case the string and color are returned.
	 */
	BOOL GetPreviewString( const String& sFormatString, double fPreviewNumber,
						  String& sOutString, Color** ppColor,
						  LanguageType eLnge = LANGUAGE_DONTKNOW );

	/** Same as <method>GetPreviewString</method> but the format code string
		may be either language/country eLnge or en_US english US */
	BOOL GetPreviewStringGuess( const String& sFormatString, double fPreviewNumber,
						  String& sOutString, Color** ppColor,
						  LanguageType eLnge = LANGUAGE_DONTKNOW );

	/** Test whether the format code string is already present in container
		@return
			NUMBERFORMAT_ENTRY_NOT_FOUND if not found, else the format index.
	 */
	ULONG TestNewString( const String& sFormatString,
						LanguageType eLnge = LANGUAGE_DONTKNOW );

	/// Whether format index nFIndex is of type text or not
	BOOL IsTextFormat(ULONG nFIndex) const;
	/// Whether the 4th string subcode of format index nFIndex is present
	BOOL HasTextFormat(ULONG nFIndex) const;

	/// Load all formats from a stream
	BOOL Load( SvStream& rStream );
	/// Save all formats to a stream
	BOOL Save( SvStream& rStream ) const;
	/// Reset of "Used" flags
	void PrepareSave();

	/// Flag format index as used
	void SetFormatUsed(ULONG nFIndex);

	/// Get additional info of a format index, e.g. for dialog box
	void GetFormatSpecialInfo(ULONG nFormat, BOOL& bThousand, BOOL& IsRed,
							  USHORT& nPrecision, USHORT& nAnzLeading);

	/// Count of decimals
	USHORT GetFormatPrecision( ULONG nFormat ) const;

	/** Get additional info of a format code string, e.g. for dialog box.
		Uses a temporary parse, if possible use only if format code is not
		present in container yet, otherwise ineffective.
		@return
			0 if format code string parsed without errors, otherwise error
			position (like nCheckPos on <method>PutEntry</method>)
	 */
	ULONG GetFormatSpecialInfo( const String&, BOOL& bThousand, BOOL& IsRed,
							  USHORT& nPrecision, USHORT& nAnzLeading,
							  LanguageType eLnge = LANGUAGE_DONTKNOW );

	/// Check if format code string may be deleted by user
	BOOL IsUserDefined( const String& sStr, LanguageType eLnge = LANGUAGE_DONTKNOW );

	/** Return the format index of the format code string for language/country,
		or NUMBERFORMAT_ENTRY_NOT_FOUND */
	ULONG GetEntryKey( const String& sStr, LanguageType eLnge = LANGUAGE_DONTKNOW );

	/// Return the format for a format index
	const SvNumberformat* GetEntry(ULONG nKey) const
		{ return (SvNumberformat*) aFTable.Get(nKey); }

	/// Return the format index of the standard default number format for language/country
	ULONG GetStandardIndex(LanguageType eLnge = LANGUAGE_DONTKNOW);

	/// Return the format index of the default format of a type for language/country
	ULONG GetStandardFormat(short eType, LanguageType eLnge = LANGUAGE_DONTKNOW);

	/** Return the format index of the default format of a type for language/country.
		Maybe not the default format but a special builtin format, e.g. for
		NF_TIME_HH_MMSS00, if that format is passed in nFIndex. */
	ULONG GetStandardFormat( ULONG nFIndex, short eType, LanguageType eLnge );

	/** Return the format index of the default format of a type for language/country.
		Maybe not the default format but a special builtin format, e.g. for
		NF_TIME_HH_MMSS00, or NF_TIME_HH_MMSS if fNumber >= 1.0  */
	ULONG GetStandardFormat( double fNumber, ULONG nFIndex, short eType,
							LanguageType eLnge );

	/// Whether nFIndex is a special builtin format
	BOOL IsSpecialStandardFormat( ULONG nFIndex, LanguageType eLnge );

	/// Return the reference date
	Date* GetNullDate();
	/// Return the standard decimal precision
	short GetStandardPrec();
	/// Return whether zero suppression is switched on
	BOOL GetNoZero() { return bNoZero; }
	/** Get the type of a format (or NUMBERFORMAT_UNDEFINED if no entry),
	 	but with NUMBERFORMAT_DEFINED masked out */
	short GetType(ULONG nFIndex);

	/// As the name says
	void ClearMergeTable();
	/// Merge in all new entries from rNewTable and return a table of resulting new format indices
	SvULONGTable* MergeFormatter(SvNumberFormatter& rNewTable);

	/// Whether a merge table is present or not
	inline BOOL HasMergeFmtTbl() const;
	/// Return the new format index for an old format index, if a merge table exists
	inline ULONG GetMergeFmtIndex( ULONG nOldFmt ) const;

	/// Return the last used position ever of a language/country combination
	USHORT GetLastInsertKey(ULONG CLOffset);

	/** Return the format index of a builtin format for a specific language/country.
		If nFormat is not a builtin format nFormat is returned. */
	ULONG GetFormatForLanguageIfBuiltIn( ULONG nFormat,
									LanguageType eLnge = LANGUAGE_DONTKNOW );

	/** Return the format index for a builtin format of a specific language
		@see NfIndexTableOffset
	 */
	ULONG GetFormatIndex( NfIndexTableOffset, LanguageType eLnge = LANGUAGE_DONTKNOW );

	/** Return enum index of a format index of a builtin format,
		NF_INDEX_TABLE_ENTRIES if it's not a builtin format.
		@see NfIndexTableOffset
	 */
	NfIndexTableOffset GetIndexTableOffset( ULONG nFormat ) const;

	/** Set evaluation type and order of input date strings
		@see NfEvalDateFormat
	 */
	void SetEvalDateFormat( NfEvalDateFormat eEDF ) { eEvalDateFormat = eEDF; }
	NfEvalDateFormat GetEvalDateFormat() const { return eEvalDateFormat; }

	/** Set TwoDigitYearStart, how the input string scanner handles a two digit year.
		Default from VCL: 1930, 30-99 19xx, 00-29 20xx

		<p> Historically (prior to src513e) it was a two digit number determing
		until which number the string scanner recognizes a year to be 20xx,
		default <= 29 is used by SFX/OfaMiscCfg.
		The name Year2000 is kept although the actual functionality is now a
		TwoDigitYearStart which might be in any century.
	 */
	void	SetYear2000( USHORT nVal );
	USHORT	GetYear2000() const;
	static	USHORT	GetYear2000Default();

	USHORT	ExpandTwoDigitYear( USHORT nYear ) const;
	inline	static	USHORT	ExpandTwoDigitYear( USHORT nYear, USHORT nTwoDigitYearStart );

	/// DEPRICATED: Return first character of the decimal separator of the current language/country
    sal_Unicode GetDecSep() const { return GetNumDecimalSep().GetChar(0); }
	/// Return the decimal separator of the current language/country
    String GetDecimalSep() const { return GetNumDecimalSep(); }

	/// Return the decimal separator matching the locale of the given format
	String GetFormatDecimalSep( ULONG nFormat ) const;

	/// Return a <type>SvPtrArr</type> with pointers to <type>NfCurrencyEntry</type> entries
    static const NfCurrencyTable& GetTheCurrencyTable();

	/** Searches, according to the default locale currency, an entry of the
		CurrencyTable which is <bold>not</bold> the first (LANGUAGE_SYSTEM) entry.
		@return
			<NULL/> if not found
			else pointer to <type>NfCurrencyEntry</type>
	 */
    static const NfCurrencyEntry*  MatchSystemCurrency();

	/** Return a <type>NfCurrencyEntry</type> matching a language/country.
		If language/country is LANGUAGE_SYSTEM a <method>MatchSystemCurrency</method>
		call is tried to get an entry. If that fails or the corresponding
		language/country is not present the entry for LANGUAGE_SYSTEM is returned.
	 */
    static const NfCurrencyEntry&  GetCurrencyEntry( LanguageType );

    /** Return a <type>NfCurrencyEntry</type> pointer matching a language/country
        and currency abbreviation (AKA banking symbol).
        This method is meant for the configuration of the default currency.
		@return
			<NULL/> if not found
			else pointer to <type>NfCurrencyEntry</type>
	 */
    static const NfCurrencyEntry*  GetCurrencyEntry( const String& rAbbrev,
                LanguageType eLang );

    /** Set the default system currency. The combination of abbreviation and
        language must match an existent element of theCurrencyTable. If not,
        the SYSTEM (current locale) entry becomes the default.
        This method is meant for the configuration of the default currency.
     */
    static void SetDefaultSystemCurrency( const String& rAbbrev, LanguageType eLang );

	/** Get all standard formats for a specific currency, formats are
		appended to the <type>NfWSStringsDtor</type> list.
		@param bBank
			<TRUE/>: generate only format strings with currency abbreviation
			<FALSE/>: mixed format strings
		@return
			position of default format
	 */
	USHORT	GetCurrencyFormatStrings( NfWSStringsDtor&, const NfCurrencyEntry&,
				BOOL bBank ) const;

	/** Whether nFormat is of type NUMBERFORMAT_CURRENCY and the format code
		contains a new SYMBOLTYPE_CURRENCY and if so which one [$xxx-nnn].
		If ppEntry is not NULL and exactly one entry is found, a [$xxx-nnn] is
		returned, even if the format code only contains [$xxx] !
	 */
	BOOL	GetNewCurrencySymbolString( ULONG nFormat, String& rSymbol,
				const NfCurrencyEntry** ppEntry = NULL, BOOL* pBank = NULL ) const;

	/** Look up the corresponding <type>NfCurrencyEntry</type> matching
		rSymbol (may be CurrencySymbol or CurrencyAbbreviation) and possibly
		a rExtension (being yyy of [$xxx-yyy]) or a given language/country
		value. Tries to match a rSymbol with rExtension first, then with
	 	eFormatLanguage, then rSymbol only. This is because a currency entry
		might have been constructed using I18N locale data where a used locale
		of a currrency format code must not necessarily match the locale of
		the locale data itself, e.g. [$HK$-40C] (being "zh_HK" locale) in
		zh_CN locale data. Here the rExtension would have the value 0x40c but
	 	eFormatLanguage of the number format would have the value of zh_CN
	 	locale, the value with which the corresponding CurrencyEntry is
		constructed.

		@param bFoundBank
			Only used for output.
	 		If the return value is not <NULL/> this value is set to <TRUE/> if
	 		the matching entry was found by comparing rSymbol against the
	 		CurrencyAbbreviation (AKA BankSymbol).
	 		If the return value is <NULL/> the value of bFoundBank is undefined.
		@param rSymbol
			Currency symbol, preferably obtained of a format by a call to
			<method>SvNumberformat::GetNewCurrencySymbol()</method>
		@param rExtension
			Currency extension, preferably obtained of a format by a call to
			<method>SvNumberformat::GetNewCurrencySymbol()</method>
		@param eFormatLanguage
			The language/country value of the format of which rSymbol and
			rExtension are obtained (<method>SvNumberformat::GetLanguage()</method>).
		@param bOnlyStringLanguage
			If <TRUE/> only entries with language/country of rExtension are
			checked, no match on eFormatLanguage. If rExtension is empty all
	 		entries are checked.
		@return
			The matching entry if unique (in which case bFoundBank is set),
	 		else <NULL/>.
	 */
    static const NfCurrencyEntry* GetCurrencyEntry( BOOL & bFoundBank,
				const String& rSymbol, const String& rExtension,
                LanguageType eFormatLanguage, BOOL bOnlyStringLanguage = FALSE );

    /// Get compatibility ("automatic" old style) currency from I18N locale data
    void GetCompatibilityCurrency( String& rSymbol, String& rAbbrev ) const;

	/// Fill rList with the language/country codes that have been allocated
	void	GetUsedLanguages( SvUShorts& rList );

	/** Return a keyword for a language/country and <type>NfKeywordIndex</type>
		for XML import, to generate number format strings. */
	String GetKeyword( LanguageType eLnge, USHORT nIndex );

    /** Return the GENERAL keyword in proper case ("General") for a
        language/country, used in XML import */
    String GetStandardName( LanguageType eLnge );

	/// Skip a NumberFormatter in stream, Chart needs this
	static void SkipNumberFormatterInStream( SvStream& );

	/// return a proper language for LANGUAGE_SYSTEM, LANGUAGE_DONTKNOW, LANGUAGE_NONE
	static LanguageType GetProperLanguage( LanguageType eLang );

	/// Convert a LanguageType to a Locale with handling of GetProperLanguage().
	/// Don't use to convert a Language to a Locale for file storage because
	/// it substitutes SYSTEM and NONE, use only at runtime!
	static ::com::sun::star::lang::Locale ConvertLanguageToLocale( LanguageType eLang );

private:
	::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xServiceManager;
	::com::sun::star::lang::Locale aLocale;
	SvNumberFormatTable aFTable;			// Table of format keys to format entries
	Table aDefaultFormatKeys;				// Table of default standard to format keys
	SvNumberFormatTable* pFormatTable;		// For the UI dialog
	SvULONGTable* pMergeTable;				// List of indices for merging two formatters
	CharClass* pCharClass;					// CharacterClassification
    OnDemandLocaleDataWrapper xLocaleData;  // LocaleData switched between SYSTEM, ENGLISH and other
    OnDemandTransliterationWrapper xTransliteration;    // Transliteration loaded on demand
    OnDemandCalendarWrapper xCalendar;      // Calendar loaded on demand
	ImpSvNumberInputScan* pStringScanner;	// Input string scanner
	ImpSvNumberformatScan* pFormatScanner;	// Format code string scanner
    Link aColorLink;                        // User defined color table CallBack
	ULONG MaxCLOffset;						// Max language/country offset used
	ULONG nDefaultSystemCurrencyFormat;		// NewCurrency matching SYSTEM locale
    LanguageType IniLnge;                   // Initialized setting language/country
	LanguageType ActLnge;                   // Current setting language/country
	NfEvalDateFormat eEvalDateFormat;		// DateFormat evaluation
	BOOL bNoZero;							// Zero value suppression

    // cached locale data items needed almost any time
    String aDecimalSep;
    String aThousandSep;
    String aDateSep;

#ifdef _ZFORLIST_CXX				// ----- private Methoden -----

	static BOOL 			bCurrencyTableInitialized;
	static NfCurrencyTable	theCurrencyTable;
	static USHORT			nSystemCurrencyPosition;
    static SvNumberFormatterRegistry_Impl* pFormatterRegistry;

    // get the registry, create one if none exists
    static SvNumberFormatterRegistry_Impl& GetFormatterRegistry();

	// called by ctors
	void ImpConstruct( LanguageType eLang );

    // Changes initialized language/country, clears the entries and generates
    // new ones, may ONLY be called by the binary file format load
	void ImpChangeSysCL( LanguageType eLnge, BOOL bLoadingSO5 );

	// Generate builtin formats provided by i18n behind CLOffset,
	// if bLoadingSO5==FALSE also generate additional i18n formats.
	void ImpGenerateFormats( ULONG CLOffset, BOOL bLoadingSO5 );

	// Generate additional formats provided by i18n
	void ImpGenerateAdditionalFormats(
				ULONG CLOffset,
				NumberFormatCodeWrapper& rNumberFormatCode,
				BOOL bAfterLoadingSO5 );

	SvNumberformat* ImpInsertFormat(
				const ::com::sun::star::i18n::NumberFormatCode& rCode,
				ULONG nPos,
				BOOL bAfterLoadingSO5 = FALSE,
				sal_Int16 nOrgIndex = 0 );
	// ImpInsertNewStandardFormat for new (since version ...) builtin formats
	SvNumberformat* ImpInsertNewStandardFormat(
				const ::com::sun::star::i18n::NumberFormatCode& rCode,
				ULONG nPos,
				USHORT nVersion,
				BOOL bAfterLoadingSO5 = FALSE,
				sal_Int16 nOrgIndex = 0 );

	// Return CLOffset or (MaxCLOffset + SV_COUNTRY_LANGUAGE_OFFSET) if new language/country
	ULONG ImpGetCLOffset(LanguageType eLnge) const;

	// Test whether format code already exists, then return index key,
	// otherwise NUMBERFORMAT_ENTRY_NOT_FOUND
	ULONG ImpIsEntry( const String& rString,
						ULONG CLOffset,
						LanguageType eLnge );

	// Create builtin formats for language/country if necessary, return CLOffset
	ULONG ImpGenerateCL( LanguageType eLnge, BOOL bLoadingSO5 = FALSE );

    // Build negative currency format, old compatibility style
    void ImpGetNegCurrFormat( String& sNegStr, const String& rCurrSymbol );
    // Build positive currency format, old compatibility style
    void ImpGetPosCurrFormat( String& sPosStr, const String& rCurrSymbol );

	// Create <type>theCurrencyTable</type> with all <type>NfCurrencyEntry</type>
    static void ImpInitCurrencyTable();

	// Return the format index of the currency format of the system locale.
	// Format is created if not already present.
	ULONG	ImpGetDefaultSystemCurrencyFormat();

	// Return the format index of the currency format of the current locale.
	// Format is created if not already present.
	ULONG	ImpGetDefaultCurrencyFormat();

	// Return the default format for a given type and current locale.
	// May ONLY be called from within GetStandardFormat().
	ULONG	ImpGetDefaultFormat( short nType );

	// Return the index in a sequence of format codes matching an enum of
	// NfIndexTableOffset. If not found 0 is returned. If the sequence doesn't
	// contain any format code elements a default element is created and inserted.
	sal_Int32 ImpGetFormatCodeIndex(
		::com::sun::star::uno::Sequence< ::com::sun::star::i18n::NumberFormatCode >& rSeq,
		const NfIndexTableOffset nTabOff );

    // Adjust a sequence of format codes to contain only one (THE) default
    // instead of multiple defaults for short/medium/long types.
    // If there is no medium but a short and a long default the long is taken.
    // Return the default index in the sequence.
    // Non-PRODUCT version may check locale data for matching defaults in one
    // FormatElement group.
    sal_Int32 ImpAdjustFormatCodeDefault(
        ::com::sun::star::i18n::NumberFormatCode * pFormatArr,
        sal_Int32 nCount
#ifndef PRODUCT
        , BOOL bCheckCorrectness = TRUE
#endif
        );

	// used as a loop body inside of GetNewCurrencySymbolString() and GetCurrencyEntry()
#ifdef PRODUCT
	inline
#endif
        static BOOL SvNumberFormatter::ImpLookupCurrencyEntryLoopBody(
			const NfCurrencyEntry*& pFoundEntry, BOOL& bFoundBank,
            const NfCurrencyEntry* pData, USHORT nPos, const String& rSymbol );

    // link to be set at <method>SvtSysLocaleOptions::SetCurrencyChangeLink()</method>
    DECL_STATIC_LINK( SvNumberFormatter, CurrencyChangeLink, void* );

#endif // _ZFORLIST_CXX

public:

    // own static mutex, may also be used by internal class SvNumberFormatterRegistry_Impl
    static ::osl::Mutex&        GetMutex();

    // called by SvNumberFormatterRegistry_Impl::Notify if the default system currency changes
    void ResetDefaultSystemCurrency();

    // Replace the SYSTEM language/country format codes. Called upon change of
    // the user configurable locale.
    // Old compatibility codes are replaced, user defined are converted, and
    // new format codes are appended.
    void ReplaceSystemCL( LanguageType eOldLanguage );

	inline ::com::sun::star::uno::Reference<
		::com::sun::star::lang::XMultiServiceFactory >
		GetServiceManager() const { return xServiceManager; }

	//! The following method is not to be used from outside but must be
    //! public for the InputScanner.
	// return the current FormatScanner
	inline const ImpSvNumberformatScan* GetFormatScanner() const { return pFormatScanner; }

    //! The following methods are not to be used from outside but must be
    //! public for the InputScanner and FormatScanner.

    // return current (!) Locale
	inline const ::com::sun::star::lang::Locale& GetLocale() const { return aLocale; }

    // return corresponding Transliteration wrapper
    inline const ::utl::TransliterationWrapper* GetTransliteration() const { return xTransliteration.get(); }

	// return the corresponding CharacterClassification wrapper
	inline const CharClass* GetCharClass() const { return pCharClass; }

	// return the corresponding LocaleData wrapper
    inline const LocaleDataWrapper* GetLocaleData() const { return xLocaleData.get(); }

	// return the corresponding Calendar wrapper
    inline CalendarWrapper* GetCalendar() const { return xCalendar.get(); }

    // cached locale data items

    // return the corresponding decimal separator
    inline const String& GetNumDecimalSep() const { return aDecimalSep; }

    // return the corresponding group (AKA thousand) separator
    inline const String& GetNumThousandSep() const { return aThousandSep; }

    // return the corresponding date separator
    inline const String& GetDateSep() const { return aDateSep; }

};

// --------------------------- inline --------------------------------------

inline ULONG SvNumberFormatter::GetMergeFmtIndex( ULONG nOldFmt ) const
{
    ULONG* pU = (pMergeTable && pMergeTable->Count()) ? (ULONG*)pMergeTable->Get( nOldFmt ) : 0;
	return pU ? *pU : nOldFmt;
}

inline BOOL SvNumberFormatter::HasMergeFmtTbl() const
{
    return pMergeTable && (0 != pMergeTable->Count());
}

// static
inline USHORT SvNumberFormatter::ExpandTwoDigitYear(
			USHORT nYear, USHORT nTwoDigitYearStart )
{
	if ( nYear < 100 )
	{
		if ( nYear < (nTwoDigitYearStart % 100) )
			return nYear + (((nTwoDigitYearStart / 100) + 1) * 100);
		else
			return nYear + ((nTwoDigitYearStart / 100) * 100);
	}
	return nYear;
}

#endif	// _ZFORLIST_HXX
