Drop unneeded indirection and unused argument
[LibreOffice.git] / svl / source / numbers / zforlist.cxx
blob5907193a63b3c810ffc8ac6725d4737d5183be19
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <sal/log.hxx>
23 #include <officecfg/Office/Common.hxx>
24 #include <svl/zforlist.hxx>
25 #include <svl/currencytable.hxx>
27 #include <comphelper/lok.hxx>
28 #include <comphelper/string.hxx>
29 #include <o3tl/string_view.hxx>
30 #include <tools/debug.hxx>
31 #include <unotools/charclass.hxx>
32 #include <unotools/configmgr.hxx>
33 #include <i18nlangtag/mslangid.hxx>
34 #include <unotools/localedatawrapper.hxx>
35 #include <com/sun/star/i18n/KNumberFormatUsage.hpp>
36 #include <com/sun/star/i18n/KNumberFormatType.hpp>
37 #include <com/sun/star/i18n/FormatElement.hpp>
38 #include <com/sun/star/i18n/Currency2.hpp>
39 #include <com/sun/star/i18n/NumberFormatCode.hpp>
40 #include <com/sun/star/i18n/XNumberFormatCode.hpp>
41 #include <com/sun/star/i18n/NumberFormatMapper.hpp>
42 #include <comphelper/processfactory.hxx>
44 #include <osl/mutex.hxx>
46 #include "zforscan.hxx"
47 #include "zforfind.hxx"
48 #include <svl/zformat.hxx>
49 #include <i18npool/reservedconstants.hxx>
51 #include <unotools/syslocaleoptions.hxx>
52 #include <unotools/digitgroupingiterator.hxx>
53 #include <rtl/strbuf.hxx>
54 #include <rtl/math.hxx>
56 #include <atomic>
57 #include <limits>
58 #include <memory>
59 #include <set>
61 using namespace ::com::sun::star;
62 using namespace ::com::sun::star::uno;
63 using namespace ::com::sun::star::i18n;
64 using namespace ::com::sun::star::lang;
66 // Constants for type offsets per Country/Language (CL)
67 #define ZF_STANDARD 0
68 #define ZF_STANDARD_PERCENT 10
69 #define ZF_STANDARD_CURRENCY 20
70 #define ZF_STANDARD_DATE 30
71 #define ZF_STANDARD_TIME 60
72 #define ZF_STANDARD_DURATION (ZF_STANDARD_TIME + 4)
73 #define ZF_STANDARD_DATETIME 70
74 #define ZF_STANDARD_SCIENTIFIC 80
75 #define ZF_STANDARD_FRACTION 85
77 // Additional builtin formats, historically not fitting into the first 10 of a
78 // category. Make sure it doesn't spill over to the next category.
79 #define ZF_STANDARD_DATE_SYS_DMMMYYYY (ZF_STANDARD_DATE + 10)
80 #define ZF_STANDARD_DATE_SYS_DMMMMYYYY (ZF_STANDARD_DATE + 11)
81 #define ZF_STANDARD_DATE_SYS_NNDMMMYY (ZF_STANDARD_DATE + 12)
82 #define ZF_STANDARD_DATE_SYS_NNDMMMMYYYY (ZF_STANDARD_DATE + 13)
83 #define ZF_STANDARD_DATE_SYS_NNNNDMMMMYYYY (ZF_STANDARD_DATE + 14)
84 #define ZF_STANDARD_DATE_DIN_DMMMYYYY (ZF_STANDARD_DATE + 15)
85 #define ZF_STANDARD_DATE_DIN_DMMMMYYYY (ZF_STANDARD_DATE + 16)
86 #define ZF_STANDARD_DATE_DIN_MMDD (ZF_STANDARD_DATE + 17)
87 #define ZF_STANDARD_DATE_DIN_YYMMDD (ZF_STANDARD_DATE + 18)
88 #define ZF_STANDARD_DATE_DIN_YYYYMMDD (ZF_STANDARD_DATE + 19)
89 #define ZF_STANDARD_DATE_WW (ZF_STANDARD_DATE + 20)
91 #define ZF_STANDARD_LOGICAL SV_MAX_COUNT_STANDARD_FORMATS-1 // 99
92 #define ZF_STANDARD_TEXT SV_MAX_COUNT_STANDARD_FORMATS // 100
94 static_assert( ZF_STANDARD_TEXT == NF_STANDARD_FORMAT_TEXT, "definition mismatch" );
96 static_assert( NF_INDEX_TABLE_RESERVED_START == i18npool::nStopPredefinedFormatIndex,
97 "NfIndexTableOffset does not match i18npool's locale data predefined format code index bounds.");
99 static_assert( NF_INDEX_TABLE_ENTRIES <= i18npool::nFirstFreeFormatIndex,
100 "NfIndexTableOffset crosses i18npool's locale data reserved format code index bounds.\n"
101 "You will need to adapt all locale data files defining index values "
102 "(formatIndex=\"...\") in that range and increment those and when done "
103 "adjust nFirstFreeFormatIndex in include/i18npool/reservedconstants.hxx");
105 /* Locale that is set if an unknown locale (from another system) is loaded of
106 * legacy documents. Can not be SYSTEM because else, for example, a German "DM"
107 * (old currency) is recognized as a date (#53155#). */
108 #define UNKNOWN_SUBSTITUTE LANGUAGE_ENGLISH_US
110 // Same order as in include/svl/zforlist.hxx enum NfIndexTableOffset
111 sal_uInt32 const indexTable[NF_INDEX_TABLE_ENTRIES] = {
112 ZF_STANDARD, // NF_NUMBER_STANDARD
113 ZF_STANDARD + 1, // NF_NUMBER_INT
114 ZF_STANDARD + 2, // NF_NUMBER_DEC2
115 ZF_STANDARD + 3, // NF_NUMBER_1000INT
116 ZF_STANDARD + 4, // NF_NUMBER_1000DEC2
117 ZF_STANDARD + 5, // NF_NUMBER_SYSTEM
118 ZF_STANDARD_SCIENTIFIC, // NF_SCIENTIFIC_000E000
119 ZF_STANDARD_SCIENTIFIC + 1, // NF_SCIENTIFIC_000E00
120 ZF_STANDARD_PERCENT, // NF_PERCENT_INT
121 ZF_STANDARD_PERCENT + 1, // NF_PERCENT_DEC2
122 ZF_STANDARD_FRACTION, // NF_FRACTION_1D
123 ZF_STANDARD_FRACTION + 1, // NF_FRACTION_2D
124 ZF_STANDARD_CURRENCY, // NF_CURRENCY_1000INT
125 ZF_STANDARD_CURRENCY + 1, // NF_CURRENCY_1000DEC2
126 ZF_STANDARD_CURRENCY + 2, // NF_CURRENCY_1000INT_RED
127 ZF_STANDARD_CURRENCY + 3, // NF_CURRENCY_1000DEC2_RED
128 ZF_STANDARD_CURRENCY + 4, // NF_CURRENCY_1000DEC2_CCC
129 ZF_STANDARD_CURRENCY + 5, // NF_CURRENCY_1000DEC2_DASHED
130 ZF_STANDARD_DATE, // NF_DATE_SYSTEM_SHORT
131 ZF_STANDARD_DATE + 8, // NF_DATE_SYSTEM_LONG
132 ZF_STANDARD_DATE + 7, // NF_DATE_SYS_DDMMYY
133 ZF_STANDARD_DATE + 6, // NF_DATE_SYS_DDMMYYYY
134 ZF_STANDARD_DATE + 9, // NF_DATE_SYS_DMMMYY
135 ZF_STANDARD_DATE_SYS_DMMMYYYY, // NF_DATE_SYS_DMMMYYYY
136 ZF_STANDARD_DATE_DIN_DMMMYYYY, // NF_DATE_DIN_DMMMYYYY
137 ZF_STANDARD_DATE_SYS_DMMMMYYYY, // NF_DATE_SYS_DMMMMYYYY
138 ZF_STANDARD_DATE_DIN_DMMMMYYYY, // NF_DATE_DIN_DMMMMYYYY
139 ZF_STANDARD_DATE_SYS_NNDMMMYY, // NF_DATE_SYS_NNDMMMYY
140 ZF_STANDARD_DATE + 1, // NF_DATE_DEF_NNDDMMMYY
141 ZF_STANDARD_DATE_SYS_NNDMMMMYYYY, // NF_DATE_SYS_NNDMMMMYYYY
142 ZF_STANDARD_DATE_SYS_NNNNDMMMMYYYY, // NF_DATE_SYS_NNNNDMMMMYYYY
143 ZF_STANDARD_DATE_DIN_MMDD, // NF_DATE_DIN_MMDD
144 ZF_STANDARD_DATE_DIN_YYMMDD, // NF_DATE_DIN_YYMMDD
145 ZF_STANDARD_DATE_DIN_YYYYMMDD, // NF_DATE_DIN_YYYYMMDD
146 ZF_STANDARD_DATE + 2, // NF_DATE_SYS_MMYY
147 ZF_STANDARD_DATE + 3, // NF_DATE_SYS_DDMMM
148 ZF_STANDARD_DATE + 4, // NF_DATE_MMMM
149 ZF_STANDARD_DATE + 5, // NF_DATE_QQJJ
150 ZF_STANDARD_DATE_WW, // NF_DATE_WW
151 ZF_STANDARD_TIME, // NF_TIME_HHMM
152 ZF_STANDARD_TIME + 1, // NF_TIME_HHMMSS
153 ZF_STANDARD_TIME + 2, // NF_TIME_HHMMAMPM
154 ZF_STANDARD_TIME + 3, // NF_TIME_HHMMSSAMPM
155 ZF_STANDARD_TIME + 4, // NF_TIME_HH_MMSS
156 ZF_STANDARD_TIME + 5, // NF_TIME_MMSS00
157 ZF_STANDARD_TIME + 6, // NF_TIME_HH_MMSS00
158 ZF_STANDARD_DATETIME, // NF_DATETIME_SYSTEM_SHORT_HHMM
159 ZF_STANDARD_DATETIME + 1, // NF_DATETIME_SYS_DDMMYYYY_HHMMSS
160 ZF_STANDARD_LOGICAL, // NF_BOOLEAN
161 ZF_STANDARD_TEXT, // NF_TEXT
162 ZF_STANDARD_DATETIME + 2, // NF_DATETIME_SYS_DDMMYYYY_HHMM
163 ZF_STANDARD_FRACTION + 2, // NF_FRACTION_3D
164 ZF_STANDARD_FRACTION + 3, // NF_FRACTION_2
165 ZF_STANDARD_FRACTION + 4, // NF_FRACTION_4
166 ZF_STANDARD_FRACTION + 5, // NF_FRACTION_8
167 ZF_STANDARD_FRACTION + 6, // NF_FRACTION_16
168 ZF_STANDARD_FRACTION + 7, // NF_FRACTION_10
169 ZF_STANDARD_FRACTION + 8, // NF_FRACTION_100
170 ZF_STANDARD_DATETIME + 3, // NF_DATETIME_ISO_YYYYMMDD_HHMMSS
171 ZF_STANDARD_DATETIME + 4, // NF_DATETIME_ISO_YYYYMMDD_HHMMSS000
172 ZF_STANDARD_DATETIME + 5, // NF_DATETIME_ISO_YYYYMMDDTHHMMSS
173 ZF_STANDARD_DATETIME + 6 // NF_DATETIME_ISO_YYYYMMDDTHHMMSS000
177 instead of every number formatter being a listener we have a registry which
178 also handles one instance of the SysLocale options
181 class SvNumberFormatterRegistry_Impl : public utl::ConfigurationListener
183 std::vector< SvNumberFormatter* >
184 aFormatters;
185 SvtSysLocaleOptions aSysLocaleOptions;
186 LanguageType eSysLanguage;
188 public:
189 SvNumberFormatterRegistry_Impl();
190 virtual ~SvNumberFormatterRegistry_Impl() override;
192 void Insert( SvNumberFormatter* pThis )
193 { aFormatters.push_back( pThis ); }
195 void Remove( SvNumberFormatter const * pThis );
197 size_t Count() const
198 { return aFormatters.size(); }
200 virtual void ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints ) override;
203 SvNumberFormatterRegistry_Impl::SvNumberFormatterRegistry_Impl()
204 : eSysLanguage(MsLangId::getRealLanguage( LANGUAGE_SYSTEM ))
206 aSysLocaleOptions.AddListener( this );
210 SvNumberFormatterRegistry_Impl::~SvNumberFormatterRegistry_Impl()
212 aSysLocaleOptions.RemoveListener( this );
216 void SvNumberFormatterRegistry_Impl::Remove( SvNumberFormatter const * pThis )
218 auto it = std::find(aFormatters.begin(), aFormatters.end(), pThis);
219 if (it != aFormatters.end())
220 aFormatters.erase( it );
223 void SvNumberFormatterRegistry_Impl::ConfigurationChanged( utl::ConfigurationBroadcaster*,
224 ConfigurationHints nHint)
226 ::osl::MutexGuard aGuard( SvNumberFormatter::GetGlobalMutex() );
228 if ( nHint & ConfigurationHints::Locale )
230 for(SvNumberFormatter* pFormatter : aFormatters)
231 pFormatter->ReplaceSystemCL( eSysLanguage );
232 eSysLanguage = MsLangId::getRealLanguage( LANGUAGE_SYSTEM );
234 if ( nHint & ConfigurationHints::Currency )
236 for(SvNumberFormatter* pFormatter : aFormatters)
237 pFormatter->ResetDefaultSystemCurrency();
239 if ( nHint & ConfigurationHints::DatePatterns )
241 for(SvNumberFormatter* pFormatter : aFormatters)
242 pFormatter->InvalidateDateAcceptancePatterns();
246 static std::atomic<bool> g_CurrencyTableInitialized;
248 SvNumberFormatterRegistry_Impl* SvNumberFormatter::pFormatterRegistry = nullptr;
249 namespace
251 NfCurrencyTable& theCurrencyTable()
253 static NfCurrencyTable SINGLETON;
254 return SINGLETON;
257 NfCurrencyTable& theLegacyOnlyCurrencyTable()
259 static NfCurrencyTable SINGLETON;
260 return SINGLETON;
263 /** THE set of installed locales. */
264 std::set< LanguageType > theInstalledLocales;
267 sal_uInt16 SvNumberFormatter::nSystemCurrencyPosition = 0;
269 // Whether BankSymbol (not CurrencySymbol!) is always at the end (1 $;-1 $) or
270 // language dependent.
271 #define NF_BANKSYMBOL_FIX_POSITION 1
273 const sal_uInt16 SvNumberFormatter::UNLIMITED_PRECISION = ::std::numeric_limits<sal_uInt16>::max();
274 const sal_uInt16 SvNumberFormatter::INPUTSTRING_PRECISION = ::std::numeric_limits<sal_uInt16>::max()-1;
276 void SvNFEngine::ChangeIntl(SvNFLanguageData& rCurrentLanguage, LanguageType eLnge)
278 rCurrentLanguage.ChangeIntl(eLnge);
281 void SvNFEngine::ChangeNullDate(SvNFLanguageData& rCurrentLanguage, sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear)
283 rCurrentLanguage.pFormatScanner->ChangeNullDate(nDay, nMonth, nYear);
284 rCurrentLanguage.pStringScanner->ChangeNullDate(nDay, nMonth, nYear);
287 SvNFLanguageData::SvNFLanguageData(const Reference<XComponentContext>& rxContext, LanguageType eLang,
288 const SvNumberFormatter& rColorCallback)
289 : xContext(rxContext)
290 , IniLnge(eLang)
291 , ActLnge(eLang)
292 , aLanguageTag(eLang)
293 , eEvalDateFormat(NF_EVALDATEFORMAT_INTL)
295 xCharClass.changeLocale(xContext, aLanguageTag);
296 xLocaleData.init(xContext, aLanguageTag);
297 xCalendar.init(xContext, aLanguageTag.getLocale());
298 xTransliteration.init(xContext, ActLnge);
300 // cached locale data items
301 const LocaleDataWrapper* pLoc = GetLocaleData();
302 aDecimalSep = pLoc->getNumDecimalSep();
303 aDecimalSepAlt = pLoc->getNumDecimalSepAlt();
304 aThousandSep = pLoc->getNumThousandSep();
305 aDateSep = pLoc->getDateSep();
307 pStringScanner.reset(new ImpSvNumberInputScan(*this));
308 pFormatScanner.reset(new ImpSvNumberformatScan(*this, rColorCallback));
311 SvNFLanguageData::SvNFLanguageData(const SvNFLanguageData& rOther)
312 : xContext(rOther.xContext)
313 , IniLnge(rOther.IniLnge)
314 , ActLnge(rOther.ActLnge)
315 , aLanguageTag(rOther.aLanguageTag)
316 , aDecimalSep(rOther.aDecimalSep)
317 , aDecimalSepAlt(rOther.aDecimalSepAlt)
318 , aThousandSep(rOther.aThousandSep)
319 , aDateSep(rOther.aDateSep)
320 , eEvalDateFormat(rOther.eEvalDateFormat)
322 xCharClass.changeLocale(xContext, aLanguageTag);
323 xLocaleData.init(xContext, aLanguageTag);
324 xCalendar.init(xContext, aLanguageTag.getLocale());
325 xTransliteration.init(xContext, ActLnge);
327 pStringScanner.reset(new ImpSvNumberInputScan(*this));
328 pFormatScanner.reset(new ImpSvNumberformatScan(*this, rOther.pFormatScanner->getColorCallback()));
331 SvNFLanguageData::~SvNFLanguageData()
335 const LocaleDataWrapper* SvNFLanguageData::GetLocaleData() const { return xLocaleData.get(); }
337 const CharClass* SvNFLanguageData::GetCharClass() const { return xCharClass.get(); }
339 CalendarWrapper* SvNFLanguageData::GetCalendar() const { return xCalendar.get(); }
341 const ::utl::TransliterationWrapper* SvNFLanguageData::GetTransliteration() const
343 return xTransliteration.get();
346 const LanguageTag& SvNFLanguageData::GetLanguageTag() const { return aLanguageTag; }
348 const ImpSvNumberformatScan* SvNFLanguageData::GetFormatScanner() const { return pFormatScanner.get(); }
350 const OUString& SvNFLanguageData::GetNumDecimalSep() const { return aDecimalSep; }
352 const OUString& SvNFLanguageData::GetNumDecimalSepAlt() const { return aDecimalSepAlt; }
354 const OUString& SvNFLanguageData::GetNumThousandSep() const { return aThousandSep; }
356 const OUString& SvNFLanguageData::GetDateSep() const { return aDateSep; }
358 bool SvNFLanguageData::IsDecimalSep( std::u16string_view rStr ) const
360 if (rStr == GetNumDecimalSep())
361 return true;
362 if (GetNumDecimalSepAlt().isEmpty())
363 return false;
364 return rStr == GetNumDecimalSepAlt();
367 OUString SvNFLanguageData::GetLangDecimalSep( LanguageType nLang )
369 if (nLang == ActLnge)
371 return GetNumDecimalSep();
373 OUString aRet;
374 LanguageType eSaveLang = xLocaleData.getCurrentLanguage();
375 if (nLang == eSaveLang)
377 aRet = xLocaleData->getNumDecimalSep();
379 else
381 LanguageTag aSaveLocale( xLocaleData->getLanguageTag() );
382 xLocaleData.changeLocale( LanguageTag( nLang));
383 aRet = xLocaleData->getNumDecimalSep();
384 xLocaleData.changeLocale( aSaveLocale );
386 return aRet;
389 void SvNFLanguageData::ChangeIntl(LanguageType eLnge)
391 if (ActLnge == eLnge)
392 return;
394 ActLnge = eLnge;
396 aLanguageTag.reset( eLnge );
397 xCharClass.changeLocale( xContext, aLanguageTag );
398 xLocaleData.changeLocale( aLanguageTag );
399 xCalendar.changeLocale( aLanguageTag.getLocale() );
400 xTransliteration.changeLocale( eLnge );
402 // cached locale data items, initialize BEFORE calling ChangeIntl below
403 const LocaleDataWrapper* pLoc = GetLocaleData();
404 aDecimalSep = pLoc->getNumDecimalSep();
405 aDecimalSepAlt = pLoc->getNumDecimalSepAlt();
406 aThousandSep = pLoc->getNumThousandSep();
407 aDateSep = pLoc->getDateSep();
409 pFormatScanner->ChangeIntl();
410 pStringScanner->ChangeIntl();
413 SvNumberFormatter::SvNumberFormatter( const Reference< XComponentContext >& rxContext,
414 LanguageType eLang )
415 : m_xContext( rxContext )
416 , IniLnge(eLang != LANGUAGE_DONTKNOW ? eLang : UNKNOWN_SUBSTITUTE)
417 , m_aRWPolicy(SvNFEngine::GetRWPolicy(m_aFormatData))
418 , m_aCurrentLanguage(rxContext, IniLnge, *this)
419 , m_xNatNum(m_xContext)
422 // 0 .. 999 for initialized language formats
423 m_aFormatData.ImpGenerateFormats(m_aCurrentLanguage, GetNatNum(), 0, false);
425 ::osl::MutexGuard aGuard( GetGlobalMutex() );
426 GetFormatterRegistry().Insert( this );
429 SvNumberFormatter::~SvNumberFormatter()
432 ::osl::MutexGuard aGuard( GetGlobalMutex() );
433 pFormatterRegistry->Remove( this );
434 if ( !pFormatterRegistry->Count() )
436 delete pFormatterRegistry;
437 pFormatterRegistry = nullptr;
441 m_aFormatData.aFTable.clear();
442 ClearMergeTable();
445 void SvNumberFormatter::ChangeIntl(LanguageType eLnge)
447 ::osl::MutexGuard aGuard( GetInstanceMutex() );
448 SvNFEngine::ChangeIntl(m_aCurrentLanguage, eLnge);
451 // static
452 ::osl::Mutex& SvNumberFormatter::GetGlobalMutex()
454 // #i77768# Due to a static reference in the toolkit lib
455 // we need a mutex that lives longer than the svl library.
456 // Otherwise the dtor would use a destructed mutex!!
457 static osl::Mutex* persistentMutex(new osl::Mutex);
459 return *persistentMutex;
463 // static
464 SvNumberFormatterRegistry_Impl& SvNumberFormatter::GetFormatterRegistry()
466 ::osl::MutexGuard aGuard( GetGlobalMutex() );
467 if ( !pFormatterRegistry )
469 pFormatterRegistry = new SvNumberFormatterRegistry_Impl;
471 return *pFormatterRegistry;
474 void SvNumberFormatter::SetColorLink( const Link<sal_uInt16,Color*>& rColorTableCallBack )
476 ::osl::MutexGuard aGuard( GetInstanceMutex() );
477 aColorLink = rColorTableCallBack;
480 Color* SvNumberFormatter::GetUserDefColor(sal_uInt16 nIndex) const
482 ::osl::MutexGuard aGuard( GetInstanceMutex() );
483 if( aColorLink.IsSet() )
485 return aColorLink.Call(nIndex);
487 else
489 return nullptr;
493 void SvNumberFormatter::ChangeNullDate(sal_uInt16 nDay,
494 sal_uInt16 nMonth,
495 sal_Int16 nYear)
497 ::osl::MutexGuard aGuard( GetInstanceMutex() );
498 SvNFEngine::ChangeNullDate(m_aCurrentLanguage, nDay, nMonth, nYear);
501 const Date& SvNFLanguageData::GetNullDate() const
503 return pFormatScanner->GetNullDate();
506 void SvNFLanguageData::ChangeStandardPrec(short nPrec)
508 pFormatScanner->ChangeStandardPrec(nPrec);
511 const Date& SvNumberFormatter::GetNullDate() const
513 ::osl::MutexGuard aGuard( GetInstanceMutex() );
514 return m_aCurrentLanguage.GetNullDate();
517 void SvNumberFormatter::ChangeStandardPrec(short nPrec)
519 ::osl::MutexGuard aGuard( GetInstanceMutex() );
520 m_aCurrentLanguage.ChangeStandardPrec(nPrec);
523 void SvNumberFormatter::SetNoZero(bool bNZ)
525 ::osl::MutexGuard aGuard( GetInstanceMutex() );
526 m_aFormatData.SetNoZero(bNZ);
529 sal_uInt16 SvNumberFormatter::GetStandardPrec() const
531 ::osl::MutexGuard aGuard( GetInstanceMutex() );
532 return m_aCurrentLanguage.pFormatScanner->GetStandardPrec();
535 bool SvNumberFormatter::GetNoZero() const
537 ::osl::MutexGuard aGuard( GetInstanceMutex() );
538 return m_aFormatData.GetNoZero();
541 void SvNumberFormatter::ReplaceSystemCL( LanguageType eOldLanguage )
543 sal_uInt32 nCLOffset = m_aFormatData.ImpGetCLOffset( LANGUAGE_SYSTEM );
544 if ( nCLOffset > m_aFormatData.MaxCLOffset )
546 return ; // no SYSTEM entries to replace
548 const sal_uInt32 nMaxBuiltin = nCLOffset + SV_MAX_COUNT_STANDARD_FORMATS;
549 const sal_uInt32 nNextCL = nCLOffset + SV_COUNTRY_LANGUAGE_OFFSET;
550 sal_uInt32 nKey;
552 // remove old builtin formats
553 auto it = m_aFormatData.aFTable.find( nCLOffset );
554 while ( it != m_aFormatData.aFTable.end() && (nKey = it->first) >= nCLOffset && nKey <= nMaxBuiltin )
556 it = m_aFormatData.aFTable.erase(it);
559 // move additional and user defined to temporary table
560 SvNumberFormatTable aOldTable;
561 while ( it != m_aFormatData.aFTable.end() && (nKey = it->first) >= nCLOffset && nKey < nNextCL )
563 aOldTable[ nKey ] = it->second.release();
564 it = m_aFormatData.aFTable.erase(it);
567 // generate new old builtin formats
568 // reset m_aCurrentLanguage.ActLnge otherwise ChangeIntl() wouldn't switch if already LANGUAGE_SYSTEM
569 m_aCurrentLanguage.ActLnge = LANGUAGE_DONTKNOW;
570 ChangeIntl( LANGUAGE_SYSTEM );
571 m_aFormatData.ImpGenerateFormats(m_aCurrentLanguage, GetNatNum(), nCLOffset, true);
573 // convert additional and user defined from old system to new system
574 SvNumberformat* pStdFormat = m_aFormatData.GetFormatEntry( nCLOffset + ZF_STANDARD );
575 sal_uInt32 nLastKey = nMaxBuiltin;
576 m_aCurrentLanguage.pFormatScanner->SetConvertMode( eOldLanguage, LANGUAGE_SYSTEM, true , true);
577 while ( !aOldTable.empty() )
579 nKey = aOldTable.begin()->first;
580 if ( nLastKey < nKey )
582 nLastKey = nKey;
584 std::unique_ptr<SvNumberformat> pOldEntry(aOldTable.begin()->second);
585 aOldTable.erase( nKey );
586 OUString aString( pOldEntry->GetFormatstring() );
588 // Same as PutEntry() but assures key position even if format code is
589 // a duplicate. Also won't mix up any LastInsertKey.
590 ChangeIntl( eOldLanguage );
591 LanguageType eLge = eOldLanguage; // ConvertMode changes this
592 bool bCheck = false;
593 sal_Int32 nCheckPos = -1;
594 std::unique_ptr<SvNumberformat> pNewEntry(new SvNumberformat( aString, m_aCurrentLanguage.pFormatScanner.get(),
595 m_aCurrentLanguage.pStringScanner.get(), GetNatNum(), nCheckPos, eLge ));
596 if ( nCheckPos == 0 )
598 SvNumFormatType eCheckType = pNewEntry->GetType();
599 if ( eCheckType != SvNumFormatType::UNDEFINED )
601 pNewEntry->SetType( eCheckType | SvNumFormatType::DEFINED );
603 else
605 pNewEntry->SetType( SvNumFormatType::DEFINED );
608 if ( m_aFormatData.aFTable.emplace( nKey, std::move(pNewEntry) ).second )
610 bCheck = true;
613 DBG_ASSERT( bCheck, "SvNumberFormatter::ReplaceSystemCL: couldn't convert" );
615 m_aCurrentLanguage.pFormatScanner->SetConvertMode(false);
616 pStdFormat->SetLastInsertKey( sal_uInt16(nLastKey - nCLOffset), SvNumberformat::FormatterPrivateAccess() );
618 // append new system additional formats
619 css::uno::Reference< css::i18n::XNumberFormatCode > xNFC = i18n::NumberFormatMapper::create( m_xContext );
620 ImpGenerateAdditionalFormats( nCLOffset, xNFC );
623 const css::uno::Reference<css::uno::XComponentContext>& SvNumberFormatter::GetComponentContext() const
625 return m_xContext;
628 const NativeNumberWrapper& SvNumberFormatter::GetNatNum() const { return m_xNatNum.get(); }
630 bool SvNFFormatData::IsTextFormat(sal_uInt32 F_Index) const
632 const SvNumberformat* pFormat = GetFormatEntry(F_Index);
633 return pFormat && pFormat->IsTextFormat();
636 bool SvNumberFormatter::IsTextFormat(sal_uInt32 F_Index) const
638 ::osl::MutexGuard aGuard( GetInstanceMutex() );
639 return m_aFormatData.IsTextFormat(F_Index);
642 bool SvNFFormatData::PutEntry(SvNFLanguageData& rCurrentLanguage,
643 const NativeNumberWrapper& rNatNum,
644 OUString& rString,
645 sal_Int32& nCheckPos,
646 SvNumFormatType& nType,
647 sal_uInt32& nKey, // format key
648 LanguageType eLnge,
649 bool bReplaceBooleanEquivalent)
651 nKey = 0;
652 if (rString.isEmpty()) // empty string
654 nCheckPos = 1; // -> Error
655 return false;
657 eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
658 rCurrentLanguage.ChangeIntl(eLnge); // change locale if necessary
659 LanguageType eLge = eLnge; // non-const for ConvertMode
660 bool bCheck = false;
661 std::unique_ptr<SvNumberformat> p_Entry(new SvNumberformat(rString,
662 rCurrentLanguage.pFormatScanner.get(),
663 rCurrentLanguage.pStringScanner.get(),
664 rNatNum,
665 nCheckPos,
666 eLge,
667 bReplaceBooleanEquivalent));
669 if (nCheckPos == 0) // Format ok
670 { // Type comparison:
671 SvNumFormatType eCheckType = p_Entry->GetType();
672 if ( eCheckType != SvNumFormatType::UNDEFINED)
674 p_Entry->SetType(eCheckType | SvNumFormatType::DEFINED);
675 nType = eCheckType;
677 else
679 p_Entry->SetType(SvNumFormatType::DEFINED);
680 nType = SvNumFormatType::DEFINED;
683 sal_uInt32 CLOffset = ImpGenerateCL(rCurrentLanguage, rNatNum, eLge); // create new standard formats if necessary
685 nKey = ImpIsEntry(p_Entry->GetFormatstring(),CLOffset, eLge);
686 if (nKey == NUMBERFORMAT_ENTRY_NOT_FOUND) // only in not yet present
688 SvNumberformat* pStdFormat = GetFormatEntry(CLOffset + ZF_STANDARD);
689 sal_uInt32 nPos = CLOffset + pStdFormat->GetLastInsertKey( SvNumberformat::FormatterPrivateAccess() );
690 if (nPos+1 - CLOffset >= SV_COUNTRY_LANGUAGE_OFFSET)
692 SAL_WARN( "svl.numbers", "SvNumberFormatter::PutEntry: too many formats for CL");
694 else if (!aFTable.emplace( nPos+1, std::move(p_Entry)).second)
696 SAL_WARN( "svl.numbers", "SvNumberFormatter::PutEntry: dup position");
698 else
700 bCheck = true;
701 nKey = nPos+1;
702 pStdFormat->SetLastInsertKey(static_cast<sal_uInt16>(nKey-CLOffset), SvNumberformat::FormatterPrivateAccess());
706 return bCheck;
709 bool SvNumberFormatter::PutEntry(OUString& rString,
710 sal_Int32& nCheckPos,
711 SvNumFormatType& nType,
712 sal_uInt32& nKey, // format key
713 LanguageType eLnge,
714 bool bReplaceBooleanEquivalent)
716 ::osl::MutexGuard aGuard( GetInstanceMutex() );
717 return m_aFormatData.PutEntry(m_aCurrentLanguage, GetNatNum(), rString, nCheckPos, nType, nKey, eLnge, bReplaceBooleanEquivalent);
720 bool SvNumberFormatter::PutandConvertEntry(OUString& rString,
721 sal_Int32& nCheckPos,
722 SvNumFormatType& nType,
723 sal_uInt32& nKey,
724 LanguageType eLnge,
725 LanguageType eNewLnge,
726 bool bConvertDateOrder,
727 bool bReplaceBooleanEquivalent )
729 ::osl::MutexGuard aGuard( GetInstanceMutex() );
730 bool bRes;
731 if (eNewLnge == LANGUAGE_DONTKNOW)
733 eNewLnge = IniLnge;
735 m_aCurrentLanguage.pFormatScanner->SetConvertMode(eLnge, eNewLnge, false, bConvertDateOrder);
736 bRes = PutEntry(rString, nCheckPos, nType, nKey, eLnge, bReplaceBooleanEquivalent);
737 m_aCurrentLanguage.pFormatScanner->SetConvertMode(false);
739 if (bReplaceBooleanEquivalent && nCheckPos == 0 && nType == SvNumFormatType::DEFINED
740 && nKey != NUMBERFORMAT_ENTRY_NOT_FOUND)
742 // The boolean string formats are always "user defined" without any
743 // other type.
744 const SvNumberformat* pEntry = m_aFormatData.GetFormatEntry(nKey);
745 if (pEntry && pEntry->GetType() == SvNumFormatType::DEFINED)
747 // Replace boolean string format with Boolean in target locale, in
748 // case the source strings are the target locale's.
749 const OUString aSaveString = rString;
750 ChangeIntl(eNewLnge);
751 if (m_aCurrentLanguage.pFormatScanner->ReplaceBooleanEquivalent( rString))
753 const sal_Int32 nSaveCheckPos = nCheckPos;
754 const SvNumFormatType nSaveType = nType;
755 const sal_uInt32 nSaveKey = nKey;
756 const bool bTargetRes = PutEntry(rString, nCheckPos, nType, nKey, eNewLnge, false);
757 if (nCheckPos == 0 && nType == SvNumFormatType::LOGICAL && nKey != NUMBERFORMAT_ENTRY_NOT_FOUND)
759 bRes = bTargetRes;
761 else
763 SAL_WARN("svl.numbers", "SvNumberFormatter::PutandConvertEntry: can't scan boolean replacement");
764 // Live with the source boolean string format.
765 rString = aSaveString;
766 nCheckPos = nSaveCheckPos;
767 nType = nSaveType;
768 nKey = nSaveKey;
773 return bRes;
776 bool SvNumberFormatter::PutandConvertEntrySystem(OUString& rString,
777 sal_Int32& nCheckPos,
778 SvNumFormatType& nType,
779 sal_uInt32& nKey,
780 LanguageType eLnge,
781 LanguageType eNewLnge)
783 ::osl::MutexGuard aGuard( GetInstanceMutex() );
784 bool bRes;
785 if (eNewLnge == LANGUAGE_DONTKNOW)
787 eNewLnge = IniLnge;
789 m_aCurrentLanguage.pFormatScanner->SetConvertMode(eLnge, eNewLnge, true, true);
790 bRes = PutEntry(rString, nCheckPos, nType, nKey, eLnge);
791 m_aCurrentLanguage.pFormatScanner->SetConvertMode(false);
792 return bRes;
795 sal_uInt32 SvNumberFormatter::GetIndexPuttingAndConverting( OUString & rString, LanguageType eLnge,
796 LanguageType eSysLnge, SvNumFormatType & rType,
797 bool & rNewInserted, sal_Int32 & rCheckPos )
799 ::osl::MutexGuard aGuard( GetInstanceMutex() );
800 sal_uInt32 nKey = NUMBERFORMAT_ENTRY_NOT_FOUND;
801 rNewInserted = false;
802 rCheckPos = 0;
804 // #62389# empty format string (of Writer) => General standard format
805 if (rString.isEmpty())
807 // nothing
809 else if (eLnge == LANGUAGE_SYSTEM && eSysLnge != SvtSysLocale().GetLanguageTag().getLanguageType())
811 sal_uInt32 nOrig = GetEntryKey( rString, eSysLnge );
812 if (nOrig == NUMBERFORMAT_ENTRY_NOT_FOUND)
814 nKey = nOrig; // none available, maybe user-defined
816 else
818 nKey = GetFormatForLanguageIfBuiltIn( nOrig, SvtSysLocale().GetLanguageTag().getLanguageType() );
820 if (nKey == nOrig)
822 // Not a builtin format, convert.
823 // The format code string may get modified and adapted to the real
824 // language and wouldn't match eSysLnge anymore, do that on a copy.
825 OUString aTmp( rString);
826 rNewInserted = PutandConvertEntrySystem( aTmp, rCheckPos, rType,
827 nKey, eLnge, SvtSysLocale().GetLanguageTag().getLanguageType());
828 if (rCheckPos > 0)
830 SAL_WARN( "svl.numbers", "SvNumberFormatter::GetIndexPuttingAndConverting: bad format code string for current locale");
831 nKey = NUMBERFORMAT_ENTRY_NOT_FOUND;
835 else
837 nKey = GetEntryKey( rString, eLnge);
838 if (nKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
840 rNewInserted = PutEntry( rString, rCheckPos, rType, nKey, eLnge);
841 if (rCheckPos > 0)
843 SAL_WARN( "svl.numbers", "SvNumberFormatter::GetIndexPuttingAndConverting: bad format code string for specified locale");
844 nKey = NUMBERFORMAT_ENTRY_NOT_FOUND;
848 if (nKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
850 nKey = GetStandardIndex( eLnge);
852 rType = GetType( nKey);
853 // Convert any (!) old "automatic" currency format to new fixed currency
854 // default format.
855 if (rType & SvNumFormatType::CURRENCY)
857 const SvNumberformat* pFormat = GetEntry( nKey);
858 if (!pFormat->HasNewCurrency())
860 if (rNewInserted)
862 DeleteEntry( nKey); // don't leave trails of rubbish
863 rNewInserted = false;
865 nKey = GetStandardFormat( SvNumFormatType::CURRENCY, eLnge);
868 return nKey;
871 void SvNumberFormatter::DeleteEntry(sal_uInt32 nKey)
873 ::osl::MutexGuard aGuard( GetInstanceMutex() );
874 m_aFormatData.aFTable.erase(nKey);
877 void SvNumberFormatter::GetUsedLanguages( std::vector<LanguageType>& rList )
879 ::osl::MutexGuard aGuard( GetInstanceMutex() );
880 rList.clear();
882 sal_uInt32 nOffset = 0;
883 while (nOffset <= m_aFormatData.MaxCLOffset)
885 SvNumberformat* pFormat = m_aFormatData.GetFormatEntry(nOffset);
886 if (pFormat)
888 rList.push_back( pFormat->GetLanguage() );
890 nOffset += SV_COUNTRY_LANGUAGE_OFFSET;
895 void SvNumberFormatter::FillKeywordTable( NfKeywordTable& rKeywords,
896 LanguageType eLang )
898 ::osl::MutexGuard aGuard( GetInstanceMutex() );
899 ChangeIntl( eLang );
900 const NfKeywordTable & rTable = m_aCurrentLanguage.pFormatScanner->GetKeywords();
901 for ( sal_uInt16 i = 0; i < NF_KEYWORD_ENTRIES_COUNT; ++i )
903 rKeywords[i] = rTable[i];
908 void SvNumberFormatter::FillKeywordTableForExcel( NfKeywordTable& rKeywords )
910 ::osl::MutexGuard aGuard( GetInstanceMutex() );
911 FillKeywordTable( rKeywords, LANGUAGE_ENGLISH_US );
913 // Replace upper case "GENERAL" with proper case "General".
914 rKeywords[ NF_KEY_GENERAL ] = GetStandardName( LANGUAGE_ENGLISH_US );
916 // Excel or OOXML do not specify format code keywords case sensitivity,
917 // but given and writes them lower case. Using upper case even lead to an
918 // odd misrepresentation in iOS viewer and OSX Quicklook viewer that
919 // strangely use "D" and "DD" for "days since beginning of year", which is
920 // nowhere defined. See tdf#126773
921 // Use lower case for all date and time keywords where known. See OOXML
922 // ECMA-376-1:2016 18.8.31 numFmts (Number Formats)
923 rKeywords[ NF_KEY_MI ] = "m";
924 rKeywords[ NF_KEY_MMI ] = "mm";
925 rKeywords[ NF_KEY_M ] = "m";
926 rKeywords[ NF_KEY_MM ] = "mm";
927 rKeywords[ NF_KEY_MMM ] = "mmm";
928 rKeywords[ NF_KEY_MMMM ] = "mmmm";
929 rKeywords[ NF_KEY_MMMMM ] = "mmmmm";
930 rKeywords[ NF_KEY_H ] = "h";
931 rKeywords[ NF_KEY_HH ] = "hh";
932 rKeywords[ NF_KEY_S ] = "s";
933 rKeywords[ NF_KEY_SS ] = "ss";
934 /* XXX: not defined in OOXML: rKeywords[ NF_KEY_Q ] = "q"; */
935 /* XXX: not defined in OOXML: rKeywords[ NF_KEY_QQ ] = "qq"; */
936 rKeywords[ NF_KEY_D ] = "d";
937 rKeywords[ NF_KEY_DD ] = "dd";
938 rKeywords[ NF_KEY_DDD ] = "ddd";
939 rKeywords[ NF_KEY_DDDD ] = "dddd";
940 rKeywords[ NF_KEY_YY ] = "yy";
941 rKeywords[ NF_KEY_YYYY ] = "yyyy";
942 /* XXX: not defined in OOXML: rKeywords[ NF_KEY_AAA ] = "aaa"; */
943 /* XXX: not defined in OOXML: rKeywords[ NF_KEY_AAAA ] = "aaaa"; */
944 rKeywords[ NF_KEY_EC ] = "e";
945 rKeywords[ NF_KEY_EEC ] = "ee";
946 rKeywords[ NF_KEY_G ] = "g";
947 rKeywords[ NF_KEY_GG ] = "gg";
948 rKeywords[ NF_KEY_GGG ] = "ggg";
949 rKeywords[ NF_KEY_R ] = "r";
950 rKeywords[ NF_KEY_RR ] = "rr";
951 /* XXX: not defined in OOXML: rKeywords[ NF_KEY_WW ] = "ww"; */
953 // Remap codes unknown to Excel.
954 rKeywords[ NF_KEY_NN ] = "ddd";
955 rKeywords[ NF_KEY_NNN ] = "dddd";
956 // NNNN gets a separator appended in SvNumberformat::GetMappedFormatString()
957 rKeywords[ NF_KEY_NNNN ] = "dddd";
958 // Export the Thai T NatNum modifier. This must be uppercase for internal reasons.
959 rKeywords[ NF_KEY_THAI_T ] = "T";
962 static OUString lcl_buildBooleanStringFormat(const SvNumberformat* pEntry, const NativeNumberWrapper& rNatNum, const SvNFLanguageData& rCurrentLang)
964 // Build Boolean number format, which needs non-zero and zero subformat
965 // codes with TRUE and FALSE strings.
966 const Color* pColor = nullptr;
967 OUString aFormatStr, aTemp;
968 pEntry->GetOutputString( 1.0, aTemp, &pColor, rNatNum, rCurrentLang );
969 aFormatStr += "\"" + aTemp + "\";\"" + aTemp + "\";\"";
970 pEntry->GetOutputString( 0.0, aTemp, &pColor, rNatNum, rCurrentLang );
971 aFormatStr += aTemp + "\"";
972 return aFormatStr;
975 OUString SvNumberFormatter::GetFormatStringForExcel( sal_uInt32 nKey, const NfKeywordTable& rKeywords,
976 SvNumberFormatter& rTempFormatter ) const
978 ::osl::MutexGuard aGuard( GetInstanceMutex() );
979 OUString aFormatStr;
980 if (const SvNumberformat* pEntry = GetEntry( nKey))
982 if (pEntry->GetType() == SvNumFormatType::LOGICAL)
984 // Build a source locale dependent string boolean. This is
985 // expected when loading the document in the same locale or if
986 // several locales are used, but not for other system/default
987 // locales. You can't have both. We could force to English for all
988 // locales like below, but Excel would display English strings then
989 // even for the system locale matching this locale. YMMV.
990 aFormatStr = lcl_buildBooleanStringFormat(pEntry, GetNatNum(), m_aCurrentLanguage);
992 else
994 bool bIsLOK = comphelper::LibreOfficeKit::isActive();
995 bool bSystemLanguage = false;
996 LanguageType nLang = pEntry->GetLanguage();
997 if (nLang == LANGUAGE_SYSTEM)
999 bSystemLanguage = true;
1000 nLang = SvtSysLocale().GetLanguageTag().getLanguageType();
1002 if (!bIsLOK && nLang != LANGUAGE_ENGLISH_US)
1004 sal_Int32 nCheckPos;
1005 SvNumFormatType nType = SvNumFormatType::DEFINED;
1006 sal_uInt32 nTempKey;
1007 OUString aTemp( pEntry->GetFormatstring());
1008 /* TODO: do we want bReplaceBooleanEquivalent=true in any case
1009 * to write it as English string boolean? */
1010 rTempFormatter.PutandConvertEntry( aTemp, nCheckPos, nType, nTempKey, nLang, LANGUAGE_ENGLISH_US,
1011 false /*bConvertDateOrder*/, false /*bReplaceBooleanEquivalent*/);
1012 SAL_WARN_IF( nCheckPos != 0, "svl.numbers",
1013 "SvNumberFormatter::GetFormatStringForExcel - format code not convertible");
1014 if (nTempKey != NUMBERFORMAT_ENTRY_NOT_FOUND)
1015 pEntry = rTempFormatter.GetEntry( nTempKey);
1018 if (pEntry)
1020 if (pEntry->GetType() == SvNumFormatType::LOGICAL)
1022 // This would be reached if bReplaceBooleanEquivalent was
1023 // true and the source format is a string boolean like
1024 // >"VRAI";"VRAI";"FAUX"< recognized as real boolean and
1025 // properly converted. Then written as
1026 // >"TRUE";"TRUE";"FALSE"<
1027 aFormatStr = lcl_buildBooleanStringFormat(pEntry, GetNatNum(), m_aCurrentLanguage);
1029 else
1031 // m_aCurrentLanguage.GetLocaleData() returns the current locale's data, so switch
1032 // before (which doesn't do anything if it was the same locale
1033 // already).
1034 rTempFormatter.ChangeIntl( LANGUAGE_ENGLISH_US);
1035 aFormatStr = pEntry->GetMappedFormatstring( rKeywords, *rTempFormatter.m_aCurrentLanguage.GetLocaleData(), nLang,
1036 bSystemLanguage);
1041 else
1043 SAL_WARN("svl.numbers","SvNumberFormatter::GetFormatStringForExcel - format not found: " << nKey);
1046 if (aFormatStr.isEmpty())
1047 aFormatStr = "General";
1048 return aFormatStr;
1052 OUString SvNumberFormatter::GetKeyword( LanguageType eLnge, sal_uInt16 nIndex )
1054 ::osl::MutexGuard aGuard( GetInstanceMutex() );
1055 ChangeIntl(eLnge);
1056 const NfKeywordTable & rTable = m_aCurrentLanguage.pFormatScanner->GetKeywords();
1057 if ( nIndex < NF_KEYWORD_ENTRIES_COUNT )
1059 return rTable[nIndex];
1061 SAL_WARN( "svl.numbers", "GetKeyword: invalid index");
1062 return OUString();
1066 OUString SvNumberFormatter::GetStandardName( LanguageType eLnge )
1068 ::osl::MutexGuard aGuard( GetInstanceMutex() );
1069 ChangeIntl( eLnge );
1070 return m_aCurrentLanguage.pFormatScanner->GetStandardName();
1073 SvNFFormatData::SvNFFormatData()
1074 : MaxCLOffset(0)
1075 , nDefaultSystemCurrencyFormat(NUMBERFORMAT_ENTRY_NOT_FOUND)
1076 , bNoZero(false)
1080 SvNFFormatData::~SvNFFormatData() = default;
1082 sal_uInt32 SvNFFormatData::ImpGetCLOffset(LanguageType eLnge) const
1084 sal_uInt32 nOffset = 0;
1085 while (nOffset <= MaxCLOffset)
1087 const SvNumberformat* pFormat = GetFormatEntry(nOffset);
1088 if (pFormat && pFormat->GetLanguage() == eLnge)
1090 return nOffset;
1092 nOffset += SV_COUNTRY_LANGUAGE_OFFSET;
1094 return nOffset;
1097 sal_uInt32 SvNFFormatData::ImpIsEntry(std::u16string_view rString,
1098 sal_uInt32 nCLOffset,
1099 LanguageType eLnge) const
1101 sal_uInt32 res = NUMBERFORMAT_ENTRY_NOT_FOUND;
1102 auto it = aFTable.find( nCLOffset);
1103 while ( res == NUMBERFORMAT_ENTRY_NOT_FOUND &&
1104 it != aFTable.end() && it->second->GetLanguage() == eLnge )
1106 if ( rString == it->second->GetFormatstring() )
1108 res = it->first;
1110 else
1112 ++it;
1115 return res;
1118 sal_uInt32 SvNumberFormatter::ImpIsEntry(std::u16string_view rString,
1119 sal_uInt32 nCLOffset,
1120 LanguageType eLnge) const
1122 return m_aFormatData.ImpIsEntry(rString, nCLOffset, eLnge);
1125 SvNumberFormatTable& SvNumberFormatter::GetFirstEntryTable(
1126 SvNumFormatType& eType,
1127 sal_uInt32& FIndex,
1128 LanguageType& rLnge)
1130 ::osl::MutexGuard aGuard( GetInstanceMutex() );
1131 SvNumFormatType eTypetmp = eType;
1132 if (eType == SvNumFormatType::ALL) // empty cell or don't care
1134 rLnge = IniLnge;
1136 else
1138 SvNumberformat* pFormat = m_aFormatData.GetFormatEntry(FIndex);
1139 if (!pFormat)
1141 rLnge = IniLnge;
1142 eType = SvNumFormatType::ALL;
1143 eTypetmp = eType;
1145 else
1147 rLnge = pFormat->GetLanguage();
1148 eType = pFormat->GetMaskedType();
1149 if (eType == SvNumFormatType::ALL)
1151 eType = SvNumFormatType::DEFINED;
1152 eTypetmp = eType;
1154 else if (eType == SvNumFormatType::DATETIME)
1156 eTypetmp = eType;
1157 eType = SvNumFormatType::DATE;
1159 else
1161 eTypetmp = eType;
1165 ChangeIntl(rLnge);
1166 return GetEntryTable(eTypetmp, FIndex, rLnge);
1169 sal_uInt32 SvNFFormatData::ImpGenerateCL(SvNFLanguageData& rCurrentLanguage, const NativeNumberWrapper& rNatNum, LanguageType eLnge)
1171 rCurrentLanguage.ChangeIntl(eLnge);
1172 sal_uInt32 CLOffset = ImpGetCLOffset(rCurrentLanguage.ActLnge);
1173 if (CLOffset > MaxCLOffset)
1175 // new CL combination
1176 if (LocaleDataWrapper::areChecksEnabled())
1178 const LanguageTag aLoadedLocale = rCurrentLanguage.xLocaleData->getLoadedLanguageTag();
1179 if ( !aLoadedLocale.equals( rCurrentLanguage.aLanguageTag ) )
1181 LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo( u"SvNumberFormatter::ImpGenerateCL: locales don't match:" ));
1183 // test XML locale data FormatElement entries
1185 uno::Sequence< i18n::FormatElement > xSeq = rCurrentLanguage.xLocaleData->getAllFormats();
1186 // A test for completeness of formatindex="0" ...
1187 // formatindex="47" is not needed here since it is done in
1188 // ImpGenerateFormats().
1190 // Test for dupes of formatindex="..."
1191 for ( sal_Int32 j = 0; j < xSeq.getLength(); j++ )
1193 sal_Int16 nIdx = xSeq[j].formatIndex;
1194 OUStringBuffer aDupes;
1195 for ( sal_Int32 i = 0; i < xSeq.getLength(); i++ )
1197 if ( i != j && xSeq[i].formatIndex == nIdx )
1199 aDupes.append( OUString::number(i) + "(" + xSeq[i].formatKey + ") ");
1202 if ( !aDupes.isEmpty() )
1204 OUString aMsg = "XML locale data FormatElement formatindex dupe: "
1205 + OUString::number(nIdx)
1206 + "\nFormatElements: "
1207 + OUString::number( j )
1208 + "("
1209 + xSeq[j].formatKey
1210 + ") "
1211 + aDupes;
1212 LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo( aMsg ));
1218 MaxCLOffset += SV_COUNTRY_LANGUAGE_OFFSET;
1219 ImpGenerateFormats(rCurrentLanguage, rNatNum, MaxCLOffset, false/*bNoAdditionalFormats*/ );
1220 CLOffset = MaxCLOffset;
1222 return CLOffset;
1225 SvNumberFormatTable& SvNumberFormatter::ChangeCL(SvNumFormatType eType,
1226 sal_uInt32& FIndex,
1227 LanguageType eLnge)
1229 ::osl::MutexGuard aGuard( GetInstanceMutex() );
1230 m_aFormatData.ImpGenerateCL(m_aCurrentLanguage, GetNatNum(), eLnge);
1231 return GetEntryTable(eType, FIndex, m_aCurrentLanguage.ActLnge);
1234 SvNumberFormatTable& SvNumberFormatter::GetEntryTable(
1235 SvNumFormatType eType,
1236 sal_uInt32& FIndex,
1237 LanguageType eLnge)
1239 ::osl::MutexGuard aGuard( GetInstanceMutex() );
1240 if ( pFormatTable )
1242 pFormatTable->clear();
1244 else
1246 pFormatTable.reset( new SvNumberFormatTable );
1248 ChangeIntl(eLnge);
1249 sal_uInt32 CLOffset = m_aFormatData.ImpGetCLOffset(m_aCurrentLanguage.ActLnge);
1251 // Might generate and insert a default format for the given type
1252 // (e.g. currency) => has to be done before collecting formats.
1253 sal_uInt32 nDefaultIndex = GetStandardFormat( eType, m_aCurrentLanguage.ActLnge );
1255 auto it = m_aFormatData.aFTable.find( CLOffset);
1257 if (eType == SvNumFormatType::ALL)
1259 while (it != m_aFormatData.aFTable.end() && it->second->GetLanguage() == m_aCurrentLanguage.ActLnge)
1260 { // copy all entries to output table
1261 (*pFormatTable)[ it->first ] = it->second.get();
1262 ++it;
1265 else
1267 while (it != m_aFormatData.aFTable.end() && it->second->GetLanguage() == m_aCurrentLanguage.ActLnge)
1268 { // copy entries of queried type to output table
1269 if ((it->second->GetType()) & eType)
1270 (*pFormatTable)[ it->first ] = it->second.get();
1271 ++it;
1274 if ( !pFormatTable->empty() )
1275 { // select default if queried format doesn't exist or queried type or
1276 // language differ from existing format
1277 SvNumberformat* pEntry = m_aFormatData.GetFormatEntry(FIndex);
1278 if ( !pEntry || !(pEntry->GetType() & eType) || pEntry->GetLanguage() != m_aCurrentLanguage.ActLnge )
1280 FIndex = nDefaultIndex;
1283 return *pFormatTable;
1286 namespace {
1288 const SvNumberformat* ImpSubstituteEntry(SvNFLanguageData& rCurrentLanguage, const SvNFFormatData& rFormatData,
1289 const NativeNumberWrapper& rNatNum, const SvNFEngine::Accessor& rFuncs,
1290 const SvNumberformat* pFormat, sal_uInt32* o_pRealKey)
1292 if (!pFormat || !pFormat->IsSubstituted())
1293 return pFormat;
1295 // XXX NOTE: substitution can not be done in GetFormatEntry() as otherwise
1296 // to be substituted formats would "vanish", i.e. from the number formatter
1297 // dialog or when exporting to Excel.
1299 sal_uInt32 nKey;
1300 if (pFormat->IsSystemTimeFormat())
1302 /* TODO: should we have NF_TIME_SYSTEM for consistency? */
1303 nKey = SvNFEngine::GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs,
1304 SvNumFormatType::TIME, LANGUAGE_SYSTEM);
1306 else if (pFormat->IsSystemLongDateFormat())
1307 /* TODO: either that above, or have a long option for GetStandardFormat() */
1309 nKey = SvNFEngine::GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum,
1310 NF_DATE_SYSTEM_LONG, LANGUAGE_SYSTEM);
1312 else
1313 return pFormat;
1315 if (o_pRealKey)
1316 *o_pRealKey = nKey;
1317 return rFormatData.GetFormatEntry(nKey);
1322 bool SvNFEngine::IsNumberFormat(SvNFLanguageData& rCurrentLanguage,
1323 const SvNFFormatData& rFormatData,
1324 const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
1325 const OUString& sString,
1326 sal_uInt32& F_Index,
1327 double& fOutNumber,
1328 SvNumInputOptions eInputOptions)
1330 SvNumFormatType FType;
1331 // For the 0 General format directly use the init/system locale and avoid
1332 // all overhead that is associated with a format passed to the scanner.
1333 const SvNumberformat* pFormat = (F_Index == 0 ? nullptr :
1334 ::ImpSubstituteEntry(rCurrentLanguage, rFormatData, rNatNum, rFuncs, rFormatData.GetFormatEntry(F_Index), nullptr));
1335 if (!pFormat)
1337 rCurrentLanguage.ChangeIntl(rCurrentLanguage.IniLnge);
1338 FType = SvNumFormatType::NUMBER;
1340 else
1342 FType = pFormat->GetMaskedType();
1343 if (FType == SvNumFormatType::ALL)
1345 FType = SvNumFormatType::DEFINED;
1347 rCurrentLanguage.ChangeIntl(pFormat->GetLanguage());
1348 // Avoid scanner overhead with the General format of any locale.
1349 // These are never substituted above so safe to ignore.
1350 if ((F_Index % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
1352 assert(FType == SvNumFormatType::NUMBER);
1353 pFormat = nullptr;
1357 bool res;
1358 SvNumFormatType RType = FType;
1359 if (RType == SvNumFormatType::TEXT)
1361 res = false; // type text preset => no conversion to number
1363 else
1365 res = rCurrentLanguage.pStringScanner->IsNumberFormat(sString, RType, fOutNumber, pFormat, rNatNum, eInputOptions);
1367 if (res && !SvNumberFormatter::IsCompatible(FType, RType)) // non-matching type
1369 switch ( RType )
1371 case SvNumFormatType::DATE :
1372 // Preserve ISO 8601 input.
1373 if (rCurrentLanguage.pStringScanner->CanForceToIso8601( DateOrder::Invalid))
1375 F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATE_DIN_YYYYMMDD, rCurrentLanguage.ActLnge );
1377 else
1379 F_Index = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, RType, rCurrentLanguage.ActLnge);
1381 break;
1382 case SvNumFormatType::TIME :
1383 if ( rCurrentLanguage.pStringScanner->GetDecPos() )
1385 // 100th seconds
1386 if ( rCurrentLanguage.pStringScanner->GetNumericsCount() > 3 || fOutNumber < 0.0 )
1388 F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_TIME_HH_MMSS00, rCurrentLanguage.ActLnge);
1390 else
1392 F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_TIME_MMSS00, rCurrentLanguage.ActLnge);
1395 else if ( fOutNumber >= 1.0 || fOutNumber < 0.0 )
1397 F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_TIME_HH_MMSS, rCurrentLanguage.ActLnge);
1399 else
1401 F_Index = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, RType, rCurrentLanguage.ActLnge );
1403 break;
1404 case SvNumFormatType::DATETIME :
1405 // Preserve ISO 8601 input.
1406 if (rCurrentLanguage.pStringScanner->HasIso8601Tsep())
1408 if (rCurrentLanguage.pStringScanner->GetDecPos())
1409 F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDDTHHMMSS000, rCurrentLanguage.ActLnge);
1410 else
1411 F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDDTHHMMSS, rCurrentLanguage.ActLnge);
1413 else if (rCurrentLanguage.pStringScanner->CanForceToIso8601( DateOrder::Invalid))
1415 if (rCurrentLanguage.pStringScanner->GetDecPos())
1416 F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDD_HHMMSS000, rCurrentLanguage.ActLnge);
1417 else
1418 F_Index = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDD_HHMMSS, rCurrentLanguage.ActLnge);
1420 else
1422 F_Index = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, RType, rCurrentLanguage.ActLnge);
1424 break;
1425 default:
1426 F_Index = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, RType, rCurrentLanguage.ActLnge);
1429 return res;
1432 bool SvNumberFormatter::IsNumberFormat(const OUString& sString,
1433 sal_uInt32& F_Index,
1434 double& fOutNumber,
1435 SvNumInputOptions eInputOptions)
1437 ::osl::MutexGuard aGuard( GetInstanceMutex() );
1439 return SvNFEngine::IsNumberFormat(m_aCurrentLanguage, m_aFormatData, GetNatNum(),
1440 m_aRWPolicy, sString, F_Index, fOutNumber,
1441 eInputOptions);
1444 LanguageType SvNumberFormatter::GetLanguage() const
1446 return IniLnge; // immutable
1449 // static
1450 bool SvNumberFormatter::IsCompatible(SvNumFormatType eOldType, SvNumFormatType eNewType)
1452 if (eOldType == eNewType)
1454 return true;
1456 else if (eOldType == SvNumFormatType::DEFINED)
1458 return true;
1460 else
1462 switch (eNewType)
1464 case SvNumFormatType::NUMBER:
1465 switch (eOldType)
1467 case SvNumFormatType::PERCENT:
1468 case SvNumFormatType::CURRENCY:
1469 case SvNumFormatType::SCIENTIFIC:
1470 case SvNumFormatType::FRACTION:
1471 case SvNumFormatType::DEFINED:
1472 return true;
1473 case SvNumFormatType::LOGICAL:
1474 default:
1475 return false;
1477 break;
1478 case SvNumFormatType::DATE:
1479 switch (eOldType)
1481 case SvNumFormatType::DATETIME:
1482 return true;
1483 default:
1484 return false;
1486 break;
1487 case SvNumFormatType::TIME:
1488 switch (eOldType)
1490 case SvNumFormatType::DATETIME:
1491 return true;
1492 default:
1493 return false;
1495 break;
1496 case SvNumFormatType::DATETIME:
1497 switch (eOldType)
1499 case SvNumFormatType::TIME:
1500 case SvNumFormatType::DATE:
1501 return true;
1502 default:
1503 return false;
1505 break;
1506 case SvNumFormatType::DURATION:
1507 return false;
1508 default:
1509 return false;
1514 static sal_uInt32 ImpGetSearchOffset(SvNumFormatType nType, sal_uInt32 CLOffset)
1516 sal_uInt32 nSearch;
1517 switch( nType )
1519 case SvNumFormatType::DATE:
1520 nSearch = CLOffset + ZF_STANDARD_DATE;
1521 break;
1522 case SvNumFormatType::TIME:
1523 nSearch = CLOffset + ZF_STANDARD_TIME;
1524 break;
1525 case SvNumFormatType::DATETIME:
1526 nSearch = CLOffset + ZF_STANDARD_DATETIME;
1527 break;
1528 case SvNumFormatType::DURATION:
1529 nSearch = CLOffset + ZF_STANDARD_DURATION;
1530 break;
1531 case SvNumFormatType::PERCENT:
1532 nSearch = CLOffset + ZF_STANDARD_PERCENT;
1533 break;
1534 case SvNumFormatType::SCIENTIFIC:
1535 nSearch = CLOffset + ZF_STANDARD_SCIENTIFIC;
1536 break;
1537 default:
1538 nSearch = CLOffset + ZF_STANDARD;
1540 return nSearch;
1543 // static
1544 sal_uInt32 SvNFEngine::ImpGetDefaultFormat(const SvNFFormatData& rFormatData,
1545 const SvNFEngine::Accessor& rFuncs,
1546 SvNumFormatType nType, sal_uInt32 CLOffset)
1548 sal_uInt32 nSearch = ImpGetSearchOffset(nType, CLOffset);
1550 sal_uInt32 nDefaultFormat = rFuncs.mFindFormat(nSearch);
1551 if (nDefaultFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
1552 return nDefaultFormat;
1554 nDefaultFormat = ImpGetDefaultFormat(rFormatData, nType, CLOffset);
1555 rFuncs.mCacheFormat(nSearch, nDefaultFormat);
1556 return nDefaultFormat;
1559 namespace {
1561 sal_uInt32 ImpGetFormatIndex(NfIndexTableOffset nTabOff, sal_uInt32 nCLOffset)
1563 if (nTabOff >= NF_INDEX_TABLE_ENTRIES)
1564 return NUMBERFORMAT_ENTRY_NOT_FOUND;
1566 if (indexTable[nTabOff] == NUMBERFORMAT_ENTRY_NOT_FOUND)
1567 return NUMBERFORMAT_ENTRY_NOT_FOUND;
1569 return nCLOffset + indexTable[nTabOff];
1572 bool ImpIsSpecialStandardFormat(sal_uInt32 nFIndex, sal_uInt32 nCLOffset)
1574 return
1575 nFIndex == ImpGetFormatIndex( NF_TIME_MMSS00, nCLOffset ) ||
1576 nFIndex == ImpGetFormatIndex( NF_TIME_HH_MMSS00, nCLOffset ) ||
1577 nFIndex == ImpGetFormatIndex( NF_TIME_HH_MMSS, nCLOffset )
1581 bool ImpIsSpecialStandardFormat(SvNFLanguageData& rCurrentLanguage,
1582 const NativeNumberWrapper& rNatNum, const SvNFEngine::Accessor& rFuncs,
1583 sal_uInt32 nFIndex, LanguageType eLnge)
1585 eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
1586 sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)
1587 return ::ImpIsSpecialStandardFormat(nFIndex, nCLOffset);
1591 sal_uInt32 SvNFEngine::ImpGetStandardFormat(SvNFLanguageData& rCurrentLanguage,
1592 const SvNFFormatData& rFormatData,
1593 const NativeNumberWrapper& rNatNum,
1594 const SvNFEngine::Accessor& rFuncs,
1595 SvNumFormatType eType,
1596 sal_uInt32 CLOffset,
1597 LanguageType eLnge)
1599 switch(eType)
1601 case SvNumFormatType::CURRENCY:
1602 return rFuncs.mGetDefaultCurrency(rCurrentLanguage, rNatNum, CLOffset, eLnge);
1603 case SvNumFormatType::DURATION :
1604 return ImpGetFormatIndex(NF_TIME_HH_MMSS, CLOffset);
1605 case SvNumFormatType::DATE:
1606 case SvNumFormatType::TIME:
1607 case SvNumFormatType::DATETIME:
1608 case SvNumFormatType::PERCENT:
1609 case SvNumFormatType::SCIENTIFIC:
1610 return ImpGetDefaultFormat(rFormatData, rFuncs, eType, CLOffset);
1611 case SvNumFormatType::FRACTION:
1612 return CLOffset + ZF_STANDARD_FRACTION;
1613 case SvNumFormatType::LOGICAL:
1614 return CLOffset + ZF_STANDARD_LOGICAL;
1615 case SvNumFormatType::TEXT:
1616 return CLOffset + ZF_STANDARD_TEXT;
1617 case SvNumFormatType::ALL:
1618 case SvNumFormatType::DEFINED:
1619 case SvNumFormatType::NUMBER:
1620 case SvNumFormatType::UNDEFINED:
1621 default:
1622 return CLOffset + ZF_STANDARD;
1626 sal_uInt32 SvNFEngine::GetStandardFormat(SvNFLanguageData& rCurrentLanguage,
1627 const SvNFFormatData& rFormatData,
1628 const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
1629 SvNumFormatType eType,
1630 LanguageType eLnge)
1632 eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
1634 sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)
1636 return ImpGetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, eType, nCLOffset, eLnge);
1639 sal_uInt32 SvNumberFormatter::GetStandardFormat( SvNumFormatType eType, LanguageType eLnge )
1641 ::osl::MutexGuard aGuard(GetInstanceMutex());
1642 return SvNFEngine::GetStandardFormat(m_aCurrentLanguage, m_aFormatData, GetNatNum(), m_aRWPolicy, eType, eLnge);
1645 bool SvNumberFormatter::ImpIsSpecialStandardFormat(sal_uInt32 nFIndex, LanguageType eLnge)
1647 ::osl::MutexGuard aGuard(GetInstanceMutex());
1648 return ::ImpIsSpecialStandardFormat(m_aCurrentLanguage, GetNatNum(), m_aRWPolicy, nFIndex, eLnge);
1651 sal_uInt32 SvNFEngine::GetStandardFormat(SvNFLanguageData& rCurrentLanguage,
1652 const SvNFFormatData& rFormatData,
1653 const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
1654 sal_uInt32 nFIndex, SvNumFormatType eType, LanguageType eLnge)
1656 eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
1658 sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)
1660 if (::ImpIsSpecialStandardFormat(nFIndex, nCLOffset))
1661 return nFIndex;
1663 return ImpGetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, eType, nCLOffset, eLnge);
1666 namespace
1668 sal_uInt32 FindCachedDefaultFormat(const SvNFFormatData::DefaultFormatKeysMap& rDefaultFormatKeys, sal_uInt32 nSearch)
1670 auto it = rDefaultFormatKeys.find(nSearch);
1671 sal_uInt32 nDefaultFormat = (it != rDefaultFormatKeys.end() ?
1672 it->second : NUMBERFORMAT_ENTRY_NOT_FOUND);
1673 return nDefaultFormat;
1677 sal_uInt32 SvNFFormatData::FindCachedDefaultFormat(sal_uInt32 nSearch) const
1679 return ::FindCachedDefaultFormat(aDefaultFormatKeys, nSearch);
1682 // static
1683 sal_uInt32 SvNFEngine::ImpGetDefaultFormat(const SvNFFormatData& rFormatData, SvNumFormatType nType, sal_uInt32 CLOffset)
1685 sal_uInt32 nDefaultFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
1687 // look for a defined standard
1688 sal_uInt32 nStopKey = CLOffset + SV_COUNTRY_LANGUAGE_OFFSET;
1689 sal_uInt32 nKey(0);
1690 auto it2 = rFormatData.aFTable.find( CLOffset );
1691 while ( it2 != rFormatData.aFTable.end() && (nKey = it2->first ) >= CLOffset && nKey < nStopKey )
1693 const SvNumberformat* pEntry = it2->second.get();
1694 if ( pEntry->IsStandard() && (pEntry->GetMaskedType() == nType) )
1696 nDefaultFormat = nKey;
1697 break; // while
1699 ++it2;
1702 if ( nDefaultFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
1703 { // none found, use old fixed standards
1704 switch( nType )
1706 case SvNumFormatType::DATE:
1707 nDefaultFormat = CLOffset + ZF_STANDARD_DATE;
1708 break;
1709 case SvNumFormatType::TIME:
1710 nDefaultFormat = CLOffset + ZF_STANDARD_TIME+1;
1711 break;
1712 case SvNumFormatType::DATETIME:
1713 nDefaultFormat = CLOffset + ZF_STANDARD_DATETIME;
1714 break;
1715 case SvNumFormatType::DURATION:
1716 nDefaultFormat = CLOffset + ZF_STANDARD_DURATION;
1717 break;
1718 case SvNumFormatType::PERCENT:
1719 nDefaultFormat = CLOffset + ZF_STANDARD_PERCENT+1;
1720 break;
1721 case SvNumFormatType::SCIENTIFIC:
1722 nDefaultFormat = CLOffset + ZF_STANDARD_SCIENTIFIC;
1723 break;
1724 default:
1725 nDefaultFormat = CLOffset + ZF_STANDARD;
1729 return nDefaultFormat;
1732 sal_uInt32 SvNumberFormatter::GetStandardFormat( sal_uInt32 nFIndex, SvNumFormatType eType,
1733 LanguageType eLnge )
1735 ::osl::MutexGuard aGuard(GetInstanceMutex());
1737 return SvNFEngine::GetStandardFormat(m_aCurrentLanguage, m_aFormatData, GetNatNum(),
1738 m_aRWPolicy, nFIndex, eType, eLnge);
1741 sal_uInt32 SvNFEngine::GetTimeFormat(SvNFLanguageData& rCurrentLanguage,
1742 const SvNFFormatData& rFormatData,
1743 const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
1744 double fNumber, LanguageType eLnge, bool bForceDuration)
1746 bool bSign;
1747 if ( fNumber < 0.0 )
1749 bSign = true;
1750 fNumber = -fNumber;
1752 else
1753 bSign = false;
1754 double fSeconds = fNumber * 86400;
1755 if ( floor( fSeconds + 0.5 ) * 100 != floor( fSeconds * 100 + 0.5 ) )
1756 { // with 100th seconds
1757 if ( bForceDuration || bSign || fSeconds >= 3600 )
1758 return GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_TIME_HH_MMSS00, eLnge);
1759 else
1760 return GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_TIME_MMSS00, eLnge);
1762 else
1764 if ( bForceDuration || bSign || fNumber >= 1.0 )
1765 return GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_TIME_HH_MMSS, eLnge);
1766 else
1767 return GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, SvNumFormatType::TIME, eLnge);
1771 sal_uInt32 SvNumberFormatter::GetTimeFormat( double fNumber, LanguageType eLnge, bool bForceDuration )
1773 ::osl::MutexGuard aGuard( GetInstanceMutex() );
1775 return SvNFEngine::GetTimeFormat(m_aCurrentLanguage, m_aFormatData, GetNatNum(), m_aRWPolicy,
1776 fNumber, eLnge, bForceDuration);
1779 sal_uInt32 SvNFEngine::GetStandardFormat(SvNFLanguageData& rCurrentLanguage,
1780 const SvNFFormatData& rFormatData,
1781 const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
1782 double fNumber, sal_uInt32 nFIndex,
1783 SvNumFormatType eType, LanguageType eLnge)
1785 if (::ImpIsSpecialStandardFormat(rCurrentLanguage, rNatNum, rFuncs, nFIndex, eLnge))
1786 return nFIndex;
1788 switch( eType )
1790 case SvNumFormatType::DURATION :
1791 return GetTimeFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, fNumber, eLnge, true);
1792 case SvNumFormatType::TIME :
1793 return GetTimeFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, fNumber, eLnge, false);
1794 default:
1795 return GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, eType, eLnge );
1799 sal_uInt32 SvNumberFormatter::GetStandardFormat( double fNumber, sal_uInt32 nFIndex,
1800 SvNumFormatType eType, LanguageType eLnge )
1802 ::osl::MutexGuard aGuard( GetInstanceMutex() );
1803 if ( ImpIsSpecialStandardFormat( nFIndex, eLnge ) )
1804 return nFIndex;
1806 switch( eType )
1808 case SvNumFormatType::DURATION :
1809 return GetTimeFormat( fNumber, eLnge, true);
1810 case SvNumFormatType::TIME :
1811 return GetTimeFormat( fNumber, eLnge, false);
1812 default:
1813 return GetStandardFormat( eType, eLnge );
1817 sal_uInt32 SvNumberFormatter::GuessDateTimeFormat( SvNumFormatType& rType, double fNumber, LanguageType eLnge )
1819 ::osl::MutexGuard aGuard( GetInstanceMutex() );
1820 // Categorize the format according to the implementation of
1821 // SvNumberFormatter::GetEditFormat(), making assumptions about what
1822 // would be time only.
1823 sal_uInt32 nRet;
1824 if (0.0 <= fNumber && fNumber < 1.0)
1826 // Clearly a time.
1827 rType = SvNumFormatType::TIME;
1828 nRet = GetTimeFormat( fNumber, eLnge, false);
1830 else if (fabs( fNumber) * 24 < 0x7fff)
1832 // Assuming duration within 32k hours or 3.7 years.
1833 // This should be SvNumFormatType::DURATION instead, but the outer
1834 // world can't cope with that.
1835 rType = SvNumFormatType::TIME;
1836 nRet = GetTimeFormat( fNumber, eLnge, true);
1838 else if (rtl::math::approxFloor( fNumber) != fNumber)
1840 // Date+Time.
1841 rType = SvNumFormatType::DATETIME;
1842 nRet = GetFormatIndex( NF_DATETIME_SYS_DDMMYYYY_HHMMSS, eLnge);
1844 else
1846 // Date only.
1847 rType = SvNumFormatType::DATE;
1848 nRet = GetFormatIndex( NF_DATE_SYS_DDMMYYYY, eLnge);
1850 return nRet;
1853 sal_uInt32 SvNFEngine::GetEditFormat(SvNFLanguageData& rCurrentLanguage,
1854 const SvNFFormatData& rFormatData,
1855 const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
1856 double fNumber, sal_uInt32 nFIndex,
1857 SvNumFormatType eType,
1858 const SvNumberformat* pFormat,
1859 LanguageType eForLocale)
1861 const LanguageType eLang = (pFormat ? pFormat->GetLanguage() : LANGUAGE_SYSTEM);
1862 if (eForLocale == LANGUAGE_DONTKNOW)
1863 eForLocale = eLang;
1864 sal_uInt32 nKey = nFIndex;
1865 switch ( eType )
1867 // #61619# always edit using 4-digit year
1868 case SvNumFormatType::DATE :
1870 // Preserve ISO 8601 format.
1871 bool bIsoDate =
1872 nFIndex == GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATE_DIN_YYYYMMDD, eLang) ||
1873 nFIndex == GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATE_DIN_YYMMDD, eLang) ||
1874 nFIndex == GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATE_DIN_MMDD, eLang) ||
1875 (pFormat && pFormat->IsIso8601( 0 ));
1876 if (rtl::math::approxFloor( fNumber) != fNumber)
1878 // fdo#34977 preserve time when editing even if only date was
1879 // displayed.
1880 if (bIsoDate)
1881 nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDD_HHMMSS, eForLocale);
1882 else
1883 nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_SYS_DDMMYYYY_HHMMSS, eForLocale);
1885 else
1887 if (bIsoDate)
1888 nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATE_ISO_YYYYMMDD, eForLocale);
1889 else
1890 nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATE_SYS_DDMMYYYY, eForLocale);
1893 break;
1894 case SvNumFormatType::TIME :
1895 if (fNumber < 0.0 || fNumber >= 1.0)
1897 /* XXX NOTE: this is a purely arbitrary value within the limits
1898 * of a signed 16-bit. 32k hours are 3.7 years ... or
1899 * 1903-09-26 if date. */
1900 if (fabs( fNumber) * 24 < 0x7fff)
1901 nKey = GetTimeFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, fNumber, eForLocale, true);
1902 // Preserve duration, use [HH]:MM:SS instead of time.
1903 else
1904 nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_SYS_DDMMYYYY_HHMMSS, eForLocale);
1905 // Assume that a large value is a datetime with only time
1906 // displayed.
1908 else
1909 nKey = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, fNumber, nFIndex, eType, eForLocale);
1910 break;
1911 case SvNumFormatType::DURATION :
1912 nKey = GetTimeFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, fNumber, eForLocale, true);
1913 break;
1914 case SvNumFormatType::DATETIME :
1915 if (nFIndex == GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDDTHHMMSS, eLang))
1916 nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDDTHHMMSS, eForLocale );
1917 else if (nFIndex == GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDDTHHMMSS000, eLang))
1918 nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDDTHHMMSS000, eForLocale );
1919 else if (nFIndex == GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDD_HHMMSS000, eLang))
1920 nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDD_HHMMSS000, eForLocale );
1921 else if (nFIndex == GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDD_HHMMSS, eLang) || (pFormat && pFormat->IsIso8601( 0 )))
1922 nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_ISO_YYYYMMDD_HHMMSS, eForLocale );
1923 else
1924 nKey = GetFormatIndex(rCurrentLanguage, rFuncs, rNatNum, NF_DATETIME_SYS_DDMMYYYY_HHMMSS, eForLocale );
1925 break;
1926 case SvNumFormatType::NUMBER:
1927 nKey = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, eType, eForLocale);
1928 break;
1929 default:
1930 nKey = GetStandardFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs, fNumber, nFIndex, eType, eForLocale );
1932 return nKey;
1935 sal_uInt32 SvNumberFormatter::GetEditFormat( double fNumber, sal_uInt32 nFIndex,
1936 SvNumFormatType eType,
1937 SvNumberformat const * pFormat,
1938 LanguageType eForLocale )
1940 ::osl::MutexGuard aGuard( GetInstanceMutex() );
1941 return SvNFEngine::GetEditFormat(m_aCurrentLanguage, m_aFormatData, GetNatNum(),
1942 m_aRWPolicy, fNumber, nFIndex,
1943 eType, pFormat, eForLocale);
1946 OUString SvNFEngine::GetInputLineString(SvNFLanguageData& rCurrentLanguage,
1947 const SvNFFormatData& rFormatData,
1948 const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
1949 const double& fOutNumber,
1950 sal_uInt32 nFIndex,
1951 bool bFiltering, bool bForceSystemLocale)
1953 OUString sOutString;
1954 const Color* pColor;
1955 sal_uInt32 nRealKey = nFIndex;
1957 const SvNumberformat* pFormat = ImpSubstituteEntry(rCurrentLanguage, rFormatData,
1958 rNatNum, rFuncs,
1959 rFormatData.GetFormatEntry(nFIndex), &nRealKey);
1960 if (!pFormat)
1961 pFormat = rFormatData.GetFormatEntry(ZF_STANDARD);
1963 LanguageType eLang = pFormat->GetLanguage();
1964 rCurrentLanguage.ChangeIntl(eLang);
1966 SvNumFormatType eType = pFormat->GetMaskedType();
1967 if (eType == SvNumFormatType::ALL)
1969 // Mixed types in subformats, use first.
1970 /* XXX we could choose a subformat according to fOutNumber and
1971 * subformat conditions, but they may exist to suppress 0 or negative
1972 * numbers so wouldn't be a safe bet. */
1973 eType = pFormat->GetNumForInfoScannedType(0);
1975 const SvNumFormatType eTypeOrig = eType;
1977 sal_uInt16 nOldPrec = rCurrentLanguage.pFormatScanner->GetStandardPrec();
1978 bool bPrecChanged = false;
1979 if (eType == SvNumFormatType::NUMBER ||
1980 eType == SvNumFormatType::PERCENT ||
1981 eType == SvNumFormatType::CURRENCY ||
1982 eType == SvNumFormatType::SCIENTIFIC ||
1983 eType == SvNumFormatType::FRACTION)
1985 if (eType != SvNumFormatType::PERCENT) // special treatment of % later
1987 eType = SvNumFormatType::NUMBER;
1989 rCurrentLanguage.ChangeStandardPrec(SvNumberFormatter::INPUTSTRING_PRECISION);
1990 bPrecChanged = true;
1993 // if bFiltering true keep the nRealKey format
1994 if (!bFiltering)
1996 sal_uInt32 nKey = GetEditFormat(rCurrentLanguage, rFormatData, rNatNum, rFuncs,
1997 fOutNumber, nRealKey, eType, pFormat,
1998 bForceSystemLocale ? LANGUAGE_SYSTEM : LANGUAGE_DONTKNOW);
1999 if (nKey != nRealKey)
2001 pFormat = rFormatData.GetFormatEntry( nKey );
2004 assert(pFormat);
2005 if (pFormat)
2007 if ( eType == SvNumFormatType::TIME && pFormat->GetFormatPrecision() )
2009 rCurrentLanguage.ChangeStandardPrec(SvNumberFormatter::INPUTSTRING_PRECISION);
2010 bPrecChanged = true;
2012 pFormat->GetOutputString(fOutNumber, sOutString, &pColor, rNatNum, rCurrentLanguage);
2014 // The #FMT error string must not be used for input as it would lead to
2015 // data loss. This can happen for at least date(+time). Fall back to a
2016 // last resort of plain number in the locale the formatter was
2017 // constructed with.
2018 if (eTypeOrig != SvNumFormatType::NUMBER && sOutString == ImpSvNumberformatScan::sErrStr)
2020 pFormat = rFormatData.GetFormatEntry(ZF_STANDARD);
2021 assert(pFormat);
2022 if (pFormat)
2024 rCurrentLanguage.ChangeStandardPrec(SvNumberFormatter::INPUTSTRING_PRECISION);
2025 bPrecChanged = true;
2026 pFormat->GetOutputString(fOutNumber, sOutString, &pColor, rNatNum, rCurrentLanguage);
2029 assert(sOutString != ImpSvNumberformatScan::sErrStr);
2031 if (bPrecChanged)
2033 rCurrentLanguage.ChangeStandardPrec(nOldPrec);
2035 return sOutString;
2038 OUString SvNumberFormatter::GetInputLineString(const double& fOutNumber,
2039 sal_uInt32 nFIndex,
2040 bool bFiltering, bool bForceSystemLocale)
2042 ::osl::MutexGuard aGuard( GetInstanceMutex() );
2043 return SvNFEngine::GetInputLineString(m_aCurrentLanguage, m_aFormatData, GetNatNum(),
2044 m_aRWPolicy, fOutNumber, nFIndex,
2045 bFiltering, bForceSystemLocale);
2048 void SvNFEngine::GetOutputString(SvNFLanguageData& rCurrentLanguage,
2049 const SvNFFormatData& rFormatData,
2050 const OUString& sString, sal_uInt32 nFIndex, OUString& sOutString,
2051 const Color** ppColor, bool bUseStarFormat)
2053 const SvNumberformat* pFormat = rFormatData.GetFormatEntry( nFIndex );
2054 // ImpSubstituteEntry() is unnecessary here because so far only numeric
2055 // (time and date) are substituted.
2056 if (!pFormat)
2058 pFormat = rFormatData.GetFormatEntry(ZF_STANDARD_TEXT);
2060 if (!pFormat->IsTextFormat() && !pFormat->HasTextFormat())
2062 *ppColor = nullptr;
2063 sOutString = sString;
2065 else
2067 rCurrentLanguage.ChangeIntl(pFormat->GetLanguage());
2068 pFormat->GetOutputString(sString, sOutString, ppColor, bUseStarFormat);
2072 void SvNumberFormatter::GetOutputString(const OUString& sString,
2073 sal_uInt32 nFIndex,
2074 OUString& sOutString,
2075 const Color** ppColor,
2076 bool bUseStarFormat )
2078 ::osl::MutexGuard aGuard( GetInstanceMutex() );
2079 SvNFEngine::GetOutputString(m_aCurrentLanguage, m_aFormatData,
2080 sString, nFIndex, sOutString,
2081 ppColor, bUseStarFormat);
2084 void SvNFEngine::GetOutputString(SvNFLanguageData& rCurrentLanguage,
2085 const SvNFFormatData& rFormatData,
2086 const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
2087 const double& fOutNumber, sal_uInt32 nFIndex,
2088 OUString& sOutString, const Color** ppColor,
2089 bool bUseStarFormat)
2091 if (rFormatData.GetNoZero() && fOutNumber == 0.0)
2093 sOutString.clear();
2094 return;
2096 const SvNumberformat* pFormat = ImpSubstituteEntry(rCurrentLanguage, rFormatData,
2097 rNatNum, rFuncs,
2098 rFormatData.GetFormatEntry(nFIndex), nullptr);
2099 if (!pFormat)
2100 pFormat = rFormatData.GetFormatEntry(ZF_STANDARD);
2101 rCurrentLanguage.ChangeIntl(pFormat->GetLanguage());
2102 pFormat->GetOutputString(fOutNumber, sOutString, ppColor, rNatNum, rCurrentLanguage, bUseStarFormat);
2105 void SvNumberFormatter::GetOutputString(const double& fOutNumber,
2106 sal_uInt32 nFIndex,
2107 OUString& sOutString,
2108 const Color** ppColor,
2109 bool bUseStarFormat )
2111 ::osl::MutexGuard aGuard( GetInstanceMutex() );
2112 SvNFEngine::GetOutputString(m_aCurrentLanguage, m_aFormatData, GetNatNum(),
2113 m_aRWPolicy, fOutNumber, nFIndex, sOutString,
2114 ppColor, bUseStarFormat);
2117 bool SvNFEngine::GetPreviewString(SvNFLanguageData& rCurrentLanguage,
2118 const SvNFFormatData& rFormatData,
2119 const NativeNumberWrapper& rNatNum,
2120 const Accessor& rFuncs,
2121 const OUString& sFormatString,
2122 double fPreviewNumber,
2123 OUString& sOutString,
2124 const Color** ppColor,
2125 LanguageType eLnge,
2126 bool bUseStarFormat)
2128 if (sFormatString.isEmpty()) // no empty string
2129 return false;
2130 eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
2131 rCurrentLanguage.ChangeIntl(eLnge); // change locale if necessary
2132 eLnge = rCurrentLanguage.ActLnge;
2133 sal_Int32 nCheckPos = -1;
2134 OUString sTmpString = sFormatString;
2135 SvNumberformat aEntry(sTmpString,
2136 rCurrentLanguage.pFormatScanner.get(),
2137 rCurrentLanguage.pStringScanner.get(),
2138 rNatNum,
2139 nCheckPos,
2140 eLnge);
2141 if (nCheckPos == 0) // String ok
2143 sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)
2144 sal_uInt32 nKey = rFormatData.ImpIsEntry(aEntry.GetFormatstring(), nCLOffset, eLnge);
2145 if (nKey != NUMBERFORMAT_ENTRY_NOT_FOUND) // already present
2147 GetOutputString(rCurrentLanguage, rFormatData, rNatNum, rFuncs, fPreviewNumber, nKey, sOutString, ppColor, bUseStarFormat);
2149 else
2151 aEntry.GetOutputString(fPreviewNumber, sOutString, ppColor, rNatNum, rCurrentLanguage, bUseStarFormat);
2153 return true;
2155 else
2157 return false;
2161 bool SvNumberFormatter::GetPreviewString(const OUString& sFormatString,
2162 double fPreviewNumber,
2163 OUString& sOutString,
2164 const Color** ppColor,
2165 LanguageType eLnge,
2166 bool bUseStarFormat )
2168 ::osl::MutexGuard aGuard( GetInstanceMutex() );
2169 return SvNFEngine::GetPreviewString(m_aCurrentLanguage, m_aFormatData,
2170 GetNatNum(), m_aRWPolicy,
2171 sFormatString, fPreviewNumber,
2172 sOutString, ppColor, eLnge,
2173 bUseStarFormat);
2176 bool SvNFEngine::GetPreviewStringGuess(SvNFLanguageData& rCurrentLanguage,
2177 const SvNFFormatData& rFormatData,
2178 const NativeNumberWrapper& rNatNum,
2179 const Accessor& rFuncs,
2180 const OUString& sFormatString,
2181 double fPreviewNumber,
2182 OUString& sOutString,
2183 const Color** ppColor,
2184 LanguageType eLnge)
2186 if (sFormatString.isEmpty()) // no empty string
2187 return false;
2188 eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
2189 rCurrentLanguage.ChangeIntl(eLnge);
2190 eLnge = rCurrentLanguage.ActLnge;
2191 bool bEnglish = (eLnge == LANGUAGE_ENGLISH_US);
2193 OUString aFormatStringUpper( rCurrentLanguage.xCharClass->uppercase( sFormatString ) );
2194 sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)
2195 sal_uInt32 nKey = rFormatData.ImpIsEntry(aFormatStringUpper, nCLOffset, eLnge);
2196 if ( nKey != NUMBERFORMAT_ENTRY_NOT_FOUND )
2198 // Target format present
2199 GetOutputString(rCurrentLanguage, rFormatData, rNatNum, rFuncs,
2200 fPreviewNumber, nKey, sOutString, ppColor, false);
2201 return true;
2204 std::optional<SvNumberformat> pEntry;
2205 sal_Int32 nCheckPos = -1;
2206 OUString sTmpString;
2208 if ( bEnglish )
2210 sTmpString = sFormatString;
2211 pEntry.emplace( sTmpString, rCurrentLanguage.pFormatScanner.get(),
2212 rCurrentLanguage.pStringScanner.get(), rNatNum, nCheckPos, eLnge );
2214 else
2216 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, LANGUAGE_ENGLISH_US);
2217 nKey = rFormatData.ImpIsEntry(aFormatStringUpper, nCLOffset, LANGUAGE_ENGLISH_US);
2218 bool bEnglishFormat = (nKey != NUMBERFORMAT_ENTRY_NOT_FOUND);
2220 // Try English -> other or convert english to other
2221 LanguageType eFormatLang = LANGUAGE_ENGLISH_US;
2222 rCurrentLanguage.pFormatScanner->SetConvertMode( LANGUAGE_ENGLISH_US, eLnge, false, false);
2223 sTmpString = sFormatString;
2224 pEntry.emplace( sTmpString, rCurrentLanguage.pFormatScanner.get(),
2225 rCurrentLanguage.pStringScanner.get(), rNatNum, nCheckPos, eFormatLang );
2226 rCurrentLanguage.pFormatScanner->SetConvertMode( false );
2227 rCurrentLanguage.ChangeIntl(eLnge);
2229 if ( !bEnglishFormat )
2231 if ( nCheckPos != 0 || rCurrentLanguage.xTransliteration->isEqual( sFormatString,
2232 pEntry->GetFormatstring() ) )
2234 // other Format
2235 // Force locale's keywords.
2236 rCurrentLanguage.pFormatScanner->ChangeIntl( ImpSvNumberformatScan::KeywordLocalization::LocaleLegacy );
2237 sTmpString = sFormatString;
2238 pEntry.emplace( sTmpString, rCurrentLanguage.pFormatScanner.get(),
2239 rCurrentLanguage.pStringScanner.get(), rNatNum, nCheckPos, eLnge );
2241 else
2243 // verify english
2244 sal_Int32 nCheckPos2 = -1;
2245 // try other --> english
2246 eFormatLang = eLnge;
2247 rCurrentLanguage.pFormatScanner->SetConvertMode( eLnge, LANGUAGE_ENGLISH_US, false, false);
2248 sTmpString = sFormatString;
2249 SvNumberformat aEntry2( sTmpString, rCurrentLanguage.pFormatScanner.get(),
2250 rCurrentLanguage.pStringScanner.get(), rNatNum, nCheckPos2, eFormatLang );
2251 rCurrentLanguage.pFormatScanner->SetConvertMode( false );
2252 rCurrentLanguage.ChangeIntl(eLnge);
2253 if ( nCheckPos2 == 0 && !rCurrentLanguage.xTransliteration->isEqual( sFormatString,
2254 aEntry2.GetFormatstring() ) )
2256 // other Format
2257 // Force locale's keywords.
2258 rCurrentLanguage.pFormatScanner->ChangeIntl( ImpSvNumberformatScan::KeywordLocalization::LocaleLegacy );
2259 sTmpString = sFormatString;
2260 pEntry.emplace( sTmpString, rCurrentLanguage.pFormatScanner.get(),
2261 rCurrentLanguage.pStringScanner.get(), rNatNum, nCheckPos, eLnge );
2267 if (nCheckPos == 0) // String ok
2269 rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary
2270 pEntry->GetOutputString( fPreviewNumber, sOutString, ppColor, rNatNum, rCurrentLanguage );
2271 return true;
2273 return false;
2276 bool SvNumberFormatter::GetPreviewStringGuess( const OUString& sFormatString,
2277 double fPreviewNumber,
2278 OUString& sOutString,
2279 const Color** ppColor,
2280 LanguageType eLnge )
2282 ::osl::MutexGuard aGuard( GetInstanceMutex() );
2283 return SvNFEngine::GetPreviewStringGuess(m_aCurrentLanguage, m_aFormatData,
2284 GetNatNum(), m_aRWPolicy,
2285 sFormatString, fPreviewNumber,
2286 sOutString, ppColor, eLnge);
2289 bool SvNFEngine::GetPreviewString(SvNFLanguageData& rCurrentLanguage,
2290 const SvNFFormatData& rFormatData,
2291 const NativeNumberWrapper& rNatNum,
2292 const Accessor& rFuncs,
2293 const OUString& sFormatString,
2294 const OUString& sPreviewString,
2295 OUString& sOutString,
2296 const Color** ppColor,
2297 LanguageType eLnge)
2299 if (sFormatString.isEmpty()) // no empty string
2300 return false;
2301 eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
2302 rCurrentLanguage.ChangeIntl(eLnge); // switch if needed
2303 eLnge = rCurrentLanguage.ActLnge;
2304 sal_Int32 nCheckPos = -1;
2305 OUString sTmpString = sFormatString;
2306 SvNumberformat aEntry( sTmpString,
2307 rCurrentLanguage.pFormatScanner.get(),
2308 rCurrentLanguage.pStringScanner.get(),
2309 rNatNum,
2310 nCheckPos,
2311 eLnge);
2312 if (nCheckPos == 0) // String ok
2314 // May have to create standard formats for this locale.
2315 sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge);
2316 sal_uInt32 nKey = rFormatData.ImpIsEntry( aEntry.GetFormatstring(), nCLOffset, eLnge);
2317 if (nKey != NUMBERFORMAT_ENTRY_NOT_FOUND) // already present
2319 GetOutputString(rCurrentLanguage, rFormatData,
2320 sPreviewString, nKey, sOutString, ppColor, false);
2322 else
2324 // If the format is valid but not a text format and does not
2325 // include a text subformat, an empty string would result. Same as
2326 // in SvNumberFormatter::GetOutputString()
2327 if (aEntry.IsTextFormat() || aEntry.HasTextFormat())
2329 aEntry.GetOutputString( sPreviewString, sOutString, ppColor);
2331 else
2333 *ppColor = nullptr;
2334 sOutString = sPreviewString;
2337 return true;
2339 else
2341 return false;
2345 bool SvNumberFormatter::GetPreviewString( const OUString& sFormatString,
2346 const OUString& sPreviewString,
2347 OUString& sOutString,
2348 const Color** ppColor,
2349 LanguageType eLnge )
2351 ::osl::MutexGuard aGuard( GetInstanceMutex() );
2352 return SvNFEngine::GetPreviewString(m_aCurrentLanguage, m_aFormatData,
2353 GetNatNum(), m_aRWPolicy,
2354 sFormatString, sPreviewString,
2355 sOutString, ppColor, eLnge);
2358 sal_uInt32 SvNumberFormatter::TestNewString(const OUString& sFormatString,
2359 LanguageType eLnge)
2361 ::osl::MutexGuard aGuard( GetInstanceMutex() );
2362 if (sFormatString.isEmpty()) // no empty string
2364 return NUMBERFORMAT_ENTRY_NOT_FOUND;
2366 if (eLnge == LANGUAGE_DONTKNOW)
2368 eLnge = IniLnge;
2370 ChangeIntl(eLnge); // change locale if necessary
2371 eLnge = m_aCurrentLanguage.ActLnge;
2372 sal_uInt32 nRes;
2373 sal_Int32 nCheckPos = -1;
2374 OUString sTmpString = sFormatString;
2375 SvNumberformat aEntry(sTmpString,
2376 m_aCurrentLanguage.pFormatScanner.get(),
2377 m_aCurrentLanguage.pStringScanner.get(),
2378 GetNatNum(),
2379 nCheckPos,
2380 eLnge);
2381 if (nCheckPos == 0) // String ok
2383 sal_uInt32 CLOffset = m_aFormatData.ImpGenerateCL(m_aCurrentLanguage, GetNatNum(), eLnge); // create new standard formats if necessary
2384 nRes = ImpIsEntry(aEntry.GetFormatstring(),CLOffset, eLnge);
2385 // already present?
2387 else
2389 nRes = NUMBERFORMAT_ENTRY_NOT_FOUND;
2391 return nRes;
2394 SvNumberformat* SvNFFormatData::ImpInsertFormat(SvNFLanguageData& rCurrentLanguage,
2395 const NativeNumberWrapper& rNatNum,
2396 const css::i18n::NumberFormatCode& rCode,
2397 sal_uInt32 nPos, bool bAfterChangingSystemCL,
2398 sal_Int16 nOrgIndex)
2400 SAL_WARN_IF( NF_INDEX_TABLE_RESERVED_START <= rCode.Index && rCode.Index < NF_INDEX_TABLE_ENTRIES,
2401 "svl.numbers", "i18npool locale '" << rCurrentLanguage.aLanguageTag.getBcp47() <<
2402 "' uses reserved formatIndex value " << rCode.Index << ", next free: " << NF_INDEX_TABLE_ENTRIES <<
2403 " Please see description in include/svl/zforlist.hxx at end of enum NfIndexTableOffset");
2404 assert( (rCode.Index < NF_INDEX_TABLE_RESERVED_START || NF_INDEX_TABLE_ENTRIES <= rCode.Index) &&
2405 "reserved formatIndex, see warning above");
2407 OUString aCodeStr( rCode.Code );
2408 if ( rCode.Index < NF_INDEX_TABLE_RESERVED_START &&
2409 rCode.Usage == css::i18n::KNumberFormatUsage::CURRENCY &&
2410 rCode.Index != NF_CURRENCY_1000DEC2_CCC )
2411 { // strip surrounding [$...] on automatic currency
2412 if ( aCodeStr.indexOf( "[$" ) >= 0)
2413 aCodeStr = SvNumberformat::StripNewCurrencyDelimiters( aCodeStr );
2414 else
2416 if (LocaleDataWrapper::areChecksEnabled() &&
2417 rCode.Index != NF_CURRENCY_1000DEC2_CCC )
2419 OUString aMsg = "SvNumberFormatter::ImpInsertFormat: no [$...] on currency format code, index " +
2420 OUString::number( rCode.Index) +
2421 ":\n" +
2422 rCode.Code;
2423 LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo( aMsg));
2427 sal_Int32 nCheckPos = 0;
2428 std::unique_ptr<SvNumberformat> pFormat(new SvNumberformat(aCodeStr,
2429 rCurrentLanguage.pFormatScanner.get(),
2430 rCurrentLanguage.pStringScanner.get(),
2431 rNatNum,
2432 nCheckPos,
2433 rCurrentLanguage.ActLnge));
2434 if (nCheckPos != 0)
2436 if (LocaleDataWrapper::areChecksEnabled())
2438 OUString aMsg = "SvNumberFormatter::ImpInsertFormat: bad format code, index " +
2439 OUString::number( rCode.Index ) +
2440 "\n" +
2441 rCode.Code;
2442 LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo( aMsg));
2444 return nullptr;
2446 if ( rCode.Index >= NF_INDEX_TABLE_RESERVED_START )
2448 sal_uInt32 nCLOffset = nPos - (nPos % SV_COUNTRY_LANGUAGE_OFFSET);
2449 sal_uInt32 nKey = ImpIsEntry( aCodeStr, nCLOffset, rCurrentLanguage.ActLnge );
2450 if ( nKey != NUMBERFORMAT_ENTRY_NOT_FOUND )
2452 // If bAfterChangingSystemCL there will definitely be some dups,
2453 // don't cry then.
2454 if (LocaleDataWrapper::areChecksEnabled() && !bAfterChangingSystemCL)
2456 // Test for duplicate indexes in locale data.
2457 switch ( nOrgIndex )
2459 // These may be dups of integer versions for locales where
2460 // currencies have no decimals like Italian Lira.
2461 case NF_CURRENCY_1000DEC2 : // NF_CURRENCY_1000INT
2462 case NF_CURRENCY_1000DEC2_RED : // NF_CURRENCY_1000INT_RED
2463 case NF_CURRENCY_1000DEC2_DASHED : // NF_CURRENCY_1000INT_RED
2464 break;
2465 default:
2467 OUString aMsg = "SvNumberFormatter::ImpInsertFormat: dup format code, index "
2468 + OUString::number( rCode.Index )
2469 + "\n"
2470 + rCode.Code;
2471 LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo( aMsg));
2475 return nullptr;
2477 else if ( nPos - nCLOffset >= SV_COUNTRY_LANGUAGE_OFFSET )
2479 if (LocaleDataWrapper::areChecksEnabled())
2481 OUString aMsg = "SvNumberFormatter::ImpInsertFormat: too many format codes, index "
2482 + OUString::number( rCode.Index )
2483 + "\n"
2484 + rCode.Code;
2485 LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo( aMsg));
2487 return nullptr;
2490 auto pFormat2 = pFormat.get();
2491 if ( !aFTable.emplace( nPos, std::move(pFormat) ).second )
2493 if (LocaleDataWrapper::areChecksEnabled())
2495 OUString aMsg = "ImpInsertFormat: can't insert number format key pos: "
2496 + OUString::number( nPos )
2497 + ", code index "
2498 + OUString::number( rCode.Index )
2499 + "\n"
2500 + rCode.Code;
2501 LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo( aMsg));
2503 else
2505 SAL_WARN( "svl.numbers", "SvNumberFormatter::ImpInsertFormat: dup position");
2507 return nullptr;
2509 if ( rCode.Default )
2510 pFormat2->SetStandard();
2511 if ( !rCode.DefaultName.isEmpty() )
2512 pFormat2->SetComment( rCode.DefaultName );
2513 return pFormat2;
2516 void SvNumberFormatter::GetFormatSpecialInfo(sal_uInt32 nFormat,
2517 bool& bThousand,
2518 bool& IsRed,
2519 sal_uInt16& nPrecision,
2520 sal_uInt16& nLeadingCnt)
2523 ::osl::MutexGuard aGuard( GetInstanceMutex() );
2524 SvNumberformat* pFormat = m_aFormatData.GetFormatEntry( nFormat );
2525 if (pFormat)
2526 pFormat->GetFormatSpecialInfo(bThousand, IsRed,
2527 nPrecision, nLeadingCnt);
2528 else
2530 bThousand = false;
2531 IsRed = false;
2532 nPrecision = m_aCurrentLanguage.pFormatScanner->GetStandardPrec();
2533 nLeadingCnt = 0;
2537 sal_uInt16 SvNFEngine::GetFormatPrecision(SvNFLanguageData& rCurrentLanguage,
2538 const SvNFFormatData& rFormatData,
2539 sal_uInt32 nFormat)
2541 const SvNumberformat* pFormat = rFormatData.GetFormatEntry(nFormat);
2542 if (pFormat)
2543 return pFormat->GetFormatPrecision();
2544 return rCurrentLanguage.pFormatScanner->GetStandardPrec();
2547 sal_uInt16 SvNumberFormatter::GetFormatPrecision( sal_uInt32 nFormat )
2549 ::osl::MutexGuard aGuard( GetInstanceMutex() );
2550 return SvNFEngine::GetFormatPrecision(m_aCurrentLanguage, m_aFormatData, nFormat);
2553 sal_uInt16 SvNumberFormatter::GetFormatIntegerDigits( sal_uInt32 nFormat ) const
2555 ::osl::MutexGuard aGuard( GetInstanceMutex() );
2556 const SvNumberformat* pFormat = m_aFormatData.GetFormatEntry( nFormat );
2557 if ( pFormat )
2558 return pFormat->GetFormatIntegerDigits();
2559 else
2560 return 1;
2563 OUString SvNFEngine::GetFormatDecimalSep(SvNFLanguageData& rCurrentLanguage,
2564 const SvNFFormatData& rFormatData,
2565 sal_uInt32 nFormat)
2567 const SvNumberformat* pFormat = rFormatData.GetFormatEntry(nFormat);
2568 if (!pFormat)
2569 return rCurrentLanguage.GetNumDecimalSep();
2570 return rCurrentLanguage.GetLangDecimalSep(pFormat->GetLanguage());
2573 OUString SvNumberFormatter::GetFormatDecimalSep( sal_uInt32 nFormat )
2575 ::osl::MutexGuard aGuard( GetInstanceMutex() );
2576 return SvNFEngine::GetFormatDecimalSep(m_aCurrentLanguage,
2577 m_aFormatData,
2578 nFormat);
2581 bool SvNumberFormatter::IsNatNum12( sal_uInt32 nFIndex ) const
2583 ::osl::MutexGuard aGuard( GetInstanceMutex() );
2584 const SvNumberformat* pFormat = m_aFormatData.GetFormatEntry( nFIndex );
2586 return pFormat && pFormat->GetNatNumModifierString().startsWith( "[NatNum12" );
2589 sal_uInt32 SvNumberFormatter::GetFormatSpecialInfo( const OUString& rFormatString,
2590 bool& bThousand, bool& IsRed, sal_uInt16& nPrecision,
2591 sal_uInt16& nLeadingCnt, LanguageType eLnge )
2594 ::osl::MutexGuard aGuard( GetInstanceMutex() );
2595 if (eLnge == LANGUAGE_DONTKNOW)
2597 eLnge = IniLnge;
2599 ChangeIntl(eLnge); // change locale if necessary
2600 eLnge = m_aCurrentLanguage.ActLnge;
2601 OUString aTmpStr( rFormatString );
2602 sal_Int32 nCheckPos = 0;
2603 SvNumberformat aFormat( aTmpStr, m_aCurrentLanguage.pFormatScanner.get(),
2604 m_aCurrentLanguage.pStringScanner.get(), GetNatNum(), nCheckPos, eLnge );
2605 if ( nCheckPos == 0 )
2607 aFormat.GetFormatSpecialInfo( bThousand, IsRed, nPrecision, nLeadingCnt );
2609 else
2611 bThousand = false;
2612 IsRed = false;
2613 nPrecision = m_aCurrentLanguage.pFormatScanner->GetStandardPrec();
2614 nLeadingCnt = 0;
2616 return nCheckPos;
2619 OUString SvNFFormatData::GetCalcCellReturn(sal_uInt32 nFormat) const
2621 const SvNumberformat* pFormat = GetFormatEntry(nFormat);
2622 if (!pFormat)
2623 return u"G"_ustr;
2625 OUString aStr;
2626 bool bAppendPrec = true;
2627 sal_uInt16 nPrec, nLeading;
2628 bool bThousand, bIsRed;
2629 pFormat->GetFormatSpecialInfo( bThousand, bIsRed, nPrec, nLeading );
2631 switch (pFormat->GetMaskedType())
2633 case SvNumFormatType::NUMBER:
2634 if (bThousand)
2635 aStr = ",";
2636 else
2637 aStr = "F";
2638 break;
2639 case SvNumFormatType::CURRENCY:
2640 aStr = "C";
2641 break;
2642 case SvNumFormatType::SCIENTIFIC:
2643 aStr = "S";
2644 break;
2645 case SvNumFormatType::PERCENT:
2646 aStr = "P";
2647 break;
2648 default:
2650 bAppendPrec = false;
2651 switch (SvNumberFormatter::GetIndexTableOffset(nFormat))
2653 case NF_DATE_SYSTEM_SHORT:
2654 case NF_DATE_SYS_DMMMYY:
2655 case NF_DATE_SYS_DDMMYY:
2656 case NF_DATE_SYS_DDMMYYYY:
2657 case NF_DATE_SYS_DMMMYYYY:
2658 case NF_DATE_DIN_DMMMYYYY:
2659 case NF_DATE_SYS_DMMMMYYYY:
2660 case NF_DATE_DIN_DMMMMYYYY: aStr = "D1"; break;
2661 case NF_DATE_SYS_DDMMM: aStr = "D2"; break;
2662 case NF_DATE_SYS_MMYY: aStr = "D3"; break;
2663 case NF_DATETIME_SYSTEM_SHORT_HHMM:
2664 case NF_DATETIME_SYS_DDMMYYYY_HHMM:
2665 case NF_DATETIME_SYS_DDMMYYYY_HHMMSS:
2666 aStr = "D4"; break;
2667 case NF_DATE_DIN_MMDD: aStr = "D5"; break;
2668 case NF_TIME_HHMMSSAMPM: aStr = "D6"; break;
2669 case NF_TIME_HHMMAMPM: aStr = "D7"; break;
2670 case NF_TIME_HHMMSS: aStr = "D8"; break;
2671 case NF_TIME_HHMM: aStr = "D9"; break;
2672 default: aStr = "G";
2677 if (bAppendPrec)
2678 aStr += OUString::number(nPrec);
2680 if (pFormat->GetColor( 1 ))
2681 aStr += "-"; // negative color
2683 /* FIXME: this probably should not match on literal strings and only be
2684 * performed on number or currency formats, but it is what Calc originally
2685 * implemented. */
2686 if (pFormat->GetFormatstring().indexOf('(') != -1)
2687 aStr += "()";
2689 return aStr;
2692 OUString SvNumberFormatter::GetCalcCellReturn( sal_uInt32 nFormat ) const
2694 ::osl::MutexGuard aGuard( GetInstanceMutex() );
2695 return m_aFormatData.GetCalcCellReturn(nFormat);
2698 // Return the index in a sequence of format codes matching an enum of
2699 // NfIndexTableOffset. If not found 0 is returned. If the sequence doesn't
2700 // contain any format code elements a default element is created and inserted.
2701 sal_Int32 SvNFFormatData::ImpGetFormatCodeIndex(const SvNFLanguageData& rCurrentLanguage,
2702 css::uno::Sequence<css::i18n::NumberFormatCode>& rSeq,
2703 const NfIndexTableOffset nTabOff)
2705 auto pSeq = std::find_if(std::cbegin(rSeq), std::cend(rSeq),
2706 [nTabOff](const css::i18n::NumberFormatCode& rCode) { return rCode.Index == nTabOff; });
2707 if (pSeq != std::cend(rSeq))
2708 return static_cast<sal_Int32>(std::distance(std::cbegin(rSeq), pSeq));
2709 if (LocaleDataWrapper::areChecksEnabled() && (nTabOff < NF_CURRENCY_START
2710 || NF_CURRENCY_END < nTabOff || nTabOff == NF_CURRENCY_1000INT
2711 || nTabOff == NF_CURRENCY_1000INT_RED
2712 || nTabOff == NF_CURRENCY_1000DEC2_CCC))
2713 { // currency entries with decimals might not exist, e.g. Italian Lira
2714 OUString aMsg = "SvNumberFormatter::ImpGetFormatCodeIndex: not found: "
2715 + OUString::number( nTabOff );
2716 LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->appendLocaleInfo(aMsg));
2718 if ( rSeq.hasElements() )
2720 // look for a preset default
2721 pSeq = std::find_if(std::cbegin(rSeq), std::cend(rSeq),
2722 [](const css::i18n::NumberFormatCode& rCode) { return rCode.Default; });
2723 if (pSeq != std::cend(rSeq))
2724 return static_cast<sal_Int32>(std::distance(std::cbegin(rSeq), pSeq));
2725 // currencies are special, not all format codes must exist, but all
2726 // builtin number format key index positions must have a format assigned
2727 if ( NF_CURRENCY_START <= nTabOff && nTabOff <= NF_CURRENCY_END )
2729 // look for a format with decimals
2730 pSeq = std::find_if(std::cbegin(rSeq), std::cend(rSeq),
2731 [](const css::i18n::NumberFormatCode& rCode) { return rCode.Index == NF_CURRENCY_1000DEC2; });
2732 if (pSeq != std::cend(rSeq))
2733 return static_cast<sal_Int32>(std::distance(std::cbegin(rSeq), pSeq));
2734 // last resort: look for a format without decimals
2735 pSeq = std::find_if(std::cbegin(rSeq), std::cend(rSeq),
2736 [](const css::i18n::NumberFormatCode& rCode) { return rCode.Index == NF_CURRENCY_1000INT; });
2737 if (pSeq != std::cend(rSeq))
2738 return static_cast<sal_Int32>(std::distance(std::cbegin(rSeq), pSeq));
2741 else
2742 { // we need at least _some_ format
2743 rSeq = { css::i18n::NumberFormatCode() };
2744 rSeq.getArray()[0].Code = "0" + rCurrentLanguage.GetNumDecimalSep() + "############";
2746 return 0;
2749 // Adjust a sequence of format codes to contain only one (THE) default
2750 // instead of multiple defaults for short/medium/long types.
2751 // If there is no medium but a short and a long default the long is taken.
2752 // Non-PRODUCT version may check locale data for matching defaults in one
2753 // FormatElement group.
2754 void SvNFFormatData::ImpAdjustFormatCodeDefault(const SvNFLanguageData& rCurrentLanguage,
2755 css::i18n::NumberFormatCode * pFormatArr,
2756 sal_Int32 nCnt)
2758 if ( !nCnt )
2759 return;
2760 if (LocaleDataWrapper::areChecksEnabled())
2762 // check the locale data for correctness
2763 OUStringBuffer aMsg;
2764 sal_Int32 nElem, nShort, nMedium, nLong, nShortDef, nMediumDef, nLongDef;
2765 nShort = nMedium = nLong = nShortDef = nMediumDef = nLongDef = -1;
2766 for ( nElem = 0; nElem < nCnt; nElem++ )
2768 switch ( pFormatArr[nElem].Type )
2770 case i18n::KNumberFormatType::SHORT :
2771 nShort = nElem;
2772 break;
2773 case i18n::KNumberFormatType::MEDIUM :
2774 nMedium = nElem;
2775 break;
2776 case i18n::KNumberFormatType::LONG :
2777 nLong = nElem;
2778 break;
2779 default:
2780 aMsg.append("unknown type");
2782 if ( pFormatArr[nElem].Default )
2784 switch ( pFormatArr[nElem].Type )
2786 case i18n::KNumberFormatType::SHORT :
2787 if ( nShortDef != -1 )
2788 aMsg.append("dupe short type default");
2789 nShortDef = nElem;
2790 break;
2791 case i18n::KNumberFormatType::MEDIUM :
2792 if ( nMediumDef != -1 )
2793 aMsg.append("dupe medium type default");
2794 nMediumDef = nElem;
2795 break;
2796 case i18n::KNumberFormatType::LONG :
2797 if ( nLongDef != -1 )
2798 aMsg.append("dupe long type default");
2799 nLongDef = nElem;
2800 break;
2803 if (!aMsg.isEmpty())
2805 aMsg.insert(0, "SvNumberFormatter::ImpAdjustFormatCodeDefault: ");
2806 aMsg.append("\nXML locale data FormatElement formatindex: "
2807 + OUString::number(static_cast<sal_Int32>(pFormatArr[nElem].Index)));
2808 LocaleDataWrapper::outputCheckMessage(rCurrentLanguage.xLocaleData->appendLocaleInfo(aMsg));
2809 aMsg.setLength(0);
2812 if ( nShort != -1 && nShortDef == -1 )
2813 aMsg.append("no short type default ");
2814 if ( nMedium != -1 && nMediumDef == -1 )
2815 aMsg.append("no medium type default ");
2816 if ( nLong != -1 && nLongDef == -1 )
2817 aMsg.append("no long type default ");
2818 if (!aMsg.isEmpty())
2820 aMsg.insert(0, "SvNumberFormatter::ImpAdjustFormatCodeDefault: ");
2821 aMsg.append("\nXML locale data FormatElement group of: ");
2822 LocaleDataWrapper::outputCheckMessage(
2823 rCurrentLanguage.xLocaleData->appendLocaleInfo(Concat2View(aMsg + pFormatArr[0].NameID)));
2824 aMsg.setLength(0);
2827 // find the default (medium preferred, then long) and reset all other defaults
2828 sal_Int32 nElem, nDef, nMedium;
2829 nDef = nMedium = -1;
2830 for ( nElem = 0; nElem < nCnt; nElem++ )
2832 if ( pFormatArr[nElem].Default )
2834 switch ( pFormatArr[nElem].Type )
2836 case i18n::KNumberFormatType::MEDIUM :
2837 nDef = nMedium = nElem;
2838 break;
2839 case i18n::KNumberFormatType::LONG :
2840 if ( nMedium == -1 )
2841 nDef = nElem;
2842 [[fallthrough]];
2843 default:
2844 if ( nDef == -1 )
2845 nDef = nElem;
2846 pFormatArr[nElem].Default = false;
2850 if ( nDef == -1 )
2851 nDef = 0;
2852 pFormatArr[nDef].Default = true;
2855 SvNumberformat* SvNFFormatData::GetEntry(sal_uInt32 nKey) const
2857 auto it = aFTable.find( nKey);
2858 if (it != aFTable.end())
2859 return it->second.get();
2860 return nullptr;
2863 SvNumberformat* SvNFFormatData::GetFormatEntry(sal_uInt32 nKey)
2865 return GetEntry(nKey);
2868 const SvNumberformat* SvNFFormatData::GetFormatEntry(sal_uInt32 nKey) const
2870 return GetEntry(nKey);
2873 SvNumFormatType SvNFFormatData::GetType(sal_uInt32 nFIndex) const
2875 SvNumFormatType eType;
2876 const SvNumberformat* pFormat = GetFormatEntry(nFIndex);
2877 if (!pFormat)
2878 eType = SvNumFormatType::UNDEFINED;
2879 else
2881 eType = pFormat->GetMaskedType();
2882 if (eType == SvNumFormatType::ALL)
2883 eType = SvNumFormatType::DEFINED;
2885 return eType;
2888 const SvNumberformat* SvNumberFormatter::GetEntry( sal_uInt32 nKey ) const
2890 ::osl::MutexGuard aGuard(GetInstanceMutex());
2891 return m_aFormatData.GetFormatEntry(nKey);
2894 const SvNumberformat* SvNumberFormatter::GetSubstitutedEntry( sal_uInt32 nKey, sal_uInt32 & o_rNewKey )
2896 ::osl::MutexGuard aGuard( GetInstanceMutex() );
2897 // A tad ugly, but GetStandardFormat() and GetFormatIndex() in
2898 // ImpSubstituteEntry() may have to add the LANGUAGE_SYSTEM formats if not
2899 // already present (which in practice most times they are).
2900 return ImpSubstituteEntry(m_aFormatData.GetFormatEntry(nKey), &o_rNewKey);
2903 const SvNumberformat* SvNumberFormatter::ImpSubstituteEntry( const SvNumberformat* pFormat, sal_uInt32 * o_pRealKey )
2905 return ::ImpSubstituteEntry(m_aCurrentLanguage, m_aFormatData, GetNatNum(), m_aRWPolicy, pFormat, o_pRealKey);
2908 void SvNFFormatData::ImpGenerateFormats(SvNFLanguageData& rCurrentLanguage, const NativeNumberWrapper& rNatNum, sal_uInt32 CLOffset, bool bNoAdditionalFormats )
2910 bool bOldConvertMode = rCurrentLanguage.pFormatScanner->GetConvertMode();
2911 if (bOldConvertMode)
2913 rCurrentLanguage.pFormatScanner->SetConvertMode(false); // switch off for this function
2916 css::lang::Locale aLocale = rCurrentLanguage.GetLanguageTag().getLocale();
2917 css::uno::Reference< css::i18n::XNumberFormatCode > xNFC = i18n::NumberFormatMapper::create(rCurrentLanguage.GetComponentContext());
2918 sal_Int32 nIdx;
2920 // Number
2921 uno::Sequence< i18n::NumberFormatCode > aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::FIXED_NUMBER, aLocale );
2922 ImpAdjustFormatCodeDefault(rCurrentLanguage, aFormatSeq.getArray(), aFormatSeq.getLength() );
2924 // General
2925 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_NUMBER_STANDARD );
2926 SvNumberformat* pStdFormat = ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
2927 CLOffset + ZF_STANDARD /* NF_NUMBER_STANDARD */ );
2928 if (pStdFormat)
2930 // This is _the_ standard format.
2931 if (LocaleDataWrapper::areChecksEnabled() && pStdFormat->GetType() != SvNumFormatType::NUMBER)
2933 LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->
2934 appendLocaleInfo( u"SvNumberFormatter::ImpGenerateFormats: General format not NUMBER"));
2936 pStdFormat->SetType( SvNumFormatType::NUMBER );
2937 pStdFormat->SetStandard();
2938 pStdFormat->SetLastInsertKey( SV_MAX_COUNT_STANDARD_FORMATS, SvNumberformat::FormatterPrivateAccess() );
2940 else
2942 if (LocaleDataWrapper::areChecksEnabled())
2944 LocaleDataWrapper::outputCheckMessage( rCurrentLanguage.xLocaleData->
2945 appendLocaleInfo( u"SvNumberFormatter::ImpGenerateFormats: General format not insertable, nothing will work"));
2950 // Boolean
2951 OUString aFormatCode = rCurrentLanguage.pFormatScanner->GetBooleanString();
2952 sal_Int32 nCheckPos = 0;
2954 std::unique_ptr<SvNumberformat> pNewFormat(new SvNumberformat( aFormatCode, rCurrentLanguage.pFormatScanner.get(),
2955 rCurrentLanguage.pStringScanner.get(), rNatNum, nCheckPos, rCurrentLanguage.ActLnge ));
2956 pNewFormat->SetType(SvNumFormatType::LOGICAL);
2957 pNewFormat->SetStandard();
2958 if ( !aFTable.emplace(CLOffset + ZF_STANDARD_LOGICAL /* NF_BOOLEAN */,
2959 std::move(pNewFormat)).second )
2961 SAL_WARN( "svl.numbers", "SvNumberFormatter::ImpGenerateFormats: dup position Boolean");
2964 // Text
2965 aFormatCode = "@";
2966 pNewFormat.reset(new SvNumberformat( aFormatCode, rCurrentLanguage.pFormatScanner.get(),
2967 rCurrentLanguage.pStringScanner.get(), rNatNum, nCheckPos, rCurrentLanguage.ActLnge ));
2968 pNewFormat->SetType(SvNumFormatType::TEXT);
2969 pNewFormat->SetStandard();
2970 if ( !aFTable.emplace( CLOffset + ZF_STANDARD_TEXT /* NF_TEXT */,
2971 std::move(pNewFormat)).second )
2973 SAL_WARN( "svl.numbers", "SvNumberFormatter::ImpGenerateFormats: dup position Text");
2977 // 0
2978 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_NUMBER_INT );
2979 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
2980 CLOffset + ZF_STANDARD+1 /* NF_NUMBER_INT */ );
2982 // 0.00
2983 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_NUMBER_DEC2 );
2984 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
2985 CLOffset + ZF_STANDARD+2 /* NF_NUMBER_DEC2 */ );
2987 // #,##0
2988 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_NUMBER_1000INT );
2989 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
2990 CLOffset + ZF_STANDARD+3 /* NF_NUMBER_1000INT */ );
2992 // #,##0.00
2993 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_NUMBER_1000DEC2 );
2994 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
2995 CLOffset + ZF_STANDARD+4 /* NF_NUMBER_1000DEC2 */ );
2997 // #.##0,00 System country/language dependent
2998 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_NUMBER_SYSTEM );
2999 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3000 CLOffset + ZF_STANDARD+5 /* NF_NUMBER_SYSTEM */ );
3003 // Percent number
3004 aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::PERCENT_NUMBER, aLocale );
3005 ImpAdjustFormatCodeDefault(rCurrentLanguage, aFormatSeq.getArray(), aFormatSeq.getLength() );
3007 // 0%
3008 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_PERCENT_INT );
3009 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3010 CLOffset + ZF_STANDARD_PERCENT /* NF_PERCENT_INT */ );
3012 // 0.00%
3013 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_PERCENT_DEC2 );
3014 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3015 CLOffset + ZF_STANDARD_PERCENT+1 /* NF_PERCENT_DEC2 */ );
3018 // Currency. NO default standard option! Default is determined of locale
3019 // data default currency and format is generated if needed.
3020 aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::CURRENCY, aLocale );
3021 if (LocaleDataWrapper::areChecksEnabled())
3023 // though no default desired here, test for correctness of locale data
3024 ImpAdjustFormatCodeDefault(rCurrentLanguage, aFormatSeq.getArray(), aFormatSeq.getLength() );
3027 // #,##0
3028 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_CURRENCY_1000INT );
3029 // Just copy the format, to avoid COW on sequence after each possible reallocation
3030 auto aFormat = aFormatSeq[nIdx];
3031 aFormat.Default = false;
3032 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormat,
3033 CLOffset + ZF_STANDARD_CURRENCY /* NF_CURRENCY_1000INT */ );
3035 // #,##0.00
3036 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_CURRENCY_1000DEC2 );
3037 aFormat = aFormatSeq[nIdx];
3038 aFormat.Default = false;
3039 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormat,
3040 CLOffset + ZF_STANDARD_CURRENCY+1 /* NF_CURRENCY_1000DEC2 */ );
3042 // #,##0 negative red
3043 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_CURRENCY_1000INT_RED );
3044 aFormat = aFormatSeq[nIdx];
3045 aFormat.Default = false;
3046 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormat,
3047 CLOffset + ZF_STANDARD_CURRENCY+2 /* NF_CURRENCY_1000INT_RED */ );
3049 // #,##0.00 negative red
3050 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_CURRENCY_1000DEC2_RED );
3051 aFormat = aFormatSeq[nIdx];
3052 aFormat.Default = false;
3053 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormat,
3054 CLOffset + ZF_STANDARD_CURRENCY+3 /* NF_CURRENCY_1000DEC2_RED */ );
3056 // #,##0.00 USD
3057 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_CURRENCY_1000DEC2_CCC );
3058 aFormat = aFormatSeq[nIdx];
3059 aFormat.Default = false;
3060 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormat,
3061 CLOffset + ZF_STANDARD_CURRENCY+4 /* NF_CURRENCY_1000DEC2_CCC */ );
3063 // #.##0,--
3064 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_CURRENCY_1000DEC2_DASHED );
3065 aFormat = aFormatSeq[nIdx];
3066 aFormat.Default = false;
3067 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormat,
3068 CLOffset + ZF_STANDARD_CURRENCY+5 /* NF_CURRENCY_1000DEC2_DASHED */ );
3071 // Date
3072 aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::DATE, aLocale );
3073 ImpAdjustFormatCodeDefault(rCurrentLanguage, aFormatSeq.getArray(), aFormatSeq.getLength() );
3075 // DD.MM.YY System
3076 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYSTEM_SHORT );
3077 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3078 CLOffset + ZF_STANDARD_DATE /* NF_DATE_SYSTEM_SHORT */ );
3080 // NN DD.MMM YY
3081 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_DEF_NNDDMMMYY );
3082 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3083 CLOffset + ZF_STANDARD_DATE+1 /* NF_DATE_DEF_NNDDMMMYY */ );
3085 // DD.MM.YY def/System
3086 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_MMYY );
3087 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3088 CLOffset + ZF_STANDARD_DATE+2 /* NF_DATE_SYS_MMYY */ );
3090 // DD MMM
3091 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_DDMMM );
3092 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3093 CLOffset + ZF_STANDARD_DATE+3 /* NF_DATE_SYS_DDMMM */ );
3095 // MMMM
3096 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_MMMM );
3097 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3098 CLOffset + ZF_STANDARD_DATE+4 /* NF_DATE_MMMM */ );
3100 // QQ YY
3101 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_QQJJ );
3102 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3103 CLOffset + ZF_STANDARD_DATE+5 /* NF_DATE_QQJJ */ );
3105 // DD.MM.YYYY was DD.MM.[YY]YY
3106 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_DDMMYYYY );
3107 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3108 CLOffset + ZF_STANDARD_DATE+6 /* NF_DATE_SYS_DDMMYYYY */ );
3110 // DD.MM.YY def/System
3111 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_DDMMYY );
3112 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3113 CLOffset + ZF_STANDARD_DATE+7 /* NF_DATE_SYS_DDMMYY */ );
3115 // NNN, D. MMMM YYYY System
3116 // Long day of week: "NNNN" instead of "NNN," because of compatibility
3117 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYSTEM_LONG );
3118 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3119 CLOffset + ZF_STANDARD_DATE+8 /* NF_DATE_SYSTEM_LONG */ );
3121 // Hard coded but system (regional settings) delimiters dependent long date formats
3123 // D. MMM YY def/System
3124 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_DMMMYY );
3125 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3126 CLOffset + ZF_STANDARD_DATE+9 /* NF_DATE_SYS_DMMMYY */ );
3128 // D. MMM YYYY def/System
3129 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_DMMMYYYY );
3130 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3131 CLOffset + ZF_STANDARD_DATE_SYS_DMMMYYYY /* NF_DATE_SYS_DMMMYYYY */ );
3133 // D. MMMM YYYY def/System
3134 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_DMMMMYYYY );
3135 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3136 CLOffset + ZF_STANDARD_DATE_SYS_DMMMMYYYY /* NF_DATE_SYS_DMMMMYYYY */ );
3138 // NN, D. MMM YY def/System
3139 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_NNDMMMYY );
3140 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3141 CLOffset + ZF_STANDARD_DATE_SYS_NNDMMMYY /* NF_DATE_SYS_NNDMMMYY */ );
3143 // NN, D. MMMM YYYY def/System
3144 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_NNDMMMMYYYY );
3145 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3146 CLOffset + ZF_STANDARD_DATE_SYS_NNDMMMMYYYY /* NF_DATE_SYS_NNDMMMMYYYY */ );
3148 // NNN, D. MMMM YYYY def/System
3149 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_SYS_NNNNDMMMMYYYY );
3150 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3151 CLOffset + ZF_STANDARD_DATE_SYS_NNNNDMMMMYYYY /* NF_DATE_SYS_NNNNDMMMMYYYY */ );
3153 // Hard coded DIN (Deutsche Industrie Norm) and EN (European Norm) date formats
3155 // D. MMM. YYYY DIN/EN
3156 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_DIN_DMMMYYYY );
3157 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3158 CLOffset + ZF_STANDARD_DATE_DIN_DMMMYYYY /* NF_DATE_DIN_DMMMYYYY */ );
3160 // D. MMMM YYYY DIN/EN
3161 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_DIN_DMMMMYYYY );
3162 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3163 CLOffset + ZF_STANDARD_DATE_DIN_DMMMMYYYY /* NF_DATE_DIN_DMMMMYYYY */ );
3165 // MM-DD DIN/EN
3166 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_DIN_MMDD );
3167 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3168 CLOffset + ZF_STANDARD_DATE_DIN_MMDD /* NF_DATE_DIN_MMDD */ );
3170 // YY-MM-DD DIN/EN
3171 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_DIN_YYMMDD );
3172 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3173 CLOffset + ZF_STANDARD_DATE_DIN_YYMMDD /* NF_DATE_DIN_YYMMDD */ );
3175 // YYYY-MM-DD DIN/EN/ISO
3176 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATE_DIN_YYYYMMDD );
3177 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3178 CLOffset + ZF_STANDARD_DATE_DIN_YYYYMMDD /* NF_DATE_DIN_YYYYMMDD */ );
3181 // Time
3182 aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::TIME, aLocale );
3183 ImpAdjustFormatCodeDefault(rCurrentLanguage, aFormatSeq.getArray(), aFormatSeq.getLength() );
3185 // HH:MM
3186 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_TIME_HHMM );
3187 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3188 CLOffset + ZF_STANDARD_TIME /* NF_TIME_HHMM */ );
3190 // HH:MM:SS
3191 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_TIME_HHMMSS );
3192 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3193 CLOffset + ZF_STANDARD_TIME+1 /* NF_TIME_HHMMSS */ );
3195 // HH:MM AM/PM
3196 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_TIME_HHMMAMPM );
3197 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3198 CLOffset + ZF_STANDARD_TIME+2 /* NF_TIME_HHMMAMPM */ );
3200 // HH:MM:SS AM/PM
3201 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_TIME_HHMMSSAMPM );
3202 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3203 CLOffset + ZF_STANDARD_TIME+3 /* NF_TIME_HHMMSSAMPM */ );
3205 // [HH]:MM:SS
3206 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_TIME_HH_MMSS );
3207 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3208 CLOffset + ZF_STANDARD_TIME+4 /* NF_TIME_HH_MMSS */ );
3210 // MM:SS,00
3211 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_TIME_MMSS00 );
3212 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3213 CLOffset + ZF_STANDARD_TIME+5 /* NF_TIME_MMSS00 */ );
3215 // [HH]:MM:SS,00
3216 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_TIME_HH_MMSS00 );
3217 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3218 CLOffset + ZF_STANDARD_TIME+6 /* NF_TIME_HH_MMSS00 */ );
3221 // DateTime
3222 aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::DATE_TIME, aLocale );
3223 ImpAdjustFormatCodeDefault(rCurrentLanguage, aFormatSeq.getArray(), aFormatSeq.getLength() );
3225 // DD.MM.YY HH:MM System
3226 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATETIME_SYSTEM_SHORT_HHMM );
3227 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3228 CLOffset + ZF_STANDARD_DATETIME /* NF_DATETIME_SYSTEM_SHORT_HHMM */ );
3230 // DD.MM.YYYY HH:MM:SS System
3231 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATETIME_SYS_DDMMYYYY_HHMMSS );
3232 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3233 CLOffset + ZF_STANDARD_DATETIME+1 /* NF_DATETIME_SYS_DDMMYYYY_HHMMSS */ );
3235 // DD.MM.YYYY HH:MM System
3236 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_DATETIME_SYS_DDMMYYYY_HHMM );
3237 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3238 CLOffset + ZF_STANDARD_DATETIME+2 /* NF_DATETIME_SYS_DDMMYYYY_HHMM */ );
3240 const NfKeywordTable & rKeyword = rCurrentLanguage.pFormatScanner->GetKeywords();
3241 i18n::NumberFormatCode aSingleFormatCode;
3242 aSingleFormatCode.Usage = i18n::KNumberFormatUsage::DATE_TIME;
3244 // YYYY-MM-DD HH:MM:SS ISO (with blank instead of 'T')
3245 aSingleFormatCode.Code =
3246 rKeyword[NF_KEY_YYYY] + "-" +
3247 rKeyword[NF_KEY_MM] + "-" +
3248 rKeyword[NF_KEY_DD] + " " +
3249 rKeyword[NF_KEY_HH] + ":" +
3250 rKeyword[NF_KEY_MMI] + ":" +
3251 rKeyword[NF_KEY_SS];
3252 SvNumberformat* pFormat = ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
3253 CLOffset + ZF_STANDARD_DATETIME+3 /* NF_DATETIME_ISO_YYYYMMDD_HHMMSS */ );
3254 assert(pFormat);
3256 // YYYY-MM-DD HH:MM:SS,000 ISO (with blank instead of 'T') and
3257 // milliseconds and locale's time decimal separator
3258 aSingleFormatCode.Code =
3259 rKeyword[NF_KEY_YYYY] + "-" +
3260 rKeyword[NF_KEY_MM] + "-" +
3261 rKeyword[NF_KEY_DD] + " " +
3262 rKeyword[NF_KEY_HH] + ":" +
3263 rKeyword[NF_KEY_MMI] + ":" +
3264 rKeyword[NF_KEY_SS] + rCurrentLanguage.GetLocaleData()->getTime100SecSep() +
3265 "000";
3266 pFormat = ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
3267 CLOffset + ZF_STANDARD_DATETIME+4 /* NF_DATETIME_ISO_YYYYMMDD_HHMMSS000 */ );
3268 assert(pFormat);
3270 // YYYY-MM-DD"T"HH:MM:SS ISO
3271 aSingleFormatCode.Code =
3272 rKeyword[NF_KEY_YYYY] + "-" +
3273 rKeyword[NF_KEY_MM] + "-" +
3274 rKeyword[NF_KEY_DD] + "\"T\"" +
3275 rKeyword[NF_KEY_HH] + ":" +
3276 rKeyword[NF_KEY_MMI] + ":" +
3277 rKeyword[NF_KEY_SS];
3278 pFormat = ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
3279 CLOffset + ZF_STANDARD_DATETIME+5 /* NF_DATETIME_ISO_YYYYMMDDTHHMMSS */ );
3280 assert(pFormat);
3281 pFormat->SetComment(u"ISO 8601"_ustr); // not to be localized
3283 // YYYY-MM-DD"T"HH:MM:SS,000 ISO with milliseconds and ',' or '.' decimal separator
3284 aSingleFormatCode.Code =
3285 rKeyword[NF_KEY_YYYY] + "-" +
3286 rKeyword[NF_KEY_MM] + "-" +
3287 rKeyword[NF_KEY_DD] + "\"T\"" +
3288 rKeyword[NF_KEY_HH] + ":" +
3289 rKeyword[NF_KEY_MMI] + ":" +
3290 rKeyword[NF_KEY_SS] + (rCurrentLanguage.GetLocaleData()->getTime100SecSep() == "." ? "." : ",") +
3291 "000";
3292 pFormat = ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
3293 CLOffset + ZF_STANDARD_DATETIME+6 /* NF_DATETIME_ISO_YYYYMMDDTHHMMSS000 */ );
3294 assert(pFormat);
3295 pFormat->SetComment(u"ISO 8601"_ustr); // not to be localized
3298 // Scientific number
3299 aFormatSeq = xNFC->getAllFormatCode( i18n::KNumberFormatUsage::SCIENTIFIC_NUMBER, aLocale );
3300 ImpAdjustFormatCodeDefault(rCurrentLanguage, aFormatSeq.getArray(), aFormatSeq.getLength() );
3302 // 0.00E+000
3303 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_SCIENTIFIC_000E000 );
3304 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3305 CLOffset + ZF_STANDARD_SCIENTIFIC /* NF_SCIENTIFIC_000E000 */ );
3307 // 0.00E+00
3308 nIdx = ImpGetFormatCodeIndex(rCurrentLanguage, aFormatSeq, NF_SCIENTIFIC_000E00 );
3309 ImpInsertFormat(rCurrentLanguage, rNatNum, aFormatSeq[nIdx],
3310 CLOffset + ZF_STANDARD_SCIENTIFIC+1 /* NF_SCIENTIFIC_000E00 */ );
3313 // Fraction number (no default option)
3314 aSingleFormatCode.Usage = i18n::KNumberFormatUsage::FRACTION_NUMBER;
3316 // # ?/?
3317 aSingleFormatCode.Code = "# ?/?";
3318 ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
3319 CLOffset + ZF_STANDARD_FRACTION /* NF_FRACTION_1D */ );
3321 // # ??/??
3322 //! "??/" would be interpreted by the compiler as a trigraph for '\'
3323 aSingleFormatCode.Code = "# ?\?/?\?";
3324 ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
3325 CLOffset + ZF_STANDARD_FRACTION+1 /* NF_FRACTION_2D */ );
3327 // # ???/???
3328 //! "??/" would be interpreted by the compiler as a trigraph for '\'
3329 aSingleFormatCode.Code = "# ?\?\?/?\?\?";
3330 ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
3331 CLOffset + ZF_STANDARD_FRACTION+2 /* NF_FRACTION_3D */ );
3333 // # ?/2
3334 aSingleFormatCode.Code = "# ?/2";
3335 ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
3336 CLOffset + ZF_STANDARD_FRACTION+3 /* NF_FRACTION_2 */ );
3338 // # ?/4
3339 aSingleFormatCode.Code = "# ?/4";
3340 ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
3341 CLOffset + ZF_STANDARD_FRACTION+4 /* NF_FRACTION_4 */ );
3343 // # ?/8
3344 aSingleFormatCode.Code = "# ?/8";
3345 ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
3346 CLOffset + ZF_STANDARD_FRACTION+5 /* NF_FRACTION_8 */ );
3348 // # ??/16
3349 aSingleFormatCode.Code = "# ?\?/16";
3350 ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
3351 CLOffset + ZF_STANDARD_FRACTION+6 /* NF_FRACTION_16 */ );
3353 // # ??/10
3354 aSingleFormatCode.Code = "# ?\?/10";
3355 ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
3356 CLOffset + ZF_STANDARD_FRACTION+7 /* NF_FRACTION_10 */ );
3358 // # ??/100
3359 aSingleFormatCode.Code = "# ?\?/100";
3360 ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
3361 CLOffset + ZF_STANDARD_FRACTION+8 /* NF_FRACTION_100 */ );
3364 // Week of year
3365 aSingleFormatCode.Code = rKeyword[NF_KEY_WW];
3366 ImpInsertFormat(rCurrentLanguage, rNatNum, aSingleFormatCode,
3367 CLOffset + ZF_STANDARD_DATE_WW /* NF_DATE_WW */ );
3369 // Now all additional format codes provided by I18N, but only if not
3370 // changing SystemCL, then they are appended last after user defined.
3371 if ( !bNoAdditionalFormats )
3373 ImpGenerateAdditionalFormats(rCurrentLanguage, rNatNum, CLOffset, xNFC, false);
3375 if (bOldConvertMode)
3377 rCurrentLanguage.pFormatScanner->SetConvertMode(true);
3381 void SvNFFormatData::ImpGenerateAdditionalFormats(SvNFLanguageData& rCurrentLanguage,
3382 const NativeNumberWrapper& rNatNum, sal_uInt32 CLOffset,
3383 css::uno::Reference< css::i18n::XNumberFormatCode > const & rNumberFormatCode,
3384 bool bAfterChangingSystemCL )
3386 SvNumberformat* pStdFormat = GetFormatEntry( CLOffset + ZF_STANDARD );
3387 if ( !pStdFormat )
3389 SAL_WARN( "svl.numbers", "ImpGenerateAdditionalFormats: no GENERAL format" );
3390 return ;
3392 sal_uInt32 nPos = CLOffset + pStdFormat->GetLastInsertKey( SvNumberformat::FormatterPrivateAccess() );
3393 css::lang::Locale aLocale = rCurrentLanguage.GetLanguageTag().getLocale();
3395 // All currencies, this time with [$...] which was stripped in
3396 // ImpGenerateFormats for old "automatic" currency formats.
3397 uno::Sequence< i18n::NumberFormatCode > aFormatSeq = rNumberFormatCode->getAllFormatCode( i18n::KNumberFormatUsage::CURRENCY, aLocale );
3398 sal_Int32 nCodes = aFormatSeq.getLength();
3399 auto aNonConstRange = asNonConstRange(aFormatSeq);
3400 ImpAdjustFormatCodeDefault(rCurrentLanguage, aNonConstRange.begin(), nCodes);
3401 for ( i18n::NumberFormatCode& rFormat : aNonConstRange )
3403 if ( nPos - CLOffset >= SV_COUNTRY_LANGUAGE_OFFSET )
3405 SAL_WARN( "svl.numbers", "ImpGenerateAdditionalFormats: too many formats" );
3406 break; // for
3408 if ( rFormat.Index < NF_INDEX_TABLE_RESERVED_START &&
3409 rFormat.Index != NF_CURRENCY_1000DEC2_CCC )
3410 { // Insert only if not already inserted, but internal index must be
3411 // above so ImpInsertFormat can distinguish it.
3412 sal_Int16 nOrgIndex = rFormat.Index;
3413 rFormat.Index = sal::static_int_cast< sal_Int16 >(
3414 rFormat.Index + nCodes + NF_INDEX_TABLE_ENTRIES);
3415 //! no default on currency
3416 bool bDefault = rFormat.Default;
3417 rFormat.Default = false;
3418 if ( SvNumberformat* pNewFormat = ImpInsertFormat(rCurrentLanguage, rNatNum, rFormat, nPos+1,
3419 bAfterChangingSystemCL, nOrgIndex ) )
3421 pNewFormat->SetAdditionalBuiltin();
3422 nPos++;
3424 rFormat.Index = nOrgIndex;
3425 rFormat.Default = bDefault;
3429 // All additional format codes provided by I18N that are not old standard
3430 // index. Additional formats may define defaults, currently there is no
3431 // check if more than one default of a usage/type combination is provided,
3432 // like it is done for usage groups with ImpAdjustFormatCodeDefault().
3433 // There is no harm though, on first invocation ImpGetDefaultFormat() will
3434 // use the first default encountered.
3435 aFormatSeq = rNumberFormatCode->getAllFormatCodes( aLocale );
3436 for ( const auto& rFormat : std::as_const(aFormatSeq) )
3438 if ( nPos - CLOffset >= SV_COUNTRY_LANGUAGE_OFFSET )
3440 SAL_WARN( "svl.numbers", "ImpGenerateAdditionalFormats: too many formats" );
3441 break; // for
3443 if ( rFormat.Index >= NF_INDEX_TABLE_RESERVED_START )
3445 if ( SvNumberformat* pNewFormat = ImpInsertFormat(rCurrentLanguage, rNatNum, rFormat, nPos+1,
3446 bAfterChangingSystemCL ) )
3448 pNewFormat->SetAdditionalBuiltin();
3449 nPos++;
3454 pStdFormat->SetLastInsertKey( static_cast<sal_uInt16>(nPos - CLOffset), SvNumberformat::FormatterPrivateAccess() );
3457 void SvNumberFormatter::ImpGenerateAdditionalFormats( sal_uInt32 CLOffset,
3458 css::uno::Reference< css::i18n::XNumberFormatCode > const & rNumberFormatCode )
3460 m_aFormatData.ImpGenerateAdditionalFormats(m_aCurrentLanguage, GetNatNum(), CLOffset, rNumberFormatCode, /*bAfterChangingSystemCL*/true);
3463 namespace {
3465 // return position of a special character
3466 sal_Int32 ImpPosToken(const OUStringBuffer & sFormat, sal_Unicode token, sal_Int32 nStartPos = 0)
3468 sal_Int32 nLength = sFormat.getLength();
3469 for ( sal_Int32 i=nStartPos; i<nLength && i>=0 ; i++ )
3471 switch(sFormat[i])
3473 case '\"' : // skip text
3474 i = sFormat.indexOf('\"',i+1);
3475 break;
3476 case '[' : // skip condition
3477 i = sFormat.indexOf(']',i+1);
3478 break;
3479 case '\\' : // skip escaped character
3480 i++;
3481 break;
3482 case ';' :
3483 if (token == ';')
3484 return i;
3485 break;
3486 case 'e' :
3487 case 'E' :
3488 if (token == 'E')
3489 return i; // if 'E' is outside "" and [] it must be the 'E' exponent
3490 break;
3491 default : break;
3493 if ( i<0 )
3494 i--;
3496 return -2;
3501 OUString SvNFEngine::GenerateFormat(SvNFLanguageData& rCurrentLanguage,
3502 const SvNFFormatData& rFormatData,
3503 const NativeNumberWrapper& rNatNum, const Accessor& rFuncs,
3504 sal_uInt32 nIndex,
3505 LanguageType eLnge,
3506 bool bThousand,
3507 bool IsRed,
3508 sal_uInt16 nPrecision,
3509 sal_uInt16 nLeadingZeros)
3511 eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
3512 const SvNumberformat* pFormat = rFormatData.GetFormatEntry( nIndex );
3513 const SvNumFormatType eType = (pFormat ? pFormat->GetMaskedType() : SvNumFormatType::UNDEFINED);
3515 rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)
3517 utl::DigitGroupingIterator aGrouping( rCurrentLanguage.xLocaleData->getDigitGrouping());
3518 // always group of 3 for Engineering notation
3519 const sal_Int32 nDigitsInFirstGroup = ( bThousand && (eType == SvNumFormatType::SCIENTIFIC) ) ? 3 : aGrouping.get();
3520 const OUString& rThSep = rCurrentLanguage.GetNumThousandSep();
3522 OUStringBuffer sString;
3523 using comphelper::string::padToLength;
3525 if (eType & SvNumFormatType::TIME)
3527 assert(pFormat && "with !pFormat eType can only be SvNumFormatType::UNDEFINED");
3528 sString = pFormat->GetFormatStringForTimePrecision( nPrecision );
3530 else if (nLeadingZeros == 0)
3532 if (!bThousand)
3533 sString.append('#');
3534 else
3536 if (eType == SvNumFormatType::SCIENTIFIC)
3537 { // for scientific, bThousand is used for Engineering notation
3538 sString.append("###");
3540 else
3542 sString.append("#" + rThSep);
3543 padToLength(sString, sString.getLength() + nDigitsInFirstGroup, '#');
3547 else
3549 for (sal_uInt16 i = 0; i < nLeadingZeros; i++)
3551 if (bThousand && i > 0 && i == aGrouping.getPos())
3553 sString.insert(0, rThSep);
3554 aGrouping.advance();
3556 sString.insert(0, '0');
3558 if ( bThousand )
3560 sal_Int32 nDigits = (eType == SvNumFormatType::SCIENTIFIC) ? 3*((nLeadingZeros-1)/3 + 1) : nDigitsInFirstGroup + 1;
3561 for (sal_Int32 i = nLeadingZeros; i < nDigits; i++)
3563 if ( i % nDigitsInFirstGroup == 0 )
3564 sString.insert(0, rThSep);
3565 sString.insert(0, '#');
3569 if (nPrecision > 0 && eType != SvNumFormatType::FRACTION && !( eType & SvNumFormatType::TIME ) )
3571 sString.append(rCurrentLanguage.GetNumDecimalSep());
3572 padToLength(sString, sString.getLength() + nPrecision, '0');
3575 // Native Number
3576 const OUString sPosNatNumModifier = pFormat ? pFormat->GetNatNumModifierString( 0 ) : u""_ustr;
3577 const OUString sNegNatNumModifier = pFormat ?
3578 // if a negative format already exists, use its NatNum modifier
3579 // else use NatNum modifier of positive format
3580 ( pFormat->GetNumForString( 1, 0 ) ? pFormat->GetNatNumModifierString( 1 ) : sPosNatNumModifier )
3581 : u""_ustr;
3583 if (eType == SvNumFormatType::PERCENT)
3585 sString.append( pFormat->GetPercentString() );
3587 else if (eType == SvNumFormatType::SCIENTIFIC)
3589 OUStringBuffer sOldFormatString(pFormat->GetFormatstring());
3590 sal_Int32 nIndexE = ImpPosToken( sOldFormatString, 'E' );
3591 if (nIndexE > -1)
3593 sal_Int32 nIndexSep = ImpPosToken( sOldFormatString, ';', nIndexE );
3594 if (nIndexSep > nIndexE)
3595 sString.append( sOldFormatString.subView(nIndexE, nIndexSep - nIndexE) );
3596 else
3597 sString.append( sOldFormatString.subView(nIndexE) );
3600 else if (eType == SvNumFormatType::CURRENCY)
3602 OUStringBuffer sNegStr(sString);
3603 OUString aCurr;
3604 const NfCurrencyEntry* pEntry;
3605 bool bBank;
3606 bool isPosNatNum12 = sPosNatNumModifier.startsWith( "[NatNum12" );
3607 bool isNegNatNum12 = sNegNatNumModifier.startsWith( "[NatNum12" );
3608 if ( !isPosNatNum12 || !isNegNatNum12 )
3610 if (rFormatData.GetNewCurrencySymbolString(nIndex, aCurr, &pEntry, &bBank))
3612 if ( pEntry )
3614 sal_uInt16 nPosiForm = NfCurrencyEntry::GetEffectivePositiveFormat(
3615 rCurrentLanguage.xLocaleData->getCurrPositiveFormat(),
3616 pEntry->GetPositiveFormat(), bBank );
3617 sal_uInt16 nNegaForm = NfCurrencyEntry::GetEffectiveNegativeFormat(
3618 rCurrentLanguage.xLocaleData->getCurrNegativeFormat(),
3619 pEntry->GetNegativeFormat(), bBank );
3620 if ( !isPosNatNum12 )
3621 pEntry->CompletePositiveFormatString( sString, bBank, nPosiForm );
3622 if ( !isNegNatNum12 )
3623 pEntry->CompleteNegativeFormatString( sNegStr, bBank, nNegaForm );
3625 else
3626 { // assume currency abbreviation (AKA banking symbol), not symbol
3627 sal_uInt16 nPosiForm = NfCurrencyEntry::GetEffectivePositiveFormat(
3628 rCurrentLanguage.xLocaleData->getCurrPositiveFormat(),
3629 rCurrentLanguage.xLocaleData->getCurrPositiveFormat(), true );
3630 sal_uInt16 nNegaForm = NfCurrencyEntry::GetEffectiveNegativeFormat(
3631 rCurrentLanguage.xLocaleData->getCurrNegativeFormat(),
3632 rCurrentLanguage.xLocaleData->getCurrNegativeFormat(), true );
3633 if ( !isPosNatNum12 )
3634 NfCurrencyEntry::CompletePositiveFormatString( sString, aCurr, nPosiForm );
3635 if ( !isNegNatNum12 )
3636 NfCurrencyEntry::CompleteNegativeFormatString( sNegStr, aCurr, nNegaForm );
3639 else
3640 { // "automatic" old style
3641 OUString aSymbol, aAbbrev;
3642 rCurrentLanguage.GetCompatibilityCurrency(aSymbol, aAbbrev);
3643 if ( !isPosNatNum12 )
3644 NfCurrencyEntry::CompletePositiveFormatString( sString,
3645 aSymbol, rCurrentLanguage.xLocaleData->getCurrPositiveFormat() );
3646 if ( !isNegNatNum12 )
3647 NfCurrencyEntry::CompleteNegativeFormatString( sNegStr,
3648 aSymbol, rCurrentLanguage.xLocaleData->getCurrNegativeFormat() );
3651 sString.append( ';' );
3652 if (IsRed)
3654 sString.append("[" + rCurrentLanguage.pFormatScanner->GetRedString() + "]");
3656 sString.append( sNegNatNumModifier );
3657 if ( isNegNatNum12 )
3659 sString.append( '-' );
3661 sString.append(sNegStr);
3663 else if (eType == SvNumFormatType::FRACTION)
3665 OUString aIntegerFractionDelimiterString = pFormat->GetIntegerFractionDelimiterString( 0 );
3666 if ( aIntegerFractionDelimiterString == " " )
3667 sString.append( aIntegerFractionDelimiterString );
3668 else
3670 sString.append( "\"" + aIntegerFractionDelimiterString + "\"" );
3672 sString.append( pFormat->GetNumeratorString( 0 )
3673 + "/" );
3674 if ( nPrecision > 0 )
3675 padToLength(sString, sString.getLength() + nPrecision, '?');
3676 else
3677 sString.append( '#' );
3679 if (eType != SvNumFormatType::CURRENCY)
3681 bool insertBrackets = false;
3682 if ( eType != SvNumFormatType::UNDEFINED)
3684 insertBrackets = pFormat->IsNegativeInBracket();
3686 if (IsRed || insertBrackets)
3688 OUStringBuffer sTmpStr(sString);
3690 if (pFormat && pFormat->HasPositiveBracketPlaceholder())
3692 sTmpStr.append("_)");
3694 sTmpStr.append(';');
3696 if (IsRed)
3698 sTmpStr.append("[" + rCurrentLanguage.pFormatScanner->GetRedString() + "]");
3700 sTmpStr.append( sNegNatNumModifier );
3702 if (insertBrackets)
3704 sTmpStr.append("(" + sString + ")");
3706 else
3708 sTmpStr.append("-" + sString);
3710 sString = std::move(sTmpStr);
3713 sString.insert( 0, sPosNatNumModifier );
3714 return sString.makeStringAndClear();
3717 OUString SvNumberFormatter::GenerateFormat(sal_uInt32 nIndex,
3718 LanguageType eLnge,
3719 bool bThousand,
3720 bool IsRed,
3721 sal_uInt16 nPrecision,
3722 sal_uInt16 nLeadingZeros)
3724 ::osl::MutexGuard aGuard( GetInstanceMutex() );
3725 return SvNFEngine::GenerateFormat(m_aCurrentLanguage, m_aFormatData,
3726 GetNatNum(), m_aRWPolicy,
3727 nIndex, eLnge,
3728 bThousand, IsRed,
3729 nPrecision, nLeadingZeros);
3732 bool SvNumberFormatter::IsUserDefined(sal_uInt32 F_Index) const
3734 ::osl::MutexGuard aGuard( GetInstanceMutex() );
3735 const SvNumberformat* pFormat = m_aFormatData.GetFormatEntry(F_Index);
3737 return pFormat && (pFormat->GetType() & SvNumFormatType::DEFINED);
3740 bool SvNumberFormatter::IsUserDefined(std::u16string_view sStr,
3741 LanguageType eLnge)
3743 ::osl::MutexGuard aGuard( GetInstanceMutex() );
3744 if (eLnge == LANGUAGE_DONTKNOW)
3746 eLnge = IniLnge;
3748 sal_uInt32 CLOffset = m_aFormatData.ImpGenerateCL(m_aCurrentLanguage, GetNatNum(), eLnge); // create new standard formats if necessary
3749 eLnge = m_aCurrentLanguage.ActLnge;
3751 sal_uInt32 nKey = ImpIsEntry(sStr, CLOffset, eLnge);
3752 if (nKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
3754 return true;
3756 SvNumberformat* pEntry = m_aFormatData.GetFormatEntry( nKey );
3757 return pEntry && (pEntry->GetType() & SvNumFormatType::DEFINED);
3760 sal_uInt32 SvNumberFormatter::GetEntryKey(std::u16string_view sStr,
3761 LanguageType eLnge)
3763 ::osl::MutexGuard aGuard( GetInstanceMutex() );
3764 if (eLnge == LANGUAGE_DONTKNOW)
3766 eLnge = IniLnge;
3768 sal_uInt32 CLOffset = m_aFormatData.ImpGenerateCL(m_aCurrentLanguage, GetNatNum(), eLnge); // create new standard formats if necessary
3769 return ImpIsEntry(sStr, CLOffset, eLnge);
3772 sal_uInt32 SvNFEngine::GetStandardIndex(SvNFLanguageData& rCurrentLanguage,
3773 const SvNFFormatData& rFormatData,
3774 const NativeNumberWrapper& rNatNum,
3775 const Accessor& rFuncs,
3776 LanguageType eLnge)
3778 return SvNFEngine::GetStandardFormat(rCurrentLanguage, rFormatData,
3779 rNatNum, rFuncs,
3780 SvNumFormatType::NUMBER, eLnge);
3783 sal_uInt32 SvNumberFormatter::GetStandardIndex(LanguageType eLnge)
3785 ::osl::MutexGuard aGuard( GetInstanceMutex() );
3786 return SvNFEngine::GetStandardIndex(m_aCurrentLanguage, m_aFormatData, GetNatNum(),
3787 m_aRWPolicy, eLnge);
3790 SvNumFormatType SvNumberFormatter::GetType(sal_uInt32 nFIndex) const
3792 ::osl::MutexGuard aGuard(GetInstanceMutex());
3793 return m_aFormatData.GetType(nFIndex);
3796 void SvNumberFormatter::ClearMergeTable()
3798 ::osl::MutexGuard aGuard( GetInstanceMutex() );
3799 if ( pMergeTable )
3801 pMergeTable->clear();
3805 SvNumberFormatterIndexTable* SvNumberFormatter::MergeFormatter(SvNumberFormatter& rTable)
3807 ::osl::MutexGuard aGuard( GetInstanceMutex() );
3808 if ( pMergeTable )
3810 ClearMergeTable();
3812 else
3814 pMergeTable.reset( new SvNumberFormatterIndexTable );
3817 sal_uInt32 nCLOffset = 0;
3818 sal_uInt32 nOldKey, nOffset, nNewKey;
3820 for (const auto& rEntry : rTable.m_aFormatData.aFTable)
3822 SvNumberformat* pFormat = rEntry.second.get();
3823 nOldKey = rEntry.first;
3824 nOffset = nOldKey % SV_COUNTRY_LANGUAGE_OFFSET; // relative index
3825 if (nOffset == 0) // 1st format of CL
3827 nCLOffset = m_aFormatData.ImpGenerateCL(m_aCurrentLanguage, GetNatNum(), pFormat->GetLanguage());
3829 if (nOffset <= SV_MAX_COUNT_STANDARD_FORMATS) // Std.form.
3831 nNewKey = nCLOffset + nOffset;
3832 if (m_aFormatData.aFTable.find( nNewKey) == m_aFormatData.aFTable.end()) // not already present
3834 std::unique_ptr<SvNumberformat> pNewEntry(new SvNumberformat( *pFormat, *m_aCurrentLanguage.pFormatScanner ));
3835 if (!m_aFormatData.aFTable.emplace( nNewKey, std::move(pNewEntry)).second)
3837 SAL_WARN( "svl.numbers", "SvNumberFormatter::MergeFormatter: dup position");
3840 if (nNewKey != nOldKey) // new index
3842 (*pMergeTable)[nOldKey] = nNewKey;
3845 else // user defined
3847 std::unique_ptr<SvNumberformat> pNewEntry(new SvNumberformat( *pFormat, *m_aCurrentLanguage.pFormatScanner ));
3848 nNewKey = ImpIsEntry(pNewEntry->GetFormatstring(),
3849 nCLOffset,
3850 pFormat->GetLanguage());
3851 if (nNewKey == NUMBERFORMAT_ENTRY_NOT_FOUND) // only if not present yet
3853 SvNumberformat* pStdFormat = m_aFormatData.GetFormatEntry(nCLOffset + ZF_STANDARD);
3854 sal_uInt32 nPos = nCLOffset + pStdFormat->GetLastInsertKey( SvNumberformat::FormatterPrivateAccess() );
3855 nNewKey = nPos+1;
3856 if (nNewKey - nCLOffset >= SV_COUNTRY_LANGUAGE_OFFSET)
3858 SAL_WARN( "svl.numbers", "SvNumberFormatter::MergeFormatter: too many formats for CL");
3860 else if (!m_aFormatData.aFTable.emplace( nNewKey, std::move(pNewEntry)).second)
3862 SAL_WARN( "svl.numbers", "SvNumberFormatter::MergeFormatter: dup position");
3864 else
3866 pStdFormat->SetLastInsertKey(static_cast<sal_uInt16>(nNewKey - nCLOffset),
3867 SvNumberformat::FormatterPrivateAccess());
3870 if (nNewKey != nOldKey) // new index
3872 (*pMergeTable)[nOldKey] = nNewKey;
3876 return pMergeTable.get();
3880 SvNumberFormatterMergeMap SvNumberFormatter::ConvertMergeTableToMap()
3882 ::osl::MutexGuard aGuard( GetInstanceMutex() );
3883 if (!HasMergeFormatTable())
3885 return SvNumberFormatterMergeMap();
3887 SvNumberFormatterMergeMap aMap;
3888 for (const auto& rEntry : *pMergeTable)
3890 sal_uInt32 nOldKey = rEntry.first;
3891 aMap[ nOldKey ] = rEntry.second;
3893 ClearMergeTable();
3894 return aMap;
3897 namespace
3899 bool IsFormatSimpleBuiltIn(const SvNFLanguageData& rCurrentLanguage, sal_uInt32 nFormat, LanguageType eLnge)
3901 if (nFormat < SV_COUNTRY_LANGUAGE_OFFSET && eLnge == rCurrentLanguage.GetIniLanguage())
3902 return true; // it stays as it is
3904 sal_uInt32 nOffset = nFormat % SV_COUNTRY_LANGUAGE_OFFSET; // relative index
3905 if (nOffset > SV_MAX_COUNT_STANDARD_FORMATS)
3906 return true; // not a built-in format
3908 return false;
3912 sal_uInt32 SvNFEngine::GetCLOffsetRO(const SvNFFormatData& rFormatData, SvNFLanguageData&, const NativeNumberWrapper&, LanguageType eLnge)
3914 sal_uInt32 nCLOffset = rFormatData.ImpGetCLOffset(eLnge);
3915 assert(nCLOffset <= rFormatData.MaxCLOffset && "Language must have already been encountered");
3916 return nCLOffset;
3919 // we can't cache to SvNFFormatData in RO mode, so cache to a per-thread temp map which we can consult in FindFormatRO
3920 // caches can be merged with SvNumberFormatter::MergeDefaultFormatKeys
3921 void SvNFEngine::CacheFormatRO(SvNFFormatData::DefaultFormatKeysMap& rFormatCache, sal_uInt32 nSearch, sal_uInt32 nFormat)
3923 rFormatCache[nSearch] = nFormat;
3926 sal_uInt32 SvNFEngine::FindFormatRO(const SvNFFormatData& rFormatData, const SvNFFormatData::DefaultFormatKeysMap& rFormatCache, sal_uInt32 nSearch)
3928 sal_uInt32 nFormat = rFormatData.FindCachedDefaultFormat(nSearch);
3929 if (nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
3930 return nFormat;
3931 return ::FindCachedDefaultFormat(rFormatCache, nSearch);
3934 sal_uInt32 SvNFEngine::GetCLOffsetRW(SvNFFormatData& rFormatData, SvNFLanguageData& rCurrentLanguage, const NativeNumberWrapper& rNatNum, LanguageType eLnge)
3936 return rFormatData.ImpGenerateCL(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary
3939 void SvNFEngine::CacheFormatRW(SvNFFormatData& rFormatData, sal_uInt32 nSearch, sal_uInt32 nFormat)
3941 rFormatData.aDefaultFormatKeys[nSearch] = nFormat;
3944 sal_uInt32 SvNFEngine::FindFormatRW(const SvNFFormatData& rFormatData, sal_uInt32 nSearch)
3946 return rFormatData.FindCachedDefaultFormat(nSearch);
3949 sal_uInt32 SvNFEngine::DefaultCurrencyRW(SvNFFormatData& rFormatData, SvNFLanguageData& rCurrentLanguage, const NativeNumberWrapper& rNatNum, sal_uInt32 CLOffset, LanguageType eLnge)
3951 if (eLnge == LANGUAGE_SYSTEM)
3952 return rFormatData.ImpGetDefaultSystemCurrencyFormat(rCurrentLanguage, rNatNum);
3953 return rFormatData.ImpGetDefaultCurrencyFormat(rCurrentLanguage, rNatNum, CLOffset, eLnge);
3956 sal_uInt32 SvNFEngine::DefaultCurrencyRO(const SvNFFormatData& rFormatData, SvNFLanguageData&, const NativeNumberWrapper&, sal_uInt32 CLOffset, LanguageType eLnge)
3958 if (eLnge == LANGUAGE_SYSTEM)
3960 assert(rFormatData.nDefaultSystemCurrencyFormat != NUMBERFORMAT_ENTRY_NOT_FOUND);
3961 return rFormatData.nDefaultSystemCurrencyFormat;
3964 SvNFFormatData::DefaultFormatKeysMap::const_iterator it = rFormatData.aDefaultFormatKeys.find(CLOffset + ZF_STANDARD_CURRENCY);
3965 assert(it != rFormatData.aDefaultFormatKeys.end() && it->second != NUMBERFORMAT_ENTRY_NOT_FOUND);
3966 return it->second;
3969 SvNFEngine::Accessor SvNFEngine::GetRWPolicy(SvNFFormatData& rFormatData)
3971 return
3973 std::bind(SvNFEngine::GetCLOffsetRW, std::ref(rFormatData), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
3974 std::bind(SvNFEngine::CacheFormatRW, std::ref(rFormatData), std::placeholders::_1, std::placeholders::_2),
3975 std::bind(SvNFEngine::FindFormatRW, std::ref(rFormatData), std::placeholders::_1),
3976 std::bind(SvNFEngine::DefaultCurrencyRW, std::ref(rFormatData), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)
3980 void SvNumberFormatter::PrepForRoMode()
3982 SvNumberFormatter::GetTheCurrencyTable(); // create this now so threads don't attempt to create it simultaneously
3983 if (m_aFormatData.nDefaultSystemCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
3985 m_aFormatData.ImpGetDefaultSystemCurrencyFormat(m_aCurrentLanguage, GetNatNum());
3986 assert(m_aFormatData.nDefaultSystemCurrencyFormat != NUMBERFORMAT_ENTRY_NOT_FOUND);
3990 SvNFEngine::Accessor SvNFEngine::GetROPolicy(const SvNFFormatData& rFormatData, SvNFFormatData::DefaultFormatKeysMap& rFormatCache)
3992 assert(rFormatData.nDefaultSystemCurrencyFormat != NUMBERFORMAT_ENTRY_NOT_FOUND && "ensure PrepForRoMode is called");
3993 assert(g_CurrencyTableInitialized && "ensure PrepForRoMode is called");
3994 return
3996 std::bind(SvNFEngine::GetCLOffsetRO, std::ref(rFormatData), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
3997 std::bind(SvNFEngine::CacheFormatRO, std::ref(rFormatCache), std::placeholders::_1, std::placeholders::_2),
3998 std::bind(SvNFEngine::FindFormatRO, std::ref(rFormatData), std::ref(rFormatCache), std::placeholders::_1),
3999 std::bind(SvNFEngine::DefaultCurrencyRO, std::ref(rFormatData), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)
4003 sal_uInt32 SvNFEngine::GetFormatForLanguageIfBuiltIn(SvNFLanguageData& rCurrentLanguage,
4004 const NativeNumberWrapper& rNatNum,
4005 const Accessor& rFuncs,
4006 sal_uInt32 nFormat, LanguageType eLnge)
4008 eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
4010 if (IsFormatSimpleBuiltIn(rCurrentLanguage, nFormat, eLnge))
4011 return nFormat;
4013 sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge);
4014 sal_uInt32 nOffset = nFormat % SV_COUNTRY_LANGUAGE_OFFSET; // relative index
4015 return nCLOffset + nOffset;
4018 sal_uInt32 SvNumberFormatter::GetFormatForLanguageIfBuiltIn( sal_uInt32 nFormat,
4019 LanguageType eLnge )
4021 ::osl::MutexGuard aGuard(GetInstanceMutex());
4022 return SvNFEngine::GetFormatForLanguageIfBuiltIn(m_aCurrentLanguage, GetNatNum(), m_aRWPolicy, nFormat, eLnge);
4025 LanguageType SvNFLanguageData::ImpResolveLanguage(LanguageType eLnge) const
4027 return eLnge == LANGUAGE_DONTKNOW ? IniLnge : eLnge;
4030 sal_uInt32 SvNFEngine::GetFormatIndex(SvNFLanguageData& rCurrentLanguage, const Accessor& rFuncs,
4031 const NativeNumberWrapper& rNatNum, NfIndexTableOffset nTabOff,
4032 LanguageType eLnge)
4034 eLnge = rCurrentLanguage.ImpResolveLanguage(eLnge);
4036 sal_uInt32 nCLOffset = rFuncs.mGetCLOffset(rCurrentLanguage, rNatNum, eLnge); // create new standard formats if necessary (and allowed)
4038 return ImpGetFormatIndex(nTabOff, nCLOffset);
4041 sal_uInt32 SvNumberFormatter::GetFormatIndex(NfIndexTableOffset nTabOff, LanguageType eLnge)
4043 ::osl::MutexGuard aGuard(GetInstanceMutex());
4044 return SvNFEngine::GetFormatIndex(m_aCurrentLanguage, m_aRWPolicy, GetNatNum(), nTabOff, eLnge);
4047 // static
4048 NfIndexTableOffset SvNumberFormatter::GetIndexTableOffset(sal_uInt32 nFormat)
4050 sal_uInt32 nOffset = nFormat % SV_COUNTRY_LANGUAGE_OFFSET; // relative index
4051 if ( nOffset > SV_MAX_COUNT_STANDARD_FORMATS )
4053 return NF_INDEX_TABLE_ENTRIES; // not a built-in format
4056 for ( sal_uInt16 j = 0; j < NF_INDEX_TABLE_ENTRIES; j++ )
4058 if (indexTable[j] == nOffset)
4059 return static_cast<NfIndexTableOffset>(j);
4061 return NF_INDEX_TABLE_ENTRIES; // bad luck
4064 void SvNumberFormatter::SetEvalDateFormat( NfEvalDateFormat eEDF )
4066 ::osl::MutexGuard aGuard( GetInstanceMutex() );
4067 m_aCurrentLanguage.SetEvalDateFormat(eEDF);
4070 NfEvalDateFormat SvNumberFormatter::GetEvalDateFormat() const
4072 ::osl::MutexGuard aGuard( GetInstanceMutex() );
4073 return m_aCurrentLanguage.GetEvalDateFormat();
4076 void SvNumberFormatter::SetYear2000( sal_uInt16 nVal )
4078 ::osl::MutexGuard aGuard( GetInstanceMutex() );
4079 m_aCurrentLanguage.pStringScanner->SetYear2000( nVal );
4083 sal_uInt16 SvNumberFormatter::GetYear2000() const
4085 ::osl::MutexGuard aGuard( GetInstanceMutex() );
4086 return m_aCurrentLanguage.pStringScanner->GetYear2000();
4089 sal_uInt16 SvNFLanguageData::ExpandTwoDigitYear( sal_uInt16 nYear ) const
4091 if ( nYear < 100 )
4092 return SvNumberFormatter::ExpandTwoDigitYear( nYear,
4093 pStringScanner->GetYear2000() );
4094 return nYear;
4097 sal_uInt16 SvNumberFormatter::ExpandTwoDigitYear( sal_uInt16 nYear ) const
4099 return m_aCurrentLanguage.ExpandTwoDigitYear(nYear);
4102 // static
4103 sal_uInt16 SvNumberFormatter::GetYear2000Default()
4105 if (!comphelper::IsFuzzing())
4106 return officecfg::Office::Common::DateFormat::TwoDigitYear::get();
4107 return 1930;
4110 // static
4111 void SvNumberFormatter::resetTheCurrencyTable()
4113 SAL_INFO("svl", "Resetting the currency table.");
4115 nSystemCurrencyPosition = 0;
4116 g_CurrencyTableInitialized = false;
4118 GetFormatterRegistry().ConfigurationChanged(nullptr, ConfigurationHints::Locale | ConfigurationHints::Currency | ConfigurationHints::DatePatterns);
4121 // static
4122 const NfCurrencyTable& SvNumberFormatter::GetTheCurrencyTable()
4124 while (!g_CurrencyTableInitialized)
4125 ImpInitCurrencyTable();
4126 return theCurrencyTable();
4129 // static
4130 const NfCurrencyEntry* SvNumberFormatter::MatchSystemCurrency()
4132 // MUST call GetTheCurrencyTable() before accessing nSystemCurrencyPosition
4133 const NfCurrencyTable& rTable = GetTheCurrencyTable();
4134 return nSystemCurrencyPosition ? &rTable[nSystemCurrencyPosition] : nullptr;
4137 // static
4138 const NfCurrencyEntry& SvNumberFormatter::GetCurrencyEntry( LanguageType eLang )
4140 if ( eLang == LANGUAGE_SYSTEM )
4142 const NfCurrencyEntry* pCurr = MatchSystemCurrency();
4143 return pCurr ? *pCurr : GetTheCurrencyTable()[0];
4145 else
4147 eLang = MsLangId::getRealLanguage( eLang );
4148 const NfCurrencyTable& rTable = GetTheCurrencyTable();
4149 sal_uInt16 nCount = rTable.size();
4150 for ( sal_uInt16 j = 0; j < nCount; j++ )
4152 if ( rTable[j].GetLanguage() == eLang )
4153 return rTable[j];
4155 return rTable[0];
4160 // static
4161 const NfCurrencyEntry* SvNumberFormatter::GetCurrencyEntry(std::u16string_view rAbbrev, LanguageType eLang )
4163 eLang = MsLangId::getRealLanguage( eLang );
4164 const NfCurrencyTable& rTable = GetTheCurrencyTable();
4165 sal_uInt16 nCount = rTable.size();
4166 for ( sal_uInt16 j = 0; j < nCount; j++ )
4168 if ( rTable[j].GetLanguage() == eLang &&
4169 rTable[j].GetBankSymbol() == rAbbrev )
4171 return &rTable[j];
4174 return nullptr;
4178 // static
4179 const NfCurrencyEntry* SvNumberFormatter::GetLegacyOnlyCurrencyEntry( std::u16string_view rSymbol,
4180 std::u16string_view rAbbrev )
4182 GetTheCurrencyTable(); // just for initialization
4183 const NfCurrencyTable& rTable = theLegacyOnlyCurrencyTable();
4184 sal_uInt16 nCount = rTable.size();
4185 for ( sal_uInt16 j = 0; j < nCount; j++ )
4187 if ( rTable[j].GetSymbol() == rSymbol &&
4188 rTable[j].GetBankSymbol() == rAbbrev )
4190 return &rTable[j];
4193 return nullptr;
4197 // static
4198 IMPL_STATIC_LINK_NOARG( SvNumberFormatter, CurrencyChangeLink, LinkParamNone*, void )
4200 OUString aAbbrev;
4201 LanguageType eLang = LANGUAGE_SYSTEM;
4202 SvtSysLocaleOptions().GetCurrencyAbbrevAndLanguage( aAbbrev, eLang );
4203 SetDefaultSystemCurrency( aAbbrev, eLang );
4207 // static
4208 void SvNumberFormatter::SetDefaultSystemCurrency( std::u16string_view rAbbrev, LanguageType eLang )
4210 ::osl::MutexGuard aGuard( GetGlobalMutex() );
4211 if ( eLang == LANGUAGE_SYSTEM )
4213 eLang = SvtSysLocale().GetLanguageTag().getLanguageType();
4215 const NfCurrencyTable& rTable = GetTheCurrencyTable();
4216 sal_uInt16 nCount = rTable.size();
4217 if ( !rAbbrev.empty() )
4219 for ( sal_uInt16 j = 0; j < nCount; j++ )
4221 if ( rTable[j].GetLanguage() == eLang && rTable[j].GetBankSymbol() == rAbbrev )
4223 nSystemCurrencyPosition = j;
4224 return ;
4228 else
4230 for ( sal_uInt16 j = 0; j < nCount; j++ )
4232 if ( rTable[j].GetLanguage() == eLang )
4234 nSystemCurrencyPosition = j;
4235 return ;
4239 nSystemCurrencyPosition = 0; // not found => simple SYSTEM
4242 void SvNFFormatData::ResetDefaultSystemCurrency()
4244 nDefaultSystemCurrencyFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
4247 void SvNumberFormatter::ResetDefaultSystemCurrency()
4249 m_aFormatData.ResetDefaultSystemCurrency();
4252 void SvNumberFormatter::InvalidateDateAcceptancePatterns()
4254 m_aCurrentLanguage.pStringScanner->InvalidateDateAcceptancePatterns();
4257 sal_uInt32 SvNFFormatData::ImpGetDefaultSystemCurrencyFormat(SvNFLanguageData& rCurrentLanguage,
4258 const NativeNumberWrapper& rNatNum)
4260 if ( nDefaultSystemCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
4262 sal_Int32 nCheck;
4263 SvNumFormatType nType;
4264 NfWSStringsDtor aCurrList;
4265 sal_uInt16 nDefault = rCurrentLanguage.GetCurrencyFormatStrings( aCurrList,
4266 SvNumberFormatter::GetCurrencyEntry( LANGUAGE_SYSTEM ), false );
4267 DBG_ASSERT( aCurrList.size(), "where is the NewCurrency System standard format?!?" );
4268 // if already loaded or user defined nDefaultSystemCurrencyFormat
4269 // will be set to the right value
4270 PutEntry(rCurrentLanguage, rNatNum, aCurrList[ nDefault ], nCheck, nType,
4271 nDefaultSystemCurrencyFormat, LANGUAGE_SYSTEM );
4272 DBG_ASSERT( nCheck == 0, "NewCurrency CheckError" );
4273 DBG_ASSERT( nDefaultSystemCurrencyFormat != NUMBERFORMAT_ENTRY_NOT_FOUND,
4274 "nDefaultSystemCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND" );
4276 return nDefaultSystemCurrencyFormat;
4279 sal_uInt32 SvNFFormatData::ImpGetDefaultCurrencyFormat(SvNFLanguageData& rCurrentLanguage, const NativeNumberWrapper& rNatNum,
4280 sal_uInt32 CLOffset, LanguageType eLnge)
4282 DefaultFormatKeysMap::const_iterator it = aDefaultFormatKeys.find( CLOffset + ZF_STANDARD_CURRENCY );
4283 sal_uInt32 nDefaultCurrencyFormat = (it != aDefaultFormatKeys.end() ?
4284 it->second : NUMBERFORMAT_ENTRY_NOT_FOUND);
4285 if ( nDefaultCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
4287 // look for a defined standard
4288 sal_uInt32 nStopKey = CLOffset + SV_COUNTRY_LANGUAGE_OFFSET;
4289 sal_uInt32 nKey(0);
4290 auto it2 = aFTable.lower_bound( CLOffset );
4291 while ( it2 != aFTable.end() && (nKey = it2->first) >= CLOffset && nKey < nStopKey )
4293 const SvNumberformat* pEntry = it2->second.get();
4294 if ( pEntry->IsStandard() && (pEntry->GetType() & SvNumFormatType::CURRENCY) )
4296 nDefaultCurrencyFormat = nKey;
4297 break; // while
4299 ++it2;
4302 if ( nDefaultCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
4303 { // none found, create one
4304 sal_Int32 nCheck;
4305 NfWSStringsDtor aCurrList;
4306 sal_uInt16 nDefault = rCurrentLanguage.GetCurrencyFormatStrings( aCurrList,
4307 SvNumberFormatter::GetCurrencyEntry(eLnge), false );
4308 DBG_ASSERT( aCurrList.size(), "where is the NewCurrency standard format?" );
4309 if ( !aCurrList.empty() )
4311 // if already loaded or user defined nDefaultSystemCurrencyFormat
4312 // will be set to the right value
4313 SvNumFormatType nType;
4314 PutEntry(rCurrentLanguage, rNatNum, aCurrList[ nDefault ], nCheck, nType,
4315 nDefaultCurrencyFormat, eLnge );
4316 DBG_ASSERT( nCheck == 0, "NewCurrency CheckError" );
4317 DBG_ASSERT( nDefaultCurrencyFormat != NUMBERFORMAT_ENTRY_NOT_FOUND,
4318 "nDefaultCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND" );
4320 // old automatic currency format as a last resort
4321 if ( nDefaultCurrencyFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
4322 nDefaultCurrencyFormat = CLOffset + ZF_STANDARD_CURRENCY+3;
4323 else
4324 { // mark as standard so that it is found next time
4325 SvNumberformat* pEntry = GetFormatEntry( nDefaultCurrencyFormat );
4326 if ( pEntry )
4327 pEntry->SetStandard();
4330 aDefaultFormatKeys[ CLOffset + ZF_STANDARD_CURRENCY ] = nDefaultCurrencyFormat;
4332 return nDefaultCurrencyFormat;
4336 // static
4337 // true: continue; false: break loop, if pFoundEntry==NULL dupe found
4338 bool SvNumberFormatter::ImpLookupCurrencyEntryLoopBody(
4339 const NfCurrencyEntry*& pFoundEntry, bool& bFoundBank, const NfCurrencyEntry* pData,
4340 sal_uInt16 nPos, std::u16string_view rSymbol )
4342 bool bFound;
4343 if ( pData->GetSymbol() == rSymbol )
4345 bFound = true;
4346 bFoundBank = false;
4348 else if ( pData->GetBankSymbol() == rSymbol )
4350 bFound = true;
4351 bFoundBank = true;
4353 else
4354 bFound = false;
4355 if ( bFound )
4357 if ( pFoundEntry && pFoundEntry != pData )
4359 pFoundEntry = nullptr;
4360 return false; // break loop, not unique
4362 if ( nPos == 0 )
4363 { // first entry is SYSTEM
4364 pFoundEntry = MatchSystemCurrency();
4365 if ( pFoundEntry )
4367 return false; // break loop
4368 // even if there are more matching entries
4369 // this one is probably the one we are looking for
4371 else
4373 pFoundEntry = pData;
4376 else
4378 pFoundEntry = pData;
4381 return true;
4384 void SvNFFormatData::MergeDefaultFormatKeys(const DefaultFormatKeysMap& rDefaultFormatKeys)
4386 for (const auto& r : rDefaultFormatKeys)
4388 #ifndef NDEBUG
4389 auto iter = aDefaultFormatKeys.find(r.first);
4390 assert(iter == aDefaultFormatKeys.end() || iter->second == r.second);
4391 #endif
4392 aDefaultFormatKeys[r.first] = r.second;
4396 void SvNumberFormatter::MergeDefaultFormatKeys(const SvNFFormatData::DefaultFormatKeysMap& rDefaultFormatKeys)
4398 ::osl::MutexGuard aGuard( GetInstanceMutex() );
4399 m_aFormatData.MergeDefaultFormatKeys(rDefaultFormatKeys);
4402 bool SvNFFormatData::GetNewCurrencySymbolString(sal_uInt32 nFormat, OUString& rStr,
4403 const NfCurrencyEntry** ppEntry /* = NULL */,
4404 bool* pBank /* = NULL */) const
4406 if ( ppEntry )
4407 *ppEntry = nullptr;
4408 if ( pBank )
4409 *pBank = false;
4411 const SvNumberformat* pFormat = GetFormatEntry(nFormat);
4412 if ( pFormat )
4414 OUString aSymbol, aExtension;
4415 if ( pFormat->GetNewCurrencySymbol( aSymbol, aExtension ) )
4417 OUStringBuffer sBuff(128); // guess-estimate of a value that will pretty much guarantee no re-alloc
4418 if ( ppEntry )
4420 bool bFoundBank = false;
4421 // we definitely need an entry matching the format code string
4422 const NfCurrencyEntry* pFoundEntry = SvNumberFormatter::GetCurrencyEntry(
4423 bFoundBank, aSymbol, aExtension, pFormat->GetLanguage(),
4424 true );
4425 if ( pFoundEntry )
4427 *ppEntry = pFoundEntry;
4428 if ( pBank )
4429 *pBank = bFoundBank;
4430 rStr = pFoundEntry->BuildSymbolString(bFoundBank);
4433 if ( rStr.isEmpty() )
4434 { // analog to BuildSymbolString
4435 sBuff.append("[$");
4436 if ( aSymbol.indexOf( '-' ) != -1 ||
4437 aSymbol.indexOf( ']' ) != -1 )
4439 sBuff.append("\"" + aSymbol + "\"");
4441 else
4443 sBuff.append(aSymbol);
4445 if ( !aExtension.isEmpty() )
4447 sBuff.append(aExtension);
4449 sBuff.append(']');
4451 rStr = sBuff.makeStringAndClear();
4452 return true;
4455 rStr.clear();
4456 return false;
4459 bool SvNumberFormatter::GetNewCurrencySymbolString( sal_uInt32 nFormat, OUString& rStr,
4460 const NfCurrencyEntry** ppEntry /* = NULL */,
4461 bool* pBank /* = NULL */ ) const
4463 ::osl::MutexGuard aGuard( GetInstanceMutex() );
4464 return m_aFormatData.GetNewCurrencySymbolString(nFormat, rStr, ppEntry, pBank);
4467 // static
4468 const NfCurrencyEntry* SvNumberFormatter::GetCurrencyEntry( bool & bFoundBank,
4469 std::u16string_view rSymbol,
4470 std::u16string_view rExtension,
4471 LanguageType eFormatLanguage,
4472 bool bOnlyStringLanguage )
4474 sal_Int32 nExtLen = rExtension.size();
4475 LanguageType eExtLang;
4476 if ( nExtLen )
4478 // rExtension should be a 16-bit hex value max FFFF which may contain a
4479 // leading "-" separator (that is not a minus sign, but toInt32 can be
4480 // used to parse it, with post-processing as necessary):
4481 sal_Int32 nExtLang = o3tl::toInt32(rExtension, 16);
4482 if ( !nExtLang )
4484 eExtLang = LANGUAGE_DONTKNOW;
4486 else
4488 if (nExtLang < 0)
4489 nExtLang = -nExtLang;
4490 SAL_WARN_IF(nExtLang > 0xFFFF, "svl.numbers", "Out of range Lang Id: " << nExtLang << " from input string: " << OUString(rExtension));
4491 eExtLang = LanguageType(nExtLang & 0xFFFF);
4494 else
4496 eExtLang = LANGUAGE_DONTKNOW;
4498 const NfCurrencyEntry* pFoundEntry = nullptr;
4499 const NfCurrencyTable& rTable = GetTheCurrencyTable();
4500 sal_uInt16 nCount = rTable.size();
4501 bool bCont = true;
4503 // first try with given extension language/country
4504 if ( nExtLen )
4506 for ( sal_uInt16 j = 0; j < nCount && bCont; j++ )
4508 LanguageType eLang = rTable[j].GetLanguage();
4509 if ( eLang == eExtLang ||
4510 ((eExtLang == LANGUAGE_DONTKNOW) &&
4511 (eLang == LANGUAGE_SYSTEM)))
4513 bCont = ImpLookupCurrencyEntryLoopBody( pFoundEntry, bFoundBank,
4514 &rTable[j], j, rSymbol );
4519 // ok?
4520 if ( pFoundEntry || !bCont || (bOnlyStringLanguage && nExtLen) )
4522 return pFoundEntry;
4524 if ( !bOnlyStringLanguage )
4526 // now try the language/country of the number format
4527 for ( sal_uInt16 j = 0; j < nCount && bCont; j++ )
4529 LanguageType eLang = rTable[j].GetLanguage();
4530 if ( eLang == eFormatLanguage ||
4531 ((eFormatLanguage == LANGUAGE_DONTKNOW) &&
4532 (eLang == LANGUAGE_SYSTEM)))
4534 bCont = ImpLookupCurrencyEntryLoopBody( pFoundEntry, bFoundBank,
4535 &rTable[j], j, rSymbol );
4539 // ok?
4540 if ( pFoundEntry || !bCont )
4542 return pFoundEntry;
4546 // then try without language/country if no extension specified
4547 if ( !nExtLen )
4549 for ( sal_uInt16 j = 0; j < nCount && bCont; j++ )
4551 bCont = ImpLookupCurrencyEntryLoopBody( pFoundEntry, bFoundBank,
4552 &rTable[j], j, rSymbol );
4556 return pFoundEntry;
4559 void SvNFLanguageData::GetCompatibilityCurrency( OUString& rSymbol, OUString& rAbbrev ) const
4561 const css::uno::Sequence< css::i18n::Currency2 >
4562 xCurrencies( xLocaleData->getAllCurrencies() );
4564 auto pCurrency = std::find_if(xCurrencies.begin(), xCurrencies.end(),
4565 [](const css::i18n::Currency2& rCurrency) { return rCurrency.UsedInCompatibleFormatCodes; });
4566 if (pCurrency != xCurrencies.end())
4568 rSymbol = pCurrency->Symbol;
4569 rAbbrev = pCurrency->BankSymbol;
4571 else
4573 if (LocaleDataWrapper::areChecksEnabled())
4575 LocaleDataWrapper::outputCheckMessage( xLocaleData->
4576 appendLocaleInfo( u"GetCompatibilityCurrency: none?"));
4578 rSymbol = xLocaleData->getCurrSymbol();
4579 rAbbrev = xLocaleData->getCurrBankSymbol();
4583 void SvNumberFormatter::GetCompatibilityCurrency( OUString& rSymbol, OUString& rAbbrev ) const
4585 ::osl::MutexGuard aGuard( GetInstanceMutex() );
4586 m_aCurrentLanguage.GetCompatibilityCurrency(rSymbol, rAbbrev);
4589 static void lcl_CheckCurrencySymbolPosition( const NfCurrencyEntry& rCurr )
4591 switch ( rCurr.GetPositiveFormat() )
4593 case 0: // $1
4594 case 1: // 1$
4595 case 2: // $ 1
4596 case 3: // 1 $
4597 break;
4598 default:
4599 LocaleDataWrapper::outputCheckMessage( "lcl_CheckCurrencySymbolPosition: unknown PositiveFormat");
4600 break;
4602 switch ( rCurr.GetNegativeFormat() )
4604 case 0: // ($1)
4605 case 1: // -$1
4606 case 2: // $-1
4607 case 3: // $1-
4608 case 4: // (1$)
4609 case 5: // -1$
4610 case 6: // 1-$
4611 case 7: // 1$-
4612 case 8: // -1 $
4613 case 9: // -$ 1
4614 case 10: // 1 $-
4615 case 11: // $ -1
4616 case 12 : // $ 1-
4617 case 13 : // 1- $
4618 case 14 : // ($ 1)
4619 case 15 : // (1 $)
4620 break;
4621 default:
4622 LocaleDataWrapper::outputCheckMessage( "lcl_CheckCurrencySymbolPosition: unknown NegativeFormat");
4623 break;
4627 // static
4628 bool SvNumberFormatter::IsLocaleInstalled( LanguageType eLang )
4630 // The set is initialized as a side effect of the currency table
4631 // created, make sure that exists, which usually is the case unless a
4632 // SvNumberFormatter was never instantiated.
4633 GetTheCurrencyTable();
4634 return theInstalledLocales.find( eLang) != theInstalledLocales.end();
4637 // static
4638 void SvNumberFormatter::ImpInitCurrencyTable()
4640 // Race condition possible:
4641 // ::osl::MutexGuard aGuard( GetMutex() );
4642 // while (!g_CurrencyTableInitialized)
4643 // ImpInitCurrencyTable();
4644 static bool bInitializing = false;
4645 if (g_CurrencyTableInitialized || bInitializing)
4647 return ;
4649 bInitializing = true;
4651 LanguageType eSysLang = SvtSysLocale().GetLanguageTag().getLanguageType();
4652 std::optional<LocaleDataWrapper> pLocaleData(std::in_place,
4653 ::comphelper::getProcessComponentContext(),
4654 SvtSysLocale().GetLanguageTag() );
4655 // get user configured currency
4656 OUString aConfiguredCurrencyAbbrev;
4657 LanguageType eConfiguredCurrencyLanguage = LANGUAGE_SYSTEM;
4658 SvtSysLocaleOptions().GetCurrencyAbbrevAndLanguage(
4659 aConfiguredCurrencyAbbrev, eConfiguredCurrencyLanguage );
4660 sal_uInt16 nSecondarySystemCurrencyPosition = 0;
4661 sal_uInt16 nMatchingSystemCurrencyPosition = 0;
4663 // First entry is SYSTEM:
4664 auto& rCurrencyTable = theCurrencyTable();
4665 rCurrencyTable.insert(
4666 rCurrencyTable.begin(),
4667 NfCurrencyEntry(*pLocaleData, LANGUAGE_SYSTEM));
4668 sal_uInt16 nCurrencyPos = 1;
4670 const css::uno::Sequence< css::lang::Locale > xLoc = LocaleDataWrapper::getInstalledLocaleNames();
4671 sal_Int32 nLocaleCount = xLoc.getLength();
4672 SAL_INFO( "svl.numbers", "number of locales: \"" << nLocaleCount << "\"" );
4673 NfCurrencyTable &rLegacyOnlyCurrencyTable = theLegacyOnlyCurrencyTable();
4674 sal_uInt16 nLegacyOnlyCurrencyPos = 0;
4675 for ( css::lang::Locale const & rLocale : xLoc )
4677 LanguageType eLang = LanguageTag::convertToLanguageType( rLocale, false);
4678 theInstalledLocales.insert( eLang);
4679 pLocaleData.emplace(
4680 ::comphelper::getProcessComponentContext(),
4681 LanguageTag(rLocale) );
4682 Sequence< Currency2 > aCurrSeq = pLocaleData->getAllCurrencies();
4683 sal_Int32 nCurrencyCount = aCurrSeq.getLength();
4684 Currency2 const * const pCurrencies = aCurrSeq.getConstArray();
4686 // one default currency for each locale, insert first so it is found first
4687 sal_Int32 nDefault;
4688 for ( nDefault = 0; nDefault < nCurrencyCount; nDefault++ )
4690 if ( pCurrencies[nDefault].Default )
4691 break;
4693 std::optional<NfCurrencyEntry> pEntry;
4694 if ( nDefault < nCurrencyCount )
4696 pEntry.emplace(pCurrencies[nDefault], *pLocaleData, eLang);
4698 else
4699 { // first or ShellsAndPebbles
4700 pEntry.emplace(*pLocaleData, eLang);
4702 if (LocaleDataWrapper::areChecksEnabled())
4704 lcl_CheckCurrencySymbolPosition( *pEntry );
4706 if ( !nSystemCurrencyPosition && !aConfiguredCurrencyAbbrev.isEmpty() &&
4707 pEntry->GetBankSymbol() == aConfiguredCurrencyAbbrev &&
4708 pEntry->GetLanguage() == eConfiguredCurrencyLanguage )
4710 nSystemCurrencyPosition = nCurrencyPos;
4712 if ( !nMatchingSystemCurrencyPosition &&
4713 pEntry->GetLanguage() == eSysLang )
4715 nMatchingSystemCurrencyPosition = nCurrencyPos;
4717 rCurrencyTable.insert(
4718 rCurrencyTable.begin() + nCurrencyPos++, std::move(*pEntry));
4719 // all remaining currencies for each locale
4720 if ( nCurrencyCount > 1 )
4722 sal_Int32 nCurrency;
4723 for ( nCurrency = 0; nCurrency < nCurrencyCount; nCurrency++ )
4725 if (pCurrencies[nCurrency].LegacyOnly)
4727 rLegacyOnlyCurrencyTable.insert(
4728 rLegacyOnlyCurrencyTable.begin() + nLegacyOnlyCurrencyPos++,
4729 NfCurrencyEntry(
4730 pCurrencies[nCurrency], *pLocaleData, eLang));
4732 else if ( nCurrency != nDefault )
4734 pEntry.emplace(pCurrencies[nCurrency], *pLocaleData, eLang);
4735 // no dupes
4736 bool bInsert = true;
4737 sal_uInt16 n = rCurrencyTable.size();
4738 sal_uInt16 aCurrencyIndex = 1; // skip first SYSTEM entry
4739 for ( sal_uInt16 j=1; j<n; j++ )
4741 if ( rCurrencyTable[aCurrencyIndex++] == *pEntry )
4743 bInsert = false;
4744 break; // for
4747 if ( !bInsert )
4749 pEntry.reset();
4751 else
4753 if ( !nSecondarySystemCurrencyPosition &&
4754 (!aConfiguredCurrencyAbbrev.isEmpty() ?
4755 pEntry->GetBankSymbol() == aConfiguredCurrencyAbbrev :
4756 pEntry->GetLanguage() == eConfiguredCurrencyLanguage) )
4758 nSecondarySystemCurrencyPosition = nCurrencyPos;
4760 if ( !nMatchingSystemCurrencyPosition &&
4761 pEntry->GetLanguage() == eSysLang )
4763 nMatchingSystemCurrencyPosition = nCurrencyPos;
4765 rCurrencyTable.insert(
4766 rCurrencyTable.begin() + nCurrencyPos++, std::move(*pEntry));
4772 if ( !nSystemCurrencyPosition )
4774 nSystemCurrencyPosition = nSecondarySystemCurrencyPosition;
4776 if ((!aConfiguredCurrencyAbbrev.isEmpty() && !nSystemCurrencyPosition) &&
4777 LocaleDataWrapper::areChecksEnabled())
4779 LocaleDataWrapper::outputCheckMessage(
4780 "SvNumberFormatter::ImpInitCurrencyTable: configured currency not in I18N locale data.");
4782 // match SYSTEM if no configured currency found
4783 if ( !nSystemCurrencyPosition )
4785 nSystemCurrencyPosition = nMatchingSystemCurrencyPosition;
4787 if ((aConfiguredCurrencyAbbrev.isEmpty() && !nSystemCurrencyPosition) &&
4788 LocaleDataWrapper::areChecksEnabled())
4790 LocaleDataWrapper::outputCheckMessage(
4791 "SvNumberFormatter::ImpInitCurrencyTable: system currency not in I18N locale data.");
4793 pLocaleData.reset();
4794 SvtSysLocaleOptions::SetCurrencyChangeLink( LINK( nullptr, SvNumberFormatter, CurrencyChangeLink ) );
4795 bInitializing = false;
4796 g_CurrencyTableInitialized = true;
4799 static std::ptrdiff_t addToCurrencyFormatsList( NfWSStringsDtor& rStrArr, const OUString& rFormat )
4801 // Prevent duplicates even over subsequent calls of
4802 // GetCurrencyFormatStrings() with the same vector.
4803 NfWSStringsDtor::const_iterator it( std::find( rStrArr.begin(), rStrArr.end(), rFormat));
4804 if (it != rStrArr.end())
4805 return it - rStrArr.begin();
4807 rStrArr.push_back( rFormat);
4808 return rStrArr.size() - 1;
4811 sal_uInt16 SvNFLanguageData::GetCurrencyFormatStrings(NfWSStringsDtor& rStrArr,
4812 const NfCurrencyEntry& rCurr,
4813 bool bBank) const
4815 OUString aRed = "["
4816 + pFormatScanner->GetRedString()
4817 + "]";
4819 sal_uInt16 nDefault = 0;
4820 if ( bBank )
4822 // Only bank symbols.
4823 OUString aPositiveBank = rCurr.BuildPositiveFormatString(true, *xLocaleData);
4824 OUString aNegativeBank = rCurr.BuildNegativeFormatString(true, *xLocaleData );
4826 OUString format1 = aPositiveBank
4827 + ";"
4828 + aNegativeBank;
4829 addToCurrencyFormatsList( rStrArr, format1);
4831 OUString format2 = aPositiveBank
4832 + ";"
4833 + aRed
4834 + aNegativeBank;
4835 nDefault = addToCurrencyFormatsList( rStrArr, format2);
4837 else
4839 // Mixed formats like in SvNumberFormatter::ImpGenerateFormats() but no
4840 // duplicates if no decimals in currency.
4841 OUString aPositive = rCurr.BuildPositiveFormatString(false, *xLocaleData );
4842 OUString aNegative = rCurr.BuildNegativeFormatString(false, *xLocaleData );
4843 OUString format1;
4844 OUString format2;
4845 OUString format3;
4846 OUString format4;
4847 OUString format5;
4848 if (rCurr.GetDigits())
4850 OUString aPositiveNoDec = rCurr.BuildPositiveFormatString(false, *xLocaleData, 0);
4851 OUString aNegativeNoDec = rCurr.BuildNegativeFormatString(false, *xLocaleData, 0 );
4852 OUString aPositiveDashed = rCurr.BuildPositiveFormatString(false, *xLocaleData, 2);
4853 OUString aNegativeDashed = rCurr.BuildNegativeFormatString(false, *xLocaleData, 2);
4855 format1 = aPositiveNoDec
4856 + ";"
4857 + aNegativeNoDec;
4859 format3 = aPositiveNoDec
4860 + ";"
4861 + aRed
4862 + aNegativeNoDec;
4864 format5 = aPositiveDashed
4865 + ";"
4866 + aRed
4867 + aNegativeDashed;
4870 format2 = aPositive
4871 + ";"
4872 + aNegative;
4874 format4 = aPositive
4875 + ";"
4876 + aRed
4877 + aNegative;
4879 if (rCurr.GetDigits())
4881 addToCurrencyFormatsList( rStrArr, format1);
4883 addToCurrencyFormatsList( rStrArr, format2);
4884 if (rCurr.GetDigits())
4886 addToCurrencyFormatsList( rStrArr, format3);
4888 nDefault = addToCurrencyFormatsList( rStrArr, format4);
4889 if (rCurr.GetDigits())
4891 addToCurrencyFormatsList( rStrArr, format5);
4894 return nDefault;
4897 sal_uInt16 SvNumberFormatter::GetCurrencyFormatStrings( NfWSStringsDtor& rStrArr,
4898 const NfCurrencyEntry& rCurr,
4899 bool bBank ) const
4901 ::osl::MutexGuard aGuard(GetInstanceMutex());
4902 return m_aCurrentLanguage.GetCurrencyFormatStrings(rStrArr, rCurr, bBank);
4905 sal_uInt32 SvNumberFormatter::GetMergeFormatIndex( sal_uInt32 nOldFmt ) const
4907 ::osl::MutexGuard aGuard( GetInstanceMutex() );
4908 if (pMergeTable)
4910 SvNumberFormatterIndexTable::const_iterator it = pMergeTable->find(nOldFmt);
4911 if (it != pMergeTable->end())
4913 return it->second;
4916 return nOldFmt;
4919 bool SvNumberFormatter::HasMergeFormatTable() const
4921 ::osl::MutexGuard aGuard( GetInstanceMutex() );
4922 return pMergeTable && !pMergeTable->empty();
4925 // static
4926 sal_uInt16 SvNumberFormatter::ExpandTwoDigitYear( sal_uInt16 nYear, sal_uInt16 nTwoDigitYearStart )
4928 if ( nYear < 100 )
4930 if ( nYear < (nTwoDigitYearStart % 100) )
4932 return nYear + (((nTwoDigitYearStart / 100) + 1) * 100);
4934 else
4936 return nYear + ((nTwoDigitYearStart / 100) * 100);
4939 return nYear;
4942 NfCurrencyEntry::NfCurrencyEntry( const LocaleDataWrapper& rLocaleData, LanguageType eLang )
4944 aSymbol = rLocaleData.getCurrSymbol();
4945 aBankSymbol = rLocaleData.getCurrBankSymbol();
4946 eLanguage = eLang;
4947 nPositiveFormat = rLocaleData.getCurrPositiveFormat();
4948 nNegativeFormat = rLocaleData.getCurrNegativeFormat();
4949 nDigits = rLocaleData.getCurrDigits();
4950 cZeroChar = rLocaleData.getCurrZeroChar();
4954 NfCurrencyEntry::NfCurrencyEntry( const css::i18n::Currency & rCurr,
4955 const LocaleDataWrapper& rLocaleData, LanguageType eLang )
4957 aSymbol = rCurr.Symbol;
4958 aBankSymbol = rCurr.BankSymbol;
4959 eLanguage = eLang;
4960 nPositiveFormat = rLocaleData.getCurrPositiveFormat();
4961 nNegativeFormat = rLocaleData.getCurrNegativeFormat();
4962 nDigits = rCurr.DecimalPlaces;
4963 cZeroChar = rLocaleData.getCurrZeroChar();
4966 bool NfCurrencyEntry::operator==( const NfCurrencyEntry& r ) const
4968 return aSymbol == r.aSymbol
4969 && aBankSymbol == r.aBankSymbol
4970 && eLanguage == r.eLanguage
4974 OUString NfCurrencyEntry::BuildSymbolString(bool bBank,
4975 bool bWithoutExtension) const
4977 OUStringBuffer aBuf("[$");
4978 if (bBank)
4980 aBuf.append(aBankSymbol);
4982 else
4984 if ( aSymbol.indexOf( '-' ) >= 0 ||
4985 aSymbol.indexOf( ']' ) >= 0)
4987 aBuf.append("\"" + aSymbol + "\"");
4989 else
4991 aBuf.append(aSymbol);
4993 if ( !bWithoutExtension && eLanguage != LANGUAGE_DONTKNOW && eLanguage != LANGUAGE_SYSTEM )
4995 sal_Int32 nLang = static_cast<sal_uInt16>(eLanguage);
4996 aBuf.append("-" + OUString::number(nLang, 16).toAsciiUpperCase());
4999 aBuf.append(']');
5000 return aBuf.makeStringAndClear();
5003 OUString NfCurrencyEntry::Impl_BuildFormatStringNumChars( const LocaleDataWrapper& rLoc,
5004 sal_uInt16 nDecimalFormat) const
5006 OUStringBuffer aBuf("#" + rLoc.getNumThousandSep() + "##0");
5007 if (nDecimalFormat && nDigits)
5009 aBuf.append(rLoc.getNumDecimalSep());
5010 sal_Unicode cDecimalChar = nDecimalFormat == 2 ? '-' : cZeroChar;
5011 for (sal_uInt16 i = 0; i < nDigits; ++i)
5013 aBuf.append(cDecimalChar);
5016 return aBuf.makeStringAndClear();
5020 OUString NfCurrencyEntry::BuildPositiveFormatString(bool bBank, const LocaleDataWrapper& rLoc,
5021 sal_uInt16 nDecimalFormat) const
5023 OUStringBuffer sBuf(Impl_BuildFormatStringNumChars(rLoc, nDecimalFormat));
5024 sal_uInt16 nPosiForm = NfCurrencyEntry::GetEffectivePositiveFormat( rLoc.getCurrPositiveFormat(),
5025 nPositiveFormat, bBank );
5026 CompletePositiveFormatString(sBuf, bBank, nPosiForm);
5027 return sBuf.makeStringAndClear();
5031 OUString NfCurrencyEntry::BuildNegativeFormatString(bool bBank,
5032 const LocaleDataWrapper& rLoc, sal_uInt16 nDecimalFormat ) const
5034 OUStringBuffer sBuf(Impl_BuildFormatStringNumChars(rLoc, nDecimalFormat));
5035 sal_uInt16 nNegaForm = NfCurrencyEntry::GetEffectiveNegativeFormat( rLoc.getCurrNegativeFormat(),
5036 nNegativeFormat, bBank );
5037 CompleteNegativeFormatString(sBuf, bBank, nNegaForm);
5038 return sBuf.makeStringAndClear();
5042 void NfCurrencyEntry::CompletePositiveFormatString(OUStringBuffer& rStr, bool bBank,
5043 sal_uInt16 nPosiForm) const
5045 OUString aSymStr = BuildSymbolString(bBank);
5046 NfCurrencyEntry::CompletePositiveFormatString( rStr, aSymStr, nPosiForm );
5050 void NfCurrencyEntry::CompleteNegativeFormatString(OUStringBuffer& rStr, bool bBank,
5051 sal_uInt16 nNegaForm) const
5053 OUString aSymStr = BuildSymbolString(bBank);
5054 NfCurrencyEntry::CompleteNegativeFormatString( rStr, aSymStr, nNegaForm );
5058 // static
5059 void NfCurrencyEntry::CompletePositiveFormatString(OUStringBuffer& rStr, std::u16string_view rSymStr,
5060 sal_uInt16 nPositiveFormat)
5062 switch( nPositiveFormat )
5064 case 0: // $1
5065 rStr.insert(0, rSymStr);
5066 break;
5067 case 1: // 1$
5068 rStr.append(rSymStr);
5069 break;
5070 case 2: // $ 1
5072 rStr.insert(0, OUString::Concat(rSymStr) + " ");
5074 break;
5075 case 3: // 1 $
5077 rStr.append(' ');
5078 rStr.append(rSymStr);
5080 break;
5081 default:
5082 SAL_WARN( "svl.numbers", "NfCurrencyEntry::CompletePositiveFormatString: unknown option");
5083 break;
5088 // static
5089 void NfCurrencyEntry::CompleteNegativeFormatString(OUStringBuffer& rStr,
5090 std::u16string_view rSymStr,
5091 sal_uInt16 nNegaForm)
5093 switch( nNegaForm )
5095 case 0: // ($1)
5097 rStr.insert(0, OUString::Concat("(") + rSymStr);
5098 rStr.append(')');
5100 break;
5101 case 1: // -$1
5103 rStr.insert(0, OUString::Concat("-") + rSymStr);
5105 break;
5106 case 2: // $-1
5108 rStr.insert(0, OUString::Concat(rSymStr) + "-");
5110 break;
5111 case 3: // $1-
5113 rStr.insert(0, rSymStr);
5114 rStr.append('-');
5116 break;
5117 case 4: // (1$)
5119 rStr.insert(0, '(');
5120 rStr.append(rSymStr);
5121 rStr.append(')');
5123 break;
5124 case 5: // -1$
5126 rStr.append(rSymStr);
5127 rStr.insert(0, '-');
5129 break;
5130 case 6: // 1-$
5132 rStr.append('-');
5133 rStr.append(rSymStr);
5135 break;
5136 case 7: // 1$-
5138 rStr.append(rSymStr);
5139 rStr.append('-');
5141 break;
5142 case 8: // -1 $
5144 rStr.append(' ');
5145 rStr.append(rSymStr);
5146 rStr.insert(0, '-');
5148 break;
5149 case 9: // -$ 1
5151 rStr.insert(0, OUString::Concat("-") + rSymStr + " ");
5153 break;
5154 case 10: // 1 $-
5156 rStr.append(' ');
5157 rStr.append(rSymStr);
5158 rStr.append('-');
5160 break;
5161 case 11: // $ -1
5163 rStr.insert(0, OUString::Concat(rSymStr) + " -");
5165 break;
5166 case 12 : // $ 1-
5168 rStr.insert(0, OUString::Concat(rSymStr) + " ");
5169 rStr.append('-');
5171 break;
5172 case 13 : // 1- $
5174 rStr.append('-');
5175 rStr.append(' ');
5176 rStr.append(rSymStr);
5178 break;
5179 case 14 : // ($ 1)
5181 rStr.insert(0, OUString::Concat("(") + rSymStr + " ");
5182 rStr.append(')');
5184 break;
5185 case 15 : // (1 $)
5187 rStr.insert(0, '(');
5188 rStr.append(' ');
5189 rStr.append(rSymStr);
5190 rStr.append(')');
5192 break;
5193 default:
5194 SAL_WARN( "svl.numbers", "NfCurrencyEntry::CompleteNegativeFormatString: unknown option");
5195 break;
5200 // static
5201 sal_uInt16 NfCurrencyEntry::GetEffectivePositiveFormat( sal_uInt16 nIntlFormat,
5202 sal_uInt16 nCurrFormat, bool bBank )
5204 if ( bBank )
5206 #if NF_BANKSYMBOL_FIX_POSITION
5207 (void) nIntlFormat; // avoid warnings
5208 return 3;
5209 #else
5210 switch ( nIntlFormat )
5212 case 0: // $1
5213 nIntlFormat = 2; // $ 1
5214 break;
5215 case 1: // 1$
5216 nIntlFormat = 3; // 1 $
5217 break;
5218 case 2: // $ 1
5219 break;
5220 case 3: // 1 $
5221 break;
5222 default:
5223 SAL_WARN( "svl.numbers", "NfCurrencyEntry::GetEffectivePositiveFormat: unknown option");
5224 break;
5226 return nIntlFormat;
5227 #endif
5229 else
5230 return nCurrFormat;
5234 //! Call this only if nCurrFormat is really with parentheses!
5235 static sal_uInt16 lcl_MergeNegativeParenthesisFormat( sal_uInt16 nIntlFormat, sal_uInt16 nCurrFormat )
5237 short nSign = 0; // -1:=bracket 0:=left, 1:=middle, 2:=right
5238 switch ( nIntlFormat )
5240 case 0: // ($1)
5241 case 4: // (1$)
5242 case 14 : // ($ 1)
5243 case 15 : // (1 $)
5244 return nCurrFormat;
5245 case 1: // -$1
5246 case 5: // -1$
5247 case 8: // -1 $
5248 case 9: // -$ 1
5249 nSign = 0;
5250 break;
5251 case 2: // $-1
5252 case 6: // 1-$
5253 case 11 : // $ -1
5254 case 13 : // 1- $
5255 nSign = 1;
5256 break;
5257 case 3: // $1-
5258 case 7: // 1$-
5259 case 10: // 1 $-
5260 case 12 : // $ 1-
5261 nSign = 2;
5262 break;
5263 default:
5264 SAL_WARN( "svl.numbers", "lcl_MergeNegativeParenthesisFormat: unknown option");
5265 break;
5268 switch ( nCurrFormat )
5270 case 0: // ($1)
5271 switch ( nSign )
5273 case 0:
5274 return 1; // -$1
5275 case 1:
5276 return 2; // $-1
5277 case 2:
5278 return 3; // $1-
5280 break;
5281 case 4: // (1$)
5282 switch ( nSign )
5284 case 0:
5285 return 5; // -1$
5286 case 1:
5287 return 6; // 1-$
5288 case 2:
5289 return 7; // 1$-
5291 break;
5292 case 14 : // ($ 1)
5293 switch ( nSign )
5295 case 0:
5296 return 9; // -$ 1
5297 case 1:
5298 return 11; // $ -1
5299 case 2:
5300 return 12; // $ 1-
5302 break;
5303 case 15 : // (1 $)
5304 switch ( nSign )
5306 case 0:
5307 return 8; // -1 $
5308 case 1:
5309 return 13; // 1- $
5310 case 2:
5311 return 10; // 1 $-
5313 break;
5315 return nCurrFormat;
5319 // static
5320 sal_uInt16 NfCurrencyEntry::GetEffectiveNegativeFormat( sal_uInt16 nIntlFormat,
5321 sal_uInt16 nCurrFormat, bool bBank )
5323 if ( bBank )
5325 #if NF_BANKSYMBOL_FIX_POSITION
5326 return 8;
5327 #else
5328 switch ( nIntlFormat )
5330 case 0: // ($1)
5331 // nIntlFormat = 14; // ($ 1)
5332 nIntlFormat = 9; // -$ 1
5333 break;
5334 case 1: // -$1
5335 nIntlFormat = 9; // -$ 1
5336 break;
5337 case 2: // $-1
5338 nIntlFormat = 11; // $ -1
5339 break;
5340 case 3: // $1-
5341 nIntlFormat = 12; // $ 1-
5342 break;
5343 case 4: // (1$)
5344 // nIntlFormat = 15; // (1 $)
5345 nIntlFormat = 8; // -1 $
5346 break;
5347 case 5: // -1$
5348 nIntlFormat = 8; // -1 $
5349 break;
5350 case 6: // 1-$
5351 nIntlFormat = 13; // 1- $
5352 break;
5353 case 7: // 1$-
5354 nIntlFormat = 10; // 1 $-
5355 break;
5356 case 8: // -1 $
5357 break;
5358 case 9: // -$ 1
5359 break;
5360 case 10: // 1 $-
5361 break;
5362 case 11: // $ -1
5363 break;
5364 case 12 : // $ 1-
5365 break;
5366 case 13 : // 1- $
5367 break;
5368 case 14 : // ($ 1)
5369 // nIntlFormat = 14; // ($ 1)
5370 nIntlFormat = 9; // -$ 1
5371 break;
5372 case 15 : // (1 $)
5373 // nIntlFormat = 15; // (1 $)
5374 nIntlFormat = 8; // -1 $
5375 break;
5376 default:
5377 SAL_WARN( "svl.numbers", "NfCurrencyEntry::GetEffectiveNegativeFormat: unknown option");
5378 break;
5380 #endif
5382 else if ( nIntlFormat != nCurrFormat )
5384 switch ( nCurrFormat )
5386 case 0: // ($1)
5387 nIntlFormat = lcl_MergeNegativeParenthesisFormat(
5388 nIntlFormat, nCurrFormat );
5389 break;
5390 case 1: // -$1
5391 nIntlFormat = nCurrFormat;
5392 break;
5393 case 2: // $-1
5394 nIntlFormat = nCurrFormat;
5395 break;
5396 case 3: // $1-
5397 nIntlFormat = nCurrFormat;
5398 break;
5399 case 4: // (1$)
5400 nIntlFormat = lcl_MergeNegativeParenthesisFormat(
5401 nIntlFormat, nCurrFormat );
5402 break;
5403 case 5: // -1$
5404 nIntlFormat = nCurrFormat;
5405 break;
5406 case 6: // 1-$
5407 nIntlFormat = nCurrFormat;
5408 break;
5409 case 7: // 1$-
5410 nIntlFormat = nCurrFormat;
5411 break;
5412 case 8: // -1 $
5413 nIntlFormat = nCurrFormat;
5414 break;
5415 case 9: // -$ 1
5416 nIntlFormat = nCurrFormat;
5417 break;
5418 case 10: // 1 $-
5419 nIntlFormat = nCurrFormat;
5420 break;
5421 case 11: // $ -1
5422 nIntlFormat = nCurrFormat;
5423 break;
5424 case 12 : // $ 1-
5425 nIntlFormat = nCurrFormat;
5426 break;
5427 case 13 : // 1- $
5428 nIntlFormat = nCurrFormat;
5429 break;
5430 case 14 : // ($ 1)
5431 nIntlFormat = lcl_MergeNegativeParenthesisFormat(
5432 nIntlFormat, nCurrFormat );
5433 break;
5434 case 15 : // (1 $)
5435 nIntlFormat = lcl_MergeNegativeParenthesisFormat(
5436 nIntlFormat, nCurrFormat );
5437 break;
5438 default:
5439 SAL_WARN( "svl.numbers", "NfCurrencyEntry::GetEffectiveNegativeFormat: unknown option");
5440 break;
5443 return nIntlFormat;
5446 const NfKeywordTable & SvNumberFormatter::GetKeywords( sal_uInt32 nKey )
5448 osl::MutexGuard aGuard( GetInstanceMutex() );
5449 const SvNumberformat* pFormat = m_aFormatData.GetFormatEntry( nKey);
5450 if (pFormat)
5451 ChangeIntl( pFormat->GetLanguage());
5452 else
5453 ChangeIntl( IniLnge);
5454 return m_aCurrentLanguage.pFormatScanner->GetKeywords();
5457 // static
5458 const NfKeywordTable & SvNumberFormatter::GetEnglishKeywords()
5460 return ImpSvNumberformatScan::GetEnglishKeywords();
5463 // static
5464 const std::vector<Color> & SvNumberFormatter::GetStandardColors()
5466 return ImpSvNumberformatScan::GetStandardColors();
5469 // static
5470 size_t SvNumberFormatter::GetMaxDefaultColors()
5472 return ImpSvNumberformatScan::GetMaxDefaultColors();
5475 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */