/* -*- 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::nStopPredefinedFormatInde
x,
"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(const 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(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(aLanguageTag);
xCalendar.init(xContext, aLanguageTag.getLocale());
xTransliteration.init(xContext, ActLnge);
pStringScanner.reset(new ImpSvNumberInputScan(*this ));
pFormatScanner.reset(new ImpSvNumberformatScan(*this , rOther.pFormatScanner->getColorCallback(),
rOther.pFormatScanner->GetNullDate()));
}
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 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:
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=94 H=90 G=91
¤ Dauer der Verarbeitung: 0.18 Sekunden
¤
*© Formatika GbR, Deutschland