/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "zforscan.hxx" #include "zforfind.hxx" #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::i18n; using namespace ::com::sun::star::lang; using namespace ::std; // 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(); } } SvNumberFormatterRegistry_Impl* SvNumberFormatter::pFormatterRegistry = nullptr; volatile bool SvNumberFormatter::bCurrencyTableInitialized = false; 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::max(); const sal_uInt16 SvNumberFormatter::INPUTSTRING_PRECISION = ::std::numeric_limits::max()-1; SvNumberFormatter::SvNumberFormatter( const Reference< XComponentContext >& rxContext, LanguageType eLang ) : m_xContext( rxContext ) , maLanguageTag( eLang) { ImpConstruct( eLang ); } SvNumberFormatter::~SvNumberFormatter() { { ::osl::MutexGuard aGuard( GetGlobalMutex() ); pFormatterRegistry->Remove( this ); if ( !pFormatterRegistry->Count() ) { delete pFormatterRegistry; pFormatterRegistry = nullptr; } } aFTable.clear(); ClearMergeTable(); } void SvNumberFormatter::ImpConstruct( LanguageType eLang ) { if ( eLang == LANGUAGE_DONTKNOW ) { eLang = UNKNOWN_SUBSTITUTE; } IniLnge = eLang; ActLnge = eLang; eEvalDateFormat = NF_EVALDATEFORMAT_INTL; nDefaultSystemCurrencyFormat = NUMBERFORMAT_ENTRY_NOT_FOUND; maLanguageTag.reset( eLang ); xCharClass.changeLocale( m_xContext, maLanguageTag ); xLocaleData.init( m_xContext, maLanguageTag ); xCalendar.init( m_xContext, maLanguageTag.getLocale() ); xTransliteration.init( m_xContext, eLang ); xNatNum.init( m_xContext ); // 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 ) ); pFormatTable = nullptr; MaxCLOffset = 0; ImpGenerateFormats( 0, false ); // 0 .. 999 for initialized language formats pMergeTable = nullptr; bNoZero = false; ::osl::MutexGuard aGuard( GetGlobalMutex() ); GetFormatterRegistry().Insert( this ); } void SvNumberFormatter::ChangeIntl(LanguageType eLnge) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); if (ActLnge == eLnge) return; ActLnge = eLnge; maLanguageTag.reset( eLnge ); xCharClass.changeLocale( m_xContext, maLanguageTag ); xLocaleData.changeLocale( maLanguageTag ); xCalendar.changeLocale( maLanguageTag.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(); } // 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& rColorTableCallBack ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); aColorLink = rColorTableCallBack; } Color* SvNumberFormatter::GetUserDefColor(sal_uInt16 nIndex) { ::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() ); pFormatScanner->ChangeNullDate(nDay, nMonth, nYear); pStringScanner->ChangeNullDate(nDay, nMonth, nYear); } const Date& SvNumberFormatter::GetNullDate() const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); return pFormatScanner->GetNullDate(); } void SvNumberFormatter::ChangeStandardPrec(short nPrec) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); pFormatScanner->ChangeStandardPrec(nPrec); } void SvNumberFormatter::SetNoZero(bool bNZ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); bNoZero = bNZ; } sal_uInt16 SvNumberFormatter::GetStandardPrec() const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); return pFormatScanner->GetStandardPrec(); } bool SvNumberFormatter::GetNoZero() const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); return bNoZero; } void SvNumberFormatter::ReplaceSystemCL( LanguageType eOldLanguage ) { sal_uInt32 nCLOffset = ImpGetCLOffset( LANGUAGE_SYSTEM ); if ( nCLOffset > MaxCLOffset ) { return ; // no SYSTEM entries to replace } const sal_uInt32 nMaxBuiltin = nCLOffset + SV_MAX_COUNT_STANDARD_FORMATS; const sal_uInt32 nNextCL = nCLOffset + SV_COUNTRY_LANGUAGE_OFFSET; sal_uInt32 nKey; // remove old builtin formats auto it = aFTable.find( nCLOffset ); while ( it != aFTable.end() && (nKey = it->first) >= nCLOffset && nKey <= nMaxBuiltin ) { it = aFTable.erase(it); } // move additional and user defined to temporary table SvNumberFormatTable aOldTable; while ( it != aFTable.end() && (nKey = it->first) >= nCLOffset && nKey < nNextCL ) { aOldTable[ nKey ] = it->second.release(); it = aFTable.erase(it); } // generate new old builtin formats // reset ActLnge otherwise ChangeIntl() wouldn't switch if already LANGUAGE_SYSTEM ActLnge = LANGUAGE_DONTKNOW; ChangeIntl( LANGUAGE_SYSTEM ); ImpGenerateFormats( nCLOffset, true ); // convert additional and user defined from old system to new system SvNumberformat* pStdFormat = GetFormatEntry( nCLOffset + ZF_STANDARD ); sal_uInt32 nLastKey = nMaxBuiltin; pFormatScanner->SetConvertMode( eOldLanguage, LANGUAGE_SYSTEM, true , true); while ( !aOldTable.empty() ) { nKey = aOldTable.begin()->first; if ( nLastKey < nKey ) { nLastKey = nKey; } std::unique_ptr 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 pNewEntry(new SvNumberformat( aString, pFormatScanner.get(), pStringScanner.get(), nCheckPos, eLge )); if ( nCheckPos == 0 ) { SvNumFormatType eCheckType = pNewEntry->GetType(); if ( eCheckType != SvNumFormatType::UNDEFINED ) { pNewEntry->SetType( eCheckType | SvNumFormatType::DEFINED ); } else { pNewEntry->SetType( SvNumFormatType::DEFINED ); } if ( aFTable.emplace( nKey, std::move(pNewEntry) ).second ) { bCheck = true; } } DBG_ASSERT( bCheck, "SvNumberFormatter::ReplaceSystemCL: couldn't convert" ); } 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, true ); } const css::uno::Reference& SvNumberFormatter::GetComponentContext() const { return m_xContext; } const ImpSvNumberformatScan* SvNumberFormatter::GetFormatScanner() const { return pFormatScanner.get(); } const LanguageTag& SvNumberFormatter::GetLanguageTag() const { return maLanguageTag; } const ::utl::TransliterationWrapper* SvNumberFormatter::GetTransliteration() const { return xTransliteration.get(); } const CharClass* SvNumberFormatter::GetCharClass() const { return xCharClass.get(); } const LocaleDataWrapper* SvNumberFormatter::GetLocaleData() const { return xLocaleData.get(); } CalendarWrapper* SvNumberFormatter::GetCalendar() const { return xCalendar.get(); } const NativeNumberWrapper* SvNumberFormatter::GetNatNum() const { return xNatNum.get(); } const OUString& SvNumberFormatter::GetNumDecimalSep() const { return aDecimalSep; } const OUString& SvNumberFormatter::GetNumDecimalSepAlt() const { return aDecimalSepAlt; } const OUString& SvNumberFormatter::GetNumThousandSep() const { return aThousandSep; } const OUString& SvNumberFormatter::GetDateSep() const { return aDateSep; } bool SvNumberFormatter::IsDecimalSep( std::u16string_view rStr ) const { if (rStr == GetNumDecimalSep()) return true; if (GetNumDecimalSepAlt().isEmpty()) return false; return rStr == GetNumDecimalSepAlt(); } bool SvNumberFormatter::IsTextFormat(sal_uInt32 F_Index) const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); const SvNumberformat* pFormat = GetFormatEntry(F_Index); return pFormat && pFormat->IsTextFormat(); } bool SvNumberFormatter::PutEntry(OUString& rString, sal_Int32& nCheckPos, SvNumFormatType& nType, sal_uInt32& nKey, // format key LanguageType eLnge, bool bReplaceBooleanEquivalent) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); nKey = 0; if (rString.isEmpty()) // empty string { nCheckPos = 1; // -> Error return false; } if (eLnge == LANGUAGE_DONTKNOW) { eLnge = IniLnge; } ChangeIntl(eLnge); // change locale if necessary LanguageType eLge = eLnge; // non-const for ConvertMode bool bCheck = false; std::unique_ptr p_Entry(new SvNumberformat(rString, pFormatScanner.get(), pStringScanner.get(), 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(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(nKey-CLOffset), SvNumberformat::FormatterPrivateAccess()); } } } return bCheck; } 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; } pFormatScanner->SetConvertMode(eLnge, eNewLnge, false, bConvertDateOrder); bRes = PutEntry(rString, nCheckPos, nType, nKey, eLnge, bReplaceBooleanEquivalent); 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 = 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 (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; } pFormatScanner->SetConvertMode(eLnge, eNewLnge, true, true); bRes = PutEntry(rString, nCheckPos, nType, nKey, eLnge); 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() ); aFTable.erase(nKey); } void SvNumberFormatter::GetUsedLanguages( std::vector& rList ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); rList.clear(); sal_uInt32 nOffset = 0; while (nOffset <= MaxCLOffset) { SvNumberformat* pFormat = 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 = 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( SvNumberformat* pEntry ) { // 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 ); aFormatStr += "\"" + aTemp + "\";\"" + aTemp + "\";\""; pEntry->GetOutputString( 0.0, aTemp, &pColor ); 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( const_cast< SvNumberformat* >(pEntry)); } else { bool bSystemLanguage = false; LanguageType nLang = pEntry->GetLanguage(); if (nLang == LANGUAGE_SYSTEM) { bSystemLanguage = true; nLang = SvtSysLocale().GetLanguageTag().getLanguageType(); } if (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( const_cast< SvNumberformat* >(pEntry)); } else { // 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.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 = 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 pFormatScanner->GetStandardName(); } sal_uInt32 SvNumberFormatter::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 SvNumberFormatter::ImpIsEntry(std::u16string_view rString, sal_uInt32 nCLOffset, LanguageType eLnge) { 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; } 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 = 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 SvNumberFormatter::ImpGenerateCL( LanguageType eLnge ) { ChangeIntl(eLnge); sal_uInt32 CLOffset = ImpGetCLOffset(ActLnge); if (CLOffset > MaxCLOffset) { // new CL combination if (LocaleDataWrapper::areChecksEnabled()) { const LanguageTag& rLoadedLocale = xLocaleData->getLoadedLanguageTag(); if ( !rLoadedLocale.equals( maLanguageTag ) ) { LocaleDataWrapper::outputCheckMessage( xLocaleData->appendLocaleInfo( u"SvNumberFormatter::ImpGenerateCL: locales don't match:" )); } // test XML locale data FormatElement entries { uno::Sequence< i18n::FormatElement > xSeq = xLocaleData->getAllFormats(); // A test for completeness of formatindex="0" ... // formatindex="47" is not needed here since it is done in // ImpGenerateFormats(). // Test for dupes of formatindex="..." for ( sal_Int32 j = 0; j < xSeq.getLength(); j++ ) { sal_Int16 nIdx = xSeq[j].formatIndex; OUStringBuffer aDupes; for ( sal_Int32 i = 0; i < xSeq.getLength(); i++ ) { if ( i != j && xSeq[i].formatIndex == nIdx ) { aDupes.append( i ); aDupes.append("("); aDupes.append(xSeq[i].formatKey); aDupes.append( ") "); } } if ( !aDupes.isEmpty() ) { OUString aMsg = "XML locale data FormatElement formatindex dupe: " + OUString::number(nIdx) + "\nFormatElements: " + OUString::number( j ) + "(" + xSeq[j].formatKey + ") " + aDupes.makeStringAndClear(); LocaleDataWrapper::outputCheckMessage( xLocaleData->appendLocaleInfo( aMsg )); } } } } MaxCLOffset += SV_COUNTRY_LANGUAGE_OFFSET; ImpGenerateFormats( MaxCLOffset, false/*bNoAdditionalFormats*/ ); CLOffset = MaxCLOffset; } return CLOffset; } SvNumberFormatTable& SvNumberFormatter::ChangeCL(SvNumFormatType eType, sal_uInt32& FIndex, LanguageType eLnge) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); ImpGenerateCL(eLnge); return GetEntryTable(eType, FIndex, 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 = ImpGetCLOffset(ActLnge); // Might generate and insert a default format for the given type // (e.g. currency) => has to be done before collecting formats. sal_uInt32 nDefaultIndex = GetStandardFormat( eType, ActLnge ); auto it = aFTable.find( CLOffset); if (eType == SvNumFormatType::ALL) { while (it != aFTable.end() && it->second->GetLanguage() == ActLnge) { // copy all entries to output table (*pFormatTable)[ it->first ] = it->second.get(); ++it; } } else { while (it != aFTable.end() && it->second->GetLanguage() == 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 = GetFormatEntry(FIndex); if ( !pEntry || !(pEntry->GetType() & eType) || pEntry->GetLanguage() != ActLnge ) { FIndex = nDefaultIndex; } } return *pFormatTable; } bool SvNumberFormatter::IsNumberFormat(const OUString& sString, sal_uInt32& F_Index, double& fOutNumber, SvNumInputOptions eInputOptions) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); 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( GetFormatEntry(F_Index))); if (!pFormat) { ChangeIntl(IniLnge); FType = SvNumFormatType::NUMBER; } else { FType = pFormat->GetMaskedType(); if (FType == SvNumFormatType::ALL) { FType = SvNumFormatType::DEFINED; } 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 = pStringScanner->IsNumberFormat(sString, RType, fOutNumber, pFormat, eInputOptions); } if (res && !IsCompatible(FType, RType)) // non-matching type { switch ( RType ) { case SvNumFormatType::DATE : // Preserve ISO 8601 input. if (pStringScanner->CanForceToIso8601( DateOrder::Invalid)) { F_Index = GetFormatIndex( NF_DATE_DIN_YYYYMMDD, ActLnge ); } else { F_Index = GetStandardFormat( RType, ActLnge ); } break; case SvNumFormatType::TIME : if ( pStringScanner->GetDecPos() ) { // 100th seconds if ( pStringScanner->GetNumericsCount() > 3 || fOutNumber < 0.0 ) { F_Index = GetFormatIndex( NF_TIME_HH_MMSS00, ActLnge ); } else { F_Index = GetFormatIndex( NF_TIME_MMSS00, ActLnge ); } } else if ( fOutNumber >= 1.0 || fOutNumber < 0.0 ) { F_Index = GetFormatIndex( NF_TIME_HH_MMSS, ActLnge ); } else { F_Index = GetStandardFormat( RType, ActLnge ); } break; case SvNumFormatType::DATETIME : // Preserve ISO 8601 input. if (pStringScanner->HasIso8601Tsep()) { if (pStringScanner->GetDecPos()) F_Index = GetFormatIndex( NF_DATETIME_ISO_YYYYMMDDTHHMMSS000, ActLnge ); else F_Index = GetFormatIndex( NF_DATETIME_ISO_YYYYMMDDTHHMMSS, ActLnge ); } else if (pStringScanner->CanForceToIso8601( DateOrder::Invalid)) { if (pStringScanner->GetDecPos()) F_Index = GetFormatIndex( NF_DATETIME_ISO_YYYYMMDD_HHMMSS000, ActLnge ); else F_Index = GetFormatIndex( NF_DATETIME_ISO_YYYYMMDD_HHMMSS, ActLnge ); } else { F_Index = GetStandardFormat( RType, ActLnge ); } break; default: F_Index = GetStandardFormat( RType, ActLnge ); } } return res; } LanguageType SvNumberFormatter::GetLanguage() const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); return IniLnge; } // 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; } } } sal_uInt32 SvNumberFormatter::ImpGetDefaultFormat( SvNumFormatType nType ) { sal_uInt32 CLOffset = ImpGetCLOffset( ActLnge ); 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; } DefaultFormatKeysMap::const_iterator it = aDefaultFormatKeys.find( nSearch); sal_uInt32 nDefaultFormat = (it != aDefaultFormatKeys.end() ? it->second : NUMBERFORMAT_ENTRY_NOT_FOUND); if ( nDefaultFormat == NUMBERFORMAT_ENTRY_NOT_FOUND ) { // look for a defined standard sal_uInt32 nStopKey = CLOffset + SV_COUNTRY_LANGUAGE_OFFSET; sal_uInt32 nKey(0); auto it2 = aFTable.find( CLOffset ); while ( it2 != 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; } } aDefaultFormatKeys[ nSearch ] = nDefaultFormat; } return nDefaultFormat; } sal_uInt32 SvNumberFormatter::GetStandardFormat( SvNumFormatType eType, LanguageType eLnge ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); if (eLnge == LANGUAGE_DONTKNOW) { eLnge = IniLnge; } sal_uInt32 CLOffset = ImpGenerateCL(eLnge); switch(eType) { case SvNumFormatType::CURRENCY: return ( eLnge == LANGUAGE_SYSTEM ) ? ImpGetDefaultSystemCurrencyFormat() : ImpGetDefaultCurrencyFormat(); case SvNumFormatType::DURATION : return GetFormatIndex( NF_TIME_HH_MMSS, eLnge); case SvNumFormatType::DATE: case SvNumFormatType::TIME: case SvNumFormatType::DATETIME: case SvNumFormatType::PERCENT: case SvNumFormatType::SCIENTIFIC: return ImpGetDefaultFormat( eType ); 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; } } bool SvNumberFormatter::IsSpecialStandardFormat( sal_uInt32 nFIndex, LanguageType eLnge ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); return nFIndex == GetFormatIndex( NF_TIME_MMSS00, eLnge ) || nFIndex == GetFormatIndex( NF_TIME_HH_MMSS00, eLnge ) || nFIndex == GetFormatIndex( NF_TIME_HH_MMSS, eLnge ) ; } sal_uInt32 SvNumberFormatter::GetStandardFormat( sal_uInt32 nFIndex, SvNumFormatType eType, LanguageType eLnge ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); if ( IsSpecialStandardFormat( nFIndex, eLnge ) ) return nFIndex; else return GetStandardFormat( eType, eLnge ); } sal_uInt32 SvNumberFormatter::GetTimeFormat( double fNumber, LanguageType eLnge, bool bForceDuration ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); 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( NF_TIME_HH_MMSS00, eLnge ); else return GetFormatIndex( NF_TIME_MMSS00, eLnge ); } else { if ( bForceDuration || bSign || fNumber >= 1.0 ) return GetFormatIndex( NF_TIME_HH_MMSS, eLnge ); else return GetStandardFormat( SvNumFormatType::TIME, eLnge ); } } sal_uInt32 SvNumberFormatter::GetStandardFormat( double fNumber, sal_uInt32 nFIndex, SvNumFormatType eType, LanguageType eLnge ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); if ( IsSpecialStandardFormat( 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 SvNumberFormatter::GetEditFormat( double fNumber, sal_uInt32 nFIndex, SvNumFormatType eType, SvNumberformat const * pFormat, LanguageType eForLocale ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); 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( NF_DATE_DIN_YYYYMMDD, eLang) || nFIndex == GetFormatIndex( NF_DATE_DIN_YYMMDD, eLang) || nFIndex == GetFormatIndex( 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( NF_DATETIME_ISO_YYYYMMDD_HHMMSS, eForLocale); else nKey = GetFormatIndex( NF_DATETIME_SYS_DDMMYYYY_HHMMSS, eForLocale ); } else { if (bIsoDate) nKey = GetFormatIndex( NF_DATE_ISO_YYYYMMDD, eForLocale); else nKey = GetFormatIndex( 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( fNumber, eForLocale, true); // Preserve duration, use [HH]:MM:SS instead of time. else nKey = GetFormatIndex( NF_DATETIME_SYS_DDMMYYYY_HHMMSS, eForLocale ); // Assume that a large value is a datetime with only time // displayed. } else nKey = GetStandardFormat( fNumber, nFIndex, eType, eForLocale ); break; case SvNumFormatType::DURATION : nKey = GetTimeFormat( fNumber, eForLocale, true); break; case SvNumFormatType::DATETIME : if (nFIndex == GetFormatIndex( NF_DATETIME_ISO_YYYYMMDDTHHMMSS, eLang)) nKey = GetFormatIndex( NF_DATETIME_ISO_YYYYMMDDTHHMMSS, eForLocale ); else if (nFIndex == GetFormatIndex( NF_DATETIME_ISO_YYYYMMDDTHHMMSS000, eLang)) nKey = GetFormatIndex( NF_DATETIME_ISO_YYYYMMDDTHHMMSS000, eForLocale ); else if (nFIndex == GetFormatIndex( NF_DATETIME_ISO_YYYYMMDD_HHMMSS000, eLang)) nKey = GetFormatIndex( NF_DATETIME_ISO_YYYYMMDD_HHMMSS000, eForLocale ); else if (nFIndex == GetFormatIndex( NF_DATETIME_ISO_YYYYMMDD_HHMMSS, eLang) || (pFormat && pFormat->IsIso8601( 0 ))) nKey = GetFormatIndex( NF_DATETIME_ISO_YYYYMMDD_HHMMSS, eForLocale ); else nKey = GetFormatIndex( NF_DATETIME_SYS_DDMMYYYY_HHMMSS, eForLocale ); break; case SvNumFormatType::NUMBER: nKey = GetStandardFormat( eType, eForLocale ); break; default: nKey = GetStandardFormat( fNumber, nFIndex, eType, eForLocale ); } return nKey; } void SvNumberFormatter::GetInputLineString(const double& fOutNumber, sal_uInt32 nFIndex, OUString& sOutString, bool bFiltering, bool bForceSystemLocale) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); const Color* pColor; sal_uInt32 nRealKey = nFIndex; SvNumberformat* pFormat = ImpSubstituteEntry( GetFormatEntry( nFIndex ), &nRealKey); if (!pFormat) { pFormat = GetFormatEntry(ZF_STANDARD); } LanguageType eLang = pFormat->GetLanguage(); 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 = 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; } ChangeStandardPrec(INPUTSTRING_PRECISION); bPrecChanged = true; } sal_uInt32 nKey = GetEditFormat( fOutNumber, nRealKey, eType, pFormat, bForceSystemLocale ? LANGUAGE_SYSTEM : LANGUAGE_DONTKNOW); // if bFiltering true keep the nRealKey format if ( nKey != nRealKey && !bFiltering ) { pFormat = GetFormatEntry( nKey ); } assert(pFormat); if (pFormat) { if ( eType == SvNumFormatType::TIME && pFormat->GetFormatPrecision() ) { ChangeStandardPrec(INPUTSTRING_PRECISION); bPrecChanged = true; } pFormat->GetOutputString(fOutNumber, sOutString, &pColor); // 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 = GetFormatEntry(ZF_STANDARD); assert(pFormat); if (pFormat) { ChangeStandardPrec(INPUTSTRING_PRECISION); bPrecChanged = true; pFormat->GetOutputString(fOutNumber, sOutString, &pColor); } } assert(sOutString != ImpSvNumberformatScan::sErrStr); } if (bPrecChanged) { ChangeStandardPrec(nOldPrec); } } void SvNumberFormatter::GetOutputString(const OUString& sString, sal_uInt32 nFIndex, OUString& sOutString, const Color** ppColor, bool bUseStarFormat ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); SvNumberformat* pFormat = GetFormatEntry( nFIndex ); // ImpSubstituteEntry() is unnecessary here because so far only numeric // (time and date) are substituted. if (!pFormat) { pFormat = GetFormatEntry(ZF_STANDARD_TEXT); } if (!pFormat->IsTextFormat() && !pFormat->HasTextFormat()) { *ppColor = nullptr; sOutString = sString; } else { ChangeIntl(pFormat->GetLanguage()); if ( bUseStarFormat ) { pFormat->SetStarFormatSupport( true ); } pFormat->GetOutputString(sString, sOutString, ppColor); if ( bUseStarFormat ) { pFormat->SetStarFormatSupport( false ); } } } void SvNumberFormatter::GetOutputString(const double& fOutNumber, sal_uInt32 nFIndex, OUString& sOutString, const Color** ppColor, bool bUseStarFormat ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); if (bNoZero && fOutNumber == 0.0) { sOutString.clear(); return; } SvNumberformat* pFormat = ImpSubstituteEntry( GetFormatEntry( nFIndex )); if (!pFormat) pFormat = GetFormatEntry(ZF_STANDARD); ChangeIntl(pFormat->GetLanguage()); if ( bUseStarFormat ) pFormat->SetStarFormatSupport( true ); pFormat->GetOutputString(fOutNumber, sOutString, ppColor); if ( bUseStarFormat ) pFormat->SetStarFormatSupport( false ); } bool SvNumberFormatter::GetPreviewString(const OUString& sFormatString, double fPreviewNumber, OUString& sOutString, const Color** ppColor, LanguageType eLnge, bool bUseStarFormat ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); if (sFormatString.isEmpty()) // no empty string { return false; } if (eLnge == LANGUAGE_DONTKNOW) { eLnge = IniLnge; } ChangeIntl(eLnge); // change locale if necessary eLnge = ActLnge; sal_Int32 nCheckPos = -1; OUString sTmpString = sFormatString; SvNumberformat aEntry(sTmpString, pFormatScanner.get(), pStringScanner.get(), nCheckPos, eLnge); if (nCheckPos == 0) // String ok { sal_uInt32 CLOffset = ImpGenerateCL(eLnge); // create new standard formats if necessary sal_uInt32 nKey = ImpIsEntry(aEntry.GetFormatstring(),CLOffset, eLnge); if (nKey != NUMBERFORMAT_ENTRY_NOT_FOUND) // already present { GetOutputString(fPreviewNumber, nKey, sOutString, ppColor, bUseStarFormat); } else { if ( bUseStarFormat ) { aEntry.SetStarFormatSupport( true ); } aEntry.GetOutputString(fPreviewNumber, sOutString, ppColor); if ( bUseStarFormat ) { aEntry.SetStarFormatSupport( false ); } } return true; } else { return false; } } bool SvNumberFormatter::GetPreviewStringGuess( const OUString& sFormatString, double fPreviewNumber, OUString& sOutString, const Color** ppColor, LanguageType eLnge ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); if (sFormatString.isEmpty()) // no empty string { return false; } if (eLnge == LANGUAGE_DONTKNOW) { eLnge = IniLnge; } ChangeIntl( eLnge ); eLnge = ActLnge; bool bEnglish = (eLnge == LANGUAGE_ENGLISH_US); OUString aFormatStringUpper( xCharClass->uppercase( sFormatString ) ); sal_uInt32 nCLOffset = ImpGenerateCL( eLnge ); sal_uInt32 nKey = ImpIsEntry( aFormatStringUpper, nCLOffset, eLnge ); if ( nKey != NUMBERFORMAT_ENTRY_NOT_FOUND ) { // Target format present GetOutputString( fPreviewNumber, nKey, sOutString, ppColor ); return true; } std::optional pEntry; sal_Int32 nCheckPos = -1; OUString sTmpString; if ( bEnglish ) { sTmpString = sFormatString; pEntry.emplace( sTmpString, pFormatScanner.get(), pStringScanner.get(), nCheckPos, eLnge ); } else { nCLOffset = ImpGenerateCL( LANGUAGE_ENGLISH_US ); nKey = 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; pFormatScanner->SetConvertMode( LANGUAGE_ENGLISH_US, eLnge, false, false); sTmpString = sFormatString; pEntry.emplace( sTmpString, pFormatScanner.get(), pStringScanner.get(), nCheckPos, eFormatLang ); pFormatScanner->SetConvertMode( false ); ChangeIntl( eLnge ); if ( !bEnglishFormat ) { if ( nCheckPos != 0 || xTransliteration->isEqual( sFormatString, pEntry->GetFormatstring() ) ) { // other Format // Force locale's keywords. pFormatScanner->ChangeIntl( ImpSvNumberformatScan::KeywordLocalization::LocaleLegacy ); sTmpString = sFormatString; pEntry.emplace( sTmpString, pFormatScanner.get(), pStringScanner.get(), nCheckPos, eLnge ); } else { // verify english sal_Int32 nCheckPos2 = -1; // try other --> english eFormatLang = eLnge; pFormatScanner->SetConvertMode( eLnge, LANGUAGE_ENGLISH_US, false, false); sTmpString = sFormatString; SvNumberformat aEntry2( sTmpString, pFormatScanner.get(), pStringScanner.get(), nCheckPos2, eFormatLang ); pFormatScanner->SetConvertMode( false ); ChangeIntl( eLnge ); if ( nCheckPos2 == 0 && !xTransliteration->isEqual( sFormatString, aEntry2.GetFormatstring() ) ) { // other Format // Force locale's keywords. pFormatScanner->ChangeIntl( ImpSvNumberformatScan::KeywordLocalization::LocaleLegacy ); sTmpString = sFormatString; pEntry.emplace( sTmpString, pFormatScanner.get(), pStringScanner.get(), nCheckPos, eLnge ); } } } } if (nCheckPos == 0) // String ok { ImpGenerateCL( eLnge ); // create new standard formats if necessary pEntry->GetOutputString( fPreviewNumber, sOutString, ppColor ); return true; } return false; } bool SvNumberFormatter::GetPreviewString( const OUString& sFormatString, const OUString& sPreviewString, OUString& sOutString, const Color** ppColor, LanguageType eLnge ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); if (sFormatString.isEmpty()) // no empty string { return false; } if (eLnge == LANGUAGE_DONTKNOW) { eLnge = IniLnge; } ChangeIntl(eLnge); // switch if needed eLnge = ActLnge; sal_Int32 nCheckPos = -1; OUString sTmpString = sFormatString; SvNumberformat aEntry( sTmpString, pFormatScanner.get(), pStringScanner.get(), nCheckPos, eLnge); if (nCheckPos == 0) // String ok { // May have to create standard formats for this locale. sal_uInt32 CLOffset = ImpGenerateCL(eLnge); sal_uInt32 nKey = ImpIsEntry( aEntry.GetFormatstring(), CLOffset, eLnge); if (nKey != NUMBERFORMAT_ENTRY_NOT_FOUND) // already present { GetOutputString( sPreviewString, nKey, sOutString, ppColor); } else { // If the format is valid but not a text format and does not // include a text subformat, an empty string would result. Same as // in SvNumberFormatter::GetOutputString() if (aEntry.IsTextFormat() || aEntry.HasTextFormat()) { aEntry.GetOutputString( sPreviewString, sOutString, ppColor); } else { *ppColor = nullptr; sOutString = sPreviewString; } } return true; } else { return false; } } 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 = ActLnge; sal_uInt32 nRes; sal_Int32 nCheckPos = -1; OUString sTmpString = sFormatString; SvNumberformat aEntry(sTmpString, pFormatScanner.get(), pStringScanner.get(), nCheckPos, eLnge); if (nCheckPos == 0) // String ok { sal_uInt32 CLOffset = ImpGenerateCL(eLnge); // create new standard formats if necessary nRes = ImpIsEntry(aEntry.GetFormatstring(),CLOffset, eLnge); // already present? } else { nRes = NUMBERFORMAT_ENTRY_NOT_FOUND; } return nRes; } SvNumberformat* SvNumberFormatter::ImpInsertFormat( 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 '" << maLanguageTag.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( xLocaleData->appendLocaleInfo( aMsg)); } } } sal_Int32 nCheckPos = 0; std::unique_ptr pFormat(new SvNumberformat(aCodeStr, pFormatScanner.get(), pStringScanner.get(), nCheckPos, ActLnge)); if (nCheckPos != 0) { if (LocaleDataWrapper::areChecksEnabled()) { OUString aMsg = "SvNumberFormatter::ImpInsertFormat: bad format code, index " + OUString::number( rCode.Index ) + "\n" + rCode.Code; LocaleDataWrapper::outputCheckMessage( 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, 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( 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( 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( 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 = GetFormatEntry( nFormat ); if (pFormat) pFormat->GetFormatSpecialInfo(bThousand, IsRed, nPrecision, nLeadingCnt); else { bThousand = false; IsRed = false; nPrecision = pFormatScanner->GetStandardPrec(); nLeadingCnt = 0; } } sal_uInt16 SvNumberFormatter::GetFormatPrecision( sal_uInt32 nFormat ) const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); const SvNumberformat* pFormat = GetFormatEntry( nFormat ); if ( pFormat ) return pFormat->GetFormatPrecision(); else return pFormatScanner->GetStandardPrec(); } sal_uInt16 SvNumberFormatter::GetFormatIntegerDigits( sal_uInt32 nFormat ) const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); const SvNumberformat* pFormat = GetFormatEntry( nFormat ); if ( pFormat ) return pFormat->GetFormatIntegerDigits(); else return 1; } OUString SvNumberFormatter::GetFormatDecimalSep( sal_uInt32 nFormat ) const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); const SvNumberformat* pFormat = GetFormatEntry(nFormat); if (!pFormat) { return GetNumDecimalSep(); } return GetLangDecimalSep( pFormat->GetLanguage()); } OUString SvNumberFormatter::GetLangDecimalSep( LanguageType nLang ) const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); if (nLang == ActLnge) { return GetNumDecimalSep(); } OUString aRet; LanguageType eSaveLang = xLocaleData.getCurrentLanguage(); if (nLang == eSaveLang) { aRet = xLocaleData->getNumDecimalSep(); } else { LanguageTag aSaveLocale( xLocaleData->getLanguageTag() ); const_cast(this)->xLocaleData.changeLocale( LanguageTag( nLang)); aRet = xLocaleData->getNumDecimalSep(); const_cast(this)->xLocaleData.changeLocale( aSaveLocale ); } return aRet; } 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 = ActLnge; OUString aTmpStr( rFormatString ); sal_Int32 nCheckPos = 0; SvNumberformat aFormat( aTmpStr, pFormatScanner.get(), pStringScanner.get(), nCheckPos, eLnge ); if ( nCheckPos == 0 ) { aFormat.GetFormatSpecialInfo( bThousand, IsRed, nPrecision, nLeadingCnt ); } else { bThousand = false; IsRed = false; nPrecision = pFormatScanner->GetStandardPrec(); nLeadingCnt = 0; } return nCheckPos; } OUString SvNumberFormatter::GetCalcCellReturn( sal_uInt32 nFormat ) const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); const SvNumberformat* pFormat = GetFormatEntry( nFormat ); if (!pFormat) return "G"; 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 (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; } sal_Int32 SvNumberFormatter::ImpGetFormatCodeIndex( 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(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( 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(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(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(std::distance(std::cbegin(rSeq), pSeq)); } } else { // we need at least _some_ format rSeq = { css::i18n::NumberFormatCode() }; rSeq.getArray()[0].Code = "0" + GetNumDecimalSep() + "############"; } return 0; } void SvNumberFormatter::ImpAdjustFormatCodeDefault( css::i18n::NumberFormatCode * pFormatArr, sal_Int32 nCnt ) { if ( !nCnt ) return; if (LocaleDataWrapper::areChecksEnabled()) { // check the locale data for correctness OStringBuffer 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: "); aMsg.append(static_cast(pFormatArr[nElem].Index)); OUString aUMsg(OStringToOUString(aMsg.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US)); LocaleDataWrapper::outputCheckMessage(xLocaleData->appendLocaleInfo(aUMsg)); } } 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: "); OUString aUMsg(OStringToOUString(aMsg.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US)); LocaleDataWrapper::outputCheckMessage( xLocaleData->appendLocaleInfo(OUStringConcatenation(aUMsg + pFormatArr[0].NameID))); } } // 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* SvNumberFormatter::GetFormatEntry( sal_uInt32 nKey ) { auto it = aFTable.find( nKey); if (it != aFTable.end()) return it->second.get(); return nullptr; } const SvNumberformat* SvNumberFormatter::GetFormatEntry( sal_uInt32 nKey ) const { return GetEntry( nKey); } const SvNumberformat* SvNumberFormatter::GetEntry( sal_uInt32 nKey ) const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); auto it = aFTable.find( nKey); if (it != aFTable.end()) return it->second.get(); return nullptr; } const SvNumberformat* SvNumberFormatter::GetSubstitutedEntry( sal_uInt32 nKey, sal_uInt32 & o_rNewKey ) const { ::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). SvNumberFormatter* pThis = const_cast(this); return pThis->ImpSubstituteEntry( pThis->GetFormatEntry( nKey), &o_rNewKey); } SvNumberformat* SvNumberFormatter::ImpSubstituteEntry( 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 = GetStandardFormat( SvNumFormatType::TIME, LANGUAGE_SYSTEM); else if (pFormat->IsSystemLongDateFormat()) /* TODO: either that above, or have a long option for GetStandardFormat() */ nKey = GetFormatIndex( NF_DATE_SYSTEM_LONG, LANGUAGE_SYSTEM); else return pFormat; if (o_pRealKey) *o_pRealKey = nKey; auto it = aFTable.find( nKey); return it == aFTable.end() ? nullptr : it->second.get(); } void SvNumberFormatter::ImpGenerateFormats( sal_uInt32 CLOffset, bool bNoAdditionalFormats ) { bool bOldConvertMode = pFormatScanner->GetConvertMode(); if (bOldConvertMode) { pFormatScanner->SetConvertMode(false); // switch off for this function } css::lang::Locale aLocale = GetLanguageTag().getLocale(); css::uno::Reference< css::i18n::XNumberFormatCode > xNFC = i18n::NumberFormatMapper::create( m_xContext ); sal_Int32 nIdx; // Number uno::Sequence< i18n::NumberFormatCode > aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::FIXED_NUMBER, aLocale ); ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() ); // General nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_STANDARD ); SvNumberformat* pStdFormat = ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD /* NF_NUMBER_STANDARD */ ); if (pStdFormat) { // This is _the_ standard format. if (LocaleDataWrapper::areChecksEnabled() && pStdFormat->GetType() != SvNumFormatType::NUMBER) { LocaleDataWrapper::outputCheckMessage( 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( xLocaleData-> appendLocaleInfo( u"SvNumberFormatter::ImpGenerateFormats: General format not insertable, nothing will work")); } } { // Boolean OUString aFormatCode = pFormatScanner->GetBooleanString(); sal_Int32 nCheckPos = 0; std::unique_ptr pNewFormat(new SvNumberformat( aFormatCode, pFormatScanner.get(), pStringScanner.get(), nCheckPos, 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, pFormatScanner.get(), pStringScanner.get(), nCheckPos, 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( aFormatSeq, NF_NUMBER_INT ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD+1 /* NF_NUMBER_INT */ ); // 0.00 nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_DEC2 ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD+2 /* NF_NUMBER_DEC2 */ ); // #,##0 nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_1000INT ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD+3 /* NF_NUMBER_1000INT */ ); // #,##0.00 nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_1000DEC2 ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD+4 /* NF_NUMBER_1000DEC2 */ ); // #.##0,00 System country/language dependent nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_NUMBER_SYSTEM ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD+5 /* NF_NUMBER_SYSTEM */ ); // Percent number aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::PERCENT_NUMBER, aLocale ); ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() ); // 0% nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_PERCENT_INT ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_PERCENT /* NF_PERCENT_INT */ ); // 0.00% nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_PERCENT_DEC2 ); ImpInsertFormat( 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( aFormatSeq.getArray(), aFormatSeq.getLength() ); } // #,##0 nIdx = ImpGetFormatCodeIndex( 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( aFormat, CLOffset + ZF_STANDARD_CURRENCY /* NF_CURRENCY_1000INT */ ); // #,##0.00 nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000DEC2 ); aFormat = aFormatSeq[nIdx]; aFormat.Default = false; ImpInsertFormat( aFormat, CLOffset + ZF_STANDARD_CURRENCY+1 /* NF_CURRENCY_1000DEC2 */ ); // #,##0 negative red nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000INT_RED ); aFormat = aFormatSeq[nIdx]; aFormat.Default = false; ImpInsertFormat(aFormat, CLOffset + ZF_STANDARD_CURRENCY+2 /* NF_CURRENCY_1000INT_RED */ ); // #,##0.00 negative red nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000DEC2_RED ); aFormat = aFormatSeq[nIdx]; aFormat.Default = false; ImpInsertFormat(aFormat, CLOffset + ZF_STANDARD_CURRENCY+3 /* NF_CURRENCY_1000DEC2_RED */ ); // #,##0.00 USD nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000DEC2_CCC ); aFormat = aFormatSeq[nIdx]; aFormat.Default = false; ImpInsertFormat( aFormat, CLOffset + ZF_STANDARD_CURRENCY+4 /* NF_CURRENCY_1000DEC2_CCC */ ); // #.##0,-- nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_CURRENCY_1000DEC2_DASHED ); aFormat = aFormatSeq[nIdx]; aFormat.Default = false; ImpInsertFormat(aFormat, CLOffset + ZF_STANDARD_CURRENCY+5 /* NF_CURRENCY_1000DEC2_DASHED */ ); // Date aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::DATE, aLocale ); ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() ); // DD.MM.YY System nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYSTEM_SHORT ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE /* NF_DATE_SYSTEM_SHORT */ ); // NN DD.MMM YY nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DEF_NNDDMMMYY ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE+1 /* NF_DATE_DEF_NNDDMMMYY */ ); // DD.MM.YY def/System nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_MMYY ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE+2 /* NF_DATE_SYS_MMYY */ ); // DD MMM nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DDMMM ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE+3 /* NF_DATE_SYS_DDMMM */ ); // MMMM nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_MMMM ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE+4 /* NF_DATE_MMMM */ ); // QQ YY nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_QQJJ ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE+5 /* NF_DATE_QQJJ */ ); // DD.MM.YYYY was DD.MM.[YY]YY nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DDMMYYYY ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE+6 /* NF_DATE_SYS_DDMMYYYY */ ); // DD.MM.YY def/System nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DDMMYY ); ImpInsertFormat( 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( aFormatSeq, NF_DATE_SYSTEM_LONG ); ImpInsertFormat( 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( aFormatSeq, NF_DATE_SYS_DMMMYY ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE+9 /* NF_DATE_SYS_DMMMYY */ ); // D. MMM YYYY def/System nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DMMMYYYY ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE_SYS_DMMMYYYY /* NF_DATE_SYS_DMMMYYYY */ ); // D. MMMM YYYY def/System nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_DMMMMYYYY ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE_SYS_DMMMMYYYY /* NF_DATE_SYS_DMMMMYYYY */ ); // NN, D. MMM YY def/System nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_NNDMMMYY ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE_SYS_NNDMMMYY /* NF_DATE_SYS_NNDMMMYY */ ); // NN, D. MMMM YYYY def/System nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_NNDMMMMYYYY ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE_SYS_NNDMMMMYYYY /* NF_DATE_SYS_NNDMMMMYYYY */ ); // NNN, D. MMMM YYYY def/System nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_SYS_NNNNDMMMMYYYY ); ImpInsertFormat( 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( aFormatSeq, NF_DATE_DIN_DMMMYYYY ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE_DIN_DMMMYYYY /* NF_DATE_DIN_DMMMYYYY */ ); // D. MMMM YYYY DIN/EN nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DIN_DMMMMYYYY ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE_DIN_DMMMMYYYY /* NF_DATE_DIN_DMMMMYYYY */ ); // MM-DD DIN/EN nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DIN_MMDD ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE_DIN_MMDD /* NF_DATE_DIN_MMDD */ ); // YY-MM-DD DIN/EN nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DIN_YYMMDD ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE_DIN_YYMMDD /* NF_DATE_DIN_YYMMDD */ ); // YYYY-MM-DD DIN/EN/ISO nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATE_DIN_YYYYMMDD ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATE_DIN_YYYYMMDD /* NF_DATE_DIN_YYYYMMDD */ ); // Time aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::TIME, aLocale ); ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() ); // HH:MM nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HHMM ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_TIME /* NF_TIME_HHMM */ ); // HH:MM:SS nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HHMMSS ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_TIME+1 /* NF_TIME_HHMMSS */ ); // HH:MM AM/PM nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HHMMAMPM ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_TIME+2 /* NF_TIME_HHMMAMPM */ ); // HH:MM:SS AM/PM nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HHMMSSAMPM ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_TIME+3 /* NF_TIME_HHMMSSAMPM */ ); // [HH]:MM:SS nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HH_MMSS ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_TIME+4 /* NF_TIME_HH_MMSS */ ); // MM:SS,00 nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_MMSS00 ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_TIME+5 /* NF_TIME_MMSS00 */ ); // [HH]:MM:SS,00 nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_TIME_HH_MMSS00 ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_TIME+6 /* NF_TIME_HH_MMSS00 */ ); // DateTime aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::DATE_TIME, aLocale ); ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() ); // DD.MM.YY HH:MM System nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATETIME_SYSTEM_SHORT_HHMM ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATETIME /* NF_DATETIME_SYSTEM_SHORT_HHMM */ ); // DD.MM.YYYY HH:MM:SS System nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATETIME_SYS_DDMMYYYY_HHMMSS ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATETIME+1 /* NF_DATETIME_SYS_DDMMYYYY_HHMMSS */ ); // DD.MM.YYYY HH:MM System nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_DATETIME_SYS_DDMMYYYY_HHMM ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_DATETIME+2 /* NF_DATETIME_SYS_DDMMYYYY_HHMM */ ); const NfKeywordTable & rKeyword = 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( 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] + GetLocaleData()->getTime100SecSep() + "000"; pFormat = ImpInsertFormat( 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( aSingleFormatCode, CLOffset + ZF_STANDARD_DATETIME+5 /* NF_DATETIME_ISO_YYYYMMDDTHHMMSS */ ); assert(pFormat); pFormat->SetComment("ISO 8601"); // 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] + (GetLocaleData()->getTime100SecSep() == "." ? "." : ",") + "000"; pFormat = ImpInsertFormat( aSingleFormatCode, CLOffset + ZF_STANDARD_DATETIME+6 /* NF_DATETIME_ISO_YYYYMMDDTHHMMSS000 */ ); assert(pFormat); pFormat->SetComment("ISO 8601"); // not to be localized // Scientific number aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::SCIENTIFIC_NUMBER, aLocale ); ImpAdjustFormatCodeDefault( aFormatSeq.getArray(), aFormatSeq.getLength() ); // 0.00E+000 nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_SCIENTIFIC_000E000 ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_SCIENTIFIC /* NF_SCIENTIFIC_000E000 */ ); // 0.00E+00 nIdx = ImpGetFormatCodeIndex( aFormatSeq, NF_SCIENTIFIC_000E00 ); ImpInsertFormat( aFormatSeq[nIdx], CLOffset + ZF_STANDARD_SCIENTIFIC+1 /* NF_SCIENTIFIC_000E00 */ ); // Fraction number (no default option) aSingleFormatCode.Usage = i18n::KNumberFormatUsage::FRACTION_NUMBER; // # ?/? aSingleFormatCode.Code = "# ?/?"; ImpInsertFormat( aSingleFormatCode, CLOffset + ZF_STANDARD_FRACTION /* NF_FRACTION_1D */ ); // # ??/?? //! "??/" would be interpreted by the compiler as a trigraph for '\' aSingleFormatCode.Code = "# ?\?/?\?"; ImpInsertFormat( aSingleFormatCode, CLOffset + ZF_STANDARD_FRACTION+1 /* NF_FRACTION_2D */ ); // # ???/??? //! "??/" would be interpreted by the compiler as a trigraph for '\' aSingleFormatCode.Code = "# ?\?\?/?\?\?"; ImpInsertFormat( aSingleFormatCode, CLOffset + ZF_STANDARD_FRACTION+2 /* NF_FRACTION_3D */ ); // # ?/2 aSingleFormatCode.Code = "# ?/2"; ImpInsertFormat( aSingleFormatCode, CLOffset + ZF_STANDARD_FRACTION+3 /* NF_FRACTION_2 */ ); // # ?/4 aSingleFormatCode.Code = "# ?/4"; ImpInsertFormat( aSingleFormatCode, CLOffset + ZF_STANDARD_FRACTION+4 /* NF_FRACTION_4 */ ); // # ?/8 aSingleFormatCode.Code = "# ?/8"; ImpInsertFormat( aSingleFormatCode, CLOffset + ZF_STANDARD_FRACTION+5 /* NF_FRACTION_8 */ ); // # ??/16 aSingleFormatCode.Code = "# ?\?/16"; ImpInsertFormat( aSingleFormatCode, CLOffset + ZF_STANDARD_FRACTION+6 /* NF_FRACTION_16 */ ); // # ??/10 aSingleFormatCode.Code = "# ?\?/10"; ImpInsertFormat( aSingleFormatCode, CLOffset + ZF_STANDARD_FRACTION+7 /* NF_FRACTION_10 */ ); // # ??/100 aSingleFormatCode.Code = "# ?\?/100"; ImpInsertFormat( aSingleFormatCode, CLOffset + ZF_STANDARD_FRACTION+8 /* NF_FRACTION_100 */ ); // Week of year aSingleFormatCode.Code = rKeyword[NF_KEY_WW]; ImpInsertFormat( 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( CLOffset, xNFC, false ); } if (bOldConvertMode) { pFormatScanner->SetConvertMode(true); } } void SvNumberFormatter::ImpGenerateAdditionalFormats( 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 = 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( 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( 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( rFormat, nPos+1, bAfterChangingSystemCL ) ) { pNewFormat->SetAdditionalBuiltin(); nPos++; } } } pStdFormat->SetLastInsertKey( static_cast(nPos - CLOffset), SvNumberformat::FormatterPrivateAccess() ); } sal_Int32 SvNumberFormatter::ImpPosToken ( const OUStringBuffer & sFormat, sal_Unicode token, sal_Int32 nStartPos /* = 0*/ ) const { sal_Int32 nLength = sFormat.getLength(); for ( sal_Int32 i=nStartPos; 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 SvNumberFormatter::GenerateFormat(sal_uInt32 nIndex, LanguageType eLnge, bool bThousand, bool IsRed, sal_uInt16 nPrecision, sal_uInt16 nLeadingZeros) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); if (eLnge == LANGUAGE_DONTKNOW) { eLnge = IniLnge; } const SvNumberformat* pFormat = GetFormatEntry( nIndex ); const SvNumFormatType eType = (pFormat ? pFormat->GetMaskedType() : SvNumFormatType::UNDEFINED); ImpGenerateCL(eLnge); // create new standard formats if necessary utl::DigitGroupingIterator aGrouping( xLocaleData->getDigitGrouping()); // always group of 3 for Engineering notation const sal_Int32 nDigitsInFirstGroup = ( bThousand && (eType == SvNumFormatType::SCIENTIFIC) ) ? 3 : aGrouping.get(); const OUString& rThSep = 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('#'); 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(GetNumDecimalSep()); padToLength(sString, sString.getLength() + nPrecision, '0'); } 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; if ( GetNewCurrencySymbolString( nIndex, aCurr, &pEntry, &bBank ) ) { if ( pEntry ) { sal_uInt16 nPosiForm = NfCurrencyEntry::GetEffectivePositiveFormat( xLocaleData->getCurrPositiveFormat(), pEntry->GetPositiveFormat(), bBank ); sal_uInt16 nNegaForm = NfCurrencyEntry::GetEffectiveNegativeFormat( xLocaleData->getCurrNegativeFormat(), pEntry->GetNegativeFormat(), bBank ); pEntry->CompletePositiveFormatString( sString, bBank, nPosiForm ); pEntry->CompleteNegativeFormatString( sNegStr, bBank, nNegaForm ); } else { // assume currency abbreviation (AKA banking symbol), not symbol sal_uInt16 nPosiForm = NfCurrencyEntry::GetEffectivePositiveFormat( xLocaleData->getCurrPositiveFormat(), xLocaleData->getCurrPositiveFormat(), true ); sal_uInt16 nNegaForm = NfCurrencyEntry::GetEffectiveNegativeFormat( xLocaleData->getCurrNegativeFormat(), xLocaleData->getCurrNegativeFormat(), true ); NfCurrencyEntry::CompletePositiveFormatString( sString, aCurr, nPosiForm ); NfCurrencyEntry::CompleteNegativeFormatString( sNegStr, aCurr, nNegaForm ); } } else { // "automatic" old style OUString aSymbol, aAbbrev; GetCompatibilityCurrency( aSymbol, aAbbrev ); NfCurrencyEntry::CompletePositiveFormatString( sString, aSymbol, xLocaleData->getCurrPositiveFormat() ); NfCurrencyEntry::CompleteNegativeFormatString( sNegStr, aSymbol, xLocaleData->getCurrNegativeFormat() ); } if (IsRed) { sString.append(';'); sString.append('['); sString.append(pFormatScanner->GetRedString()); sString.append(']'); } else { sString.append(';'); } sString.append(sNegStr); } else if (eType == SvNumFormatType::FRACTION) { OUString aIntegerFractionDelimiterString = pFormat->GetIntegerFractionDelimiterString( 0 ); if ( aIntegerFractionDelimiterString == " " ) sString.append( aIntegerFractionDelimiterString ); else { sString.append( '"' ); sString.append( aIntegerFractionDelimiterString ); sString.append( '"' ); } sString.append( pFormat->GetNumeratorString( 0 ) ); sString.append( '/' ); 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(')'); } sTmpStr.append(';'); if (IsRed) { sTmpStr.append('['); sTmpStr.append(pFormatScanner->GetRedString()); sTmpStr.append(']'); } if (insertBrackets) { sTmpStr.append('('); sTmpStr.append(sString); sTmpStr.append(')'); } else { sTmpStr.append('-'); sTmpStr.append(sString); } sString = sTmpStr; } } return sString.makeStringAndClear(); } bool SvNumberFormatter::IsUserDefined(sal_uInt32 F_Index) const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); const SvNumberformat* pFormat = 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 = ImpGenerateCL(eLnge); // create new standard formats if necessary eLnge = ActLnge; sal_uInt32 nKey = ImpIsEntry(sStr, CLOffset, eLnge); if (nKey == NUMBERFORMAT_ENTRY_NOT_FOUND) { return true; } SvNumberformat* pEntry = 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 = ImpGenerateCL(eLnge); // create new standard formats if necessary return ImpIsEntry(sStr, CLOffset, eLnge); } sal_uInt32 SvNumberFormatter::GetStandardIndex(LanguageType eLnge) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); if (eLnge == LANGUAGE_DONTKNOW) { eLnge = IniLnge; } return GetStandardFormat(SvNumFormatType::NUMBER, eLnge); } SvNumFormatType SvNumberFormatter::GetType(sal_uInt32 nFIndex) const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); 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; } 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.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 = ImpGenerateCL(pFormat->GetLanguage()); } if (nOffset <= SV_MAX_COUNT_STANDARD_FORMATS) // Std.form. { nNewKey = nCLOffset + nOffset; if (aFTable.find( nNewKey) == aFTable.end()) // not already present { std::unique_ptr pNewEntry(new SvNumberformat( *pFormat, *pFormatScanner )); if (!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 pNewEntry(new SvNumberformat( *pFormat, *pFormatScanner )); nNewKey = ImpIsEntry(pNewEntry->GetFormatstring(), nCLOffset, pFormat->GetLanguage()); if (nNewKey == NUMBERFORMAT_ENTRY_NOT_FOUND) // only if not present yet { SvNumberformat* pStdFormat = 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 (!aFTable.emplace( nNewKey, std::move(pNewEntry)).second) { SAL_WARN( "svl.numbers", "SvNumberFormatter::MergeFormatter: dup position"); } else { pStdFormat->SetLastInsertKey(static_cast(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; } sal_uInt32 SvNumberFormatter::GetFormatForLanguageIfBuiltIn( sal_uInt32 nFormat, LanguageType eLnge ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); if ( eLnge == LANGUAGE_DONTKNOW ) { eLnge = IniLnge; } if ( nFormat < SV_COUNTRY_LANGUAGE_OFFSET && eLnge == IniLnge ) { return nFormat; // it stays as it is } sal_uInt32 nOffset = nFormat % SV_COUNTRY_LANGUAGE_OFFSET; // relative index if ( nOffset > SV_MAX_COUNT_STANDARD_FORMATS ) { return nFormat; // not a built-in format } sal_uInt32 nCLOffset = ImpGenerateCL(eLnge); // create new standard formats if necessary return nCLOffset + nOffset; } sal_uInt32 SvNumberFormatter::GetFormatIndex( NfIndexTableOffset nTabOff, LanguageType eLnge ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); if (nTabOff >= NF_INDEX_TABLE_ENTRIES) return NUMBERFORMAT_ENTRY_NOT_FOUND; if (eLnge == LANGUAGE_DONTKNOW) eLnge = IniLnge; if (indexTable[nTabOff] == NUMBERFORMAT_ENTRY_NOT_FOUND) return NUMBERFORMAT_ENTRY_NOT_FOUND; sal_uInt32 nCLOffset = ImpGenerateCL(eLnge); // create new standard formats if necessary return nCLOffset + indexTable[nTabOff]; } NfIndexTableOffset SvNumberFormatter::GetIndexTableOffset( sal_uInt32 nFormat ) const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); 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(j); } return NF_INDEX_TABLE_ENTRIES; // bad luck } void SvNumberFormatter::SetEvalDateFormat( NfEvalDateFormat eEDF ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); eEvalDateFormat = eEDF; } NfEvalDateFormat SvNumberFormatter::GetEvalDateFormat() const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); return eEvalDateFormat; } void SvNumberFormatter::SetYear2000( sal_uInt16 nVal ) { ::osl::MutexGuard aGuard( GetInstanceMutex() ); pStringScanner->SetYear2000( nVal ); } sal_uInt16 SvNumberFormatter::GetYear2000() const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); return pStringScanner->GetYear2000(); } sal_uInt16 SvNumberFormatter::ExpandTwoDigitYear( sal_uInt16 nYear ) const { if ( nYear < 100 ) return SvNumberFormatter::ExpandTwoDigitYear( nYear, pStringScanner->GetYear2000() ); return nYear; } // static sal_uInt16 SvNumberFormatter::GetYear2000Default() { if (!utl::ConfigManager::IsFuzzing()) return officecfg::Office::Common::DateFormat::TwoDigitYear::get(); return 1930; } // static void SvNumberFormatter::resetTheCurrencyTable() { SAL_INFO("svl", "Resetting the currency table."); nSystemCurrencyPosition = 0; bCurrencyTableInitialized = false; GetFormatterRegistry().ConfigurationChanged(nullptr, ConfigurationHints::Locale | ConfigurationHints::Currency | ConfigurationHints::DatePatterns); } // static const NfCurrencyTable& SvNumberFormatter::GetTheCurrencyTable() { while ( !bCurrencyTableInitialized ) 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 SvNumberFormatter::ResetDefaultSystemCurrency() { nDefaultSystemCurrencyFormat = NUMBERFORMAT_ENTRY_NOT_FOUND; } void SvNumberFormatter::InvalidateDateAcceptancePatterns() { pStringScanner->InvalidateDateAcceptancePatterns(); } sal_uInt32 SvNumberFormatter::ImpGetDefaultSystemCurrencyFormat() { if ( nDefaultSystemCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND ) { sal_Int32 nCheck; SvNumFormatType nType; NfWSStringsDtor aCurrList; sal_uInt16 nDefault = GetCurrencyFormatStrings( aCurrList, 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( 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 SvNumberFormatter::ImpGetDefaultCurrencyFormat() { sal_uInt32 CLOffset = ImpGetCLOffset( ActLnge ); 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 = GetCurrencyFormatStrings( aCurrList, GetCurrencyEntry( ActLnge ), 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( aCurrList[ nDefault ], nCheck, nType, nDefaultCurrencyFormat, ActLnge ); DBG_ASSERT( nCheck == 0, "NewCurrency CheckError" ); DBG_ASSERT( nDefaultCurrencyFormat != NUMBERFORMAT_ENTRY_NOT_FOUND, "nDefaultCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND" ); } // old automatic currency format as a last resort if ( nDefaultCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND ) nDefaultCurrencyFormat = CLOffset + ZF_STANDARD_CURRENCY+3; else { // mark as standard so that it is found next time SvNumberformat* pEntry = 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; } bool SvNumberFormatter::GetNewCurrencySymbolString( sal_uInt32 nFormat, OUString& rStr, const NfCurrencyEntry** ppEntry /* = NULL */, bool* pBank /* = NULL */ ) const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); 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 = 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('"'); sBuff.append( aSymbol); sBuff.append('"'); } else { sBuff.append(aSymbol); } if ( !aExtension.isEmpty() ) { sBuff.append(aExtension); } sBuff.append(']'); } rStr = sBuff.makeStringAndClear(); return true; } } rStr.clear(); return false; } // static const NfCurrencyEntry* SvNumberFormatter::GetCurrencyEntry( bool & bFoundBank, std::u16string_view rSymbol, const OUString& rExtension, LanguageType eFormatLanguage, bool bOnlyStringLanguage ) { sal_Int32 nExtLen = rExtension.getLength(); 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 = rExtension.toInt32( 16 ); if ( !nExtLang ) { eExtLang = LANGUAGE_DONTKNOW; } else { eExtLang = LanguageType((nExtLang < 0) ? -nExtLang : nExtLang); } } 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 SvNumberFormatter::GetCompatibilityCurrency( OUString& rSymbol, OUString& rAbbrev ) const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); 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(); } } 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 ( !bCurrencyTableInitialized ) // ImpInitCurrencyTable(); static bool bInitializing = false; if ( bCurrencyTableInitialized || bInitializing ) { return ; } bInitializing = true; LanguageType eSysLang = SvtSysLocale().GetLanguageTag().getLanguageType(); std::optional 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 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; jGetBankSymbol() == 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; bCurrencyTableInitialized = 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 SvNumberFormatter::GetCurrencyFormatStrings( NfWSStringsDtor& rStrArr, const NfCurrencyEntry& rCurr, bool bBank ) const { ::osl::MutexGuard aGuard( GetInstanceMutex() ); 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_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(eLanguage); aBuf.append('-').append(OUString::number(nLang, 16).toAsciiUpperCase()); } } aBuf.append(']'); return aBuf.makeStringAndClear(); } OUString NfCurrencyEntry::Impl_BuildFormatStringNumChars( const LocaleDataWrapper& rLoc, sal_uInt16 nDecimalFormat) const { OUStringBuffer aBuf; aBuf.append("#" + 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, ' '); rStr.insert(0, 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 nNegativeFormat) { switch( nNegativeFormat ) { case 0: // ($1) { rStr.insert(0, rSymStr); rStr.insert(0, '('); rStr.append(')'); } break; case 1: // -$1 { rStr.insert(0, rSymStr); rStr.insert(0, '-'); } break; case 2: // $-1 { rStr.insert(0, '-'); rStr.insert(0, 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, ' '); rStr.insert(0, rSymStr); rStr.insert(0, '-'); } break; case 10: // 1 $- { rStr.append(' '); rStr.append(rSymStr); rStr.append('-'); } break; case 11: // $ -1 { rStr.insert(0, " -"); rStr.insert(0, rSymStr); } break; case 12 : // $ 1- { rStr.insert(0, ' '); rStr.insert(0, rSymStr); rStr.append('-'); } break; case 13 : // 1- $ { rStr.append('-'); rStr.append(' '); rStr.append(rSymStr); } break; case 14 : // ($ 1) { rStr.insert(0, ' '); rStr.insert(0, rSymStr); rStr.insert(0, '('); 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 = GetFormatEntry( nKey); if (pFormat) ChangeIntl( pFormat->GetLanguage()); else ChangeIntl( IniLnge); return pFormatScanner->GetKeywords(); } const NfKeywordTable & SvNumberFormatter::GetEnglishKeywords() const { return ImpSvNumberformatScan::GetEnglishKeywords(); } const std::vector & SvNumberFormatter::GetStandardColors() const { return ImpSvNumberformatScan::GetStandardColors(); } size_t SvNumberFormatter::GetMaxDefaultColors() const { return ImpSvNumberformatScan::GetMaxDefaultColors(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */