5475 lines
212 KiB
C++
5475 lines
212 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <sal/config.h>
|
|
|
|
#include <sal/log.hxx>
|
|
#include <officecfg/Office/Common.hxx>
|
|
#include <svl/zforlist.hxx>
|
|
#include <svl/currencytable.hxx>
|
|
|
|
#include <comphelper/lok.hxx>
|
|
#include <comphelper/string.hxx>
|
|
#include <o3tl/string_view.hxx>
|
|
#include <tools/debug.hxx>
|
|
#include <unotools/charclass.hxx>
|
|
#include <unotools/configmgr.hxx>
|
|
#include <i18nlangtag/mslangid.hxx>
|
|
#include <unotools/localedatawrapper.hxx>
|
|
#include <com/sun/star/i18n/KNumberFormatUsage.hpp>
|
|
#include <com/sun/star/i18n/KNumberFormatType.hpp>
|
|
#include <com/sun/star/i18n/FormatElement.hpp>
|
|
#include <com/sun/star/i18n/Currency2.hpp>
|
|
#include <com/sun/star/i18n/NumberFormatCode.hpp>
|
|
#include <com/sun/star/i18n/XNumberFormatCode.hpp>
|
|
#include <com/sun/star/i18n/NumberFormatMapper.hpp>
|
|
#include <comphelper/processfactory.hxx>
|
|
|
|
#include <osl/mutex.hxx>
|
|
|
|
#include "zforscan.hxx"
|
|
#include "zforfind.hxx"
|
|
#include <svl/zformat.hxx>
|
|
#include <i18npool/reservedconstants.hxx>
|
|
|
|
#include <unotools/syslocaleoptions.hxx>
|
|
#include <unotools/digitgroupingiterator.hxx>
|
|
#include <rtl/strbuf.hxx>
|
|
#include <rtl/math.hxx>
|
|
|
|
#include <atomic>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <set>
|
|
|
|
using namespace ::com::sun::star;
|
|
using namespace ::com::sun::star::uno;
|
|
using namespace ::com::sun::star::i18n;
|
|
using namespace ::com::sun::star::lang;
|
|
|
|
// 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 60
|
|
#define ZF_STANDARD_DURATION (ZF_STANDARD_TIME + 4)
|
|
#define ZF_STANDARD_DATETIME 70
|
|
#define ZF_STANDARD_SCIENTIFIC 80
|
|
#define ZF_STANDARD_FRACTION 85
|
|
|
|
// Additional builtin formats, historically not fitting into the first 10 of a
|
|
// category. Make sure it doesn't spill over to the next category.
|
|
#define ZF_STANDARD_DATE_SYS_DMMMYYYY (ZF_STANDARD_DATE + 10)
|
|
#define ZF_STANDARD_DATE_SYS_DMMMMYYYY (ZF_STANDARD_DATE + 11)
|
|
#define ZF_STANDARD_DATE_SYS_NNDMMMYY (ZF_STANDARD_DATE + 12)
|
|
#define ZF_STANDARD_DATE_SYS_NNDMMMMYYYY (ZF_STANDARD_DATE + 13)
|
|
#define ZF_STANDARD_DATE_SYS_NNNNDMMMMYYYY (ZF_STANDARD_DATE + 14)
|
|
#define ZF_STANDARD_DATE_DIN_DMMMYYYY (ZF_STANDARD_DATE + 15)
|
|
#define ZF_STANDARD_DATE_DIN_DMMMMYYYY (ZF_STANDARD_DATE + 16)
|
|
#define ZF_STANDARD_DATE_DIN_MMDD (ZF_STANDARD_DATE + 17)
|
|
#define ZF_STANDARD_DATE_DIN_YYMMDD (ZF_STANDARD_DATE + 18)
|
|
#define ZF_STANDARD_DATE_DIN_YYYYMMDD (ZF_STANDARD_DATE + 19)
|
|
#define ZF_STANDARD_DATE_WW (ZF_STANDARD_DATE + 20)
|
|
|
|
#define ZF_STANDARD_LOGICAL SV_MAX_COUNT_STANDARD_FORMATS-1 // 99
|
|
#define ZF_STANDARD_TEXT SV_MAX_COUNT_STANDARD_FORMATS // 100
|
|
|
|
static_assert( ZF_STANDARD_TEXT == NF_STANDARD_FORMAT_TEXT, "definition mismatch" );
|
|
|
|
static_assert( NF_INDEX_TABLE_RESERVED_START == i18npool::nStopPredefinedFormatIndex,
|
|
"NfIndexTableOffset does not match i18npool's locale data predefined format code index bounds.");
|
|
|
|
static_assert( NF_INDEX_TABLE_ENTRIES <= i18npool::nFirstFreeFormatIndex,
|
|
"NfIndexTableOffset crosses i18npool's locale data reserved format code index bounds.\n"
|
|
"You will need to adapt all locale data files defining index values "
|
|
"(formatIndex=\"...\") in that range and increment those and when done "
|
|
"adjust nFirstFreeFormatIndex in include/i18npool/reservedconstants.hxx");
|
|
|
|
/* 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
|
|
|
|
// Same order as in include/svl/zforlist.hxx enum NfIndexTableOffset
|
|
sal_uInt32 const indexTable[NF_INDEX_TABLE_ENTRIES] = {
|
|
ZF_STANDARD, // NF_NUMBER_STANDARD
|
|
ZF_STANDARD + 1, // NF_NUMBER_INT
|
|
ZF_STANDARD + 2, // NF_NUMBER_DEC2
|
|
ZF_STANDARD + 3, // NF_NUMBER_1000INT
|
|
ZF_STANDARD + 4, // NF_NUMBER_1000DEC2
|
|
ZF_STANDARD + 5, // NF_NUMBER_SYSTEM
|
|
ZF_STANDARD_SCIENTIFIC, // NF_SCIENTIFIC_000E000
|
|
ZF_STANDARD_SCIENTIFIC + 1, // NF_SCIENTIFIC_000E00
|
|
ZF_STANDARD_PERCENT, // NF_PERCENT_INT
|
|
ZF_STANDARD_PERCENT + 1, // NF_PERCENT_DEC2
|
|
ZF_STANDARD_FRACTION, // NF_FRACTION_1D
|
|
ZF_STANDARD_FRACTION + 1, // NF_FRACTION_2D
|
|
ZF_STANDARD_CURRENCY, // NF_CURRENCY_1000INT
|
|
ZF_STANDARD_CURRENCY + 1, // NF_CURRENCY_1000DEC2
|
|
ZF_STANDARD_CURRENCY + 2, // NF_CURRENCY_1000INT_RED
|
|
ZF_STANDARD_CURRENCY + 3, // NF_CURRENCY_1000DEC2_RED
|
|
ZF_STANDARD_CURRENCY + 4, // NF_CURRENCY_1000DEC2_CCC
|
|
ZF_STANDARD_CURRENCY + 5, // NF_CURRENCY_1000DEC2_DASHED
|
|
ZF_STANDARD_DATE, // NF_DATE_SYSTEM_SHORT
|
|
ZF_STANDARD_DATE + 8, // NF_DATE_SYSTEM_LONG
|
|
ZF_STANDARD_DATE + 7, // NF_DATE_SYS_DDMMYY
|
|
ZF_STANDARD_DATE + 6, // NF_DATE_SYS_DDMMYYYY
|
|
ZF_STANDARD_DATE + 9, // NF_DATE_SYS_DMMMYY
|
|
ZF_STANDARD_DATE_SYS_DMMMYYYY, // NF_DATE_SYS_DMMMYYYY
|
|
ZF_STANDARD_DATE_DIN_DMMMYYYY, // NF_DATE_DIN_DMMMYYYY
|
|
ZF_STANDARD_DATE_SYS_DMMMMYYYY, // NF_DATE_SYS_DMMMMYYYY
|
|
ZF_STANDARD_DATE_DIN_DMMMMYYYY, // NF_DATE_DIN_DMMMMYYYY
|
|
ZF_STANDARD_DATE_SYS_NNDMMMYY, // NF_DATE_SYS_NNDMMMYY
|
|
ZF_STANDARD_DATE + 1, // NF_DATE_DEF_NNDDMMMYY
|
|
ZF_STANDARD_DATE_SYS_NNDMMMMYYYY, // NF_DATE_SYS_NNDMMMMYYYY
|
|
ZF_STANDARD_DATE_SYS_NNNNDMMMMYYYY, // NF_DATE_SYS_NNNNDMMMMYYYY
|
|
ZF_STANDARD_DATE_DIN_MMDD, // NF_DATE_DIN_MMDD
|
|
ZF_STANDARD_DATE_DIN_YYMMDD, // NF_DATE_DIN_YYMMDD
|
|
ZF_STANDARD_DATE_DIN_YYYYMMDD, // NF_DATE_DIN_YYYYMMDD
|
|
ZF_STANDARD_DATE + 2, // NF_DATE_SYS_MMYY
|
|
ZF_STANDARD_DATE + 3, // NF_DATE_SYS_DDMMM
|
|
ZF_STANDARD_DATE + 4, // NF_DATE_MMMM
|
|
ZF_STANDARD_DATE + 5, // NF_DATE_QQJJ
|
|
ZF_STANDARD_DATE_WW, // NF_DATE_WW
|
|
ZF_STANDARD_TIME, // NF_TIME_HHMM
|
|
ZF_STANDARD_TIME + 1, // NF_TIME_HHMMSS
|
|
ZF_STANDARD_TIME + 2, // NF_TIME_HHMMAMPM
|
|
ZF_STANDARD_TIME + 3, // NF_TIME_HHMMSSAMPM
|
|
ZF_STANDARD_TIME + 4, // NF_TIME_HH_MMSS
|
|
ZF_STANDARD_TIME + 5, // NF_TIME_MMSS00
|
|
ZF_STANDARD_TIME + 6, // NF_TIME_HH_MMSS00
|
|
ZF_STANDARD_DATETIME, // NF_DATETIME_SYSTEM_SHORT_HHMM
|
|
ZF_STANDARD_DATETIME + 1, // NF_DATETIME_SYS_DDMMYYYY_HHMMSS
|
|
ZF_STANDARD_LOGICAL, // NF_BOOLEAN
|
|
ZF_STANDARD_TEXT, // NF_TEXT
|
|
ZF_STANDARD_DATETIME + 2, // NF_DATETIME_SYS_DDMMYYYY_HHMM
|
|
ZF_STANDARD_FRACTION + 2, // NF_FRACTION_3D
|
|
ZF_STANDARD_FRACTION + 3, // NF_FRACTION_2
|
|
ZF_STANDARD_FRACTION + 4, // NF_FRACTION_4
|
|
ZF_STANDARD_FRACTION + 5, // NF_FRACTION_8
|
|
ZF_STANDARD_FRACTION + 6, // NF_FRACTION_16
|
|
ZF_STANDARD_FRACTION + 7, // NF_FRACTION_10
|
|
ZF_STANDARD_FRACTION + 8, // NF_FRACTION_100
|
|
ZF_STANDARD_DATETIME + 3, // NF_DATETIME_ISO_YYYYMMDD_HHMMSS
|
|
ZF_STANDARD_DATETIME + 4, // NF_DATETIME_ISO_YYYYMMDD_HHMMSS000
|
|
ZF_STANDARD_DATETIME + 5, // NF_DATETIME_ISO_YYYYMMDDTHHMMSS
|
|
ZF_STANDARD_DATETIME + 6 // NF_DATETIME_ISO_YYYYMMDDTHHMMSS000
|
|
};
|
|
|
|
/**
|
|
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
|
|
{
|
|
std::vector< SvNumberFormatter* >
|
|
aFormatters;
|
|
SvtSysLocaleOptions aSysLocaleOptions;
|
|
LanguageType eSysLanguage;
|
|
|
|
public:
|
|
SvNumberFormatterRegistry_Impl();
|
|
virtual ~SvNumberFormatterRegistry_Impl() override;
|
|
|
|
void Insert( SvNumberFormatter* pThis )
|
|
{ aFormatters.push_back( pThis ); }
|
|
|
|
void Remove( SvNumberFormatter const * pThis );
|
|
|
|
size_t Count() const
|
|
{ return aFormatters.size(); }
|
|
|
|
virtual void ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints ) override;
|
|
};
|
|
|
|
SvNumberFormatterRegistry_Impl::SvNumberFormatterRegistry_Impl()
|
|
: eSysLanguage(MsLangId::getRealLanguage( LANGUAGE_SYSTEM ))
|
|
{
|
|
aSysLocaleOptions.AddListener( this );
|
|
}
|
|
|
|
|
|
SvNumberFormatterRegistry_Impl::~SvNumberFormatterRegistry_Impl()
|
|
{
|
|
aSysLocaleOptions.RemoveListener( this );
|
|
}
|
|
|
|
|
|
void SvNumberFormatterRegistry_Impl::Remove( SvNumberFormatter const * pThis )
|
|
{
|
|
auto it = std::find(aFormatters.begin(), aFormatters.end(), pThis);
|
|
if (it != aFormatters.end())
|
|
aFormatters.erase( it );
|
|
}
|
|
|
|
void SvNumberFormatterRegistry_Impl::ConfigurationChanged( utl::ConfigurationBroadcaster*,
|
|
ConfigurationHints nHint)
|
|
{
|
|
::osl::MutexGuard aGuard( SvNumberFormatter::GetGlobalMutex() );
|
|
|
|
if ( nHint & ConfigurationHints::Locale )
|
|
{
|
|
for(SvNumberFormatter* pFormatter : aFormatters)
|
|
pFormatter->ReplaceSystemCL( eSysLanguage );
|
|
eSysLanguage = MsLangId::getRealLanguage( LANGUAGE_SYSTEM );
|
|
}
|
|
if ( nHint & ConfigurationHints::Currency )
|
|
{
|
|
for(SvNumberFormatter* pFormatter : aFormatters)
|
|
pFormatter->ResetDefaultSystemCurrency();
|
|
}
|
|
if ( nHint & ConfigurationHints::DatePatterns )
|
|
{
|
|
for(SvNumberFormatter* pFormatter : aFormatters)
|
|
pFormatter->InvalidateDateAcceptancePatterns();
|
|
}
|
|
}
|
|
|
|
static std::atomic<bool> g_CurrencyTableInitialized;
|
|
|
|
SvNumberFormatterRegistry_Impl* SvNumberFormatter::pFormatterRegistry = nullptr;
|
|
namespace
|
|
{
|
|
NfCurrencyTable& theCurrencyTable()
|
|
{
|
|
static NfCurrencyTable SINGLETON;
|
|
return SINGLETON;
|
|
}
|
|
|
|
NfCurrencyTable& theLegacyOnlyCurrencyTable()
|
|
{
|
|
static NfCurrencyTable SINGLETON;
|
|
return SINGLETON;
|
|
}
|
|
|
|
/** THE set of installed locales. */
|
|
std::set< LanguageType > theInstalledLocales;
|
|
|
|
}
|
|
sal_uInt16 SvNumberFormatter::nSystemCurrencyPosition = 0;
|
|
|
|
// Whether BankSymbol (not CurrencySymbol!) is always at the end (1 $;-1 $) or
|
|
// language dependent.
|
|
#define NF_BANKSYMBOL_FIX_POSITION 1
|
|
|
|
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;
|
|
|
|
void SvNFEngine::ChangeIntl(SvNFLanguageData& rCurrentLanguage, LanguageType eLnge)
|
|
{
|
|
rCurrentLanguage.ChangeIntl(eLnge);
|
|
}
|
|
|
|
void SvNFEngine::ChangeNullDate(SvNFLanguageData& rCurrentLanguage, sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear)
|
|
{
|
|
rCurrentLanguage.pFormatScanner->ChangeNullDate(nDay, nMonth, nYear);
|
|
rCurrentLanguage.pStringScanner->ChangeNullDate(nDay, nMonth, nYear);
|
|
}
|
|
|
|
SvNFLanguageData::SvNFLanguageData(const Reference<XComponentContext>& rxContext, LanguageType eLang,
|
|
const SvNumberFormatter& rColorCallback)
|
|
: xContext(rxContext)
|
|
, IniLnge(eLang)
|
|
, ActLnge(eLang)
|
|
, aLanguageTag(eLang)
|
|
, eEvalDateFormat(NF_EVALDATEFORMAT_INTL)
|
|
{
|
|
xCharClass.changeLocale(xContext, aLanguageTag);
|
|
xLocaleData.init(xContext, aLanguageTag);
|
|
xCalendar.init(xContext, aLanguageTag.getLocale());
|
|
xTransliteration.init(xContext, ActLnge);
|
|
|
|
// cached locale data items
|
|
const LocaleDataWrapper* pLoc = GetLocaleData();
|
|
aDecimalSep = pLoc->getNumDecimalSep();
|
|
aDecimalSepAlt = pLoc->getNumDecimalSepAlt();
|
|
aThousandSep = pLoc->getNumThousandSep();
|
|
aDateSep = pLoc->getDateSep();
|
|
|
|
pStringScanner.reset(new ImpSvNumberInputScan(*this));
|
|
pFormatScanner.reset(new ImpSvNumberformatScan(*this, rColorCallback));
|
|
}
|
|
|
|
SvNFLanguageData::SvNFLanguageData(const SvNFLanguageData& rOther)
|
|
: xContext(rOther.xContext)
|
|
, IniLnge(rOther.IniLnge)
|
|
, ActLnge(rOther.ActLnge)
|
|
, aLanguageTag(rOther.aLanguageTag)
|
|
, aDecimalSep(rOther.aDecimalSep)
|
|
, aDecimalSepAlt(rOther.aDecimalSepAlt)
|
|
, aThousandSep(rOther.aThousandSep)
|
|
, aDateSep(rOther.aDateSep)
|
|
, eEvalDateFormat(rOther.eEvalDateFormat)
|
|
{
|
|
xCharClass.changeLocale(xContext, aLanguageTag);
|
|
xLocaleData.init(xContext, aLanguageTag);
|
|
xCalendar.init(xContext, aLanguageTag.getLocale());
|
|
xTransliteration.init(xContext, ActLnge);
|
|
|
|
pStringScanner.reset(new ImpSvNumberInputScan(*this));
|
|
pFormatScanner.reset(new ImpSvNumberformatScan(*this, rOther.pFormatScanner->getColorCallback()));
|
|
}
|
|
|
|
SvNFLanguageData::~SvNFLanguageData()
|
|
{
|
|
}
|
|
|
|
const LocaleDataWrapper* SvNFLanguageData::GetLocaleData() const { return xLocaleData.get(); }
|
|
|
|
const CharClass* SvNFLanguageData::GetCharClass() const { return xCharClass.get(); }
|
|
|
|
CalendarWrapper* SvNFLanguageData::GetCalendar() const { return xCalendar.get(); }
|
|
|
|
const ::utl::TransliterationWrapper* SvNFLanguageData::GetTransliteration() const
|
|
{
|
|
return xTransliteration.get();
|
|
}
|
|
|
|
const LanguageTag& SvNFLanguageData::GetLanguageTag() const { return aLanguageTag; }
|
|
|
|
const ImpSvNumberformatScan* SvNFLanguageData::GetFormatScanner() const { return pFormatScanner.get(); }
|
|
|
|
const OUString& SvNFLanguageData::GetNumDecimalSep() const { return aDecimalSep; }
|
|
|
|
const OUString& SvNFLanguageData::GetNumDecimalSepAlt() const { return aDecimalSepAlt; }
|
|
|
|
const OUString& SvNFLanguageData::GetNumThousandSep() const { return aThousandSep; }
|
|
|
|
const OUString& SvNFLanguageData::GetDateSep() const { return aDateSep; }
|
|
|
|
bool SvNFLanguageData::IsDecimalSep( std::u16string_view rStr ) const
|
|
{
|
|
if (rStr == GetNumDecimalSep())
|
|
return true;
|
|
if (GetNumDecimalSepAlt().isEmpty())
|
|
return false;
|
|
return rStr == GetNumDecimalSepAlt();
|
|
}
|
|
|
|
OUString SvNFLanguageData::GetLangDecimalSep( LanguageType nLang )
|
|
{
|
|
if (nLang == ActLnge)
|
|
{
|
|
return GetNumDecimalSep();
|
|
}
|
|
OUString aRet;
|
|
LanguageType eSaveLang = xLocaleData.getCurrentLanguage();
|
|
if (nLang == eSaveLang)
|
|
{
|
|
aRet = xLocaleData->getNumDecimalSep();
|
|
}
|
|
else
|
|
{
|
|
LanguageTag aSaveLocale( xLocaleData->getLanguageTag() );
|
|
xLocaleData.changeLocale( LanguageTag( nLang));
|
|
aRet = xLocaleData->getNumDecimalSep();
|
|
xLocaleData.changeLocale( aSaveLocale );
|
|
}
|
|
return aRet;
|
|
}
|
|
|
|
void SvNFLanguageData::ChangeIntl(LanguageType eLnge)
|
|
{
|
|
if (ActLnge == eLnge)
|
|
return;
|
|
|
|
ActLnge = eLnge;
|
|
|
|
aLanguageTag.reset( eLnge );
|
|
xCharClass.changeLocale( xContext, aLanguageTag );
|
|
xLocaleData.changeLocale( aLanguageTag );
|
|
xCalendar.changeLocale( aLanguageTag.getLocale() );
|
|
xTransliteration.changeLocale( eLnge );
|
|
|
|
// cached locale data items, initialize BEFORE calling ChangeIntl below
|
|
const LocaleDataWrapper* pLoc = GetLocaleData();
|
|
aDecimalSep = pLoc->getNumDecimalSep();
|
|
aDecimalSepAlt = pLoc->getNumDecimalSepAlt();
|
|
aThousandSep = pLoc->getNumThousandSep();
|
|
aDateSep = pLoc->getDateSep();
|
|
|
|
pFormatScanner->ChangeIntl();
|
|
pStringScanner->ChangeIntl();
|
|
}
|
|
|
|
SvNumberFormatter::SvNumberFormatter( const Reference< XComponentContext >& rxContext,
|
|
LanguageType eLang )
|
|
: m_xContext( rxContext )
|
|
, IniLnge(eLang != LANGUAGE_DONTKNOW ? eLang : UNKNOWN_SUBSTITUTE)
|
|
, m_aRWPolicy(SvNFEngine::GetRWPolicy(m_aFormatData))
|
|
, m_aCurrentLanguage(rxContext, IniLnge, *this)
|
|
, m_xNatNum(m_xContext)
|
|
{
|
|
|
|
// 0 .. 999 for initialized language formats
|
|
m_aFormatData.ImpGenerateFormats(m_aCurrentLanguage, GetNatNum(), 0, false);
|
|
|
|
::osl::MutexGuard aGuard( GetGlobalMutex() );
|
|
GetFormatterRegistry().Insert( this );
|
|
}
|
|
|
|
SvNumberFormatter::~SvNumberFormatter()
|
|
{
|
|
{
|
|
::osl::MutexGuard aGuard( GetGlobalMutex() );
|
|
pFormatterRegistry->Remove( this );
|
|
if ( !pFormatterRegistry->Count() )
|
|
{
|
|
delete pFormatterRegistry;
|
|
pFormatterRegistry = nullptr;
|
|
}
|
|
}
|
|
|
|
m_aFormatData.aFTable.clear();
|
|
ClearMergeTable();
|
|
}
|
|
|
|
void SvNumberFormatter::ChangeIntl(LanguageType eLnge)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
SvNFEngine::ChangeIntl(m_aCurrentLanguage, eLnge);
|
|
}
|
|
|
|
// static
|
|
::osl::Mutex& SvNumberFormatter::GetGlobalMutex()
|
|
{
|
|
// #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!!
|
|
static osl::Mutex* persistentMutex(new osl::Mutex);
|
|
|
|
return *persistentMutex;
|
|
}
|
|
|
|
|
|
// static
|
|
SvNumberFormatterRegistry_Impl& SvNumberFormatter::GetFormatterRegistry()
|
|
{
|
|
::osl::MutexGuard aGuard( GetGlobalMutex() );
|
|
if ( !pFormatterRegistry )
|
|
{
|
|
pFormatterRegistry = new SvNumberFormatterRegistry_Impl;
|
|
}
|
|
return *pFormatterRegistry;
|
|
}
|
|
|
|
void SvNumberFormatter::SetColorLink( const Link<sal_uInt16,Color*>& rColorTableCallBack )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
aColorLink = rColorTableCallBack;
|
|
}
|
|
|
|
Color* SvNumberFormatter::GetUserDefColor(sal_uInt16 nIndex) const
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
if( aColorLink.IsSet() )
|
|
{
|
|
return aColorLink.Call(nIndex);
|
|
}
|
|
else
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void SvNumberFormatter::ChangeNullDate(sal_uInt16 nDay,
|
|
sal_uInt16 nMonth,
|
|
sal_Int16 nYear)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
SvNFEngine::ChangeNullDate(m_aCurrentLanguage, nDay, nMonth, nYear);
|
|
}
|
|
|
|
const Date& SvNFLanguageData::GetNullDate() const
|
|
{
|
|
return pFormatScanner->GetNullDate();
|
|
}
|
|
|
|
void SvNFLanguageData::ChangeStandardPrec(short nPrec)
|
|
{
|
|
pFormatScanner->ChangeStandardPrec(nPrec);
|
|
}
|
|
|
|
const Date& SvNumberFormatter::GetNullDate() const
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return m_aCurrentLanguage.GetNullDate();
|
|
}
|
|
|
|
void SvNumberFormatter::ChangeStandardPrec(short nPrec)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
m_aCurrentLanguage.ChangeStandardPrec(nPrec);
|
|
}
|
|
|
|
void SvNumberFormatter::SetNoZero(bool bNZ)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
m_aFormatData.SetNoZero(bNZ);
|
|
}
|
|
|
|
sal_uInt16 SvNumberFormatter::GetStandardPrec() const
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return m_aCurrentLanguage.pFormatScanner->GetStandardPrec();
|
|
}
|
|
|
|
bool SvNumberFormatter::GetNoZero() const
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return m_aFormatData.GetNoZero();
|
|
}
|
|
|
|
void SvNumberFormatter::ReplaceSystemCL( LanguageType eOldLanguage )
|
|
{
|
|
sal_uInt32 nCLOffset = m_aFormatData.ImpGetCLOffset( LANGUAGE_SYSTEM );
|
|
if ( nCLOffset > m_aFormatData.MaxCLOffset )
|
|
{
|
|
return ; // no SYSTEM entries to replace
|
|
}
|
|
const sal_uInt32 nMaxBuiltin = nCLOffset + SV_MAX_COUNT_STANDARD_FORMATS;
|
|
const sal_uInt32 nNextCL = nCLOffset + SV_COUNTRY_LANGUAGE_OFFSET;
|
|
sal_uInt32 nKey;
|
|
|
|
// remove old builtin formats
|
|
auto it = m_aFormatData.aFTable.find( nCLOffset );
|
|
while ( it != m_aFormatData.aFTable.end() && (nKey = it->first) >= nCLOffset && nKey <= nMaxBuiltin )
|
|
{
|
|
it = m_aFormatData.aFTable.erase(it);
|
|
}
|
|
|
|
// move additional and user defined to temporary table
|
|
SvNumberFormatTable aOldTable;
|
|
while ( it != m_aFormatData.aFTable.end() && (nKey = it->first) >= nCLOffset && nKey < nNextCL )
|
|
{
|
|
aOldTable[ nKey ] = it->second.release();
|
|
it = m_aFormatData.aFTable.erase(it);
|
|
}
|
|
|
|
// generate new old builtin formats
|
|
// reset m_aCurrentLanguage.ActLnge otherwise ChangeIntl() wouldn't switch if already LANGUAGE_SYSTEM
|
|
m_aCurrentLanguage.ActLnge = LANGUAGE_DONTKNOW;
|
|
ChangeIntl( LANGUAGE_SYSTEM );
|
|
m_aFormatData.ImpGenerateFormats(m_aCurrentLanguage, GetNatNum(), nCLOffset, true);
|
|
|
|
// convert additional and user defined from old system to new system
|
|
SvNumberformat* pStdFormat = m_aFormatData.GetFormatEntry( nCLOffset + ZF_STANDARD );
|
|
sal_uInt32 nLastKey = nMaxBuiltin;
|
|
m_aCurrentLanguage.pFormatScanner->SetConvertMode( eOldLanguage, LANGUAGE_SYSTEM, true , true);
|
|
while ( !aOldTable.empty() )
|
|
{
|
|
nKey = aOldTable.begin()->first;
|
|
if ( nLastKey < nKey )
|
|
{
|
|
nLastKey = nKey;
|
|
}
|
|
std::unique_ptr<SvNumberformat> pOldEntry(aOldTable.begin()->second);
|
|
aOldTable.erase( nKey );
|
|
OUString aString( pOldEntry->GetFormatstring() );
|
|
|
|
// 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;
|
|
sal_Int32 nCheckPos = -1;
|
|
std::unique_ptr<SvNumberformat> pNewEntry(new SvNumberformat( aString, m_aCurrentLanguage.pFormatScanner.get(),
|
|
m_aCurrentLanguage.pStringScanner.get(), GetNatNum(), nCheckPos, eLge ));
|
|
if ( nCheckPos == 0 )
|
|
{
|
|
SvNumFormatType eCheckType = pNewEntry->GetType();
|
|
if ( eCheckType != SvNumFormatType::UNDEFINED )
|
|
{
|
|
pNewEntry->SetType( eCheckType | SvNumFormatType::DEFINED );
|
|
}
|
|
else
|
|
{
|
|
pNewEntry->SetType( SvNumFormatType::DEFINED );
|
|
}
|
|
|
|
if ( m_aFormatData.aFTable.emplace( nKey, std::move(pNewEntry) ).second )
|
|
{
|
|
bCheck = true;
|
|
}
|
|
}
|
|
DBG_ASSERT( bCheck, "SvNumberFormatter::ReplaceSystemCL: couldn't convert" );
|
|
}
|
|
m_aCurrentLanguage.pFormatScanner->SetConvertMode(false);
|
|
pStdFormat->SetLastInsertKey( sal_uInt16(nLastKey - nCLOffset), SvNumberformat::FormatterPrivateAccess() );
|
|
|
|
// append new system additional formats
|
|
css::uno::Reference< css::i18n::XNumberFormatCode > xNFC = i18n::NumberFormatMapper::create( m_xContext );
|
|
ImpGenerateAdditionalFormats( nCLOffset, xNFC );
|
|
}
|
|
|
|
const css::uno::Reference<css::uno::XComponentContext>& SvNumberFormatter::GetComponentContext() const
|
|
{
|
|
return m_xContext;
|
|
}
|
|
|
|
const NativeNumberWrapper& SvNumberFormatter::GetNatNum() const { return m_xNatNum.get(); }
|
|
|
|
bool SvNFFormatData::IsTextFormat(sal_uInt32 F_Index) const
|
|
{
|
|
const SvNumberformat* pFormat = GetFormatEntry(F_Index);
|
|
return pFormat && pFormat->IsTextFormat();
|
|
}
|
|
|
|
bool SvNumberFormatter::IsTextFormat(sal_uInt32 F_Index) const
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return m_aFormatData.IsTextFormat(F_Index);
|
|
}
|
|
|
|
bool SvNFFormatData::PutEntry(SvNFLanguageData& rCurrentLanguage,
|
|
const NativeNumberWrapper& rNatNum,
|
|
OUString& rString,
|
|
sal_Int32& nCheckPos,
|
|
SvNumFormatType& nType,
|
|
sal_uInt32& nKey, // format key
|
|
LanguageType eLnge,
|
|
bool bReplaceBooleanEquivalent)
|
|
{
|
|
nKey = 0;
|
|
if (rString.isEmpty()) // empty string
|
|
{
|
|
nCheckPos = 1; // -> Error
|
|
return false;
|
|
}
|
|
eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
|
|
rCurrentLanguage.ChangeIntl(eLnge); // change locale if necessary
|
|
LanguageType eLge = eLnge; // non-const for ConvertMode
|
|
bool bCheck = false;
|
|
std::unique_ptr<SvNumberformat> p_Entry(new SvNumberformat(rString,
|
|
rCurrentLanguage.pFormatScanner.get(),
|
|
rCurrentLanguage.pStringScanner.get(),
|
|
rNatNum,
|
|
nCheckPos,
|
|
eLge,
|
|
bReplaceBooleanEquivalent));
|
|
|
|
if (nCheckPos == 0) // Format ok
|
|
{ // Type comparison:
|
|
SvNumFormatType eCheckType = p_Entry->GetType();
|
|
if ( eCheckType != SvNumFormatType::UNDEFINED)
|
|
{
|
|
p_Entry->SetType(eCheckType | SvNumFormatType::DEFINED);
|
|
nType = eCheckType;
|
|
}
|
|
else
|
|
{
|
|
p_Entry->SetType(SvNumFormatType::DEFINED);
|
|
nType = SvNumFormatType::DEFINED;
|
|
}
|
|
|
|
sal_uInt32 CLOffset = ImpGenerateCL(rCurrentLanguage, rNatNum, eLge); // create new standard formats if necessary
|
|
|
|
nKey = ImpIsEntry(p_Entry->GetFormatstring(),CLOffset, eLge);
|
|
if (nKey == NUMBERFORMAT_ENTRY_NOT_FOUND) // only in not yet present
|
|
{
|
|
SvNumberformat* pStdFormat = GetFormatEntry(CLOffset + ZF_STANDARD);
|
|
sal_uInt32 nPos = CLOffset + pStdFormat->GetLastInsertKey( SvNumberformat::FormatterPrivateAccess() );
|
|
if (nPos+1 - CLOffset >= SV_COUNTRY_LANGUAGE_OFFSET)
|
|
{
|
|
SAL_WARN( "svl.numbers", "SvNumberFormatter::PutEntry: too many formats for CL");
|
|
}
|
|
else if (!aFTable.emplace( nPos+1, std::move(p_Entry)).second)
|
|
{
|
|
SAL_WARN( "svl.numbers", "SvNumberFormatter::PutEntry: dup position");
|
|
}
|
|
else
|
|
{
|
|
bCheck = true;
|
|
nKey = nPos+1;
|
|
pStdFormat->SetLastInsertKey(static_cast<sal_uInt16>(nKey-CLOffset), SvNumberformat::FormatterPrivateAccess());
|
|
}
|
|
}
|
|
}
|
|
return bCheck;
|
|
}
|
|
|
|
bool SvNumberFormatter::PutEntry(OUString& rString,
|
|
sal_Int32& nCheckPos,
|
|
SvNumFormatType& nType,
|
|
sal_uInt32& nKey, // format key
|
|
LanguageType eLnge,
|
|
bool bReplaceBooleanEquivalent)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return m_aFormatData.PutEntry(m_aCurrentLanguage, GetNatNum(), rString, nCheckPos, nType, nKey, eLnge, bReplaceBooleanEquivalent);
|
|
}
|
|
|
|
bool SvNumberFormatter::PutandConvertEntry(OUString& rString,
|
|
sal_Int32& nCheckPos,
|
|
SvNumFormatType& nType,
|
|
sal_uInt32& nKey,
|
|
LanguageType eLnge,
|
|
LanguageType eNewLnge,
|
|
bool bConvertDateOrder,
|
|
bool bReplaceBooleanEquivalent )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
bool bRes;
|
|
if (eNewLnge == LANGUAGE_DONTKNOW)
|
|
{
|
|
eNewLnge = IniLnge;
|
|
}
|
|
m_aCurrentLanguage.pFormatScanner->SetConvertMode(eLnge, eNewLnge, false, bConvertDateOrder);
|
|
bRes = PutEntry(rString, nCheckPos, nType, nKey, eLnge, bReplaceBooleanEquivalent);
|
|
m_aCurrentLanguage.pFormatScanner->SetConvertMode(false);
|
|
|
|
if (bReplaceBooleanEquivalent && nCheckPos == 0 && nType == SvNumFormatType::DEFINED
|
|
&& nKey != NUMBERFORMAT_ENTRY_NOT_FOUND)
|
|
{
|
|
// The boolean string formats are always "user defined" without any
|
|
// other type.
|
|
const SvNumberformat* pEntry = m_aFormatData.GetFormatEntry(nKey);
|
|
if (pEntry && pEntry->GetType() == SvNumFormatType::DEFINED)
|
|
{
|
|
// Replace boolean string format with Boolean in target locale, in
|
|
// case the source strings are the target locale's.
|
|
const OUString aSaveString = rString;
|
|
ChangeIntl(eNewLnge);
|
|
if (m_aCurrentLanguage.pFormatScanner->ReplaceBooleanEquivalent( rString))
|
|
{
|
|
const sal_Int32 nSaveCheckPos = nCheckPos;
|
|
const SvNumFormatType nSaveType = nType;
|
|
const sal_uInt32 nSaveKey = nKey;
|
|
const bool bTargetRes = PutEntry(rString, nCheckPos, nType, nKey, eNewLnge, false);
|
|
if (nCheckPos == 0 && nType == SvNumFormatType::LOGICAL && nKey != NUMBERFORMAT_ENTRY_NOT_FOUND)
|
|
{
|
|
bRes = bTargetRes;
|
|
}
|
|
else
|
|
{
|
|
SAL_WARN("svl.numbers", "SvNumberFormatter::PutandConvertEntry: can't scan boolean replacement");
|
|
// Live with the source boolean string format.
|
|
rString = aSaveString;
|
|
nCheckPos = nSaveCheckPos;
|
|
nType = nSaveType;
|
|
nKey = nSaveKey;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bRes;
|
|
}
|
|
|
|
bool SvNumberFormatter::PutandConvertEntrySystem(OUString& rString,
|
|
sal_Int32& nCheckPos,
|
|
SvNumFormatType& nType,
|
|
sal_uInt32& nKey,
|
|
LanguageType eLnge,
|
|
LanguageType eNewLnge)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
bool bRes;
|
|
if (eNewLnge == LANGUAGE_DONTKNOW)
|
|
{
|
|
eNewLnge = IniLnge;
|
|
}
|
|
m_aCurrentLanguage.pFormatScanner->SetConvertMode(eLnge, eNewLnge, true, true);
|
|
bRes = PutEntry(rString, nCheckPos, nType, nKey, eLnge);
|
|
m_aCurrentLanguage.pFormatScanner->SetConvertMode(false);
|
|
return bRes;
|
|
}
|
|
|
|
sal_uInt32 SvNumberFormatter::GetIndexPuttingAndConverting( OUString & rString, LanguageType eLnge,
|
|
LanguageType eSysLnge, SvNumFormatType & rType,
|
|
bool & rNewInserted, sal_Int32 & rCheckPos )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
sal_uInt32 nKey = NUMBERFORMAT_ENTRY_NOT_FOUND;
|
|
rNewInserted = false;
|
|
rCheckPos = 0;
|
|
|
|
// #62389# empty format string (of Writer) => General standard format
|
|
if (rString.isEmpty())
|
|
{
|
|
// nothing
|
|
}
|
|
else if (eLnge == LANGUAGE_SYSTEM && eSysLnge != SvtSysLocale().GetLanguageTag().getLanguageType())
|
|
{
|
|
sal_uInt32 nOrig = GetEntryKey( rString, eSysLnge );
|
|
if (nOrig == NUMBERFORMAT_ENTRY_NOT_FOUND)
|
|
{
|
|
nKey = nOrig; // none available, maybe user-defined
|
|
}
|
|
else
|
|
{
|
|
nKey = GetFormatForLanguageIfBuiltIn( nOrig, SvtSysLocale().GetLanguageTag().getLanguageType() );
|
|
}
|
|
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.
|
|
OUString aTmp( rString);
|
|
rNewInserted = PutandConvertEntrySystem( aTmp, rCheckPos, rType,
|
|
nKey, eLnge, SvtSysLocale().GetLanguageTag().getLanguageType());
|
|
if (rCheckPos > 0)
|
|
{
|
|
SAL_WARN( "svl.numbers", "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)
|
|
{
|
|
SAL_WARN( "svl.numbers", "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 & SvNumFormatType::CURRENCY)
|
|
{
|
|
const SvNumberformat* pFormat = GetEntry( nKey);
|
|
if (!pFormat->HasNewCurrency())
|
|
{
|
|
if (rNewInserted)
|
|
{
|
|
DeleteEntry( nKey); // don't leave trails of rubbish
|
|
rNewInserted = false;
|
|
}
|
|
nKey = GetStandardFormat( SvNumFormatType::CURRENCY, eLnge);
|
|
}
|
|
}
|
|
return nKey;
|
|
}
|
|
|
|
void SvNumberFormatter::DeleteEntry(sal_uInt32 nKey)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
m_aFormatData.aFTable.erase(nKey);
|
|
}
|
|
|
|
void SvNumberFormatter::GetUsedLanguages( std::vector<LanguageType>& rList )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
rList.clear();
|
|
|
|
sal_uInt32 nOffset = 0;
|
|
while (nOffset <= m_aFormatData.MaxCLOffset)
|
|
{
|
|
SvNumberformat* pFormat = m_aFormatData.GetFormatEntry(nOffset);
|
|
if (pFormat)
|
|
{
|
|
rList.push_back( pFormat->GetLanguage() );
|
|
}
|
|
nOffset += SV_COUNTRY_LANGUAGE_OFFSET;
|
|
}
|
|
}
|
|
|
|
|
|
void SvNumberFormatter::FillKeywordTable( NfKeywordTable& rKeywords,
|
|
LanguageType eLang )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
ChangeIntl( eLang );
|
|
const NfKeywordTable & rTable = m_aCurrentLanguage.pFormatScanner->GetKeywords();
|
|
for ( sal_uInt16 i = 0; i < NF_KEYWORD_ENTRIES_COUNT; ++i )
|
|
{
|
|
rKeywords[i] = rTable[i];
|
|
}
|
|
}
|
|
|
|
|
|
void SvNumberFormatter::FillKeywordTableForExcel( NfKeywordTable& rKeywords )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
FillKeywordTable( rKeywords, LANGUAGE_ENGLISH_US );
|
|
|
|
// Replace upper case "GENERAL" with proper case "General".
|
|
rKeywords[ NF_KEY_GENERAL ] = GetStandardName( LANGUAGE_ENGLISH_US );
|
|
|
|
// Excel or OOXML do not specify format code keywords case sensitivity,
|
|
// but given and writes them lower case. Using upper case even lead to an
|
|
// odd misrepresentation in iOS viewer and OSX Quicklook viewer that
|
|
// strangely use "D" and "DD" for "days since beginning of year", which is
|
|
// nowhere defined. See tdf#126773
|
|
// Use lower case for all date and time keywords where known. See OOXML
|
|
// ECMA-376-1:2016 18.8.31 numFmts (Number Formats)
|
|
rKeywords[ NF_KEY_MI ] = "m";
|
|
rKeywords[ NF_KEY_MMI ] = "mm";
|
|
rKeywords[ NF_KEY_M ] = "m";
|
|
rKeywords[ NF_KEY_MM ] = "mm";
|
|
rKeywords[ NF_KEY_MMM ] = "mmm";
|
|
rKeywords[ NF_KEY_MMMM ] = "mmmm";
|
|
rKeywords[ NF_KEY_MMMMM ] = "mmmmm";
|
|
rKeywords[ NF_KEY_H ] = "h";
|
|
rKeywords[ NF_KEY_HH ] = "hh";
|
|
rKeywords[ NF_KEY_S ] = "s";
|
|
rKeywords[ NF_KEY_SS ] = "ss";
|
|
/* XXX: not defined in OOXML: rKeywords[ NF_KEY_Q ] = "q"; */
|
|
/* XXX: not defined in OOXML: rKeywords[ NF_KEY_QQ ] = "qq"; */
|
|
rKeywords[ NF_KEY_D ] = "d";
|
|
rKeywords[ NF_KEY_DD ] = "dd";
|
|
rKeywords[ NF_KEY_DDD ] = "ddd";
|
|
rKeywords[ NF_KEY_DDDD ] = "dddd";
|
|
rKeywords[ NF_KEY_YY ] = "yy";
|
|
rKeywords[ NF_KEY_YYYY ] = "yyyy";
|
|
/* XXX: not defined in OOXML: rKeywords[ NF_KEY_AAA ] = "aaa"; */
|
|
/* XXX: not defined in OOXML: rKeywords[ NF_KEY_AAAA ] = "aaaa"; */
|
|
rKeywords[ NF_KEY_EC ] = "e";
|
|
rKeywords[ NF_KEY_EEC ] = "ee";
|
|
rKeywords[ NF_KEY_G ] = "g";
|
|
rKeywords[ NF_KEY_GG ] = "gg";
|
|
rKeywords[ NF_KEY_GGG ] = "ggg";
|
|
rKeywords[ NF_KEY_R ] = "r";
|
|
rKeywords[ NF_KEY_RR ] = "rr";
|
|
/* XXX: not defined in OOXML: rKeywords[ NF_KEY_WW ] = "ww"; */
|
|
|
|
// Remap codes unknown to Excel.
|
|
rKeywords[ NF_KEY_NN ] = "ddd";
|
|
rKeywords[ NF_KEY_NNN ] = "dddd";
|
|
// NNNN gets a separator appended in SvNumberformat::GetMappedFormatString()
|
|
rKeywords[ NF_KEY_NNNN ] = "dddd";
|
|
// Export the Thai T NatNum modifier. This must be uppercase for internal reasons.
|
|
rKeywords[ NF_KEY_THAI_T ] = "T";
|
|
}
|
|
|
|
static OUString lcl_buildBooleanStringFormat(const SvNumberformat* pEntry, const NativeNumberWrapper& rNatNum, const SvNFLanguageData& rCurrentLang)
|
|
{
|
|
// Build Boolean number format, which needs non-zero and zero subformat
|
|
// codes with TRUE and FALSE strings.
|
|
const Color* pColor = nullptr;
|
|
OUString aFormatStr, aTemp;
|
|
pEntry->GetOutputString( 1.0, aTemp, &pColor, rNatNum, rCurrentLang );
|
|
aFormatStr += "\"" + aTemp + "\";\"" + aTemp + "\";\"";
|
|
pEntry->GetOutputString( 0.0, aTemp, &pColor, rNatNum, rCurrentLang );
|
|
aFormatStr += aTemp + "\"";
|
|
return aFormatStr;
|
|
}
|
|
|
|
OUString SvNumberFormatter::GetFormatStringForExcel( sal_uInt32 nKey, const NfKeywordTable& rKeywords,
|
|
SvNumberFormatter& rTempFormatter ) const
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
OUString aFormatStr;
|
|
if (const SvNumberformat* pEntry = GetEntry( nKey))
|
|
{
|
|
if (pEntry->GetType() == SvNumFormatType::LOGICAL)
|
|
{
|
|
// Build a source locale dependent string boolean. This is
|
|
// expected when loading the document in the same locale or if
|
|
// several locales are used, but not for other system/default
|
|
// locales. You can't have both. We could force to English for all
|
|
// locales like below, but Excel would display English strings then
|
|
// even for the system locale matching this locale. YMMV.
|
|
aFormatStr = lcl_buildBooleanStringFormat(pEntry, GetNatNum(), m_aCurrentLanguage);
|
|
}
|
|
else
|
|
{
|
|
bool bIsLOK = comphelper::LibreOfficeKit::isActive();
|
|
bool bSystemLanguage = false;
|
|
LanguageType nLang = pEntry->GetLanguage();
|
|
if (nLang == LANGUAGE_SYSTEM)
|
|
{
|
|
bSystemLanguage = true;
|
|
nLang = SvtSysLocale().GetLanguageTag().getLanguageType();
|
|
}
|
|
if (!bIsLOK && nLang != LANGUAGE_ENGLISH_US)
|
|
{
|
|
sal_Int32 nCheckPos;
|
|
SvNumFormatType nType = SvNumFormatType::DEFINED;
|
|
sal_uInt32 nTempKey;
|
|
OUString aTemp( pEntry->GetFormatstring());
|
|
/* TODO: do we want bReplaceBooleanEquivalent=true in any case
|
|
* to write it as English string boolean? */
|
|
rTempFormatter.PutandConvertEntry( aTemp, nCheckPos, nType, nTempKey, nLang, LANGUAGE_ENGLISH_US,
|
|
false /*bConvertDateOrder*/, false /*bReplaceBooleanEquivalent*/);
|
|
SAL_WARN_IF( nCheckPos != 0, "svl.numbers",
|
|
"SvNumberFormatter::GetFormatStringForExcel - format code not convertible");
|
|
if (nTempKey != NUMBERFORMAT_ENTRY_NOT_FOUND)
|
|
pEntry = rTempFormatter.GetEntry( nTempKey);
|
|
}
|
|
|
|
if (pEntry)
|
|
{
|
|
if (pEntry->GetType() == SvNumFormatType::LOGICAL)
|
|
{
|
|
// This would be reached if bReplaceBooleanEquivalent was
|
|
// true and the source format is a string boolean like
|
|
// >"VRAI";"VRAI";"FAUX"< recognized as real boolean and
|
|
// properly converted. Then written as
|
|
// >"TRUE";"TRUE";"FALSE"<
|
|
aFormatStr = lcl_buildBooleanStringFormat(pEntry, GetNatNum(), m_aCurrentLanguage);
|
|
}
|
|
else
|
|
{
|
|
// m_aCurrentLanguage.GetLocaleData() returns the current locale's data, so switch
|
|
// before (which doesn't do anything if it was the same locale
|
|
// already).
|
|
rTempFormatter.ChangeIntl( LANGUAGE_ENGLISH_US);
|
|
aFormatStr = pEntry->GetMappedFormatstring( rKeywords, *rTempFormatter.m_aCurrentLanguage.GetLocaleData(), nLang,
|
|
bSystemLanguage);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SAL_WARN("svl.numbers","SvNumberFormatter::GetFormatStringForExcel - format not found: " << nKey);
|
|
}
|
|
|
|
if (aFormatStr.isEmpty())
|
|
aFormatStr = "General";
|
|
return aFormatStr;
|
|
}
|
|
|
|
|
|
OUString SvNumberFormatter::GetKeyword( LanguageType eLnge, sal_uInt16 nIndex )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
ChangeIntl(eLnge);
|
|
const NfKeywordTable & rTable = m_aCurrentLanguage.pFormatScanner->GetKeywords();
|
|
if ( nIndex < NF_KEYWORD_ENTRIES_COUNT )
|
|
{
|
|
return rTable[nIndex];
|
|
}
|
|
SAL_WARN( "svl.numbers", "GetKeyword: invalid index");
|
|
return OUString();
|
|
}
|
|
|
|
|
|
OUString SvNumberFormatter::GetStandardName( LanguageType eLnge )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
ChangeIntl( eLnge );
|
|
return m_aCurrentLanguage.pFormatScanner->GetStandardName();
|
|
}
|
|
|
|
SvNFFormatData::SvNFFormatData()
|
|
: MaxCLOffset(0)
|
|
, nDefaultSystemCurrencyFormat(NUMBERFORMAT_ENTRY_NOT_FOUND)
|
|
, bNoZero(false)
|
|
{
|
|
}
|
|
|
|
SvNFFormatData::~SvNFFormatData() = default;
|
|
|
|
sal_uInt32 SvNFFormatData::ImpGetCLOffset(LanguageType eLnge) const
|
|
{
|
|
sal_uInt32 nOffset = 0;
|
|
while (nOffset <= MaxCLOffset)
|
|
{
|
|
const SvNumberformat* pFormat = GetFormatEntry(nOffset);
|
|
if (pFormat && pFormat->GetLanguage() == eLnge)
|
|
{
|
|
return nOffset;
|
|
}
|
|
nOffset += SV_COUNTRY_LANGUAGE_OFFSET;
|
|
}
|
|
return nOffset;
|
|
}
|
|
|
|
sal_uInt32 SvNFFormatData::ImpIsEntry(std::u16string_view rString,
|
|
sal_uInt32 nCLOffset,
|
|
LanguageType eLnge) const
|
|
{
|
|
sal_uInt32 res = NUMBERFORMAT_ENTRY_NOT_FOUND;
|
|
auto it = aFTable.find( nCLOffset);
|
|
while ( res == NUMBERFORMAT_ENTRY_NOT_FOUND &&
|
|
it != aFTable.end() && it->second->GetLanguage() == eLnge )
|
|
{
|
|
if ( rString == it->second->GetFormatstring() )
|
|
{
|
|
res = it->first;
|
|
}
|
|
else
|
|
{
|
|
++it;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
sal_uInt32 SvNumberFormatter::ImpIsEntry(std::u16string_view rString,
|
|
sal_uInt32 nCLOffset,
|
|
LanguageType eLnge) const
|
|
{
|
|
return m_aFormatData.ImpIsEntry(rString, nCLOffset, eLnge);
|
|
}
|
|
|
|
SvNumberFormatTable& SvNumberFormatter::GetFirstEntryTable(
|
|
SvNumFormatType& eType,
|
|
sal_uInt32& FIndex,
|
|
LanguageType& rLnge)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
SvNumFormatType eTypetmp = eType;
|
|
if (eType == SvNumFormatType::ALL) // empty cell or don't care
|
|
{
|
|
rLnge = IniLnge;
|
|
}
|
|
else
|
|
{
|
|
SvNumberformat* pFormat = m_aFormatData.GetFormatEntry(FIndex);
|
|
if (!pFormat)
|
|
{
|
|
rLnge = IniLnge;
|
|
eType = SvNumFormatType::ALL;
|
|
eTypetmp = eType;
|
|
}
|
|
else
|
|
{
|
|
rLnge = pFormat->GetLanguage();
|
|
eType = pFormat->GetMaskedType();
|
|
if (eType == SvNumFormatType::ALL)
|
|
{
|
|
eType = SvNumFormatType::DEFINED;
|
|
eTypetmp = eType;
|
|
}
|
|
else if (eType == SvNumFormatType::DATETIME)
|
|
{
|
|
eTypetmp = eType;
|
|
eType = SvNumFormatType::DATE;
|
|
}
|
|
else
|
|
{
|
|
eTypetmp = eType;
|
|
}
|
|
}
|
|
}
|
|
ChangeIntl(rLnge);
|
|
return GetEntryTable(eTypetmp, FIndex, rLnge);
|
|
}
|
|
|
|
sal_uInt32 SvNFFormatData::ImpGenerateCL(SvNFLanguageData& rCurrentLanguage, const NativeNumberWrapper& rNatNum, LanguageType eLnge)
|
|
{
|
|
rCurrentLanguage.ChangeIntl(eLnge);
|
|
sal_uInt32 CLOffset = ImpGetCLOffset(rCurrentLanguage.ActLnge);
|
|
if (CLOffset > MaxCLOffset)
|
|
{
|
|
// new CL combination
|
|
if (LocaleDataWrapper::areChecksEnabled())
|
|
{
|
|
const LanguageTag aLoadedLocale = rCurrentLanguage.xLocaleData->getLoadedLanguageTag();
|
|
if ( !aLoadedLocale.equals( rCurrentLanguage.aLanguageTag ) )
|
|
{
|
|
LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo( u"SvNumberFormatter::ImpGenerateCL: locales don't match:" ));
|
|
}
|
|
// test XML locale data FormatElement entries
|
|
{
|
|
uno::Sequence< i18n::FormatElement > xSeq = rCurrentLanguage.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;
|
|
OUStringBuffer aDupes;
|
|
for ( sal_Int32 i = 0; i < xSeq.getLength(); i++ )
|
|
{
|
|
if ( i != j && xSeq[i].formatIndex == nIdx )
|
|
{
|
|
aDupes.append( OUString::number(i) + "(" + xSeq[i].formatKey + ") ");
|
|
}
|
|
}
|
|
if ( !aDupes.isEmpty() )
|
|
{
|
|
OUString aMsg = "XML locale data FormatElement formatindex dupe: "
|
|
+ OUString::number(nIdx)
|
|
+ "\nFormatElements: "
|
|
+ OUString::number( j )
|
|
+ "("
|
|
+ xSeq[j].formatKey
|
|
+ ") "
|
|
+ aDupes;
|
|
LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo( aMsg ));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
MaxCLOffset += SV_COUNTRY_LANGUAGE_OFFSET;
|
|
ImpGenerateFormats(rCurrentLanguage, rNatNum, MaxCLOffset, false/*bNoAdditionalFormats*/ );
|
|
CLOffset = MaxCLOffset;
|
|
}
|
|
return CLOffset;
|
|
}
|
|
|
|
SvNumberFormatTable& SvNumberFormatter::ChangeCL(SvNumFormatType eType,
|
|
sal_uInt32& FIndex,
|
|
LanguageType eLnge)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
m_aFormatData.ImpGenerateCL(m_aCurrentLanguage, GetNatNum(), eLnge);
|
|
return GetEntryTable(eType, FIndex, m_aCurrentLanguage.ActLnge);
|
|
}
|
|
|
|
SvNumberFormatTable& SvNumberFormatter::GetEntryTable(
|
|
SvNumFormatType eType,
|
|
sal_uInt32& FIndex,
|
|
LanguageType eLnge)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
if ( pFormatTable )
|
|
{
|
|
pFormatTable->clear();
|
|
}
|
|
else
|
|
{
|
|
pFormatTable.reset( new SvNumberFormatTable );
|
|
}
|
|
ChangeIntl(eLnge);
|
|
sal_uInt32 CLOffset = m_aFormatData.ImpGetCLOffset(m_aCurrentLanguage.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, m_aCurrentLanguage.ActLnge );
|
|
|
|
auto it = m_aFormatData.aFTable.find( CLOffset);
|
|
|
|
if (eType == SvNumFormatType::ALL)
|
|
{
|
|
while (it != m_aFormatData.aFTable.end() && it->second->GetLanguage() == m_aCurrentLanguage.ActLnge)
|
|
{ // copy all entries to output table
|
|
(*pFormatTable)[ it->first ] = it->second.get();
|
|
++it;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (it != m_aFormatData.aFTable.end() && it->second->GetLanguage() == m_aCurrentLanguage.ActLnge)
|
|
{ // copy entries of queried type to output table
|
|
if ((it->second->GetType()) & eType)
|
|
(*pFormatTable)[ it->first ] = it->second.get();
|
|
++it;
|
|
}
|
|
}
|
|
if ( !pFormatTable->empty() )
|
|
{ // select default if queried format doesn't exist or queried type or
|
|
// language differ from existing format
|
|
SvNumberformat* pEntry = m_aFormatData.GetFormatEntry(FIndex);
|
|
if ( !pEntry || !(pEntry->GetType() & eType) || pEntry->GetLanguage() != m_aCurrentLanguage.ActLnge )
|
|
{
|
|
FIndex = nDefaultIndex;
|
|
}
|
|
}
|
|
return *pFormatTable;
|
|
}
|
|
|
|
namespace {
|
|
|
|
const SvNumberformat* ImpSubstituteEntry(SvNFLanguageData& rCurrentLanguage, const SvNFFormatData& rFormatData,
|
|
const NativeNumberWrapper& rNatNum, const SvNFEngine::Accessor& rFuncs,
|
|
const SvNumberformat* pFormat, sal_uInt32* o_pRealKey)
|
|
{
|
|
if (!pFormat || !pFormat->IsSubstituted())
|
|
return pFormat;
|
|
|
|
// XXX NOTE: substitution can not be done in GetFormatEntry() as otherwise
|
|
// to be substituted formats would "vanish", i.e. from the number formatter
|
|
// dialog or when exporting to Excel.
|
|
|
|
sal_uInt32 nKey;
|
|
if (pFormat->IsSystemTimeFormat())
|
|
{
|
|
/* TODO: should we have NF_TIME_SYSTEM for consistency? */
|
|
nKey = SvNFEngine::GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs,
|
|
SvNumFormatType::TIME, LANGUAGE_SYSTEM);
|
|
}
|
|
else if (pFormat->IsSystemLongDateFormat())
|
|
/* TODO: either that above, or have a long option for GetStandardFormat() */
|
|
{
|
|
nKey = SvNFEngine::GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum,
|
|
NF_DATE_SYSTEM_LONG, LANGUAGE_SYSTEM);
|
|
}
|
|
else
|
|
return pFormat;
|
|
|
|
if (o_pRealKey)
|
|
*o_pRealKey = nKey;
|
|
return rFormatData.GetFormatEntry(nKey);
|
|
}
|
|
|
|
}
|
|
|
|
bool SvNFEngine::IsNumberFormat(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
|
|
const OUString& sString,
|
|
sal_uInt32& F_Index,
|
|
double& fOutNumber,
|
|
SvNumInputOptions eInputOptions)
|
|
{
|
|
SvNumFormatType FType;
|
|
// For the 0 General format directly use the init/system locale and avoid
|
|
// all overhead that is associated with a format passed to the scanner.
|
|
const SvNumberformat* pFormat = (F_Index == 0 ? nullptr :
|
|
::ImpSubstituteEntry(rCurrentLanguage, rFormatData, rNatNum, rFuncs, rFormatData.GetFormatEntry(F_Index), nullptr));
|
|
if (!pFormat)
|
|
{
|
|
rCurrentLanguage.ChangeIntl(rCurrentLanguage.IniLnge);
|
|
FType = SvNumFormatType::NUMBER;
|
|
}
|
|
else
|
|
{
|
|
FType = pFormat->GetMaskedType();
|
|
if (FType == SvNumFormatType::ALL)
|
|
{
|
|
FType = SvNumFormatType::DEFINED;
|
|
}
|
|
rCurrentLanguage.ChangeIntl(pFormat->GetLanguage());
|
|
// Avoid scanner overhead with the General format of any locale.
|
|
// These are never substituted above so safe to ignore.
|
|
if ((F_Index % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
|
|
{
|
|
assert(FType == SvNumFormatType::NUMBER);
|
|
pFormat = nullptr;
|
|
}
|
|
}
|
|
|
|
bool res;
|
|
SvNumFormatType RType = FType;
|
|
if (RType == SvNumFormatType::TEXT)
|
|
{
|
|
res = false; // type text preset => no conversion to number
|
|
}
|
|
else
|
|
{
|
|
res = rCurrentLanguage.pStringScanner->IsNumberFormat(sString, RType, fOutNumber, pFormat, rNatNum, eInputOptions);
|
|
}
|
|
if (res && !SvNumberFormatter::IsCompatible(FType, RType)) // non-matching type
|
|
{
|
|
switch ( RType )
|
|
{
|
|
case SvNumFormatType::DATE :
|
|
// Preserve ISO 8601 input.
|
|
if (rCurrentLanguage.pStringScanner->CanForceToIso8601( DateOrder::Invalid))
|
|
{
|
|
F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATE_DIN_YYYYMMDD, rCurrentLanguage.ActLnge );
|
|
}
|
|
else
|
|
{
|
|
F_Index = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, RType, rCurrentLanguage.ActLnge);
|
|
}
|
|
break;
|
|
case SvNumFormatType::TIME :
|
|
if ( rCurrentLanguage.pStringScanner->GetDecPos() )
|
|
{
|
|
// 100th seconds
|
|
if ( rCurrentLanguage.pStringScanner->GetNumericsCount() > 3 || fOutNumber < 0.0 )
|
|
{
|
|
F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_TIME_HH_MMSS00, rCurrentLanguage.ActLnge);
|
|
}
|
|
else
|
|
{
|
|
F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_TIME_MMSS00, rCurrentLanguage.ActLnge);
|
|
}
|
|
}
|
|
else if ( fOutNumber >= 1.0 || fOutNumber < 0.0 )
|
|
{
|
|
F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_TIME_HH_MMSS, rCurrentLanguage.ActLnge);
|
|
}
|
|
else
|
|
{
|
|
F_Index = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, RType, rCurrentLanguage.ActLnge );
|
|
}
|
|
break;
|
|
case SvNumFormatType::DATETIME :
|
|
// Preserve ISO 8601 input.
|
|
if (rCurrentLanguage.pStringScanner->HasIso8601Tsep())
|
|
{
|
|
if (rCurrentLanguage.pStringScanner->GetDecPos())
|
|
F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDDTHHMMSS000, rCurrentLanguage.ActLnge);
|
|
else
|
|
F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDDTHHMMSS, rCurrentLanguage.ActLnge);
|
|
}
|
|
else if (rCurrentLanguage.pStringScanner->CanForceToIso8601( DateOrder::Invalid))
|
|
{
|
|
if (rCurrentLanguage.pStringScanner->GetDecPos())
|
|
F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDD_HHMMSS000, rCurrentLanguage.ActLnge);
|
|
else
|
|
F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDD_HHMMSS, rCurrentLanguage.ActLnge);
|
|
}
|
|
else
|
|
{
|
|
F_Index = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, RType, rCurrentLanguage.ActLnge);
|
|
}
|
|
break;
|
|
default:
|
|
F_Index = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, RType, rCurrentLanguage.ActLnge);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
bool SvNumberFormatter::IsNumberFormat(const OUString& sString,
|
|
sal_uInt32& F_Index,
|
|
double& fOutNumber,
|
|
SvNumInputOptions eInputOptions)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
|
|
return SvNFEngine::IsNumberFormat(m_aCurrentLanguage, m_aFormatData, GetNatNum(),
|
|
m_aRWPolicy, sString, F_Index, fOutNumber,
|
|
eInputOptions);
|
|
}
|
|
|
|
LanguageType SvNumberFormatter::GetLanguage() const
|
|
{
|
|
return IniLnge; // immutable
|
|
}
|
|
|
|
// static
|
|
bool SvNumberFormatter::IsCompatible(SvNumFormatType eOldType, SvNumFormatType eNewType)
|
|
{
|
|
if (eOldType == eNewType)
|
|
{
|
|
return true;
|
|
}
|
|
else if (eOldType == SvNumFormatType::DEFINED)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
switch (eNewType)
|
|
{
|
|
case SvNumFormatType::NUMBER:
|
|
switch (eOldType)
|
|
{
|
|
case SvNumFormatType::PERCENT:
|
|
case SvNumFormatType::CURRENCY:
|
|
case SvNumFormatType::SCIENTIFIC:
|
|
case SvNumFormatType::FRACTION:
|
|
case SvNumFormatType::DEFINED:
|
|
return true;
|
|
case SvNumFormatType::LOGICAL:
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
case SvNumFormatType::DATE:
|
|
switch (eOldType)
|
|
{
|
|
case SvNumFormatType::DATETIME:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
case SvNumFormatType::TIME:
|
|
switch (eOldType)
|
|
{
|
|
case SvNumFormatType::DATETIME:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
case SvNumFormatType::DATETIME:
|
|
switch (eOldType)
|
|
{
|
|
case SvNumFormatType::TIME:
|
|
case SvNumFormatType::DATE:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
break;
|
|
case SvNumFormatType::DURATION:
|
|
return false;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
static sal_uInt32 ImpGetSearchOffset(SvNumFormatType nType, sal_uInt32 CLOffset)
|
|
{
|
|
sal_uInt32 nSearch;
|
|
switch( nType )
|
|
{
|
|
case SvNumFormatType::DATE:
|
|
nSearch = CLOffset + ZF_STANDARD_DATE;
|
|
break;
|
|
case SvNumFormatType::TIME:
|
|
nSearch = CLOffset + ZF_STANDARD_TIME;
|
|
break;
|
|
case SvNumFormatType::DATETIME:
|
|
nSearch = CLOffset + ZF_STANDARD_DATETIME;
|
|
break;
|
|
case SvNumFormatType::DURATION:
|
|
nSearch = CLOffset + ZF_STANDARD_DURATION;
|
|
break;
|
|
case SvNumFormatType::PERCENT:
|
|
nSearch = CLOffset + ZF_STANDARD_PERCENT;
|
|
break;
|
|
case SvNumFormatType::SCIENTIFIC:
|
|
nSearch = CLOffset + ZF_STANDARD_SCIENTIFIC;
|
|
break;
|
|
default:
|
|
nSearch = CLOffset + ZF_STANDARD;
|
|
}
|
|
return nSearch;
|
|
}
|
|
|
|
// static
|
|
sal_uInt32 SvNFEngine::ImpGetDefaultFormat(const SvNFFormatData& rFormatData,
|
|
const SvNFEngine::Accessor& rFuncs,
|
|
SvNumFormatType nType, sal_uInt32 CLOffset)
|
|
{
|
|
sal_uInt32 nSearch = ImpGetSearchOffset(nType, CLOffset);
|
|
|
|
sal_uInt32 nDefaultFormat = rFuncs.mFindFormat(nSearch);
|
|
if (nDefaultFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
|
|
return nDefaultFormat;
|
|
|
|
nDefaultFormat = ImpGetDefaultFormat(rFormatData, nType, CLOffset);
|
|
rFuncs.mCacheFormat(nSearch, nDefaultFormat);
|
|
return nDefaultFormat;
|
|
}
|
|
|
|
namespace {
|
|
|
|
sal_uInt32 ImpGetFormatIndex(NfIndexTableOffset nTabOff, sal_uInt32 nCLOffset)
|
|
{
|
|
if (nTabOff >= NF_INDEX_TABLE_ENTRIES)
|
|
return NUMBERFORMAT_ENTRY_NOT_FOUND;
|
|
|
|
if (indexTable[nTabOff] == NUMBERFORMAT_ENTRY_NOT_FOUND)
|
|
return NUMBERFORMAT_ENTRY_NOT_FOUND;
|
|
|
|
return nCLOffset + indexTable[nTabOff];
|
|
}
|
|
|
|
bool ImpIsSpecialStandardFormat(sal_uInt32 nFIndex, sal_uInt32 nCLOffset)
|
|
{
|
|
return
|
|
nFIndex == ImpGetFormatIndex( NF_TIME_MMSS00, nCLOffset ) ||
|
|
nFIndex == ImpGetFormatIndex( NF_TIME_HH_MMSS00, nCLOffset ) ||
|
|
nFIndex == ImpGetFormatIndex( NF_TIME_HH_MMSS, nCLOffset )
|
|
;
|
|
}
|
|
|
|
bool ImpIsSpecialStandardFormat(SvNFLanguageData& rCurrentLanguage,
|
|
const NativeNumberWrapper& rNatNum, const SvNFEngine::Accessor& rFuncs,
|
|
sal_uInt32 nFIndex, LanguageType eLnge)
|
|
{
|
|
eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
|
|
sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)
|
|
return ::ImpIsSpecialStandardFormat(nFIndex, nCLOffset);
|
|
}
|
|
}
|
|
|
|
sal_uInt32 SvNFEngine::ImpGetStandardFormat(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
const NativeNumberWrapper& rNatNum,
|
|
const SvNFEngine::Accessor& rFuncs,
|
|
SvNumFormatType eType,
|
|
sal_uInt32 CLOffset,
|
|
LanguageType eLnge)
|
|
{
|
|
switch(eType)
|
|
{
|
|
case SvNumFormatType::CURRENCY:
|
|
return rFuncs.mGetDefaultCurrency(rCurrentLanguage, rNatNum, CLOffset, eLnge);
|
|
case SvNumFormatType::DURATION :
|
|
return ImpGetFormatIndex(NF_TIME_HH_MMSS, CLOffset);
|
|
case SvNumFormatType::DATE:
|
|
case SvNumFormatType::TIME:
|
|
case SvNumFormatType::DATETIME:
|
|
case SvNumFormatType::PERCENT:
|
|
case SvNumFormatType::SCIENTIFIC:
|
|
return ImpGetDefaultFormat(rFormatData, rFuncs, eType, CLOffset);
|
|
case SvNumFormatType::FRACTION:
|
|
return CLOffset + ZF_STANDARD_FRACTION;
|
|
case SvNumFormatType::LOGICAL:
|
|
return CLOffset + ZF_STANDARD_LOGICAL;
|
|
case SvNumFormatType::TEXT:
|
|
return CLOffset + ZF_STANDARD_TEXT;
|
|
case SvNumFormatType::ALL:
|
|
case SvNumFormatType::DEFINED:
|
|
case SvNumFormatType::NUMBER:
|
|
case SvNumFormatType::UNDEFINED:
|
|
default:
|
|
return CLOffset + ZF_STANDARD;
|
|
}
|
|
}
|
|
|
|
sal_uInt32 SvNFEngine::GetStandardFormat(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
|
|
SvNumFormatType eType,
|
|
LanguageType eLnge)
|
|
{
|
|
eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
|
|
|
|
sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)
|
|
|
|
return ImpGetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, eType, nCLOffset, eLnge);
|
|
}
|
|
|
|
sal_uInt32 SvNumberFormatter::GetStandardFormat( SvNumFormatType eType, LanguageType eLnge )
|
|
{
|
|
::osl::MutexGuard aGuard(GetInstanceMutex());
|
|
return SvNFEngine::GetStandardFormat(m_aCurrentLanguage, m_aFormatData, GetNatNum(), m_aRWPolicy, eType, eLnge);
|
|
}
|
|
|
|
bool SvNumberFormatter::ImpIsSpecialStandardFormat(sal_uInt32 nFIndex, LanguageType eLnge)
|
|
{
|
|
::osl::MutexGuard aGuard(GetInstanceMutex());
|
|
return ::ImpIsSpecialStandardFormat(m_aCurrentLanguage, GetNatNum(), m_aRWPolicy, nFIndex, eLnge);
|
|
}
|
|
|
|
sal_uInt32 SvNFEngine::GetStandardFormat(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
|
|
sal_uInt32 nFIndex, SvNumFormatType eType, LanguageType eLnge)
|
|
{
|
|
eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
|
|
|
|
sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)
|
|
|
|
if (::ImpIsSpecialStandardFormat(nFIndex, nCLOffset))
|
|
return nFIndex;
|
|
|
|
return ImpGetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, eType, nCLOffset, eLnge);
|
|
}
|
|
|
|
namespace
|
|
{
|
|
sal_uInt32 FindCachedDefaultFormat(const SvNFFormatData::DefaultFormatKeysMap& rDefaultFormatKeys, sal_uInt32 nSearch)
|
|
{
|
|
auto it = rDefaultFormatKeys.find(nSearch);
|
|
sal_uInt32 nDefaultFormat = (it != rDefaultFormatKeys.end() ?
|
|
it->second : NUMBERFORMAT_ENTRY_NOT_FOUND);
|
|
return nDefaultFormat;
|
|
}
|
|
}
|
|
|
|
sal_uInt32 SvNFFormatData::FindCachedDefaultFormat(sal_uInt32 nSearch) const
|
|
{
|
|
return ::FindCachedDefaultFormat(aDefaultFormatKeys, nSearch);
|
|
}
|
|
|
|
// static
|
|
sal_uInt32 SvNFEngine::ImpGetDefaultFormat(const SvNFFormatData& rFormatData, SvNumFormatType nType, sal_uInt32 CLOffset)
|
|
{
|
|
sal_uInt32 nDefaultFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
|
|
|
|
// look for a defined standard
|
|
sal_uInt32 nStopKey = CLOffset + SV_COUNTRY_LANGUAGE_OFFSET;
|
|
sal_uInt32 nKey(0);
|
|
auto it2 = rFormatData.aFTable.find( CLOffset );
|
|
while ( it2 != rFormatData.aFTable.end() && (nKey = it2->first ) >= CLOffset && nKey < nStopKey )
|
|
{
|
|
const SvNumberformat* pEntry = it2->second.get();
|
|
if ( pEntry->IsStandard() && (pEntry->GetMaskedType() == nType) )
|
|
{
|
|
nDefaultFormat = nKey;
|
|
break; // while
|
|
}
|
|
++it2;
|
|
}
|
|
|
|
if ( nDefaultFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
|
|
{ // none found, use old fixed standards
|
|
switch( nType )
|
|
{
|
|
case SvNumFormatType::DATE:
|
|
nDefaultFormat = CLOffset + ZF_STANDARD_DATE;
|
|
break;
|
|
case SvNumFormatType::TIME:
|
|
nDefaultFormat = CLOffset + ZF_STANDARD_TIME+1;
|
|
break;
|
|
case SvNumFormatType::DATETIME:
|
|
nDefaultFormat = CLOffset + ZF_STANDARD_DATETIME;
|
|
break;
|
|
case SvNumFormatType::DURATION:
|
|
nDefaultFormat = CLOffset + ZF_STANDARD_DURATION;
|
|
break;
|
|
case SvNumFormatType::PERCENT:
|
|
nDefaultFormat = CLOffset + ZF_STANDARD_PERCENT+1;
|
|
break;
|
|
case SvNumFormatType::SCIENTIFIC:
|
|
nDefaultFormat = CLOffset + ZF_STANDARD_SCIENTIFIC;
|
|
break;
|
|
default:
|
|
nDefaultFormat = CLOffset + ZF_STANDARD;
|
|
}
|
|
}
|
|
|
|
return nDefaultFormat;
|
|
}
|
|
|
|
sal_uInt32 SvNumberFormatter::GetStandardFormat( sal_uInt32 nFIndex, SvNumFormatType eType,
|
|
LanguageType eLnge )
|
|
{
|
|
::osl::MutexGuard aGuard(GetInstanceMutex());
|
|
|
|
return SvNFEngine::GetStandardFormat(m_aCurrentLanguage, m_aFormatData, GetNatNum(),
|
|
m_aRWPolicy, nFIndex, eType, eLnge);
|
|
}
|
|
|
|
sal_uInt32 SvNFEngine::GetTimeFormat(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
|
|
double fNumber, LanguageType eLnge, bool bForceDuration)
|
|
{
|
|
bool bSign;
|
|
if ( fNumber < 0.0 )
|
|
{
|
|
bSign = true;
|
|
fNumber = -fNumber;
|
|
}
|
|
else
|
|
bSign = false;
|
|
double fSeconds = fNumber * 86400;
|
|
if ( floor( fSeconds + 0.5 ) * 100 != floor( fSeconds * 100 + 0.5 ) )
|
|
{ // with 100th seconds
|
|
if ( bForceDuration || bSign || fSeconds >= 3600 )
|
|
return GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_TIME_HH_MMSS00, eLnge);
|
|
else
|
|
return GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_TIME_MMSS00, eLnge);
|
|
}
|
|
else
|
|
{
|
|
if ( bForceDuration || bSign || fNumber >= 1.0 )
|
|
return GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_TIME_HH_MMSS, eLnge);
|
|
else
|
|
return GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, SvNumFormatType::TIME, eLnge);
|
|
}
|
|
}
|
|
|
|
sal_uInt32 SvNumberFormatter::GetTimeFormat( double fNumber, LanguageType eLnge, bool bForceDuration )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
|
|
return SvNFEngine::GetTimeFormat(m_aCurrentLanguage, m_aFormatData, GetNatNum(), m_aRWPolicy,
|
|
fNumber, eLnge, bForceDuration);
|
|
}
|
|
|
|
sal_uInt32 SvNFEngine::GetStandardFormat(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
|
|
double fNumber, sal_uInt32 nFIndex,
|
|
SvNumFormatType eType, LanguageType eLnge)
|
|
{
|
|
if (::ImpIsSpecialStandardFormat(rCurrentLanguage, rNatNum, rFuncs, nFIndex, eLnge))
|
|
return nFIndex;
|
|
|
|
switch( eType )
|
|
{
|
|
case SvNumFormatType::DURATION :
|
|
return GetTimeFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, fNumber, eLnge, true);
|
|
case SvNumFormatType::TIME :
|
|
return GetTimeFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, fNumber, eLnge, false);
|
|
default:
|
|
return GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, eType, eLnge );
|
|
}
|
|
}
|
|
|
|
sal_uInt32 SvNumberFormatter::GetStandardFormat( double fNumber, sal_uInt32 nFIndex,
|
|
SvNumFormatType eType, LanguageType eLnge )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
if ( ImpIsSpecialStandardFormat( nFIndex, eLnge ) )
|
|
return nFIndex;
|
|
|
|
switch( eType )
|
|
{
|
|
case SvNumFormatType::DURATION :
|
|
return GetTimeFormat( fNumber, eLnge, true);
|
|
case SvNumFormatType::TIME :
|
|
return GetTimeFormat( fNumber, eLnge, false);
|
|
default:
|
|
return GetStandardFormat( eType, eLnge );
|
|
}
|
|
}
|
|
|
|
sal_uInt32 SvNumberFormatter::GuessDateTimeFormat( SvNumFormatType& rType, double fNumber, LanguageType eLnge )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
// Categorize the format according to the implementation of
|
|
// SvNumberFormatter::GetEditFormat(), making assumptions about what
|
|
// would be time only.
|
|
sal_uInt32 nRet;
|
|
if (0.0 <= fNumber && fNumber < 1.0)
|
|
{
|
|
// Clearly a time.
|
|
rType = SvNumFormatType::TIME;
|
|
nRet = GetTimeFormat( fNumber, eLnge, false);
|
|
}
|
|
else if (fabs( fNumber) * 24 < 0x7fff)
|
|
{
|
|
// Assuming duration within 32k hours or 3.7 years.
|
|
// This should be SvNumFormatType::DURATION instead, but the outer
|
|
// world can't cope with that.
|
|
rType = SvNumFormatType::TIME;
|
|
nRet = GetTimeFormat( fNumber, eLnge, true);
|
|
}
|
|
else if (rtl::math::approxFloor( fNumber) != fNumber)
|
|
{
|
|
// Date+Time.
|
|
rType = SvNumFormatType::DATETIME;
|
|
nRet = GetFormatIndex( NF_DATETIME_SYS_DDMMYYYY_HHMMSS, eLnge);
|
|
}
|
|
else
|
|
{
|
|
// Date only.
|
|
rType = SvNumFormatType::DATE;
|
|
nRet = GetFormatIndex( NF_DATE_SYS_DDMMYYYY, eLnge);
|
|
}
|
|
return nRet;
|
|
}
|
|
|
|
sal_uInt32 SvNFEngine::GetEditFormat(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
|
|
double fNumber, sal_uInt32 nFIndex,
|
|
SvNumFormatType eType,
|
|
const SvNumberformat* pFormat,
|
|
LanguageType eForLocale)
|
|
{
|
|
const LanguageType eLang = (pFormat ? pFormat->GetLanguage() : LANGUAGE_SYSTEM);
|
|
if (eForLocale == LANGUAGE_DONTKNOW)
|
|
eForLocale = eLang;
|
|
sal_uInt32 nKey = nFIndex;
|
|
switch ( eType )
|
|
{
|
|
// #61619# always edit using 4-digit year
|
|
case SvNumFormatType::DATE :
|
|
{
|
|
// Preserve ISO 8601 format.
|
|
bool bIsoDate =
|
|
nFIndex == GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATE_DIN_YYYYMMDD, eLang) ||
|
|
nFIndex == GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATE_DIN_YYMMDD, eLang) ||
|
|
nFIndex == GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATE_DIN_MMDD, eLang) ||
|
|
(pFormat && pFormat->IsIso8601( 0 ));
|
|
if (rtl::math::approxFloor( fNumber) != fNumber)
|
|
{
|
|
// fdo#34977 preserve time when editing even if only date was
|
|
// displayed.
|
|
if (bIsoDate)
|
|
nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDD_HHMMSS, eForLocale);
|
|
else
|
|
nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_SYS_DDMMYYYY_HHMMSS, eForLocale);
|
|
}
|
|
else
|
|
{
|
|
if (bIsoDate)
|
|
nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATE_ISO_YYYYMMDD, eForLocale);
|
|
else
|
|
nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATE_SYS_DDMMYYYY, eForLocale);
|
|
}
|
|
}
|
|
break;
|
|
case SvNumFormatType::TIME :
|
|
if (fNumber < 0.0 || fNumber >= 1.0)
|
|
{
|
|
/* XXX NOTE: this is a purely arbitrary value within the limits
|
|
* of a signed 16-bit. 32k hours are 3.7 years ... or
|
|
* 1903-09-26 if date. */
|
|
if (fabs( fNumber) * 24 < 0x7fff)
|
|
nKey = GetTimeFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, fNumber, eForLocale, true);
|
|
// Preserve duration, use [HH]:MM:SS instead of time.
|
|
else
|
|
nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_SYS_DDMMYYYY_HHMMSS, eForLocale);
|
|
// Assume that a large value is a datetime with only time
|
|
// displayed.
|
|
}
|
|
else
|
|
nKey = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, fNumber, nFIndex, eType, eForLocale);
|
|
break;
|
|
case SvNumFormatType::DURATION :
|
|
nKey = GetTimeFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, fNumber, eForLocale, true);
|
|
break;
|
|
case SvNumFormatType::DATETIME :
|
|
if (nFIndex == GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDDTHHMMSS, eLang))
|
|
nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDDTHHMMSS, eForLocale );
|
|
else if (nFIndex == GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDDTHHMMSS000, eLang))
|
|
nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDDTHHMMSS000, eForLocale );
|
|
else if (nFIndex == GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDD_HHMMSS000, eLang))
|
|
nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDD_HHMMSS000, eForLocale );
|
|
else if (nFIndex == GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDD_HHMMSS, eLang) || (pFormat && pFormat->IsIso8601( 0 )))
|
|
nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDD_HHMMSS, eForLocale );
|
|
else
|
|
nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_SYS_DDMMYYYY_HHMMSS, eForLocale );
|
|
break;
|
|
case SvNumFormatType::NUMBER:
|
|
nKey = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, eType, eForLocale);
|
|
break;
|
|
default:
|
|
nKey = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, fNumber, nFIndex, eType, eForLocale );
|
|
}
|
|
return nKey;
|
|
}
|
|
|
|
sal_uInt32 SvNumberFormatter::GetEditFormat( double fNumber, sal_uInt32 nFIndex,
|
|
SvNumFormatType eType,
|
|
SvNumberformat const * pFormat,
|
|
LanguageType eForLocale )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return SvNFEngine::GetEditFormat(m_aCurrentLanguage, m_aFormatData, GetNatNum(),
|
|
m_aRWPolicy, fNumber, nFIndex,
|
|
eType, pFormat, eForLocale);
|
|
}
|
|
|
|
OUString SvNFEngine::GetInputLineString(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
|
|
const double& fOutNumber,
|
|
sal_uInt32 nFIndex,
|
|
bool bFiltering, bool bForceSystemLocale)
|
|
{
|
|
OUString sOutString;
|
|
const Color* pColor;
|
|
sal_uInt32 nRealKey = nFIndex;
|
|
|
|
const SvNumberformat* pFormat = ImpSubstituteEntry(rCurrentLanguage, rFormatData,
|
|
rNatNum, rFuncs,
|
|
rFormatData.GetFormatEntry(nFIndex), &nRealKey);
|
|
if (!pFormat)
|
|
pFormat = rFormatData.GetFormatEntry(ZF_STANDARD);
|
|
|
|
LanguageType eLang = pFormat->GetLanguage();
|
|
rCurrentLanguage.ChangeIntl(eLang);
|
|
|
|
SvNumFormatType eType = pFormat->GetMaskedType();
|
|
if (eType == SvNumFormatType::ALL)
|
|
{
|
|
// Mixed types in subformats, use first.
|
|
/* XXX we could choose a subformat according to fOutNumber and
|
|
* subformat conditions, but they may exist to suppress 0 or negative
|
|
* numbers so wouldn't be a safe bet. */
|
|
eType = pFormat->GetNumForInfoScannedType(0);
|
|
}
|
|
const SvNumFormatType eTypeOrig = eType;
|
|
|
|
sal_uInt16 nOldPrec = rCurrentLanguage.pFormatScanner->GetStandardPrec();
|
|
bool bPrecChanged = false;
|
|
if (eType == SvNumFormatType::NUMBER ||
|
|
eType == SvNumFormatType::PERCENT ||
|
|
eType == SvNumFormatType::CURRENCY ||
|
|
eType == SvNumFormatType::SCIENTIFIC ||
|
|
eType == SvNumFormatType::FRACTION)
|
|
{
|
|
if (eType != SvNumFormatType::PERCENT) // special treatment of % later
|
|
{
|
|
eType = SvNumFormatType::NUMBER;
|
|
}
|
|
rCurrentLanguage.ChangeStandardPrec(SvNumberFormatter::INPUTSTRING_PRECISION);
|
|
bPrecChanged = true;
|
|
}
|
|
|
|
// if bFiltering true keep the nRealKey format
|
|
if (!bFiltering)
|
|
{
|
|
sal_uInt32 nKey = GetEditFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs,
|
|
fOutNumber, nRealKey, eType, pFormat,
|
|
bForceSystemLocale ? LANGUAGE_SYSTEM : LANGUAGE_DONTKNOW);
|
|
if (nKey != nRealKey)
|
|
{
|
|
pFormat = rFormatData.GetFormatEntry( nKey );
|
|
}
|
|
}
|
|
assert(pFormat);
|
|
if (pFormat)
|
|
{
|
|
if ( eType == SvNumFormatType::TIME && pFormat->GetFormatPrecision() )
|
|
{
|
|
rCurrentLanguage.ChangeStandardPrec(SvNumberFormatter::INPUTSTRING_PRECISION);
|
|
bPrecChanged = true;
|
|
}
|
|
pFormat->GetOutputString(fOutNumber, sOutString, &pColor, rNatNum, rCurrentLanguage);
|
|
|
|
// The #FMT error string must not be used for input as it would lead to
|
|
// data loss. This can happen for at least date(+time). Fall back to a
|
|
// last resort of plain number in the locale the formatter was
|
|
// constructed with.
|
|
if (eTypeOrig != SvNumFormatType::NUMBER && sOutString == ImpSvNumberformatScan::sErrStr)
|
|
{
|
|
pFormat = rFormatData.GetFormatEntry(ZF_STANDARD);
|
|
assert(pFormat);
|
|
if (pFormat)
|
|
{
|
|
rCurrentLanguage.ChangeStandardPrec(SvNumberFormatter::INPUTSTRING_PRECISION);
|
|
bPrecChanged = true;
|
|
pFormat->GetOutputString(fOutNumber, sOutString, &pColor, rNatNum, rCurrentLanguage);
|
|
}
|
|
}
|
|
assert(sOutString != ImpSvNumberformatScan::sErrStr);
|
|
}
|
|
if (bPrecChanged)
|
|
{
|
|
rCurrentLanguage.ChangeStandardPrec(nOldPrec);
|
|
}
|
|
return sOutString;
|
|
}
|
|
|
|
OUString SvNumberFormatter::GetInputLineString(const double& fOutNumber,
|
|
sal_uInt32 nFIndex,
|
|
bool bFiltering, bool bForceSystemLocale)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return SvNFEngine::GetInputLineString(m_aCurrentLanguage, m_aFormatData, GetNatNum(),
|
|
m_aRWPolicy, fOutNumber, nFIndex,
|
|
bFiltering, bForceSystemLocale);
|
|
}
|
|
|
|
void SvNFEngine::GetOutputString(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
const OUString& sString, sal_uInt32 nFIndex, OUString& sOutString,
|
|
const Color** ppColor, bool bUseStarFormat)
|
|
{
|
|
const SvNumberformat* pFormat = rFormatData.GetFormatEntry( nFIndex );
|
|
// ImpSubstituteEntry() is unnecessary here because so far only numeric
|
|
// (time and date) are substituted.
|
|
if (!pFormat)
|
|
{
|
|
pFormat = rFormatData.GetFormatEntry(ZF_STANDARD_TEXT);
|
|
}
|
|
if (!pFormat->IsTextFormat() && !pFormat->HasTextFormat())
|
|
{
|
|
*ppColor = nullptr;
|
|
sOutString = sString;
|
|
}
|
|
else
|
|
{
|
|
rCurrentLanguage.ChangeIntl(pFormat->GetLanguage());
|
|
pFormat->GetOutputString(sString, sOutString, ppColor, bUseStarFormat);
|
|
}
|
|
}
|
|
|
|
void SvNumberFormatter::GetOutputString(const OUString& sString,
|
|
sal_uInt32 nFIndex,
|
|
OUString& sOutString,
|
|
const Color** ppColor,
|
|
bool bUseStarFormat )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
SvNFEngine::GetOutputString(m_aCurrentLanguage, m_aFormatData,
|
|
sString, nFIndex, sOutString,
|
|
ppColor, bUseStarFormat);
|
|
}
|
|
|
|
void SvNFEngine::GetOutputString(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
|
|
const double& fOutNumber, sal_uInt32 nFIndex,
|
|
OUString& sOutString, const Color** ppColor,
|
|
bool bUseStarFormat)
|
|
{
|
|
if (rFormatData.GetNoZero() && fOutNumber == 0.0)
|
|
{
|
|
sOutString.clear();
|
|
return;
|
|
}
|
|
const SvNumberformat* pFormat = ImpSubstituteEntry(rCurrentLanguage, rFormatData,
|
|
rNatNum, rFuncs,
|
|
rFormatData.GetFormatEntry(nFIndex), nullptr);
|
|
if (!pFormat)
|
|
pFormat = rFormatData.GetFormatEntry(ZF_STANDARD);
|
|
rCurrentLanguage.ChangeIntl(pFormat->GetLanguage());
|
|
pFormat->GetOutputString(fOutNumber, sOutString, ppColor, rNatNum, rCurrentLanguage, bUseStarFormat);
|
|
}
|
|
|
|
void SvNumberFormatter::GetOutputString(const double& fOutNumber,
|
|
sal_uInt32 nFIndex,
|
|
OUString& sOutString,
|
|
const Color** ppColor,
|
|
bool bUseStarFormat )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
SvNFEngine::GetOutputString(m_aCurrentLanguage, m_aFormatData, GetNatNum(),
|
|
m_aRWPolicy, fOutNumber, nFIndex, sOutString,
|
|
ppColor, bUseStarFormat);
|
|
}
|
|
|
|
bool SvNFEngine::GetPreviewString(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
const NativeNumberWrapper& rNatNum,
|
|
const Accessor& rFuncs,
|
|
const OUString& sFormatString,
|
|
double fPreviewNumber,
|
|
OUString& sOutString,
|
|
const Color** ppColor,
|
|
LanguageType eLnge,
|
|
bool bUseStarFormat)
|
|
{
|
|
if (sFormatString.isEmpty()) // no empty string
|
|
return false;
|
|
eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
|
|
rCurrentLanguage.ChangeIntl(eLnge); // change locale if necessary
|
|
eLnge = rCurrentLanguage.ActLnge;
|
|
sal_Int32 nCheckPos = -1;
|
|
OUString sTmpString = sFormatString;
|
|
SvNumberformat aEntry(sTmpString,
|
|
rCurrentLanguage.pFormatScanner.get(),
|
|
rCurrentLanguage.pStringScanner.get(),
|
|
rNatNum,
|
|
nCheckPos,
|
|
eLnge);
|
|
if (nCheckPos == 0) // String ok
|
|
{
|
|
sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)
|
|
sal_uInt32 nKey = rFormatData.ImpIsEntry(aEntry.GetFormatstring(), nCLOffset, eLnge);
|
|
if (nKey != NUMBERFORMAT_ENTRY_NOT_FOUND) // already present
|
|
{
|
|
GetOutputString(rCurrentLanguage, rFormatData, rNatNum, rFuncs, fPreviewNumber, nKey, sOutString, ppColor, bUseStarFormat);
|
|
}
|
|
else
|
|
{
|
|
aEntry.GetOutputString(fPreviewNumber, sOutString, ppColor, rNatNum, rCurrentLanguage, bUseStarFormat);
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool SvNumberFormatter::GetPreviewString(const OUString& sFormatString,
|
|
double fPreviewNumber,
|
|
OUString& sOutString,
|
|
const Color** ppColor,
|
|
LanguageType eLnge,
|
|
bool bUseStarFormat )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return SvNFEngine::GetPreviewString(m_aCurrentLanguage, m_aFormatData,
|
|
GetNatNum(), m_aRWPolicy,
|
|
sFormatString, fPreviewNumber,
|
|
sOutString, ppColor, eLnge,
|
|
bUseStarFormat);
|
|
}
|
|
|
|
bool SvNFEngine::GetPreviewStringGuess(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
const NativeNumberWrapper& rNatNum,
|
|
const Accessor& rFuncs,
|
|
const OUString& sFormatString,
|
|
double fPreviewNumber,
|
|
OUString& sOutString,
|
|
const Color** ppColor,
|
|
LanguageType eLnge)
|
|
{
|
|
if (sFormatString.isEmpty()) // no empty string
|
|
return false;
|
|
eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
|
|
rCurrentLanguage.ChangeIntl(eLnge);
|
|
eLnge = rCurrentLanguage.ActLnge;
|
|
bool bEnglish = (eLnge == LANGUAGE_ENGLISH_US);
|
|
|
|
OUString aFormatStringUpper( rCurrentLanguage.xCharClass->uppercase( sFormatString ) );
|
|
sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)
|
|
sal_uInt32 nKey = rFormatData.ImpIsEntry(aFormatStringUpper, nCLOffset, eLnge);
|
|
if ( nKey != NUMBERFORMAT_ENTRY_NOT_FOUND )
|
|
{
|
|
// Target format present
|
|
GetOutputString(rCurrentLanguage, rFormatData, rNatNum, rFuncs,
|
|
fPreviewNumber, nKey, sOutString, ppColor, false);
|
|
return true;
|
|
}
|
|
|
|
std::optional<SvNumberformat> pEntry;
|
|
sal_Int32 nCheckPos = -1;
|
|
OUString sTmpString;
|
|
|
|
if ( bEnglish )
|
|
{
|
|
sTmpString = sFormatString;
|
|
pEntry.emplace( sTmpString, rCurrentLanguage.pFormatScanner.get(),
|
|
rCurrentLanguage.pStringScanner.get(), rNatNum, nCheckPos, eLnge );
|
|
}
|
|
else
|
|
{
|
|
nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, LANGUAGE_ENGLISH_US);
|
|
nKey = rFormatData.ImpIsEntry(aFormatStringUpper, nCLOffset, LANGUAGE_ENGLISH_US);
|
|
bool bEnglishFormat = (nKey != NUMBERFORMAT_ENTRY_NOT_FOUND);
|
|
|
|
// Try English -> other or convert english to other
|
|
LanguageType eFormatLang = LANGUAGE_ENGLISH_US;
|
|
rCurrentLanguage.pFormatScanner->SetConvertMode( LANGUAGE_ENGLISH_US, eLnge, false, false);
|
|
sTmpString = sFormatString;
|
|
pEntry.emplace( sTmpString, rCurrentLanguage.pFormatScanner.get(),
|
|
rCurrentLanguage.pStringScanner.get(), rNatNum, nCheckPos, eFormatLang );
|
|
rCurrentLanguage.pFormatScanner->SetConvertMode( false );
|
|
rCurrentLanguage.ChangeIntl(eLnge);
|
|
|
|
if ( !bEnglishFormat )
|
|
{
|
|
if ( nCheckPos != 0 || rCurrentLanguage.xTransliteration->isEqual( sFormatString,
|
|
pEntry->GetFormatstring() ) )
|
|
{
|
|
// other Format
|
|
// Force locale's keywords.
|
|
rCurrentLanguage.pFormatScanner->ChangeIntl( ImpSvNumberformatScan::KeywordLocalization::LocaleLegacy );
|
|
sTmpString = sFormatString;
|
|
pEntry.emplace( sTmpString, rCurrentLanguage.pFormatScanner.get(),
|
|
rCurrentLanguage.pStringScanner.get(), rNatNum, nCheckPos, eLnge );
|
|
}
|
|
else
|
|
{
|
|
// verify english
|
|
sal_Int32 nCheckPos2 = -1;
|
|
// try other --> english
|
|
eFormatLang = eLnge;
|
|
rCurrentLanguage.pFormatScanner->SetConvertMode( eLnge, LANGUAGE_ENGLISH_US, false, false);
|
|
sTmpString = sFormatString;
|
|
SvNumberformat aEntry2( sTmpString, rCurrentLanguage.pFormatScanner.get(),
|
|
rCurrentLanguage.pStringScanner.get(), rNatNum, nCheckPos2, eFormatLang );
|
|
rCurrentLanguage.pFormatScanner->SetConvertMode( false );
|
|
rCurrentLanguage.ChangeIntl(eLnge);
|
|
if ( nCheckPos2 == 0 && !rCurrentLanguage.xTransliteration->isEqual( sFormatString,
|
|
aEntry2.GetFormatstring() ) )
|
|
{
|
|
// other Format
|
|
// Force locale's keywords.
|
|
rCurrentLanguage.pFormatScanner->ChangeIntl( ImpSvNumberformatScan::KeywordLocalization::LocaleLegacy );
|
|
sTmpString = sFormatString;
|
|
pEntry.emplace( sTmpString, rCurrentLanguage.pFormatScanner.get(),
|
|
rCurrentLanguage.pStringScanner.get(), rNatNum, nCheckPos, eLnge );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nCheckPos == 0) // String ok
|
|
{
|
|
rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary
|
|
pEntry->GetOutputString( fPreviewNumber, sOutString, ppColor, rNatNum, rCurrentLanguage );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SvNumberFormatter::GetPreviewStringGuess( const OUString& sFormatString,
|
|
double fPreviewNumber,
|
|
OUString& sOutString,
|
|
const Color** ppColor,
|
|
LanguageType eLnge )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return SvNFEngine::GetPreviewStringGuess(m_aCurrentLanguage, m_aFormatData,
|
|
GetNatNum(), m_aRWPolicy,
|
|
sFormatString, fPreviewNumber,
|
|
sOutString, ppColor, eLnge);
|
|
}
|
|
|
|
bool SvNFEngine::GetPreviewString(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
const NativeNumberWrapper& rNatNum,
|
|
const Accessor& rFuncs,
|
|
const OUString& sFormatString,
|
|
const OUString& sPreviewString,
|
|
OUString& sOutString,
|
|
const Color** ppColor,
|
|
LanguageType eLnge)
|
|
{
|
|
if (sFormatString.isEmpty()) // no empty string
|
|
return false;
|
|
eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
|
|
rCurrentLanguage.ChangeIntl(eLnge); // switch if needed
|
|
eLnge = rCurrentLanguage.ActLnge;
|
|
sal_Int32 nCheckPos = -1;
|
|
OUString sTmpString = sFormatString;
|
|
SvNumberformat aEntry( sTmpString,
|
|
rCurrentLanguage.pFormatScanner.get(),
|
|
rCurrentLanguage.pStringScanner.get(),
|
|
rNatNum,
|
|
nCheckPos,
|
|
eLnge);
|
|
if (nCheckPos == 0) // String ok
|
|
{
|
|
// May have to create standard formats for this locale.
|
|
sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge);
|
|
sal_uInt32 nKey = rFormatData.ImpIsEntry( aEntry.GetFormatstring(), nCLOffset, eLnge);
|
|
if (nKey != NUMBERFORMAT_ENTRY_NOT_FOUND) // already present
|
|
{
|
|
GetOutputString(rCurrentLanguage, rFormatData,
|
|
sPreviewString, nKey, sOutString, ppColor, false);
|
|
}
|
|
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 (aEntry.IsTextFormat() || aEntry.HasTextFormat())
|
|
{
|
|
aEntry.GetOutputString( sPreviewString, sOutString, ppColor);
|
|
}
|
|
else
|
|
{
|
|
*ppColor = nullptr;
|
|
sOutString = sPreviewString;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool SvNumberFormatter::GetPreviewString( const OUString& sFormatString,
|
|
const OUString& sPreviewString,
|
|
OUString& sOutString,
|
|
const Color** ppColor,
|
|
LanguageType eLnge )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return SvNFEngine::GetPreviewString(m_aCurrentLanguage, m_aFormatData,
|
|
GetNatNum(), m_aRWPolicy,
|
|
sFormatString, sPreviewString,
|
|
sOutString, ppColor, eLnge);
|
|
}
|
|
|
|
sal_uInt32 SvNumberFormatter::TestNewString(const OUString& sFormatString,
|
|
LanguageType eLnge)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
if (sFormatString.isEmpty()) // no empty string
|
|
{
|
|
return NUMBERFORMAT_ENTRY_NOT_FOUND;
|
|
}
|
|
if (eLnge == LANGUAGE_DONTKNOW)
|
|
{
|
|
eLnge = IniLnge;
|
|
}
|
|
ChangeIntl(eLnge); // change locale if necessary
|
|
eLnge = m_aCurrentLanguage.ActLnge;
|
|
sal_uInt32 nRes;
|
|
sal_Int32 nCheckPos = -1;
|
|
OUString sTmpString = sFormatString;
|
|
SvNumberformat aEntry(sTmpString,
|
|
m_aCurrentLanguage.pFormatScanner.get(),
|
|
m_aCurrentLanguage.pStringScanner.get(),
|
|
GetNatNum(),
|
|
nCheckPos,
|
|
eLnge);
|
|
if (nCheckPos == 0) // String ok
|
|
{
|
|
sal_uInt32 CLOffset = m_aFormatData.ImpGenerateCL(m_aCurrentLanguage, GetNatNum(), eLnge); // create new standard formats if necessary
|
|
nRes = ImpIsEntry(aEntry.GetFormatstring(),CLOffset, eLnge);
|
|
// already present?
|
|
}
|
|
else
|
|
{
|
|
nRes = NUMBERFORMAT_ENTRY_NOT_FOUND;
|
|
}
|
|
return nRes;
|
|
}
|
|
|
|
SvNumberformat* SvNFFormatData::ImpInsertFormat(SvNFLanguageData& rCurrentLanguage,
|
|
const NativeNumberWrapper& rNatNum,
|
|
const css::i18n::NumberFormatCode& rCode,
|
|
sal_uInt32 nPos, bool bAfterChangingSystemCL,
|
|
sal_Int16 nOrgIndex)
|
|
{
|
|
SAL_WARN_IF( NF_INDEX_TABLE_RESERVED_START <= rCode.Index && rCode.Index < NF_INDEX_TABLE_ENTRIES,
|
|
"svl.numbers", "i18npool locale '" << rCurrentLanguage.aLanguageTag.getBcp47() <<
|
|
"' uses reserved formatIndex value " << rCode.Index << ", next free: " << NF_INDEX_TABLE_ENTRIES <<
|
|
" Please see description in include/svl/zforlist.hxx at end of enum NfIndexTableOffset");
|
|
assert( (rCode.Index < NF_INDEX_TABLE_RESERVED_START || NF_INDEX_TABLE_ENTRIES <= rCode.Index) &&
|
|
"reserved formatIndex, see warning above");
|
|
|
|
OUString aCodeStr( rCode.Code );
|
|
if ( rCode.Index < NF_INDEX_TABLE_RESERVED_START &&
|
|
rCode.Usage == css::i18n::KNumberFormatUsage::CURRENCY &&
|
|
rCode.Index != NF_CURRENCY_1000DEC2_CCC )
|
|
{ // strip surrounding [$...] on automatic currency
|
|
if ( aCodeStr.indexOf( "[$" ) >= 0)
|
|
aCodeStr = SvNumberformat::StripNewCurrencyDelimiters( aCodeStr );
|
|
else
|
|
{
|
|
if (LocaleDataWrapper::areChecksEnabled() &&
|
|
rCode.Index != NF_CURRENCY_1000DEC2_CCC )
|
|
{
|
|
OUString aMsg = "SvNumberFormatter::ImpInsertFormat: no [$...] on currency format code, index " +
|
|
OUString::number( rCode.Index) +
|
|
":\n" +
|
|
rCode.Code;
|
|
LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo( aMsg));
|
|
}
|
|
}
|
|
}
|
|
sal_Int32 nCheckPos = 0;
|
|
std::unique_ptr<SvNumberformat> pFormat(new SvNumberformat(aCodeStr,
|
|
rCurrentLanguage.pFormatScanner.get(),
|
|
rCurrentLanguage.pStringScanner.get(),
|
|
rNatNum,
|
|
nCheckPos,
|
|
rCurrentLanguage.ActLnge));
|
|
if (nCheckPos != 0)
|
|
{
|
|
if (LocaleDataWrapper::areChecksEnabled())
|
|
{
|
|
OUString aMsg = "SvNumberFormatter::ImpInsertFormat: bad format code, index " +
|
|
OUString::number( rCode.Index ) +
|
|
"\n" +
|
|
rCode.Code;
|
|
LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo( aMsg));
|
|
}
|
|
return nullptr;
|
|
}
|
|
if ( rCode.Index >= NF_INDEX_TABLE_RESERVED_START )
|
|
{
|
|
sal_uInt32 nCLOffset = nPos - (nPos % SV_COUNTRY_LANGUAGE_OFFSET);
|
|
sal_uInt32 nKey = ImpIsEntry( aCodeStr, nCLOffset, rCurrentLanguage.ActLnge );
|
|
if ( nKey != NUMBERFORMAT_ENTRY_NOT_FOUND )
|
|
{
|
|
// If bAfterChangingSystemCL there will definitely be some dups,
|
|
// don't cry then.
|
|
if (LocaleDataWrapper::areChecksEnabled() && !bAfterChangingSystemCL)
|
|
{
|
|
// Test for duplicate indexes in locale data.
|
|
switch ( nOrgIndex )
|
|
{
|
|
// These may be dups 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:
|
|
{
|
|
OUString aMsg = "SvNumberFormatter::ImpInsertFormat: dup format code, index "
|
|
+ OUString::number( rCode.Index )
|
|
+ "\n"
|
|
+ rCode.Code;
|
|
LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo( aMsg));
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
else if ( nPos - nCLOffset >= SV_COUNTRY_LANGUAGE_OFFSET )
|
|
{
|
|
if (LocaleDataWrapper::areChecksEnabled())
|
|
{
|
|
OUString aMsg = "SvNumberFormatter::ImpInsertFormat: too many format codes, index "
|
|
+ OUString::number( rCode.Index )
|
|
+ "\n"
|
|
+ rCode.Code;
|
|
LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo( aMsg));
|
|
}
|
|
return nullptr;
|
|
}
|
|
}
|
|
auto pFormat2 = pFormat.get();
|
|
if ( !aFTable.emplace( nPos, std::move(pFormat) ).second )
|
|
{
|
|
if (LocaleDataWrapper::areChecksEnabled())
|
|
{
|
|
OUString aMsg = "ImpInsertFormat: can't insert number format key pos: "
|
|
+ OUString::number( nPos )
|
|
+ ", code index "
|
|
+ OUString::number( rCode.Index )
|
|
+ "\n"
|
|
+ rCode.Code;
|
|
LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo( aMsg));
|
|
}
|
|
else
|
|
{
|
|
SAL_WARN( "svl.numbers", "SvNumberFormatter::ImpInsertFormat: dup position");
|
|
}
|
|
return nullptr;
|
|
}
|
|
if ( rCode.Default )
|
|
pFormat2->SetStandard();
|
|
if ( !rCode.DefaultName.isEmpty() )
|
|
pFormat2->SetComment( rCode.DefaultName );
|
|
return pFormat2;
|
|
}
|
|
|
|
void SvNumberFormatter::GetFormatSpecialInfo(sal_uInt32 nFormat,
|
|
bool& bThousand,
|
|
bool& IsRed,
|
|
sal_uInt16& nPrecision,
|
|
sal_uInt16& nLeadingCnt)
|
|
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
SvNumberformat* pFormat = m_aFormatData.GetFormatEntry( nFormat );
|
|
if (pFormat)
|
|
pFormat->GetFormatSpecialInfo(bThousand, IsRed,
|
|
nPrecision, nLeadingCnt);
|
|
else
|
|
{
|
|
bThousand = false;
|
|
IsRed = false;
|
|
nPrecision = m_aCurrentLanguage.pFormatScanner->GetStandardPrec();
|
|
nLeadingCnt = 0;
|
|
}
|
|
}
|
|
|
|
sal_uInt16 SvNFEngine::GetFormatPrecision(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
sal_uInt32 nFormat)
|
|
{
|
|
const SvNumberformat* pFormat = rFormatData.GetFormatEntry(nFormat);
|
|
if (pFormat)
|
|
return pFormat->GetFormatPrecision();
|
|
return rCurrentLanguage.pFormatScanner->GetStandardPrec();
|
|
}
|
|
|
|
sal_uInt16 SvNumberFormatter::GetFormatPrecision( sal_uInt32 nFormat )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return SvNFEngine::GetFormatPrecision(m_aCurrentLanguage, m_aFormatData, nFormat);
|
|
}
|
|
|
|
sal_uInt16 SvNumberFormatter::GetFormatIntegerDigits( sal_uInt32 nFormat ) const
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
const SvNumberformat* pFormat = m_aFormatData.GetFormatEntry( nFormat );
|
|
if ( pFormat )
|
|
return pFormat->GetFormatIntegerDigits();
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
OUString SvNFEngine::GetFormatDecimalSep(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
sal_uInt32 nFormat)
|
|
{
|
|
const SvNumberformat* pFormat = rFormatData.GetFormatEntry(nFormat);
|
|
if (!pFormat)
|
|
return rCurrentLanguage.GetNumDecimalSep();
|
|
return rCurrentLanguage.GetLangDecimalSep(pFormat->GetLanguage());
|
|
}
|
|
|
|
OUString SvNumberFormatter::GetFormatDecimalSep( sal_uInt32 nFormat )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return SvNFEngine::GetFormatDecimalSep(m_aCurrentLanguage,
|
|
m_aFormatData,
|
|
nFormat);
|
|
}
|
|
|
|
bool SvNumberFormatter::IsNatNum12( sal_uInt32 nFIndex ) const
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
const SvNumberformat* pFormat = m_aFormatData.GetFormatEntry( nFIndex );
|
|
|
|
return pFormat && pFormat->GetNatNumModifierString().startsWith( "[NatNum12" );
|
|
}
|
|
|
|
sal_uInt32 SvNumberFormatter::GetFormatSpecialInfo( const OUString& rFormatString,
|
|
bool& bThousand, bool& IsRed, sal_uInt16& nPrecision,
|
|
sal_uInt16& nLeadingCnt, LanguageType eLnge )
|
|
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
if (eLnge == LANGUAGE_DONTKNOW)
|
|
{
|
|
eLnge = IniLnge;
|
|
}
|
|
ChangeIntl(eLnge); // change locale if necessary
|
|
eLnge = m_aCurrentLanguage.ActLnge;
|
|
OUString aTmpStr( rFormatString );
|
|
sal_Int32 nCheckPos = 0;
|
|
SvNumberformat aFormat( aTmpStr, m_aCurrentLanguage.pFormatScanner.get(),
|
|
m_aCurrentLanguage.pStringScanner.get(), GetNatNum(), nCheckPos, eLnge );
|
|
if ( nCheckPos == 0 )
|
|
{
|
|
aFormat.GetFormatSpecialInfo( bThousand, IsRed, nPrecision, nLeadingCnt );
|
|
}
|
|
else
|
|
{
|
|
bThousand = false;
|
|
IsRed = false;
|
|
nPrecision = m_aCurrentLanguage.pFormatScanner->GetStandardPrec();
|
|
nLeadingCnt = 0;
|
|
}
|
|
return nCheckPos;
|
|
}
|
|
|
|
OUString SvNFFormatData::GetCalcCellReturn(sal_uInt32 nFormat) const
|
|
{
|
|
const SvNumberformat* pFormat = GetFormatEntry(nFormat);
|
|
if (!pFormat)
|
|
return u"G"_ustr;
|
|
|
|
OUString aStr;
|
|
bool bAppendPrec = true;
|
|
sal_uInt16 nPrec, nLeading;
|
|
bool bThousand, bIsRed;
|
|
pFormat->GetFormatSpecialInfo( bThousand, bIsRed, nPrec, nLeading );
|
|
|
|
switch (pFormat->GetMaskedType())
|
|
{
|
|
case SvNumFormatType::NUMBER:
|
|
if (bThousand)
|
|
aStr = ",";
|
|
else
|
|
aStr = "F";
|
|
break;
|
|
case SvNumFormatType::CURRENCY:
|
|
aStr = "C";
|
|
break;
|
|
case SvNumFormatType::SCIENTIFIC:
|
|
aStr = "S";
|
|
break;
|
|
case SvNumFormatType::PERCENT:
|
|
aStr = "P";
|
|
break;
|
|
default:
|
|
{
|
|
bAppendPrec = false;
|
|
switch (SvNumberFormatter::GetIndexTableOffset(nFormat))
|
|
{
|
|
case NF_DATE_SYSTEM_SHORT:
|
|
case NF_DATE_SYS_DMMMYY:
|
|
case NF_DATE_SYS_DDMMYY:
|
|
case NF_DATE_SYS_DDMMYYYY:
|
|
case NF_DATE_SYS_DMMMYYYY:
|
|
case NF_DATE_DIN_DMMMYYYY:
|
|
case NF_DATE_SYS_DMMMMYYYY:
|
|
case NF_DATE_DIN_DMMMMYYYY: aStr = "D1"; break;
|
|
case NF_DATE_SYS_DDMMM: aStr = "D2"; break;
|
|
case NF_DATE_SYS_MMYY: aStr = "D3"; break;
|
|
case NF_DATETIME_SYSTEM_SHORT_HHMM:
|
|
case NF_DATETIME_SYS_DDMMYYYY_HHMM:
|
|
case NF_DATETIME_SYS_DDMMYYYY_HHMMSS:
|
|
aStr = "D4"; break;
|
|
case NF_DATE_DIN_MMDD: aStr = "D5"; break;
|
|
case NF_TIME_HHMMSSAMPM: aStr = "D6"; break;
|
|
case NF_TIME_HHMMAMPM: aStr = "D7"; break;
|
|
case NF_TIME_HHMMSS: aStr = "D8"; break;
|
|
case NF_TIME_HHMM: aStr = "D9"; break;
|
|
default: aStr = "G";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bAppendPrec)
|
|
aStr += OUString::number(nPrec);
|
|
|
|
if (pFormat->GetColor( 1 ))
|
|
aStr += "-"; // negative color
|
|
|
|
/* FIXME: this probably should not match on literal strings and only be
|
|
* performed on number or currency formats, but it is what Calc originally
|
|
* implemented. */
|
|
if (pFormat->GetFormatstring().indexOf('(') != -1)
|
|
aStr += "()";
|
|
|
|
return aStr;
|
|
}
|
|
|
|
OUString SvNumberFormatter::GetCalcCellReturn( sal_uInt32 nFormat ) const
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return m_aFormatData.GetCalcCellReturn(nFormat);
|
|
}
|
|
|
|
// 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 SvNFFormatData::ImpGetFormatCodeIndex(const SvNFLanguageData& rCurrentLanguage,
|
|
css::uno::Sequence<css::i18n::NumberFormatCode>& rSeq,
|
|
const NfIndexTableOffset nTabOff)
|
|
{
|
|
auto pSeq = std::find_if(std::cbegin(rSeq), std::cend(rSeq),
|
|
[nTabOff](const css::i18n::NumberFormatCode& rCode) { return rCode.Index == nTabOff; });
|
|
if (pSeq != std::cend(rSeq))
|
|
return static_cast<sal_Int32>(std::distance(std::cbegin(rSeq), pSeq));
|
|
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
|
|
OUString aMsg = "SvNumberFormatter::ImpGetFormatCodeIndex: not found: "
|
|
+ OUString::number( nTabOff );
|
|
LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo(aMsg));
|
|
}
|
|
if ( rSeq.hasElements() )
|
|
{
|
|
// look for a preset default
|
|
pSeq = std::find_if(std::cbegin(rSeq), std::cend(rSeq),
|
|
[](const css::i18n::NumberFormatCode& rCode) { return rCode.Default; });
|
|
if (pSeq != std::cend(rSeq))
|
|
return static_cast<sal_Int32>(std::distance(std::cbegin(rSeq), pSeq));
|
|
// 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
|
|
pSeq = std::find_if(std::cbegin(rSeq), std::cend(rSeq),
|
|
[](const css::i18n::NumberFormatCode& rCode) { return rCode.Index == NF_CURRENCY_1000DEC2; });
|
|
if (pSeq != std::cend(rSeq))
|
|
return static_cast<sal_Int32>(std::distance(std::cbegin(rSeq), pSeq));
|
|
// last resort: look for a format without decimals
|
|
pSeq = std::find_if(std::cbegin(rSeq), std::cend(rSeq),
|
|
[](const css::i18n::NumberFormatCode& rCode) { return rCode.Index == NF_CURRENCY_1000INT; });
|
|
if (pSeq != std::cend(rSeq))
|
|
return static_cast<sal_Int32>(std::distance(std::cbegin(rSeq), pSeq));
|
|
}
|
|
}
|
|
else
|
|
{ // we need at least _some_ format
|
|
rSeq = { css::i18n::NumberFormatCode() };
|
|
rSeq.getArray()[0].Code = "0" + rCurrentLanguage.GetNumDecimalSep() + "############";
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// 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.
|
|
// Non-PRODUCT version may check locale data for matching defaults in one
|
|
// FormatElement group.
|
|
void SvNFFormatData::ImpAdjustFormatCodeDefault(const SvNFLanguageData& rCurrentLanguage,
|
|
css::i18n::NumberFormatCode * pFormatArr,
|
|
sal_Int32 nCnt)
|
|
{
|
|
if ( !nCnt )
|
|
return;
|
|
if (LocaleDataWrapper::areChecksEnabled())
|
|
{
|
|
// check the locale data for correctness
|
|
OUStringBuffer 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.append("unknown type");
|
|
}
|
|
if ( pFormatArr[nElem].Default )
|
|
{
|
|
switch ( pFormatArr[nElem].Type )
|
|
{
|
|
case i18n::KNumberFormatType::SHORT :
|
|
if ( nShortDef != -1 )
|
|
aMsg.append("dupe short type default");
|
|
nShortDef = nElem;
|
|
break;
|
|
case i18n::KNumberFormatType::MEDIUM :
|
|
if ( nMediumDef != -1 )
|
|
aMsg.append("dupe medium type default");
|
|
nMediumDef = nElem;
|
|
break;
|
|
case i18n::KNumberFormatType::LONG :
|
|
if ( nLongDef != -1 )
|
|
aMsg.append("dupe long type default");
|
|
nLongDef = nElem;
|
|
break;
|
|
}
|
|
}
|
|
if (!aMsg.isEmpty())
|
|
{
|
|
aMsg.insert(0, "SvNumberFormatter::ImpAdjustFormatCodeDefault: ");
|
|
aMsg.append("\nXML locale data FormatElement formatindex: "
|
|
+ OUString::number(static_cast<sal_Int32>(pFormatArr[nElem].Index)));
|
|
LocaleDataWrapper::outputCheckMessage(rCurrentLanguage.xLocaleData->appendLocaleInfo(aMsg));
|
|
aMsg.setLength(0);
|
|
}
|
|
}
|
|
if ( nShort != -1 && nShortDef == -1 )
|
|
aMsg.append("no short type default ");
|
|
if ( nMedium != -1 && nMediumDef == -1 )
|
|
aMsg.append("no medium type default ");
|
|
if ( nLong != -1 && nLongDef == -1 )
|
|
aMsg.append("no long type default ");
|
|
if (!aMsg.isEmpty())
|
|
{
|
|
aMsg.insert(0, "SvNumberFormatter::ImpAdjustFormatCodeDefault: ");
|
|
aMsg.append("\nXML locale data FormatElement group of: ");
|
|
LocaleDataWrapper::outputCheckMessage(
|
|
rCurrentLanguage.xLocaleData->appendLocaleInfo(Concat2View(aMsg + pFormatArr[0].NameID)));
|
|
aMsg.setLength(0);
|
|
}
|
|
}
|
|
// 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;
|
|
[[fallthrough]];
|
|
default:
|
|
if ( nDef == -1 )
|
|
nDef = nElem;
|
|
pFormatArr[nElem].Default = false;
|
|
}
|
|
}
|
|
}
|
|
if ( nDef == -1 )
|
|
nDef = 0;
|
|
pFormatArr[nDef].Default = true;
|
|
}
|
|
|
|
SvNumberformat* SvNFFormatData::GetEntry(sal_uInt32 nKey) const
|
|
{
|
|
auto it = aFTable.find( nKey);
|
|
if (it != aFTable.end())
|
|
return it->second.get();
|
|
return nullptr;
|
|
}
|
|
|
|
SvNumberformat* SvNFFormatData::GetFormatEntry(sal_uInt32 nKey)
|
|
{
|
|
return GetEntry(nKey);
|
|
}
|
|
|
|
const SvNumberformat* SvNFFormatData::GetFormatEntry(sal_uInt32 nKey) const
|
|
{
|
|
return GetEntry(nKey);
|
|
}
|
|
|
|
SvNumFormatType SvNFFormatData::GetType(sal_uInt32 nFIndex) const
|
|
{
|
|
SvNumFormatType eType;
|
|
const SvNumberformat* pFormat = GetFormatEntry(nFIndex);
|
|
if (!pFormat)
|
|
eType = SvNumFormatType::UNDEFINED;
|
|
else
|
|
{
|
|
eType = pFormat->GetMaskedType();
|
|
if (eType == SvNumFormatType::ALL)
|
|
eType = SvNumFormatType::DEFINED;
|
|
}
|
|
return eType;
|
|
}
|
|
|
|
const SvNumberformat* SvNumberFormatter::GetEntry( sal_uInt32 nKey ) const
|
|
{
|
|
::osl::MutexGuard aGuard(GetInstanceMutex());
|
|
return m_aFormatData.GetFormatEntry(nKey);
|
|
}
|
|
|
|
const SvNumberformat* SvNumberFormatter::GetSubstitutedEntry( sal_uInt32 nKey, sal_uInt32 & o_rNewKey )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
// A tad ugly, but GetStandardFormat() and GetFormatIndex() in
|
|
// ImpSubstituteEntry() may have to add the LANGUAGE_SYSTEM formats if not
|
|
// already present (which in practice most times they are).
|
|
return ImpSubstituteEntry(m_aFormatData.GetFormatEntry(nKey), &o_rNewKey);
|
|
}
|
|
|
|
const SvNumberformat* SvNumberFormatter::ImpSubstituteEntry( const SvNumberformat* pFormat, sal_uInt32 * o_pRealKey )
|
|
{
|
|
return ::ImpSubstituteEntry(m_aCurrentLanguage, m_aFormatData, GetNatNum(), m_aRWPolicy, pFormat, o_pRealKey);
|
|
}
|
|
|
|
void SvNFFormatData::ImpGenerateFormats(SvNFLanguageData& rCurrentLanguage, const NativeNumberWrapper& rNatNum, sal_uInt32 CLOffset, bool bNoAdditionalFormats )
|
|
{
|
|
bool bOldConvertMode = rCurrentLanguage.pFormatScanner->GetConvertMode();
|
|
if (bOldConvertMode)
|
|
{
|
|
rCurrentLanguage.pFormatScanner->SetConvertMode(false); // switch off for this function
|
|
}
|
|
|
|
css::lang::Locale aLocale = rCurrentLanguage.GetLanguageTag().getLocale();
|
|
css::uno::Reference< css::i18n::XNumberFormatCode > xNFC = i18n::NumberFormatMapper::create(rCurrentLanguage.GetComponentContext());
|
|
sal_Int32 nIdx;
|
|
|
|
// Number
|
|
uno::Sequence< i18n::NumberFormatCode > aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::FIXED_NUMBER, aLocale );
|
|
ImpAdjustFormatCodeDefault(rCurrentLanguage, aFormatSeq.getArray(), aFormatSeq.getLength() );
|
|
|
|
// General
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_NUMBER_STANDARD );
|
|
SvNumberformat* pStdFormat = ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD /* NF_NUMBER_STANDARD */ );
|
|
if (pStdFormat)
|
|
{
|
|
// This is _the_ standard format.
|
|
if (LocaleDataWrapper::areChecksEnabled() && pStdFormat->GetType() != SvNumFormatType::NUMBER)
|
|
{
|
|
LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->
|
|
appendLocaleInfo( u"SvNumberFormatter::ImpGenerateFormats: General format not NUMBER"));
|
|
}
|
|
pStdFormat->SetType( SvNumFormatType::NUMBER );
|
|
pStdFormat->SetStandard();
|
|
pStdFormat->SetLastInsertKey( SV_MAX_COUNT_STANDARD_FORMATS, SvNumberformat::FormatterPrivateAccess() );
|
|
}
|
|
else
|
|
{
|
|
if (LocaleDataWrapper::areChecksEnabled())
|
|
{
|
|
LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->
|
|
appendLocaleInfo( u"SvNumberFormatter::ImpGenerateFormats: General format not insertable, nothing will work"));
|
|
}
|
|
}
|
|
|
|
{
|
|
// Boolean
|
|
OUString aFormatCode = rCurrentLanguage.pFormatScanner->GetBooleanString();
|
|
sal_Int32 nCheckPos = 0;
|
|
|
|
std::unique_ptr<SvNumberformat> pNewFormat(new SvNumberformat( aFormatCode, rCurrentLanguage.pFormatScanner.get(),
|
|
rCurrentLanguage.pStringScanner.get(), rNatNum, nCheckPos, rCurrentLanguage.ActLnge ));
|
|
pNewFormat->SetType(SvNumFormatType::LOGICAL);
|
|
pNewFormat->SetStandard();
|
|
if ( !aFTable.emplace(CLOffset + ZF_STANDARD_LOGICAL /* NF_BOOLEAN */,
|
|
std::move(pNewFormat)).second )
|
|
{
|
|
SAL_WARN( "svl.numbers", "SvNumberFormatter::ImpGenerateFormats: dup position Boolean");
|
|
}
|
|
|
|
// Text
|
|
aFormatCode = "@";
|
|
pNewFormat.reset(new SvNumberformat( aFormatCode, rCurrentLanguage.pFormatScanner.get(),
|
|
rCurrentLanguage.pStringScanner.get(), rNatNum, nCheckPos, rCurrentLanguage.ActLnge ));
|
|
pNewFormat->SetType(SvNumFormatType::TEXT);
|
|
pNewFormat->SetStandard();
|
|
if ( !aFTable.emplace( CLOffset + ZF_STANDARD_TEXT /* NF_TEXT */,
|
|
std::move(pNewFormat)).second )
|
|
{
|
|
SAL_WARN( "svl.numbers", "SvNumberFormatter::ImpGenerateFormats: dup position Text");
|
|
}
|
|
}
|
|
|
|
// 0
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_NUMBER_INT );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD+1 /* NF_NUMBER_INT */ );
|
|
|
|
// 0.00
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_NUMBER_DEC2 );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD+2 /* NF_NUMBER_DEC2 */ );
|
|
|
|
// #,##0
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_NUMBER_1000INT );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD+3 /* NF_NUMBER_1000INT */ );
|
|
|
|
// #,##0.00
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_NUMBER_1000DEC2 );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD+4 /* NF_NUMBER_1000DEC2 */ );
|
|
|
|
// #.##0,00 System country/language dependent
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_NUMBER_SYSTEM );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD+5 /* NF_NUMBER_SYSTEM */ );
|
|
|
|
|
|
// Percent number
|
|
aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::PERCENT_NUMBER, aLocale );
|
|
ImpAdjustFormatCodeDefault(rCurrentLanguage, aFormatSeq.getArray(), aFormatSeq.getLength() );
|
|
|
|
// 0%
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_PERCENT_INT );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_PERCENT /* NF_PERCENT_INT */ );
|
|
|
|
// 0.00%
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_PERCENT_DEC2 );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_PERCENT+1 /* NF_PERCENT_DEC2 */ );
|
|
|
|
|
|
// Currency. NO default standard option! Default is determined of locale
|
|
// data default currency and format is generated if needed.
|
|
aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::CURRENCY, aLocale );
|
|
if (LocaleDataWrapper::areChecksEnabled())
|
|
{
|
|
// though no default desired here, test for correctness of locale data
|
|
ImpAdjustFormatCodeDefault(rCurrentLanguage, aFormatSeq.getArray(), aFormatSeq.getLength() );
|
|
}
|
|
|
|
// #,##0
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_CURRENCY_1000INT );
|
|
// Just copy the format, to avoid COW on sequence after each possible reallocation
|
|
auto aFormat = aFormatSeq[nIdx];
|
|
aFormat.Default = false;
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormat,
|
|
CLOffset + ZF_STANDARD_CURRENCY /* NF_CURRENCY_1000INT */ );
|
|
|
|
// #,##0.00
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_CURRENCY_1000DEC2 );
|
|
aFormat = aFormatSeq[nIdx];
|
|
aFormat.Default = false;
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormat,
|
|
CLOffset + ZF_STANDARD_CURRENCY+1 /* NF_CURRENCY_1000DEC2 */ );
|
|
|
|
// #,##0 negative red
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_CURRENCY_1000INT_RED );
|
|
aFormat = aFormatSeq[nIdx];
|
|
aFormat.Default = false;
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormat,
|
|
CLOffset + ZF_STANDARD_CURRENCY+2 /* NF_CURRENCY_1000INT_RED */ );
|
|
|
|
// #,##0.00 negative red
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_CURRENCY_1000DEC2_RED );
|
|
aFormat = aFormatSeq[nIdx];
|
|
aFormat.Default = false;
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormat,
|
|
CLOffset + ZF_STANDARD_CURRENCY+3 /* NF_CURRENCY_1000DEC2_RED */ );
|
|
|
|
// #,##0.00 USD
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_CURRENCY_1000DEC2_CCC );
|
|
aFormat = aFormatSeq[nIdx];
|
|
aFormat.Default = false;
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormat,
|
|
CLOffset + ZF_STANDARD_CURRENCY+4 /* NF_CURRENCY_1000DEC2_CCC */ );
|
|
|
|
// #.##0,--
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_CURRENCY_1000DEC2_DASHED );
|
|
aFormat = aFormatSeq[nIdx];
|
|
aFormat.Default = false;
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormat,
|
|
CLOffset + ZF_STANDARD_CURRENCY+5 /* NF_CURRENCY_1000DEC2_DASHED */ );
|
|
|
|
|
|
// Date
|
|
aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::DATE, aLocale );
|
|
ImpAdjustFormatCodeDefault(rCurrentLanguage, aFormatSeq.getArray(), aFormatSeq.getLength() );
|
|
|
|
// DD.MM.YY System
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYSTEM_SHORT );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE /* NF_DATE_SYSTEM_SHORT */ );
|
|
|
|
// NN DD.MMM YY
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_DEF_NNDDMMMYY );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE+1 /* NF_DATE_DEF_NNDDMMMYY */ );
|
|
|
|
// DD.MM.YY def/System
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_MMYY );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE+2 /* NF_DATE_SYS_MMYY */ );
|
|
|
|
// DD MMM
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_DDMMM );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE+3 /* NF_DATE_SYS_DDMMM */ );
|
|
|
|
// MMMM
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_MMMM );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE+4 /* NF_DATE_MMMM */ );
|
|
|
|
// QQ YY
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_QQJJ );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE+5 /* NF_DATE_QQJJ */ );
|
|
|
|
// DD.MM.YYYY was DD.MM.[YY]YY
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_DDMMYYYY );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE+6 /* NF_DATE_SYS_DDMMYYYY */ );
|
|
|
|
// DD.MM.YY def/System
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_DDMMYY );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE+7 /* NF_DATE_SYS_DDMMYY */ );
|
|
|
|
// NNN, D. MMMM YYYY System
|
|
// Long day of week: "NNNN" instead of "NNN," because of compatibility
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYSTEM_LONG );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE+8 /* NF_DATE_SYSTEM_LONG */ );
|
|
|
|
// Hard coded but system (regional settings) delimiters dependent long date formats
|
|
|
|
// D. MMM YY def/System
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_DMMMYY );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE+9 /* NF_DATE_SYS_DMMMYY */ );
|
|
|
|
// D. MMM YYYY def/System
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_DMMMYYYY );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE_SYS_DMMMYYYY /* NF_DATE_SYS_DMMMYYYY */ );
|
|
|
|
// D. MMMM YYYY def/System
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_DMMMMYYYY );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE_SYS_DMMMMYYYY /* NF_DATE_SYS_DMMMMYYYY */ );
|
|
|
|
// NN, D. MMM YY def/System
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_NNDMMMYY );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE_SYS_NNDMMMYY /* NF_DATE_SYS_NNDMMMYY */ );
|
|
|
|
// NN, D. MMMM YYYY def/System
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_NNDMMMMYYYY );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE_SYS_NNDMMMMYYYY /* NF_DATE_SYS_NNDMMMMYYYY */ );
|
|
|
|
// NNN, D. MMMM YYYY def/System
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_NNNNDMMMMYYYY );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE_SYS_NNNNDMMMMYYYY /* NF_DATE_SYS_NNNNDMMMMYYYY */ );
|
|
|
|
// Hard coded DIN (Deutsche Industrie Norm) and EN (European Norm) date formats
|
|
|
|
// D. MMM. YYYY DIN/EN
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_DIN_DMMMYYYY );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE_DIN_DMMMYYYY /* NF_DATE_DIN_DMMMYYYY */ );
|
|
|
|
// D. MMMM YYYY DIN/EN
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_DIN_DMMMMYYYY );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE_DIN_DMMMMYYYY /* NF_DATE_DIN_DMMMMYYYY */ );
|
|
|
|
// MM-DD DIN/EN
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_DIN_MMDD );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE_DIN_MMDD /* NF_DATE_DIN_MMDD */ );
|
|
|
|
// YY-MM-DD DIN/EN
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_DIN_YYMMDD );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE_DIN_YYMMDD /* NF_DATE_DIN_YYMMDD */ );
|
|
|
|
// YYYY-MM-DD DIN/EN/ISO
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_DIN_YYYYMMDD );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATE_DIN_YYYYMMDD /* NF_DATE_DIN_YYYYMMDD */ );
|
|
|
|
|
|
// Time
|
|
aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::TIME, aLocale );
|
|
ImpAdjustFormatCodeDefault(rCurrentLanguage, aFormatSeq.getArray(), aFormatSeq.getLength() );
|
|
|
|
// HH:MM
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_TIME_HHMM );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_TIME /* NF_TIME_HHMM */ );
|
|
|
|
// HH:MM:SS
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_TIME_HHMMSS );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_TIME+1 /* NF_TIME_HHMMSS */ );
|
|
|
|
// HH:MM AM/PM
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_TIME_HHMMAMPM );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_TIME+2 /* NF_TIME_HHMMAMPM */ );
|
|
|
|
// HH:MM:SS AM/PM
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_TIME_HHMMSSAMPM );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_TIME+3 /* NF_TIME_HHMMSSAMPM */ );
|
|
|
|
// [HH]:MM:SS
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_TIME_HH_MMSS );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_TIME+4 /* NF_TIME_HH_MMSS */ );
|
|
|
|
// MM:SS,00
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_TIME_MMSS00 );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_TIME+5 /* NF_TIME_MMSS00 */ );
|
|
|
|
// [HH]:MM:SS,00
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_TIME_HH_MMSS00 );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_TIME+6 /* NF_TIME_HH_MMSS00 */ );
|
|
|
|
|
|
// DateTime
|
|
aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::DATE_TIME, aLocale );
|
|
ImpAdjustFormatCodeDefault(rCurrentLanguage, aFormatSeq.getArray(), aFormatSeq.getLength() );
|
|
|
|
// DD.MM.YY HH:MM System
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATETIME_SYSTEM_SHORT_HHMM );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATETIME /* NF_DATETIME_SYSTEM_SHORT_HHMM */ );
|
|
|
|
// DD.MM.YYYY HH:MM:SS System
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATETIME_SYS_DDMMYYYY_HHMMSS );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATETIME+1 /* NF_DATETIME_SYS_DDMMYYYY_HHMMSS */ );
|
|
|
|
// DD.MM.YYYY HH:MM System
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATETIME_SYS_DDMMYYYY_HHMM );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_DATETIME+2 /* NF_DATETIME_SYS_DDMMYYYY_HHMM */ );
|
|
|
|
const NfKeywordTable & rKeyword = rCurrentLanguage.pFormatScanner->GetKeywords();
|
|
i18n::NumberFormatCode aSingleFormatCode;
|
|
aSingleFormatCode.Usage = i18n::KNumberFormatUsage::DATE_TIME;
|
|
|
|
// YYYY-MM-DD HH:MM:SS ISO (with blank instead of 'T')
|
|
aSingleFormatCode.Code =
|
|
rKeyword[NF_KEY_YYYY] + "-" +
|
|
rKeyword[NF_KEY_MM] + "-" +
|
|
rKeyword[NF_KEY_DD] + " " +
|
|
rKeyword[NF_KEY_HH] + ":" +
|
|
rKeyword[NF_KEY_MMI] + ":" +
|
|
rKeyword[NF_KEY_SS];
|
|
SvNumberformat* pFormat = ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
|
|
CLOffset + ZF_STANDARD_DATETIME+3 /* NF_DATETIME_ISO_YYYYMMDD_HHMMSS */ );
|
|
assert(pFormat);
|
|
|
|
// YYYY-MM-DD HH:MM:SS,000 ISO (with blank instead of 'T') and
|
|
// milliseconds and locale's time decimal separator
|
|
aSingleFormatCode.Code =
|
|
rKeyword[NF_KEY_YYYY] + "-" +
|
|
rKeyword[NF_KEY_MM] + "-" +
|
|
rKeyword[NF_KEY_DD] + " " +
|
|
rKeyword[NF_KEY_HH] + ":" +
|
|
rKeyword[NF_KEY_MMI] + ":" +
|
|
rKeyword[NF_KEY_SS] + rCurrentLanguage.GetLocaleData()->getTime100SecSep() +
|
|
"000";
|
|
pFormat = ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
|
|
CLOffset + ZF_STANDARD_DATETIME+4 /* NF_DATETIME_ISO_YYYYMMDD_HHMMSS000 */ );
|
|
assert(pFormat);
|
|
|
|
// YYYY-MM-DD"T"HH:MM:SS ISO
|
|
aSingleFormatCode.Code =
|
|
rKeyword[NF_KEY_YYYY] + "-" +
|
|
rKeyword[NF_KEY_MM] + "-" +
|
|
rKeyword[NF_KEY_DD] + "\"T\"" +
|
|
rKeyword[NF_KEY_HH] + ":" +
|
|
rKeyword[NF_KEY_MMI] + ":" +
|
|
rKeyword[NF_KEY_SS];
|
|
pFormat = ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
|
|
CLOffset + ZF_STANDARD_DATETIME+5 /* NF_DATETIME_ISO_YYYYMMDDTHHMMSS */ );
|
|
assert(pFormat);
|
|
pFormat->SetComment(u"ISO 8601"_ustr); // not to be localized
|
|
|
|
// YYYY-MM-DD"T"HH:MM:SS,000 ISO with milliseconds and ',' or '.' decimal separator
|
|
aSingleFormatCode.Code =
|
|
rKeyword[NF_KEY_YYYY] + "-" +
|
|
rKeyword[NF_KEY_MM] + "-" +
|
|
rKeyword[NF_KEY_DD] + "\"T\"" +
|
|
rKeyword[NF_KEY_HH] + ":" +
|
|
rKeyword[NF_KEY_MMI] + ":" +
|
|
rKeyword[NF_KEY_SS] + (rCurrentLanguage.GetLocaleData()->getTime100SecSep() == "." ? "." : ",") +
|
|
"000";
|
|
pFormat = ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
|
|
CLOffset + ZF_STANDARD_DATETIME+6 /* NF_DATETIME_ISO_YYYYMMDDTHHMMSS000 */ );
|
|
assert(pFormat);
|
|
pFormat->SetComment(u"ISO 8601"_ustr); // not to be localized
|
|
|
|
|
|
// Scientific number
|
|
aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::SCIENTIFIC_NUMBER, aLocale );
|
|
ImpAdjustFormatCodeDefault(rCurrentLanguage, aFormatSeq.getArray(), aFormatSeq.getLength() );
|
|
|
|
// 0.00E+000
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_SCIENTIFIC_000E000 );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_SCIENTIFIC /* NF_SCIENTIFIC_000E000 */ );
|
|
|
|
// 0.00E+00
|
|
nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_SCIENTIFIC_000E00 );
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
|
|
CLOffset + ZF_STANDARD_SCIENTIFIC+1 /* NF_SCIENTIFIC_000E00 */ );
|
|
|
|
|
|
// Fraction number (no default option)
|
|
aSingleFormatCode.Usage = i18n::KNumberFormatUsage::FRACTION_NUMBER;
|
|
|
|
// # ?/?
|
|
aSingleFormatCode.Code = "# ?/?";
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
|
|
CLOffset + ZF_STANDARD_FRACTION /* NF_FRACTION_1D */ );
|
|
|
|
// # ??/??
|
|
//! "??/" would be interpreted by the compiler as a trigraph for '\'
|
|
aSingleFormatCode.Code = "# ?\?/?\?";
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
|
|
CLOffset + ZF_STANDARD_FRACTION+1 /* NF_FRACTION_2D */ );
|
|
|
|
// # ???/???
|
|
//! "??/" would be interpreted by the compiler as a trigraph for '\'
|
|
aSingleFormatCode.Code = "# ?\?\?/?\?\?";
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
|
|
CLOffset + ZF_STANDARD_FRACTION+2 /* NF_FRACTION_3D */ );
|
|
|
|
// # ?/2
|
|
aSingleFormatCode.Code = "# ?/2";
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
|
|
CLOffset + ZF_STANDARD_FRACTION+3 /* NF_FRACTION_2 */ );
|
|
|
|
// # ?/4
|
|
aSingleFormatCode.Code = "# ?/4";
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
|
|
CLOffset + ZF_STANDARD_FRACTION+4 /* NF_FRACTION_4 */ );
|
|
|
|
// # ?/8
|
|
aSingleFormatCode.Code = "# ?/8";
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
|
|
CLOffset + ZF_STANDARD_FRACTION+5 /* NF_FRACTION_8 */ );
|
|
|
|
// # ??/16
|
|
aSingleFormatCode.Code = "# ?\?/16";
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
|
|
CLOffset + ZF_STANDARD_FRACTION+6 /* NF_FRACTION_16 */ );
|
|
|
|
// # ??/10
|
|
aSingleFormatCode.Code = "# ?\?/10";
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
|
|
CLOffset + ZF_STANDARD_FRACTION+7 /* NF_FRACTION_10 */ );
|
|
|
|
// # ??/100
|
|
aSingleFormatCode.Code = "# ?\?/100";
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
|
|
CLOffset + ZF_STANDARD_FRACTION+8 /* NF_FRACTION_100 */ );
|
|
|
|
|
|
// Week of year
|
|
aSingleFormatCode.Code = rKeyword[NF_KEY_WW];
|
|
ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
|
|
CLOffset + ZF_STANDARD_DATE_WW /* NF_DATE_WW */ );
|
|
|
|
// Now all additional format codes provided by I18N, but only if not
|
|
// changing SystemCL, then they are appended last after user defined.
|
|
if ( !bNoAdditionalFormats )
|
|
{
|
|
ImpGenerateAdditionalFormats(rCurrentLanguage, rNatNum, CLOffset, xNFC, false);
|
|
}
|
|
if (bOldConvertMode)
|
|
{
|
|
rCurrentLanguage.pFormatScanner->SetConvertMode(true);
|
|
}
|
|
}
|
|
|
|
void SvNFFormatData::ImpGenerateAdditionalFormats(SvNFLanguageData& rCurrentLanguage,
|
|
const NativeNumberWrapper& rNatNum, sal_uInt32 CLOffset,
|
|
css::uno::Reference< css::i18n::XNumberFormatCode > const & rNumberFormatCode,
|
|
bool bAfterChangingSystemCL )
|
|
{
|
|
SvNumberformat* pStdFormat = GetFormatEntry( CLOffset + ZF_STANDARD );
|
|
if ( !pStdFormat )
|
|
{
|
|
SAL_WARN( "svl.numbers", "ImpGenerateAdditionalFormats: no GENERAL format" );
|
|
return ;
|
|
}
|
|
sal_uInt32 nPos = CLOffset + pStdFormat->GetLastInsertKey( SvNumberformat::FormatterPrivateAccess() );
|
|
css::lang::Locale aLocale = rCurrentLanguage.GetLanguageTag().getLocale();
|
|
|
|
// 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, aLocale );
|
|
sal_Int32 nCodes = aFormatSeq.getLength();
|
|
auto aNonConstRange = asNonConstRange(aFormatSeq);
|
|
ImpAdjustFormatCodeDefault(rCurrentLanguage, aNonConstRange.begin(), nCodes);
|
|
for ( i18n::NumberFormatCode& rFormat : aNonConstRange )
|
|
{
|
|
if ( nPos - CLOffset >= SV_COUNTRY_LANGUAGE_OFFSET )
|
|
{
|
|
SAL_WARN( "svl.numbers", "ImpGenerateAdditionalFormats: too many formats" );
|
|
break; // for
|
|
}
|
|
if ( rFormat.Index < NF_INDEX_TABLE_RESERVED_START &&
|
|
rFormat.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 = rFormat.Index;
|
|
rFormat.Index = sal::static_int_cast< sal_Int16 >(
|
|
rFormat.Index + nCodes + NF_INDEX_TABLE_ENTRIES);
|
|
//! no default on currency
|
|
bool bDefault = rFormat.Default;
|
|
rFormat.Default = false;
|
|
if ( SvNumberformat* pNewFormat = ImpInsertFormat(rCurrentLanguage, rNatNum, rFormat, nPos+1,
|
|
bAfterChangingSystemCL, nOrgIndex ) )
|
|
{
|
|
pNewFormat->SetAdditionalBuiltin();
|
|
nPos++;
|
|
}
|
|
rFormat.Index = nOrgIndex;
|
|
rFormat.Default = bDefault;
|
|
}
|
|
}
|
|
|
|
// All additional format codes provided by I18N that are not old standard
|
|
// index. Additional formats may define defaults, currently there is no
|
|
// check if more than one default of a usage/type combination is provided,
|
|
// like it is done for usage groups with ImpAdjustFormatCodeDefault().
|
|
// There is no harm though, on first invocation ImpGetDefaultFormat() will
|
|
// use the first default encountered.
|
|
aFormatSeq = rNumberFormatCode->getAllFormatCodes( aLocale );
|
|
for ( const auto& rFormat : std::as_const(aFormatSeq) )
|
|
{
|
|
if ( nPos - CLOffset >= SV_COUNTRY_LANGUAGE_OFFSET )
|
|
{
|
|
SAL_WARN( "svl.numbers", "ImpGenerateAdditionalFormats: too many formats" );
|
|
break; // for
|
|
}
|
|
if ( rFormat.Index >= NF_INDEX_TABLE_RESERVED_START )
|
|
{
|
|
if ( SvNumberformat* pNewFormat = ImpInsertFormat(rCurrentLanguage, rNatNum, rFormat, nPos+1,
|
|
bAfterChangingSystemCL ) )
|
|
{
|
|
pNewFormat->SetAdditionalBuiltin();
|
|
nPos++;
|
|
}
|
|
}
|
|
}
|
|
|
|
pStdFormat->SetLastInsertKey( static_cast<sal_uInt16>(nPos - CLOffset), SvNumberformat::FormatterPrivateAccess() );
|
|
}
|
|
|
|
void SvNumberFormatter::ImpGenerateAdditionalFormats( sal_uInt32 CLOffset,
|
|
css::uno::Reference< css::i18n::XNumberFormatCode > const & rNumberFormatCode )
|
|
{
|
|
m_aFormatData.ImpGenerateAdditionalFormats(m_aCurrentLanguage, GetNatNum(), CLOffset, rNumberFormatCode, /*bAfterChangingSystemCL*/true);
|
|
}
|
|
|
|
namespace {
|
|
|
|
// return position of a special character
|
|
sal_Int32 ImpPosToken(const OUStringBuffer & sFormat, sal_Unicode token, sal_Int32 nStartPos = 0)
|
|
{
|
|
sal_Int32 nLength = sFormat.getLength();
|
|
for ( sal_Int32 i=nStartPos; i<nLength && i>=0 ; i++ )
|
|
{
|
|
switch(sFormat[i])
|
|
{
|
|
case '\"' : // skip text
|
|
i = sFormat.indexOf('\"',i+1);
|
|
break;
|
|
case '[' : // skip condition
|
|
i = sFormat.indexOf(']',i+1);
|
|
break;
|
|
case '\\' : // skip escaped character
|
|
i++;
|
|
break;
|
|
case ';' :
|
|
if (token == ';')
|
|
return i;
|
|
break;
|
|
case 'e' :
|
|
case 'E' :
|
|
if (token == 'E')
|
|
return i; // if 'E' is outside "" and [] it must be the 'E' exponent
|
|
break;
|
|
default : break;
|
|
}
|
|
if ( i<0 )
|
|
i--;
|
|
}
|
|
return -2;
|
|
}
|
|
|
|
}
|
|
|
|
OUString SvNFEngine::GenerateFormat(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
|
|
sal_uInt32 nIndex,
|
|
LanguageType eLnge,
|
|
bool bThousand,
|
|
bool IsRed,
|
|
sal_uInt16 nPrecision,
|
|
sal_uInt16 nLeadingZeros)
|
|
{
|
|
eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
|
|
const SvNumberformat* pFormat = rFormatData.GetFormatEntry( nIndex );
|
|
const SvNumFormatType eType = (pFormat ? pFormat->GetMaskedType() : SvNumFormatType::UNDEFINED);
|
|
|
|
rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)
|
|
|
|
utl::DigitGroupingIterator aGrouping( rCurrentLanguage.xLocaleData->getDigitGrouping());
|
|
// always group of 3 for Engineering notation
|
|
const sal_Int32 nDigitsInFirstGroup = ( bThousand && (eType == SvNumFormatType::SCIENTIFIC) ) ? 3 : aGrouping.get();
|
|
const OUString& rThSep = rCurrentLanguage.GetNumThousandSep();
|
|
|
|
OUStringBuffer sString;
|
|
using comphelper::string::padToLength;
|
|
|
|
if (eType & SvNumFormatType::TIME)
|
|
{
|
|
assert(pFormat && "with !pFormat eType can only be SvNumFormatType::UNDEFINED");
|
|
sString = pFormat->GetFormatStringForTimePrecision( nPrecision );
|
|
}
|
|
else if (nLeadingZeros == 0)
|
|
{
|
|
if (!bThousand)
|
|
sString.append('#');
|
|
else
|
|
{
|
|
if (eType == SvNumFormatType::SCIENTIFIC)
|
|
{ // for scientific, bThousand is used for Engineering notation
|
|
sString.append("###");
|
|
}
|
|
else
|
|
{
|
|
sString.append("#" + rThSep);
|
|
padToLength(sString, sString.getLength() + nDigitsInFirstGroup, '#');
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (sal_uInt16 i = 0; i < nLeadingZeros; i++)
|
|
{
|
|
if (bThousand && i > 0 && i == aGrouping.getPos())
|
|
{
|
|
sString.insert(0, rThSep);
|
|
aGrouping.advance();
|
|
}
|
|
sString.insert(0, '0');
|
|
}
|
|
if ( bThousand )
|
|
{
|
|
sal_Int32 nDigits = (eType == SvNumFormatType::SCIENTIFIC) ? 3*((nLeadingZeros-1)/3 + 1) : nDigitsInFirstGroup + 1;
|
|
for (sal_Int32 i = nLeadingZeros; i < nDigits; i++)
|
|
{
|
|
if ( i % nDigitsInFirstGroup == 0 )
|
|
sString.insert(0, rThSep);
|
|
sString.insert(0, '#');
|
|
}
|
|
}
|
|
}
|
|
if (nPrecision > 0 && eType != SvNumFormatType::FRACTION && !( eType & SvNumFormatType::TIME ) )
|
|
{
|
|
sString.append(rCurrentLanguage.GetNumDecimalSep());
|
|
padToLength(sString, sString.getLength() + nPrecision, '0');
|
|
}
|
|
|
|
// Native Number
|
|
const OUString sPosNatNumModifier = pFormat ? pFormat->GetNatNumModifierString( 0 ) : u""_ustr;
|
|
const OUString sNegNatNumModifier = pFormat ?
|
|
// if a negative format already exists, use its NatNum modifier
|
|
// else use NatNum modifier of positive format
|
|
( pFormat->GetNumForString( 1, 0 ) ? pFormat->GetNatNumModifierString( 1 ) : sPosNatNumModifier )
|
|
: u""_ustr;
|
|
|
|
if (eType == SvNumFormatType::PERCENT)
|
|
{
|
|
sString.append( pFormat->GetPercentString() );
|
|
}
|
|
else if (eType == SvNumFormatType::SCIENTIFIC)
|
|
{
|
|
OUStringBuffer sOldFormatString(pFormat->GetFormatstring());
|
|
sal_Int32 nIndexE = ImpPosToken( sOldFormatString, 'E' );
|
|
if (nIndexE > -1)
|
|
{
|
|
sal_Int32 nIndexSep = ImpPosToken( sOldFormatString, ';', nIndexE );
|
|
if (nIndexSep > nIndexE)
|
|
sString.append( sOldFormatString.subView(nIndexE, nIndexSep - nIndexE) );
|
|
else
|
|
sString.append( sOldFormatString.subView(nIndexE) );
|
|
}
|
|
}
|
|
else if (eType == SvNumFormatType::CURRENCY)
|
|
{
|
|
OUStringBuffer sNegStr(sString);
|
|
OUString aCurr;
|
|
const NfCurrencyEntry* pEntry;
|
|
bool bBank;
|
|
bool isPosNatNum12 = sPosNatNumModifier.startsWith( "[NatNum12" );
|
|
bool isNegNatNum12 = sNegNatNumModifier.startsWith( "[NatNum12" );
|
|
if ( !isPosNatNum12 || !isNegNatNum12 )
|
|
{
|
|
if (rFormatData.GetNewCurrencySymbolString(nIndex, aCurr, &pEntry, &bBank))
|
|
{
|
|
if ( pEntry )
|
|
{
|
|
sal_uInt16 nPosiForm = NfCurrencyEntry::GetEffectivePositiveFormat(
|
|
rCurrentLanguage.xLocaleData->getCurrPositiveFormat(),
|
|
pEntry->GetPositiveFormat(), bBank );
|
|
sal_uInt16 nNegaForm = NfCurrencyEntry::GetEffectiveNegativeFormat(
|
|
rCurrentLanguage.xLocaleData->getCurrNegativeFormat(),
|
|
pEntry->GetNegativeFormat(), bBank );
|
|
if ( !isPosNatNum12 )
|
|
pEntry->CompletePositiveFormatString( sString, bBank, nPosiForm );
|
|
if ( !isNegNatNum12 )
|
|
pEntry->CompleteNegativeFormatString( sNegStr, bBank, nNegaForm );
|
|
}
|
|
else
|
|
{ // assume currency abbreviation (AKA banking symbol), not symbol
|
|
sal_uInt16 nPosiForm = NfCurrencyEntry::GetEffectivePositiveFormat(
|
|
rCurrentLanguage.xLocaleData->getCurrPositiveFormat(),
|
|
rCurrentLanguage.xLocaleData->getCurrPositiveFormat(), true );
|
|
sal_uInt16 nNegaForm = NfCurrencyEntry::GetEffectiveNegativeFormat(
|
|
rCurrentLanguage.xLocaleData->getCurrNegativeFormat(),
|
|
rCurrentLanguage.xLocaleData->getCurrNegativeFormat(), true );
|
|
if ( !isPosNatNum12 )
|
|
NfCurrencyEntry::CompletePositiveFormatString( sString, aCurr, nPosiForm );
|
|
if ( !isNegNatNum12 )
|
|
NfCurrencyEntry::CompleteNegativeFormatString( sNegStr, aCurr, nNegaForm );
|
|
}
|
|
}
|
|
else
|
|
{ // "automatic" old style
|
|
OUString aSymbol, aAbbrev;
|
|
rCurrentLanguage.GetCompatibilityCurrency(aSymbol, aAbbrev);
|
|
if ( !isPosNatNum12 )
|
|
NfCurrencyEntry::CompletePositiveFormatString( sString,
|
|
aSymbol, rCurrentLanguage.xLocaleData->getCurrPositiveFormat() );
|
|
if ( !isNegNatNum12 )
|
|
NfCurrencyEntry::CompleteNegativeFormatString( sNegStr,
|
|
aSymbol, rCurrentLanguage.xLocaleData->getCurrNegativeFormat() );
|
|
}
|
|
}
|
|
sString.append( ';' );
|
|
if (IsRed)
|
|
{
|
|
sString.append("[" + rCurrentLanguage.pFormatScanner->GetRedString() + "]");
|
|
}
|
|
sString.append( sNegNatNumModifier );
|
|
if ( isNegNatNum12 )
|
|
{
|
|
sString.append( '-' );
|
|
}
|
|
sString.append(sNegStr);
|
|
}
|
|
else if (eType == SvNumFormatType::FRACTION)
|
|
{
|
|
OUString aIntegerFractionDelimiterString = pFormat->GetIntegerFractionDelimiterString( 0 );
|
|
if ( aIntegerFractionDelimiterString == " " )
|
|
sString.append( aIntegerFractionDelimiterString );
|
|
else
|
|
{
|
|
sString.append( "\"" + aIntegerFractionDelimiterString + "\"" );
|
|
}
|
|
sString.append( pFormat->GetNumeratorString( 0 )
|
|
+ "/" );
|
|
if ( nPrecision > 0 )
|
|
padToLength(sString, sString.getLength() + nPrecision, '?');
|
|
else
|
|
sString.append( '#' );
|
|
}
|
|
if (eType != SvNumFormatType::CURRENCY)
|
|
{
|
|
bool insertBrackets = false;
|
|
if ( eType != SvNumFormatType::UNDEFINED)
|
|
{
|
|
insertBrackets = pFormat->IsNegativeInBracket();
|
|
}
|
|
if (IsRed || insertBrackets)
|
|
{
|
|
OUStringBuffer sTmpStr(sString);
|
|
|
|
if (pFormat && pFormat->HasPositiveBracketPlaceholder())
|
|
{
|
|
sTmpStr.append("_)");
|
|
}
|
|
sTmpStr.append(';');
|
|
|
|
if (IsRed)
|
|
{
|
|
sTmpStr.append("[" + rCurrentLanguage.pFormatScanner->GetRedString() + "]");
|
|
}
|
|
sTmpStr.append( sNegNatNumModifier );
|
|
|
|
if (insertBrackets)
|
|
{
|
|
sTmpStr.append("(" + sString + ")");
|
|
}
|
|
else
|
|
{
|
|
sTmpStr.append("-" + sString);
|
|
}
|
|
sString = std::move(sTmpStr);
|
|
}
|
|
}
|
|
sString.insert( 0, sPosNatNumModifier );
|
|
return sString.makeStringAndClear();
|
|
}
|
|
|
|
OUString SvNumberFormatter::GenerateFormat(sal_uInt32 nIndex,
|
|
LanguageType eLnge,
|
|
bool bThousand,
|
|
bool IsRed,
|
|
sal_uInt16 nPrecision,
|
|
sal_uInt16 nLeadingZeros)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return SvNFEngine::GenerateFormat(m_aCurrentLanguage, m_aFormatData,
|
|
GetNatNum(), m_aRWPolicy,
|
|
nIndex, eLnge,
|
|
bThousand, IsRed,
|
|
nPrecision, nLeadingZeros);
|
|
}
|
|
|
|
bool SvNumberFormatter::IsUserDefined(sal_uInt32 F_Index) const
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
const SvNumberformat* pFormat = m_aFormatData.GetFormatEntry(F_Index);
|
|
|
|
return pFormat && (pFormat->GetType() & SvNumFormatType::DEFINED);
|
|
}
|
|
|
|
bool SvNumberFormatter::IsUserDefined(std::u16string_view sStr,
|
|
LanguageType eLnge)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
if (eLnge == LANGUAGE_DONTKNOW)
|
|
{
|
|
eLnge = IniLnge;
|
|
}
|
|
sal_uInt32 CLOffset = m_aFormatData.ImpGenerateCL(m_aCurrentLanguage, GetNatNum(), eLnge); // create new standard formats if necessary
|
|
eLnge = m_aCurrentLanguage.ActLnge;
|
|
|
|
sal_uInt32 nKey = ImpIsEntry(sStr, CLOffset, eLnge);
|
|
if (nKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
|
|
{
|
|
return true;
|
|
}
|
|
SvNumberformat* pEntry = m_aFormatData.GetFormatEntry( nKey );
|
|
return pEntry && (pEntry->GetType() & SvNumFormatType::DEFINED);
|
|
}
|
|
|
|
sal_uInt32 SvNumberFormatter::GetEntryKey(std::u16string_view sStr,
|
|
LanguageType eLnge)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
if (eLnge == LANGUAGE_DONTKNOW)
|
|
{
|
|
eLnge = IniLnge;
|
|
}
|
|
sal_uInt32 CLOffset = m_aFormatData.ImpGenerateCL(m_aCurrentLanguage, GetNatNum(), eLnge); // create new standard formats if necessary
|
|
return ImpIsEntry(sStr, CLOffset, eLnge);
|
|
}
|
|
|
|
sal_uInt32 SvNFEngine::GetStandardIndex(SvNFLanguageData& rCurrentLanguage,
|
|
const SvNFFormatData& rFormatData,
|
|
const NativeNumberWrapper& rNatNum,
|
|
const Accessor& rFuncs,
|
|
LanguageType eLnge)
|
|
{
|
|
return SvNFEngine::GetStandardFormat(rCurrentLanguage, rFormatData,
|
|
rNatNum, rFuncs,
|
|
SvNumFormatType::NUMBER, eLnge);
|
|
}
|
|
|
|
sal_uInt32 SvNumberFormatter::GetStandardIndex(LanguageType eLnge)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return SvNFEngine::GetStandardIndex(m_aCurrentLanguage, m_aFormatData, GetNatNum(),
|
|
m_aRWPolicy, eLnge);
|
|
}
|
|
|
|
SvNumFormatType SvNumberFormatter::GetType(sal_uInt32 nFIndex) const
|
|
{
|
|
::osl::MutexGuard aGuard(GetInstanceMutex());
|
|
return m_aFormatData.GetType(nFIndex);
|
|
}
|
|
|
|
void SvNumberFormatter::ClearMergeTable()
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
if ( pMergeTable )
|
|
{
|
|
pMergeTable->clear();
|
|
}
|
|
}
|
|
|
|
SvNumberFormatterIndexTable* SvNumberFormatter::MergeFormatter(SvNumberFormatter& rTable)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
if ( pMergeTable )
|
|
{
|
|
ClearMergeTable();
|
|
}
|
|
else
|
|
{
|
|
pMergeTable.reset( new SvNumberFormatterIndexTable );
|
|
}
|
|
|
|
sal_uInt32 nCLOffset = 0;
|
|
sal_uInt32 nOldKey, nOffset, nNewKey;
|
|
|
|
for (const auto& rEntry : rTable.m_aFormatData.aFTable)
|
|
{
|
|
SvNumberformat* pFormat = rEntry.second.get();
|
|
nOldKey = rEntry.first;
|
|
nOffset = nOldKey % SV_COUNTRY_LANGUAGE_OFFSET; // relative index
|
|
if (nOffset == 0) // 1st format of CL
|
|
{
|
|
nCLOffset = m_aFormatData.ImpGenerateCL(m_aCurrentLanguage, GetNatNum(), pFormat->GetLanguage());
|
|
}
|
|
if (nOffset <= SV_MAX_COUNT_STANDARD_FORMATS) // Std.form.
|
|
{
|
|
nNewKey = nCLOffset + nOffset;
|
|
if (m_aFormatData.aFTable.find( nNewKey) == m_aFormatData.aFTable.end()) // not already present
|
|
{
|
|
std::unique_ptr<SvNumberformat> pNewEntry(new SvNumberformat( *pFormat, *m_aCurrentLanguage.pFormatScanner ));
|
|
if (!m_aFormatData.aFTable.emplace( nNewKey, std::move(pNewEntry)).second)
|
|
{
|
|
SAL_WARN( "svl.numbers", "SvNumberFormatter::MergeFormatter: dup position");
|
|
}
|
|
}
|
|
if (nNewKey != nOldKey) // new index
|
|
{
|
|
(*pMergeTable)[nOldKey] = nNewKey;
|
|
}
|
|
}
|
|
else // user defined
|
|
{
|
|
std::unique_ptr<SvNumberformat> pNewEntry(new SvNumberformat( *pFormat, *m_aCurrentLanguage.pFormatScanner ));
|
|
nNewKey = ImpIsEntry(pNewEntry->GetFormatstring(),
|
|
nCLOffset,
|
|
pFormat->GetLanguage());
|
|
if (nNewKey == NUMBERFORMAT_ENTRY_NOT_FOUND) // only if not present yet
|
|
{
|
|
SvNumberformat* pStdFormat = m_aFormatData.GetFormatEntry(nCLOffset + ZF_STANDARD);
|
|
sal_uInt32 nPos = nCLOffset + pStdFormat->GetLastInsertKey( SvNumberformat::FormatterPrivateAccess() );
|
|
nNewKey = nPos+1;
|
|
if (nNewKey - nCLOffset >= SV_COUNTRY_LANGUAGE_OFFSET)
|
|
{
|
|
SAL_WARN( "svl.numbers", "SvNumberFormatter::MergeFormatter: too many formats for CL");
|
|
}
|
|
else if (!m_aFormatData.aFTable.emplace( nNewKey, std::move(pNewEntry)).second)
|
|
{
|
|
SAL_WARN( "svl.numbers", "SvNumberFormatter::MergeFormatter: dup position");
|
|
}
|
|
else
|
|
{
|
|
pStdFormat->SetLastInsertKey(static_cast<sal_uInt16>(nNewKey - nCLOffset),
|
|
SvNumberformat::FormatterPrivateAccess());
|
|
}
|
|
}
|
|
if (nNewKey != nOldKey) // new index
|
|
{
|
|
(*pMergeTable)[nOldKey] = nNewKey;
|
|
}
|
|
}
|
|
}
|
|
return pMergeTable.get();
|
|
}
|
|
|
|
|
|
SvNumberFormatterMergeMap SvNumberFormatter::ConvertMergeTableToMap()
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
if (!HasMergeFormatTable())
|
|
{
|
|
return SvNumberFormatterMergeMap();
|
|
}
|
|
SvNumberFormatterMergeMap aMap;
|
|
for (const auto& rEntry : *pMergeTable)
|
|
{
|
|
sal_uInt32 nOldKey = rEntry.first;
|
|
aMap[ nOldKey ] = rEntry.second;
|
|
}
|
|
ClearMergeTable();
|
|
return aMap;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
bool IsFormatSimpleBuiltIn(const SvNFLanguageData& rCurrentLanguage, sal_uInt32 nFormat, LanguageType eLnge)
|
|
{
|
|
if (nFormat < SV_COUNTRY_LANGUAGE_OFFSET && eLnge == rCurrentLanguage.GetIniLanguage())
|
|
return true; // it stays as it is
|
|
|
|
sal_uInt32 nOffset = nFormat % SV_COUNTRY_LANGUAGE_OFFSET; // relative index
|
|
if (nOffset > SV_MAX_COUNT_STANDARD_FORMATS)
|
|
return true; // not a built-in format
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
sal_uInt32 SvNFEngine::GetCLOffsetRO(const SvNFFormatData& rFormatData, SvNFLanguageData&, const NativeNumberWrapper&, LanguageType eLnge)
|
|
{
|
|
sal_uInt32 nCLOffset = rFormatData.ImpGetCLOffset(eLnge);
|
|
assert(nCLOffset <= rFormatData.MaxCLOffset && "Language must have already been encountered");
|
|
return nCLOffset;
|
|
}
|
|
|
|
// we can't cache to SvNFFormatData in RO mode, so cache to a per-thread temp map which we can consult in FindFormatRO
|
|
// caches can be merged with SvNumberFormatter::MergeDefaultFormatKeys
|
|
void SvNFEngine::CacheFormatRO(SvNFFormatData::DefaultFormatKeysMap& rFormatCache, sal_uInt32 nSearch, sal_uInt32 nFormat)
|
|
{
|
|
rFormatCache[nSearch] = nFormat;
|
|
}
|
|
|
|
sal_uInt32 SvNFEngine::FindFormatRO(const SvNFFormatData& rFormatData, const SvNFFormatData::DefaultFormatKeysMap& rFormatCache, sal_uInt32 nSearch)
|
|
{
|
|
sal_uInt32 nFormat = rFormatData.FindCachedDefaultFormat(nSearch);
|
|
if (nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
|
|
return nFormat;
|
|
return ::FindCachedDefaultFormat(rFormatCache, nSearch);
|
|
}
|
|
|
|
sal_uInt32 SvNFEngine::GetCLOffsetRW(SvNFFormatData& rFormatData, SvNFLanguageData& rCurrentLanguage, const NativeNumberWrapper& rNatNum, LanguageType eLnge)
|
|
{
|
|
return rFormatData.ImpGenerateCL(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary
|
|
}
|
|
|
|
void SvNFEngine::CacheFormatRW(SvNFFormatData& rFormatData, sal_uInt32 nSearch, sal_uInt32 nFormat)
|
|
{
|
|
rFormatData.aDefaultFormatKeys[nSearch] = nFormat;
|
|
}
|
|
|
|
sal_uInt32 SvNFEngine::FindFormatRW(const SvNFFormatData& rFormatData, sal_uInt32 nSearch)
|
|
{
|
|
return rFormatData.FindCachedDefaultFormat(nSearch);
|
|
}
|
|
|
|
sal_uInt32 SvNFEngine::DefaultCurrencyRW(SvNFFormatData& rFormatData, SvNFLanguageData& rCurrentLanguage, const NativeNumberWrapper& rNatNum, sal_uInt32 CLOffset, LanguageType eLnge)
|
|
{
|
|
if (eLnge == LANGUAGE_SYSTEM)
|
|
return rFormatData.ImpGetDefaultSystemCurrencyFormat(rCurrentLanguage, rNatNum);
|
|
return rFormatData.ImpGetDefaultCurrencyFormat(rCurrentLanguage, rNatNum, CLOffset, eLnge);
|
|
}
|
|
|
|
sal_uInt32 SvNFEngine::DefaultCurrencyRO(const SvNFFormatData& rFormatData, SvNFLanguageData&, const NativeNumberWrapper&, sal_uInt32 CLOffset, LanguageType eLnge)
|
|
{
|
|
if (eLnge == LANGUAGE_SYSTEM)
|
|
{
|
|
assert(rFormatData.nDefaultSystemCurrencyFormat != NUMBERFORMAT_ENTRY_NOT_FOUND);
|
|
return rFormatData.nDefaultSystemCurrencyFormat;
|
|
}
|
|
|
|
SvNFFormatData::DefaultFormatKeysMap::const_iterator it = rFormatData.aDefaultFormatKeys.find(CLOffset + ZF_STANDARD_CURRENCY);
|
|
assert(it != rFormatData.aDefaultFormatKeys.end() && it->second != NUMBERFORMAT_ENTRY_NOT_FOUND);
|
|
return it->second;
|
|
}
|
|
|
|
SvNFEngine::Accessor SvNFEngine::GetRWPolicy(SvNFFormatData& rFormatData)
|
|
{
|
|
return
|
|
{
|
|
std::bind(SvNFEngine::GetCLOffsetRW, std::ref(rFormatData), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
|
|
std::bind(SvNFEngine::CacheFormatRW, std::ref(rFormatData), std::placeholders::_1, std::placeholders::_2),
|
|
std::bind(SvNFEngine::FindFormatRW, std::ref(rFormatData), std::placeholders::_1),
|
|
std::bind(SvNFEngine::DefaultCurrencyRW, std::ref(rFormatData), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)
|
|
};
|
|
}
|
|
|
|
void SvNumberFormatter::PrepForRoMode()
|
|
{
|
|
SvNumberFormatter::GetTheCurrencyTable(); // create this now so threads don't attempt to create it simultaneously
|
|
if (m_aFormatData.nDefaultSystemCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
|
|
{
|
|
m_aFormatData.ImpGetDefaultSystemCurrencyFormat(m_aCurrentLanguage, GetNatNum());
|
|
assert(m_aFormatData.nDefaultSystemCurrencyFormat != NUMBERFORMAT_ENTRY_NOT_FOUND);
|
|
}
|
|
}
|
|
|
|
SvNFEngine::Accessor SvNFEngine::GetROPolicy(const SvNFFormatData& rFormatData, SvNFFormatData::DefaultFormatKeysMap& rFormatCache)
|
|
{
|
|
assert(rFormatData.nDefaultSystemCurrencyFormat != NUMBERFORMAT_ENTRY_NOT_FOUND && "ensure PrepForRoMode is called");
|
|
assert(g_CurrencyTableInitialized && "ensure PrepForRoMode is called");
|
|
return
|
|
{
|
|
std::bind(SvNFEngine::GetCLOffsetRO, std::ref(rFormatData), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
|
|
std::bind(SvNFEngine::CacheFormatRO, std::ref(rFormatCache), std::placeholders::_1, std::placeholders::_2),
|
|
std::bind(SvNFEngine::FindFormatRO, std::ref(rFormatData), std::ref(rFormatCache), std::placeholders::_1),
|
|
std::bind(SvNFEngine::DefaultCurrencyRO, std::ref(rFormatData), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)
|
|
};
|
|
}
|
|
|
|
sal_uInt32 SvNFEngine::GetFormatForLanguageIfBuiltIn(SvNFLanguageData& rCurrentLanguage,
|
|
const NativeNumberWrapper& rNatNum,
|
|
const Accessor& rFuncs,
|
|
sal_uInt32 nFormat, LanguageType eLnge)
|
|
{
|
|
eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
|
|
|
|
if (IsFormatSimpleBuiltIn(rCurrentLanguage, nFormat, eLnge))
|
|
return nFormat;
|
|
|
|
sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge);
|
|
sal_uInt32 nOffset = nFormat % SV_COUNTRY_LANGUAGE_OFFSET; // relative index
|
|
return nCLOffset + nOffset;
|
|
}
|
|
|
|
sal_uInt32 SvNumberFormatter::GetFormatForLanguageIfBuiltIn( sal_uInt32 nFormat,
|
|
LanguageType eLnge )
|
|
{
|
|
::osl::MutexGuard aGuard(GetInstanceMutex());
|
|
return SvNFEngine::GetFormatForLanguageIfBuiltIn(m_aCurrentLanguage, GetNatNum(), m_aRWPolicy, nFormat, eLnge);
|
|
}
|
|
|
|
LanguageType SvNFLanguageData::ImpResolveLanguage(LanguageType eLnge) const
|
|
{
|
|
return eLnge == LANGUAGE_DONTKNOW ? IniLnge : eLnge;
|
|
}
|
|
|
|
sal_uInt32 SvNFEngine::GetFormatIndex(SvNFLanguageData& rCurrentLanguage, const Accessor& rFuncs,
|
|
const NativeNumberWrapper& rNatNum, NfIndexTableOffset nTabOff,
|
|
LanguageType eLnge)
|
|
{
|
|
eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
|
|
|
|
sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)
|
|
|
|
return ImpGetFormatIndex(nTabOff, nCLOffset);
|
|
}
|
|
|
|
sal_uInt32 SvNumberFormatter::GetFormatIndex(NfIndexTableOffset nTabOff, LanguageType eLnge)
|
|
{
|
|
::osl::MutexGuard aGuard(GetInstanceMutex());
|
|
return SvNFEngine::GetFormatIndex(m_aCurrentLanguage, m_aRWPolicy, GetNatNum(), nTabOff, eLnge);
|
|
}
|
|
|
|
// static
|
|
NfIndexTableOffset SvNumberFormatter::GetIndexTableOffset(sal_uInt32 nFormat)
|
|
{
|
|
sal_uInt32 nOffset = nFormat % SV_COUNTRY_LANGUAGE_OFFSET; // relative index
|
|
if ( nOffset > SV_MAX_COUNT_STANDARD_FORMATS )
|
|
{
|
|
return NF_INDEX_TABLE_ENTRIES; // not a built-in format
|
|
}
|
|
|
|
for ( sal_uInt16 j = 0; j < NF_INDEX_TABLE_ENTRIES; j++ )
|
|
{
|
|
if (indexTable[j] == nOffset)
|
|
return static_cast<NfIndexTableOffset>(j);
|
|
}
|
|
return NF_INDEX_TABLE_ENTRIES; // bad luck
|
|
}
|
|
|
|
void SvNumberFormatter::SetEvalDateFormat( NfEvalDateFormat eEDF )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
m_aCurrentLanguage.SetEvalDateFormat(eEDF);
|
|
}
|
|
|
|
NfEvalDateFormat SvNumberFormatter::GetEvalDateFormat() const
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return m_aCurrentLanguage.GetEvalDateFormat();
|
|
}
|
|
|
|
void SvNumberFormatter::SetYear2000( sal_uInt16 nVal )
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
m_aCurrentLanguage.pStringScanner->SetYear2000( nVal );
|
|
}
|
|
|
|
|
|
sal_uInt16 SvNumberFormatter::GetYear2000() const
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return m_aCurrentLanguage.pStringScanner->GetYear2000();
|
|
}
|
|
|
|
sal_uInt16 SvNFLanguageData::ExpandTwoDigitYear( sal_uInt16 nYear ) const
|
|
{
|
|
if ( nYear < 100 )
|
|
return SvNumberFormatter::ExpandTwoDigitYear( nYear,
|
|
pStringScanner->GetYear2000() );
|
|
return nYear;
|
|
}
|
|
|
|
sal_uInt16 SvNumberFormatter::ExpandTwoDigitYear( sal_uInt16 nYear ) const
|
|
{
|
|
return m_aCurrentLanguage.ExpandTwoDigitYear(nYear);
|
|
}
|
|
|
|
// static
|
|
sal_uInt16 SvNumberFormatter::GetYear2000Default()
|
|
{
|
|
if (!comphelper::IsFuzzing())
|
|
return officecfg::Office::Common::DateFormat::TwoDigitYear::get();
|
|
return 1930;
|
|
}
|
|
|
|
// static
|
|
void SvNumberFormatter::resetTheCurrencyTable()
|
|
{
|
|
SAL_INFO("svl", "Resetting the currency table.");
|
|
|
|
nSystemCurrencyPosition = 0;
|
|
g_CurrencyTableInitialized = false;
|
|
|
|
GetFormatterRegistry().ConfigurationChanged(nullptr, ConfigurationHints::Locale | ConfigurationHints::Currency | ConfigurationHints::DatePatterns);
|
|
}
|
|
|
|
// static
|
|
const NfCurrencyTable& SvNumberFormatter::GetTheCurrencyTable()
|
|
{
|
|
while (!g_CurrencyTableInitialized)
|
|
ImpInitCurrencyTable();
|
|
return theCurrencyTable();
|
|
}
|
|
|
|
// static
|
|
const NfCurrencyEntry* SvNumberFormatter::MatchSystemCurrency()
|
|
{
|
|
// MUST call GetTheCurrencyTable() before accessing nSystemCurrencyPosition
|
|
const NfCurrencyTable& rTable = GetTheCurrencyTable();
|
|
return nSystemCurrencyPosition ? &rTable[nSystemCurrencyPosition] : nullptr;
|
|
}
|
|
|
|
// 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.size();
|
|
for ( sal_uInt16 j = 0; j < nCount; j++ )
|
|
{
|
|
if ( rTable[j].GetLanguage() == eLang )
|
|
return rTable[j];
|
|
}
|
|
return rTable[0];
|
|
}
|
|
}
|
|
|
|
|
|
// static
|
|
const NfCurrencyEntry* SvNumberFormatter::GetCurrencyEntry(std::u16string_view rAbbrev, LanguageType eLang )
|
|
{
|
|
eLang = MsLangId::getRealLanguage( eLang );
|
|
const NfCurrencyTable& rTable = GetTheCurrencyTable();
|
|
sal_uInt16 nCount = rTable.size();
|
|
for ( sal_uInt16 j = 0; j < nCount; j++ )
|
|
{
|
|
if ( rTable[j].GetLanguage() == eLang &&
|
|
rTable[j].GetBankSymbol() == rAbbrev )
|
|
{
|
|
return &rTable[j];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
// static
|
|
const NfCurrencyEntry* SvNumberFormatter::GetLegacyOnlyCurrencyEntry( std::u16string_view rSymbol,
|
|
std::u16string_view rAbbrev )
|
|
{
|
|
GetTheCurrencyTable(); // just for initialization
|
|
const NfCurrencyTable& rTable = theLegacyOnlyCurrencyTable();
|
|
sal_uInt16 nCount = rTable.size();
|
|
for ( sal_uInt16 j = 0; j < nCount; j++ )
|
|
{
|
|
if ( rTable[j].GetSymbol() == rSymbol &&
|
|
rTable[j].GetBankSymbol() == rAbbrev )
|
|
{
|
|
return &rTable[j];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
// static
|
|
IMPL_STATIC_LINK_NOARG( SvNumberFormatter, CurrencyChangeLink, LinkParamNone*, void )
|
|
{
|
|
OUString aAbbrev;
|
|
LanguageType eLang = LANGUAGE_SYSTEM;
|
|
SvtSysLocaleOptions().GetCurrencyAbbrevAndLanguage( aAbbrev, eLang );
|
|
SetDefaultSystemCurrency( aAbbrev, eLang );
|
|
}
|
|
|
|
|
|
// static
|
|
void SvNumberFormatter::SetDefaultSystemCurrency( std::u16string_view rAbbrev, LanguageType eLang )
|
|
{
|
|
::osl::MutexGuard aGuard( GetGlobalMutex() );
|
|
if ( eLang == LANGUAGE_SYSTEM )
|
|
{
|
|
eLang = SvtSysLocale().GetLanguageTag().getLanguageType();
|
|
}
|
|
const NfCurrencyTable& rTable = GetTheCurrencyTable();
|
|
sal_uInt16 nCount = rTable.size();
|
|
if ( !rAbbrev.empty() )
|
|
{
|
|
for ( sal_uInt16 j = 0; j < nCount; j++ )
|
|
{
|
|
if ( rTable[j].GetLanguage() == eLang && rTable[j].GetBankSymbol() == rAbbrev )
|
|
{
|
|
nSystemCurrencyPosition = j;
|
|
return ;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( sal_uInt16 j = 0; j < nCount; j++ )
|
|
{
|
|
if ( rTable[j].GetLanguage() == eLang )
|
|
{
|
|
nSystemCurrencyPosition = j;
|
|
return ;
|
|
}
|
|
}
|
|
}
|
|
nSystemCurrencyPosition = 0; // not found => simple SYSTEM
|
|
}
|
|
|
|
void SvNFFormatData::ResetDefaultSystemCurrency()
|
|
{
|
|
nDefaultSystemCurrencyFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
|
|
}
|
|
|
|
void SvNumberFormatter::ResetDefaultSystemCurrency()
|
|
{
|
|
m_aFormatData.ResetDefaultSystemCurrency();
|
|
}
|
|
|
|
void SvNumberFormatter::InvalidateDateAcceptancePatterns()
|
|
{
|
|
m_aCurrentLanguage.pStringScanner->InvalidateDateAcceptancePatterns();
|
|
}
|
|
|
|
sal_uInt32 SvNFFormatData::ImpGetDefaultSystemCurrencyFormat(SvNFLanguageData& rCurrentLanguage,
|
|
const NativeNumberWrapper& rNatNum)
|
|
{
|
|
if ( nDefaultSystemCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
|
|
{
|
|
sal_Int32 nCheck;
|
|
SvNumFormatType nType;
|
|
NfWSStringsDtor aCurrList;
|
|
sal_uInt16 nDefault = rCurrentLanguage.GetCurrencyFormatStrings( aCurrList,
|
|
SvNumberFormatter::GetCurrencyEntry( LANGUAGE_SYSTEM ), false );
|
|
DBG_ASSERT( aCurrList.size(), "where is the NewCurrency System standard format?!?" );
|
|
// if already loaded or user defined nDefaultSystemCurrencyFormat
|
|
// will be set to the right value
|
|
PutEntry(rCurrentLanguage, rNatNum, aCurrList[ 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 SvNFFormatData::ImpGetDefaultCurrencyFormat(SvNFLanguageData& rCurrentLanguage, const NativeNumberWrapper& rNatNum,
|
|
sal_uInt32 CLOffset, LanguageType eLnge)
|
|
{
|
|
DefaultFormatKeysMap::const_iterator it = aDefaultFormatKeys.find( CLOffset + ZF_STANDARD_CURRENCY );
|
|
sal_uInt32 nDefaultCurrencyFormat = (it != aDefaultFormatKeys.end() ?
|
|
it->second : 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(0);
|
|
auto it2 = aFTable.lower_bound( CLOffset );
|
|
while ( it2 != aFTable.end() && (nKey = it2->first) >= CLOffset && nKey < nStopKey )
|
|
{
|
|
const SvNumberformat* pEntry = it2->second.get();
|
|
if ( pEntry->IsStandard() && (pEntry->GetType() & SvNumFormatType::CURRENCY) )
|
|
{
|
|
nDefaultCurrencyFormat = nKey;
|
|
break; // while
|
|
}
|
|
++it2;
|
|
}
|
|
|
|
if ( nDefaultCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
|
|
{ // none found, create one
|
|
sal_Int32 nCheck;
|
|
NfWSStringsDtor aCurrList;
|
|
sal_uInt16 nDefault = rCurrentLanguage.GetCurrencyFormatStrings( aCurrList,
|
|
SvNumberFormatter::GetCurrencyEntry(eLnge), false );
|
|
DBG_ASSERT( aCurrList.size(), "where is the NewCurrency standard format?" );
|
|
if ( !aCurrList.empty() )
|
|
{
|
|
// if already loaded or user defined nDefaultSystemCurrencyFormat
|
|
// will be set to the right value
|
|
SvNumFormatType nType;
|
|
PutEntry(rCurrentLanguage, rNatNum, aCurrList[ nDefault ], nCheck, nType,
|
|
nDefaultCurrencyFormat, eLnge );
|
|
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 = GetFormatEntry( nDefaultCurrencyFormat );
|
|
if ( pEntry )
|
|
pEntry->SetStandard();
|
|
}
|
|
}
|
|
aDefaultFormatKeys[ CLOffset + ZF_STANDARD_CURRENCY ] = nDefaultCurrencyFormat;
|
|
}
|
|
return nDefaultCurrencyFormat;
|
|
}
|
|
|
|
|
|
// static
|
|
// true: continue; false: break loop, if pFoundEntry==NULL dupe found
|
|
bool SvNumberFormatter::ImpLookupCurrencyEntryLoopBody(
|
|
const NfCurrencyEntry*& pFoundEntry, bool& bFoundBank, const NfCurrencyEntry* pData,
|
|
sal_uInt16 nPos, std::u16string_view rSymbol )
|
|
{
|
|
bool bFound;
|
|
if ( pData->GetSymbol() == rSymbol )
|
|
{
|
|
bFound = true;
|
|
bFoundBank = false;
|
|
}
|
|
else if ( pData->GetBankSymbol() == rSymbol )
|
|
{
|
|
bFound = true;
|
|
bFoundBank = true;
|
|
}
|
|
else
|
|
bFound = false;
|
|
if ( bFound )
|
|
{
|
|
if ( pFoundEntry && pFoundEntry != pData )
|
|
{
|
|
pFoundEntry = nullptr;
|
|
return false; // break loop, not unique
|
|
}
|
|
if ( nPos == 0 )
|
|
{ // first entry is SYSTEM
|
|
pFoundEntry = MatchSystemCurrency();
|
|
if ( pFoundEntry )
|
|
{
|
|
return false; // break loop
|
|
// even if there are more matching entries
|
|
// this one is probably the one we are looking for
|
|
}
|
|
else
|
|
{
|
|
pFoundEntry = pData;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pFoundEntry = pData;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void SvNFFormatData::MergeDefaultFormatKeys(const DefaultFormatKeysMap& rDefaultFormatKeys)
|
|
{
|
|
for (const auto& r : rDefaultFormatKeys)
|
|
{
|
|
#ifndef NDEBUG
|
|
auto iter = aDefaultFormatKeys.find(r.first);
|
|
assert(iter == aDefaultFormatKeys.end() || iter->second == r.second);
|
|
#endif
|
|
aDefaultFormatKeys[r.first] = r.second;
|
|
}
|
|
}
|
|
|
|
void SvNumberFormatter::MergeDefaultFormatKeys(const SvNFFormatData::DefaultFormatKeysMap& rDefaultFormatKeys)
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
m_aFormatData.MergeDefaultFormatKeys(rDefaultFormatKeys);
|
|
}
|
|
|
|
bool SvNFFormatData::GetNewCurrencySymbolString(sal_uInt32 nFormat, OUString& rStr,
|
|
const NfCurrencyEntry** ppEntry /* = NULL */,
|
|
bool* pBank /* = NULL */) const
|
|
{
|
|
if ( ppEntry )
|
|
*ppEntry = nullptr;
|
|
if ( pBank )
|
|
*pBank = false;
|
|
|
|
const SvNumberformat* pFormat = GetFormatEntry(nFormat);
|
|
if ( pFormat )
|
|
{
|
|
OUString aSymbol, aExtension;
|
|
if ( pFormat->GetNewCurrencySymbol( aSymbol, aExtension ) )
|
|
{
|
|
OUStringBuffer sBuff(128); // guess-estimate of a value that will pretty much guarantee no re-alloc
|
|
if ( ppEntry )
|
|
{
|
|
bool bFoundBank = false;
|
|
// we definitely need an entry matching the format code string
|
|
const NfCurrencyEntry* pFoundEntry = SvNumberFormatter::GetCurrencyEntry(
|
|
bFoundBank, aSymbol, aExtension, pFormat->GetLanguage(),
|
|
true );
|
|
if ( pFoundEntry )
|
|
{
|
|
*ppEntry = pFoundEntry;
|
|
if ( pBank )
|
|
*pBank = bFoundBank;
|
|
rStr = pFoundEntry->BuildSymbolString(bFoundBank);
|
|
}
|
|
}
|
|
if ( rStr.isEmpty() )
|
|
{ // analog to BuildSymbolString
|
|
sBuff.append("[$");
|
|
if ( aSymbol.indexOf( '-' ) != -1 ||
|
|
aSymbol.indexOf( ']' ) != -1 )
|
|
{
|
|
sBuff.append("\"" + aSymbol + "\"");
|
|
}
|
|
else
|
|
{
|
|
sBuff.append(aSymbol);
|
|
}
|
|
if ( !aExtension.isEmpty() )
|
|
{
|
|
sBuff.append(aExtension);
|
|
}
|
|
sBuff.append(']');
|
|
}
|
|
rStr = sBuff.makeStringAndClear();
|
|
return true;
|
|
}
|
|
}
|
|
rStr.clear();
|
|
return false;
|
|
}
|
|
|
|
bool SvNumberFormatter::GetNewCurrencySymbolString( sal_uInt32 nFormat, OUString& rStr,
|
|
const NfCurrencyEntry** ppEntry /* = NULL */,
|
|
bool* pBank /* = NULL */ ) const
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return m_aFormatData.GetNewCurrencySymbolString(nFormat, rStr, ppEntry, pBank);
|
|
}
|
|
|
|
// static
|
|
const NfCurrencyEntry* SvNumberFormatter::GetCurrencyEntry( bool & bFoundBank,
|
|
std::u16string_view rSymbol,
|
|
std::u16string_view rExtension,
|
|
LanguageType eFormatLanguage,
|
|
bool bOnlyStringLanguage )
|
|
{
|
|
sal_Int32 nExtLen = rExtension.size();
|
|
LanguageType eExtLang;
|
|
if ( nExtLen )
|
|
{
|
|
// rExtension should be a 16-bit hex value max FFFF which may contain a
|
|
// leading "-" separator (that is not a minus sign, but toInt32 can be
|
|
// used to parse it, with post-processing as necessary):
|
|
sal_Int32 nExtLang = o3tl::toInt32(rExtension, 16);
|
|
if ( !nExtLang )
|
|
{
|
|
eExtLang = LANGUAGE_DONTKNOW;
|
|
}
|
|
else
|
|
{
|
|
if (nExtLang < 0)
|
|
nExtLang = -nExtLang;
|
|
SAL_WARN_IF(nExtLang > 0xFFFF, "svl.numbers", "Out of range Lang Id: " << nExtLang << " from input string: " << OUString(rExtension));
|
|
eExtLang = LanguageType(nExtLang & 0xFFFF);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
eExtLang = LANGUAGE_DONTKNOW;
|
|
}
|
|
const NfCurrencyEntry* pFoundEntry = nullptr;
|
|
const NfCurrencyTable& rTable = GetTheCurrencyTable();
|
|
sal_uInt16 nCount = rTable.size();
|
|
bool bCont = true;
|
|
|
|
// first try with given extension language/country
|
|
if ( nExtLen )
|
|
{
|
|
for ( sal_uInt16 j = 0; j < nCount && bCont; j++ )
|
|
{
|
|
LanguageType eLang = rTable[j].GetLanguage();
|
|
if ( eLang == eExtLang ||
|
|
((eExtLang == LANGUAGE_DONTKNOW) &&
|
|
(eLang == LANGUAGE_SYSTEM)))
|
|
{
|
|
bCont = ImpLookupCurrencyEntryLoopBody( pFoundEntry, bFoundBank,
|
|
&rTable[j], j, rSymbol );
|
|
}
|
|
}
|
|
}
|
|
|
|
// ok?
|
|
if ( pFoundEntry || !bCont || (bOnlyStringLanguage && nExtLen) )
|
|
{
|
|
return pFoundEntry;
|
|
}
|
|
if ( !bOnlyStringLanguage )
|
|
{
|
|
// now try the language/country of the number format
|
|
for ( sal_uInt16 j = 0; j < nCount && bCont; j++ )
|
|
{
|
|
LanguageType eLang = rTable[j].GetLanguage();
|
|
if ( eLang == eFormatLanguage ||
|
|
((eFormatLanguage == LANGUAGE_DONTKNOW) &&
|
|
(eLang == LANGUAGE_SYSTEM)))
|
|
{
|
|
bCont = ImpLookupCurrencyEntryLoopBody( pFoundEntry, bFoundBank,
|
|
&rTable[j], j, rSymbol );
|
|
}
|
|
}
|
|
|
|
// ok?
|
|
if ( pFoundEntry || !bCont )
|
|
{
|
|
return pFoundEntry;
|
|
}
|
|
}
|
|
|
|
// then try without language/country if no extension specified
|
|
if ( !nExtLen )
|
|
{
|
|
for ( sal_uInt16 j = 0; j < nCount && bCont; j++ )
|
|
{
|
|
bCont = ImpLookupCurrencyEntryLoopBody( pFoundEntry, bFoundBank,
|
|
&rTable[j], j, rSymbol );
|
|
}
|
|
}
|
|
|
|
return pFoundEntry;
|
|
}
|
|
|
|
void SvNFLanguageData::GetCompatibilityCurrency( OUString& rSymbol, OUString& rAbbrev ) const
|
|
{
|
|
const css::uno::Sequence< css::i18n::Currency2 >
|
|
xCurrencies( xLocaleData->getAllCurrencies() );
|
|
|
|
auto pCurrency = std::find_if(xCurrencies.begin(), xCurrencies.end(),
|
|
[](const css::i18n::Currency2& rCurrency) { return rCurrency.UsedInCompatibleFormatCodes; });
|
|
if (pCurrency != xCurrencies.end())
|
|
{
|
|
rSymbol = pCurrency->Symbol;
|
|
rAbbrev = pCurrency->BankSymbol;
|
|
}
|
|
else
|
|
{
|
|
if (LocaleDataWrapper::areChecksEnabled())
|
|
{
|
|
LocaleDataWrapper::outputCheckMessage( xLocaleData->
|
|
appendLocaleInfo( u"GetCompatibilityCurrency: none?"));
|
|
}
|
|
rSymbol = xLocaleData->getCurrSymbol();
|
|
rAbbrev = xLocaleData->getCurrBankSymbol();
|
|
}
|
|
}
|
|
|
|
void SvNumberFormatter::GetCompatibilityCurrency( OUString& rSymbol, OUString& rAbbrev ) const
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
m_aCurrentLanguage.GetCompatibilityCurrency(rSymbol, rAbbrev);
|
|
}
|
|
|
|
static void lcl_CheckCurrencySymbolPosition( const NfCurrencyEntry& rCurr )
|
|
{
|
|
switch ( rCurr.GetPositiveFormat() )
|
|
{
|
|
case 0: // $1
|
|
case 1: // 1$
|
|
case 2: // $ 1
|
|
case 3: // 1 $
|
|
break;
|
|
default:
|
|
LocaleDataWrapper::outputCheckMessage( "lcl_CheckCurrencySymbolPosition: unknown PositiveFormat");
|
|
break;
|
|
}
|
|
switch ( rCurr.GetNegativeFormat() )
|
|
{
|
|
case 0: // ($1)
|
|
case 1: // -$1
|
|
case 2: // $-1
|
|
case 3: // $1-
|
|
case 4: // (1$)
|
|
case 5: // -1$
|
|
case 6: // 1-$
|
|
case 7: // 1$-
|
|
case 8: // -1 $
|
|
case 9: // -$ 1
|
|
case 10: // 1 $-
|
|
case 11: // $ -1
|
|
case 12 : // $ 1-
|
|
case 13 : // 1- $
|
|
case 14 : // ($ 1)
|
|
case 15 : // (1 $)
|
|
break;
|
|
default:
|
|
LocaleDataWrapper::outputCheckMessage( "lcl_CheckCurrencySymbolPosition: unknown NegativeFormat");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// static
|
|
bool SvNumberFormatter::IsLocaleInstalled( LanguageType eLang )
|
|
{
|
|
// The set is initialized as a side effect of the currency table
|
|
// created, make sure that exists, which usually is the case unless a
|
|
// SvNumberFormatter was never instantiated.
|
|
GetTheCurrencyTable();
|
|
return theInstalledLocales.find( eLang) != theInstalledLocales.end();
|
|
}
|
|
|
|
// static
|
|
void SvNumberFormatter::ImpInitCurrencyTable()
|
|
{
|
|
// Race condition possible:
|
|
// ::osl::MutexGuard aGuard( GetMutex() );
|
|
// while (!g_CurrencyTableInitialized)
|
|
// ImpInitCurrencyTable();
|
|
static bool bInitializing = false;
|
|
if (g_CurrencyTableInitialized || bInitializing)
|
|
{
|
|
return ;
|
|
}
|
|
bInitializing = true;
|
|
|
|
LanguageType eSysLang = SvtSysLocale().GetLanguageTag().getLanguageType();
|
|
std::optional<LocaleDataWrapper> pLocaleData(std::in_place,
|
|
::comphelper::getProcessComponentContext(),
|
|
SvtSysLocale().GetLanguageTag() );
|
|
// get user configured currency
|
|
OUString aConfiguredCurrencyAbbrev;
|
|
LanguageType eConfiguredCurrencyLanguage = LANGUAGE_SYSTEM;
|
|
SvtSysLocaleOptions().GetCurrencyAbbrevAndLanguage(
|
|
aConfiguredCurrencyAbbrev, eConfiguredCurrencyLanguage );
|
|
sal_uInt16 nSecondarySystemCurrencyPosition = 0;
|
|
sal_uInt16 nMatchingSystemCurrencyPosition = 0;
|
|
|
|
// First entry is SYSTEM:
|
|
auto& rCurrencyTable = theCurrencyTable();
|
|
rCurrencyTable.insert(
|
|
rCurrencyTable.begin(),
|
|
NfCurrencyEntry(*pLocaleData, LANGUAGE_SYSTEM));
|
|
sal_uInt16 nCurrencyPos = 1;
|
|
|
|
const css::uno::Sequence< css::lang::Locale > xLoc = LocaleDataWrapper::getInstalledLocaleNames();
|
|
sal_Int32 nLocaleCount = xLoc.getLength();
|
|
SAL_INFO( "svl.numbers", "number of locales: \"" << nLocaleCount << "\"" );
|
|
NfCurrencyTable &rLegacyOnlyCurrencyTable = theLegacyOnlyCurrencyTable();
|
|
sal_uInt16 nLegacyOnlyCurrencyPos = 0;
|
|
for ( css::lang::Locale const & rLocale : xLoc )
|
|
{
|
|
LanguageType eLang = LanguageTag::convertToLanguageType( rLocale, false);
|
|
theInstalledLocales.insert( eLang);
|
|
pLocaleData.emplace(
|
|
::comphelper::getProcessComponentContext(),
|
|
LanguageTag(rLocale) );
|
|
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;
|
|
}
|
|
std::optional<NfCurrencyEntry> pEntry;
|
|
if ( nDefault < nCurrencyCount )
|
|
{
|
|
pEntry.emplace(pCurrencies[nDefault], *pLocaleData, eLang);
|
|
}
|
|
else
|
|
{ // first or ShellsAndPebbles
|
|
pEntry.emplace(*pLocaleData, eLang);
|
|
}
|
|
if (LocaleDataWrapper::areChecksEnabled())
|
|
{
|
|
lcl_CheckCurrencySymbolPosition( *pEntry );
|
|
}
|
|
if ( !nSystemCurrencyPosition && !aConfiguredCurrencyAbbrev.isEmpty() &&
|
|
pEntry->GetBankSymbol() == aConfiguredCurrencyAbbrev &&
|
|
pEntry->GetLanguage() == eConfiguredCurrencyLanguage )
|
|
{
|
|
nSystemCurrencyPosition = nCurrencyPos;
|
|
}
|
|
if ( !nMatchingSystemCurrencyPosition &&
|
|
pEntry->GetLanguage() == eSysLang )
|
|
{
|
|
nMatchingSystemCurrencyPosition = nCurrencyPos;
|
|
}
|
|
rCurrencyTable.insert(
|
|
rCurrencyTable.begin() + nCurrencyPos++, std::move(*pEntry));
|
|
// all remaining currencies for each locale
|
|
if ( nCurrencyCount > 1 )
|
|
{
|
|
sal_Int32 nCurrency;
|
|
for ( nCurrency = 0; nCurrency < nCurrencyCount; nCurrency++ )
|
|
{
|
|
if (pCurrencies[nCurrency].LegacyOnly)
|
|
{
|
|
rLegacyOnlyCurrencyTable.insert(
|
|
rLegacyOnlyCurrencyTable.begin() + nLegacyOnlyCurrencyPos++,
|
|
NfCurrencyEntry(
|
|
pCurrencies[nCurrency], *pLocaleData, eLang));
|
|
}
|
|
else if ( nCurrency != nDefault )
|
|
{
|
|
pEntry.emplace(pCurrencies[nCurrency], *pLocaleData, eLang);
|
|
// no dupes
|
|
bool bInsert = true;
|
|
sal_uInt16 n = rCurrencyTable.size();
|
|
sal_uInt16 aCurrencyIndex = 1; // skip first SYSTEM entry
|
|
for ( sal_uInt16 j=1; j<n; j++ )
|
|
{
|
|
if ( rCurrencyTable[aCurrencyIndex++] == *pEntry )
|
|
{
|
|
bInsert = false;
|
|
break; // for
|
|
}
|
|
}
|
|
if ( !bInsert )
|
|
{
|
|
pEntry.reset();
|
|
}
|
|
else
|
|
{
|
|
if ( !nSecondarySystemCurrencyPosition &&
|
|
(!aConfiguredCurrencyAbbrev.isEmpty() ?
|
|
pEntry->GetBankSymbol() == aConfiguredCurrencyAbbrev :
|
|
pEntry->GetLanguage() == eConfiguredCurrencyLanguage) )
|
|
{
|
|
nSecondarySystemCurrencyPosition = nCurrencyPos;
|
|
}
|
|
if ( !nMatchingSystemCurrencyPosition &&
|
|
pEntry->GetLanguage() == eSysLang )
|
|
{
|
|
nMatchingSystemCurrencyPosition = nCurrencyPos;
|
|
}
|
|
rCurrencyTable.insert(
|
|
rCurrencyTable.begin() + nCurrencyPos++, std::move(*pEntry));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( !nSystemCurrencyPosition )
|
|
{
|
|
nSystemCurrencyPosition = nSecondarySystemCurrencyPosition;
|
|
}
|
|
if ((!aConfiguredCurrencyAbbrev.isEmpty() && !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.isEmpty() && !nSystemCurrencyPosition) &&
|
|
LocaleDataWrapper::areChecksEnabled())
|
|
{
|
|
LocaleDataWrapper::outputCheckMessage(
|
|
"SvNumberFormatter::ImpInitCurrencyTable: system currency not in I18N locale data.");
|
|
}
|
|
pLocaleData.reset();
|
|
SvtSysLocaleOptions::SetCurrencyChangeLink( LINK( nullptr, SvNumberFormatter, CurrencyChangeLink ) );
|
|
bInitializing = false;
|
|
g_CurrencyTableInitialized = true;
|
|
}
|
|
|
|
static std::ptrdiff_t addToCurrencyFormatsList( NfWSStringsDtor& rStrArr, const OUString& rFormat )
|
|
{
|
|
// Prevent duplicates even over subsequent calls of
|
|
// GetCurrencyFormatStrings() with the same vector.
|
|
NfWSStringsDtor::const_iterator it( std::find( rStrArr.begin(), rStrArr.end(), rFormat));
|
|
if (it != rStrArr.end())
|
|
return it - rStrArr.begin();
|
|
|
|
rStrArr.push_back( rFormat);
|
|
return rStrArr.size() - 1;
|
|
}
|
|
|
|
sal_uInt16 SvNFLanguageData::GetCurrencyFormatStrings(NfWSStringsDtor& rStrArr,
|
|
const NfCurrencyEntry& rCurr,
|
|
bool bBank) const
|
|
{
|
|
OUString aRed = "["
|
|
+ pFormatScanner->GetRedString()
|
|
+ "]";
|
|
|
|
sal_uInt16 nDefault = 0;
|
|
if ( bBank )
|
|
{
|
|
// Only bank symbols.
|
|
OUString aPositiveBank = rCurr.BuildPositiveFormatString(true, *xLocaleData);
|
|
OUString aNegativeBank = rCurr.BuildNegativeFormatString(true, *xLocaleData );
|
|
|
|
OUString format1 = aPositiveBank
|
|
+ ";"
|
|
+ aNegativeBank;
|
|
addToCurrencyFormatsList( rStrArr, format1);
|
|
|
|
OUString format2 = aPositiveBank
|
|
+ ";"
|
|
+ aRed
|
|
+ aNegativeBank;
|
|
nDefault = addToCurrencyFormatsList( rStrArr, format2);
|
|
}
|
|
else
|
|
{
|
|
// Mixed formats like in SvNumberFormatter::ImpGenerateFormats() but no
|
|
// duplicates if no decimals in currency.
|
|
OUString aPositive = rCurr.BuildPositiveFormatString(false, *xLocaleData );
|
|
OUString aNegative = rCurr.BuildNegativeFormatString(false, *xLocaleData );
|
|
OUString format1;
|
|
OUString format2;
|
|
OUString format3;
|
|
OUString format4;
|
|
OUString format5;
|
|
if (rCurr.GetDigits())
|
|
{
|
|
OUString aPositiveNoDec = rCurr.BuildPositiveFormatString(false, *xLocaleData, 0);
|
|
OUString aNegativeNoDec = rCurr.BuildNegativeFormatString(false, *xLocaleData, 0 );
|
|
OUString aPositiveDashed = rCurr.BuildPositiveFormatString(false, *xLocaleData, 2);
|
|
OUString aNegativeDashed = rCurr.BuildNegativeFormatString(false, *xLocaleData, 2);
|
|
|
|
format1 = aPositiveNoDec
|
|
+ ";"
|
|
+ aNegativeNoDec;
|
|
|
|
format3 = aPositiveNoDec
|
|
+ ";"
|
|
+ aRed
|
|
+ aNegativeNoDec;
|
|
|
|
format5 = aPositiveDashed
|
|
+ ";"
|
|
+ aRed
|
|
+ aNegativeDashed;
|
|
}
|
|
|
|
format2 = aPositive
|
|
+ ";"
|
|
+ aNegative;
|
|
|
|
format4 = aPositive
|
|
+ ";"
|
|
+ aRed
|
|
+ aNegative;
|
|
|
|
if (rCurr.GetDigits())
|
|
{
|
|
addToCurrencyFormatsList( rStrArr, format1);
|
|
}
|
|
addToCurrencyFormatsList( rStrArr, format2);
|
|
if (rCurr.GetDigits())
|
|
{
|
|
addToCurrencyFormatsList( rStrArr, format3);
|
|
}
|
|
nDefault = addToCurrencyFormatsList( rStrArr, format4);
|
|
if (rCurr.GetDigits())
|
|
{
|
|
addToCurrencyFormatsList( rStrArr, format5);
|
|
}
|
|
}
|
|
return nDefault;
|
|
}
|
|
|
|
sal_uInt16 SvNumberFormatter::GetCurrencyFormatStrings( NfWSStringsDtor& rStrArr,
|
|
const NfCurrencyEntry& rCurr,
|
|
bool bBank ) const
|
|
{
|
|
::osl::MutexGuard aGuard(GetInstanceMutex());
|
|
return m_aCurrentLanguage.GetCurrencyFormatStrings(rStrArr, rCurr, bBank);
|
|
}
|
|
|
|
sal_uInt32 SvNumberFormatter::GetMergeFormatIndex( sal_uInt32 nOldFmt ) const
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
if (pMergeTable)
|
|
{
|
|
SvNumberFormatterIndexTable::const_iterator it = pMergeTable->find(nOldFmt);
|
|
if (it != pMergeTable->end())
|
|
{
|
|
return it->second;
|
|
}
|
|
}
|
|
return nOldFmt;
|
|
}
|
|
|
|
bool SvNumberFormatter::HasMergeFormatTable() const
|
|
{
|
|
::osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
return pMergeTable && !pMergeTable->empty();
|
|
}
|
|
|
|
// static
|
|
sal_uInt16 SvNumberFormatter::ExpandTwoDigitYear( sal_uInt16 nYear, sal_uInt16 nTwoDigitYearStart )
|
|
{
|
|
if ( nYear < 100 )
|
|
{
|
|
if ( nYear < (nTwoDigitYearStart % 100) )
|
|
{
|
|
return nYear + (((nTwoDigitYearStart / 100) + 1) * 100);
|
|
}
|
|
else
|
|
{
|
|
return nYear + ((nTwoDigitYearStart / 100) * 100);
|
|
}
|
|
}
|
|
return nYear;
|
|
}
|
|
|
|
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 css::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();
|
|
}
|
|
|
|
bool NfCurrencyEntry::operator==( const NfCurrencyEntry& r ) const
|
|
{
|
|
return aSymbol == r.aSymbol
|
|
&& aBankSymbol == r.aBankSymbol
|
|
&& eLanguage == r.eLanguage
|
|
;
|
|
}
|
|
|
|
OUString NfCurrencyEntry::BuildSymbolString(bool bBank,
|
|
bool bWithoutExtension) const
|
|
{
|
|
OUStringBuffer aBuf("[$");
|
|
if (bBank)
|
|
{
|
|
aBuf.append(aBankSymbol);
|
|
}
|
|
else
|
|
{
|
|
if ( aSymbol.indexOf( '-' ) >= 0 ||
|
|
aSymbol.indexOf( ']' ) >= 0)
|
|
{
|
|
aBuf.append("\"" + aSymbol + "\"");
|
|
}
|
|
else
|
|
{
|
|
aBuf.append(aSymbol);
|
|
}
|
|
if ( !bWithoutExtension && eLanguage != LANGUAGE_DONTKNOW && eLanguage != LANGUAGE_SYSTEM )
|
|
{
|
|
sal_Int32 nLang = static_cast<sal_uInt16>(eLanguage);
|
|
aBuf.append("-" + OUString::number(nLang, 16).toAsciiUpperCase());
|
|
}
|
|
}
|
|
aBuf.append(']');
|
|
return aBuf.makeStringAndClear();
|
|
}
|
|
|
|
OUString NfCurrencyEntry::Impl_BuildFormatStringNumChars( const LocaleDataWrapper& rLoc,
|
|
sal_uInt16 nDecimalFormat) const
|
|
{
|
|
OUStringBuffer aBuf("#" + rLoc.getNumThousandSep() + "##0");
|
|
if (nDecimalFormat && nDigits)
|
|
{
|
|
aBuf.append(rLoc.getNumDecimalSep());
|
|
sal_Unicode cDecimalChar = nDecimalFormat == 2 ? '-' : cZeroChar;
|
|
for (sal_uInt16 i = 0; i < nDigits; ++i)
|
|
{
|
|
aBuf.append(cDecimalChar);
|
|
}
|
|
}
|
|
return aBuf.makeStringAndClear();
|
|
}
|
|
|
|
|
|
OUString NfCurrencyEntry::BuildPositiveFormatString(bool bBank, const LocaleDataWrapper& rLoc,
|
|
sal_uInt16 nDecimalFormat) const
|
|
{
|
|
OUStringBuffer sBuf(Impl_BuildFormatStringNumChars(rLoc, nDecimalFormat));
|
|
sal_uInt16 nPosiForm = NfCurrencyEntry::GetEffectivePositiveFormat( rLoc.getCurrPositiveFormat(),
|
|
nPositiveFormat, bBank );
|
|
CompletePositiveFormatString(sBuf, bBank, nPosiForm);
|
|
return sBuf.makeStringAndClear();
|
|
}
|
|
|
|
|
|
OUString NfCurrencyEntry::BuildNegativeFormatString(bool bBank,
|
|
const LocaleDataWrapper& rLoc, sal_uInt16 nDecimalFormat ) const
|
|
{
|
|
OUStringBuffer sBuf(Impl_BuildFormatStringNumChars(rLoc, nDecimalFormat));
|
|
sal_uInt16 nNegaForm = NfCurrencyEntry::GetEffectiveNegativeFormat( rLoc.getCurrNegativeFormat(),
|
|
nNegativeFormat, bBank );
|
|
CompleteNegativeFormatString(sBuf, bBank, nNegaForm);
|
|
return sBuf.makeStringAndClear();
|
|
}
|
|
|
|
|
|
void NfCurrencyEntry::CompletePositiveFormatString(OUStringBuffer& rStr, bool bBank,
|
|
sal_uInt16 nPosiForm) const
|
|
{
|
|
OUString aSymStr = BuildSymbolString(bBank);
|
|
NfCurrencyEntry::CompletePositiveFormatString( rStr, aSymStr, nPosiForm );
|
|
}
|
|
|
|
|
|
void NfCurrencyEntry::CompleteNegativeFormatString(OUStringBuffer& rStr, bool bBank,
|
|
sal_uInt16 nNegaForm) const
|
|
{
|
|
OUString aSymStr = BuildSymbolString(bBank);
|
|
NfCurrencyEntry::CompleteNegativeFormatString( rStr, aSymStr, nNegaForm );
|
|
}
|
|
|
|
|
|
// static
|
|
void NfCurrencyEntry::CompletePositiveFormatString(OUStringBuffer& rStr, std::u16string_view rSymStr,
|
|
sal_uInt16 nPositiveFormat)
|
|
{
|
|
switch( nPositiveFormat )
|
|
{
|
|
case 0: // $1
|
|
rStr.insert(0, rSymStr);
|
|
break;
|
|
case 1: // 1$
|
|
rStr.append(rSymStr);
|
|
break;
|
|
case 2: // $ 1
|
|
{
|
|
rStr.insert(0, OUString::Concat(rSymStr) + " ");
|
|
}
|
|
break;
|
|
case 3: // 1 $
|
|
{
|
|
rStr.append(' ');
|
|
rStr.append(rSymStr);
|
|
}
|
|
break;
|
|
default:
|
|
SAL_WARN( "svl.numbers", "NfCurrencyEntry::CompletePositiveFormatString: unknown option");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// static
|
|
void NfCurrencyEntry::CompleteNegativeFormatString(OUStringBuffer& rStr,
|
|
std::u16string_view rSymStr,
|
|
sal_uInt16 nNegaForm)
|
|
{
|
|
switch( nNegaForm )
|
|
{
|
|
case 0: // ($1)
|
|
{
|
|
rStr.insert(0, OUString::Concat("(") + rSymStr);
|
|
rStr.append(')');
|
|
}
|
|
break;
|
|
case 1: // -$1
|
|
{
|
|
rStr.insert(0, OUString::Concat("-") + rSymStr);
|
|
}
|
|
break;
|
|
case 2: // $-1
|
|
{
|
|
rStr.insert(0, OUString::Concat(rSymStr) + "-");
|
|
}
|
|
break;
|
|
case 3: // $1-
|
|
{
|
|
rStr.insert(0, rSymStr);
|
|
rStr.append('-');
|
|
}
|
|
break;
|
|
case 4: // (1$)
|
|
{
|
|
rStr.insert(0, '(');
|
|
rStr.append(rSymStr);
|
|
rStr.append(')');
|
|
}
|
|
break;
|
|
case 5: // -1$
|
|
{
|
|
rStr.append(rSymStr);
|
|
rStr.insert(0, '-');
|
|
}
|
|
break;
|
|
case 6: // 1-$
|
|
{
|
|
rStr.append('-');
|
|
rStr.append(rSymStr);
|
|
}
|
|
break;
|
|
case 7: // 1$-
|
|
{
|
|
rStr.append(rSymStr);
|
|
rStr.append('-');
|
|
}
|
|
break;
|
|
case 8: // -1 $
|
|
{
|
|
rStr.append(' ');
|
|
rStr.append(rSymStr);
|
|
rStr.insert(0, '-');
|
|
}
|
|
break;
|
|
case 9: // -$ 1
|
|
{
|
|
rStr.insert(0, OUString::Concat("-") + rSymStr + " ");
|
|
}
|
|
break;
|
|
case 10: // 1 $-
|
|
{
|
|
rStr.append(' ');
|
|
rStr.append(rSymStr);
|
|
rStr.append('-');
|
|
}
|
|
break;
|
|
case 11: // $ -1
|
|
{
|
|
rStr.insert(0, OUString::Concat(rSymStr) + " -");
|
|
}
|
|
break;
|
|
case 12 : // $ 1-
|
|
{
|
|
rStr.insert(0, OUString::Concat(rSymStr) + " ");
|
|
rStr.append('-');
|
|
}
|
|
break;
|
|
case 13 : // 1- $
|
|
{
|
|
rStr.append('-');
|
|
rStr.append(' ');
|
|
rStr.append(rSymStr);
|
|
}
|
|
break;
|
|
case 14 : // ($ 1)
|
|
{
|
|
rStr.insert(0, OUString::Concat("(") + rSymStr + " ");
|
|
rStr.append(')');
|
|
}
|
|
break;
|
|
case 15 : // (1 $)
|
|
{
|
|
rStr.insert(0, '(');
|
|
rStr.append(' ');
|
|
rStr.append(rSymStr);
|
|
rStr.append(')');
|
|
}
|
|
break;
|
|
default:
|
|
SAL_WARN( "svl.numbers", "NfCurrencyEntry::CompleteNegativeFormatString: unknown option");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// static
|
|
sal_uInt16 NfCurrencyEntry::GetEffectivePositiveFormat( sal_uInt16 nIntlFormat,
|
|
sal_uInt16 nCurrFormat, bool bBank )
|
|
{
|
|
if ( bBank )
|
|
{
|
|
#if NF_BANKSYMBOL_FIX_POSITION
|
|
(void) nIntlFormat; // avoid warnings
|
|
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:
|
|
SAL_WARN( "svl.numbers", "NfCurrencyEntry::GetEffectivePositiveFormat: unknown option");
|
|
break;
|
|
}
|
|
return nIntlFormat;
|
|
#endif
|
|
}
|
|
else
|
|
return nCurrFormat;
|
|
}
|
|
|
|
|
|
//! Call this only if nCurrFormat is really with parentheses!
|
|
static sal_uInt16 lcl_MergeNegativeParenthesisFormat( sal_uInt16 nIntlFormat, sal_uInt16 nCurrFormat )
|
|
{
|
|
short nSign = 0; // -1:=bracket 0:=left, 1:=middle, 2:=right
|
|
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:
|
|
SAL_WARN( "svl.numbers", "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, 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:
|
|
SAL_WARN( "svl.numbers", "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:
|
|
SAL_WARN( "svl.numbers", "NfCurrencyEntry::GetEffectiveNegativeFormat: unknown option");
|
|
break;
|
|
}
|
|
}
|
|
return nIntlFormat;
|
|
}
|
|
|
|
const NfKeywordTable & SvNumberFormatter::GetKeywords( sal_uInt32 nKey )
|
|
{
|
|
osl::MutexGuard aGuard( GetInstanceMutex() );
|
|
const SvNumberformat* pFormat = m_aFormatData.GetFormatEntry( nKey);
|
|
if (pFormat)
|
|
ChangeIntl( pFormat->GetLanguage());
|
|
else
|
|
ChangeIntl( IniLnge);
|
|
return m_aCurrentLanguage.pFormatScanner->GetKeywords();
|
|
}
|
|
|
|
// static
|
|
const NfKeywordTable & SvNumberFormatter::GetEnglishKeywords()
|
|
{
|
|
return ImpSvNumberformatScan::GetEnglishKeywords();
|
|
}
|
|
|
|
// static
|
|
const std::vector<Color> & SvNumberFormatter::GetStandardColors()
|
|
{
|
|
return ImpSvNumberformatScan::GetStandardColors();
|
|
}
|
|
|
|
// static
|
|
size_t SvNumberFormatter::GetMaxDefaultColors()
|
|
{
|
|
return ImpSvNumberformatScan::GetMaxDefaultColors();
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|