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