blob: 4ae2b4456ee72c338c998312cd5f0d491ccc93c4 [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_unotools.hxx"
#include <string.h> // memcpy()
#include <stdio.h> // fprintf(), stderr
#include <unotools/localedatawrapper.hxx>
#include <unotools/numberformatcodewrapper.hxx>
#include <unotools/calendarwrapper.hxx>
#include <unotools/digitgroupingiterator.hxx>
#include <tools/string.hxx>
#include <tools/debug.hxx>
#include <i18npool/mslangid.hxx>
#ifndef _COMPHELPER_COMPONENTFACTORY_HXX_
#include <comphelper/componentfactory.hxx>
#endif
#include <unotools/processfactory.hxx>
#include <com/sun/star/uno/XInterface.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/i18n/KNumberFormatUsage.hpp>
#include <com/sun/star/i18n/KNumberFormatType.hpp>
#include <com/sun/star/i18n/CalendarFieldIndex.hpp>
#include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
#ifndef _COM_SUN_STAR_I18N_NUMBERFORMATINDEX_HPP_
#include <com/sun/star/i18n/NumberFormatIndex.hdl>
#endif
#include <rtl/instance.hxx>
#define LOCALEDATA_LIBRARYNAME "i18npool"
#define LOCALEDATA_SERVICENAME "com.sun.star.i18n.LocaleData"
static const int nDateFormatInvalid = -1;
static const sal_uInt16 nCurrFormatInvalid = 0xffff;
static const sal_uInt16 nCurrFormatDefault = 0;
using namespace ::com::sun::star;
using namespace ::com::sun::star::i18n;
using namespace ::com::sun::star::uno;
namespace
{
struct InstalledLocales
: public rtl::Static<
uno::Sequence< lang::Locale >, InstalledLocales >
{};
struct InstalledLanguageTypes
: public rtl::Static<
uno::Sequence< sal_uInt16 >, InstalledLanguageTypes >
{};
}
sal_uInt8 LocaleDataWrapper::nLocaleDataChecking = 0;
LocaleDataWrapper::LocaleDataWrapper(
const Reference< lang::XMultiServiceFactory > & xSF,
const lang::Locale& rLocale
)
:
xSMgr( xSF ),
bLocaleDataItemValid( sal_False ),
bReservedWordValid( sal_False )
{
setLocale( rLocale );
if ( xSMgr.is() )
{
try
{
xLD = Reference< XLocaleData2 > ( xSMgr->createInstance(
::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( LOCALEDATA_SERVICENAME ) ) ),
uno::UNO_QUERY );
}
catch ( Exception& e )
{
#ifdef DBG_UTIL
ByteString aMsg( "LocaleDataWrapper ctor: Exception caught\n" );
aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 );
DBG_ERRORFILE( aMsg.GetBuffer() );
#else
(void)e;
#endif
}
}
else
{ // try to get an instance somehow
DBG_ERRORFILE( "LocaleDataWrapper: no service manager, trying own" );
try
{
Reference< XInterface > xI = ::comphelper::getComponentInstance(
::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( LLCF_LIBNAME( LOCALEDATA_LIBRARYNAME ) ) ),
::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( LOCALEDATA_SERVICENAME ) ) );
if ( xI.is() )
{
Any x = xI->queryInterface( ::getCppuType((const Reference< XLocaleData2 >*)0) );
x >>= xLD;
}
}
catch ( Exception& e )
{
#ifdef DBG_UTIL
ByteString aMsg( "getComponentInstance: Exception caught\n" );
aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 );
DBG_ERRORFILE( aMsg.GetBuffer() );
#else
(void)e;
#endif
}
}
}
LocaleDataWrapper::~LocaleDataWrapper()
{
}
void LocaleDataWrapper::setLocale( const ::com::sun::star::lang::Locale& rLocale )
{
::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nCriticalChange );
aLocale = rLocale;
invalidateData();
}
const ::com::sun::star::lang::Locale& LocaleDataWrapper::getLocale() const
{
::utl::ReadWriteGuard aGuard( aMutex );
return aLocale;
}
void LocaleDataWrapper::invalidateData()
{
aCurrSymbol.Erase();
aCurrBankSymbol.Erase();
nDateFormat = nLongDateFormat = nDateFormatInvalid;
nCurrPositiveFormat = nCurrNegativeFormat = nCurrDigits = nCurrFormatInvalid;
if ( bLocaleDataItemValid )
{
for ( sal_Int32 j=0; j<LocaleItem::COUNT; j++ )
{
aLocaleItem[j].Erase();
}
bLocaleDataItemValid = sal_False;
}
if ( bReservedWordValid )
{
for ( sal_Int16 j=0; j<reservedWords::COUNT; j++ )
{
aReservedWord[j].Erase();
}
bReservedWordValid = sal_False;
}
xDefaultCalendar.reset();
if (aGrouping.getLength())
aGrouping[0] = 0;
// dummies
cCurrZeroChar = '0';
}
::com::sun::star::i18n::LanguageCountryInfo LocaleDataWrapper::getLanguageCountryInfo() const
{
try
{
if ( xLD.is() )
return xLD->getLanguageCountryInfo( getLocale() );
}
catch ( Exception& e )
{
#ifdef DBG_UTIL
ByteString aMsg( "getLanguageCountryInfo: Exception caught\n" );
aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 );
DBG_ERRORFILE( aMsg.GetBuffer() );
#else
(void)e;
#endif
}
return ::com::sun::star::i18n::LanguageCountryInfo();
}
::com::sun::star::i18n::LocaleDataItem LocaleDataWrapper::getLocaleItem() const
{
try
{
if ( xLD.is() )
return xLD->getLocaleItem( getLocale() );
}
catch ( Exception& e )
{
#ifdef DBG_UTIL
ByteString aMsg( "getLocaleItem: Exception caught\n" );
aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 );
DBG_ERRORFILE( aMsg.GetBuffer() );
#else
(void)e;
#endif
}
return ::com::sun::star::i18n::LocaleDataItem();
}
::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Calendar > LocaleDataWrapper::getAllCalendars() const
{
try
{
if ( xLD.is() )
return xLD->getAllCalendars( getLocale() );
}
catch ( Exception& e )
{
#ifdef DBG_UTIL
ByteString aMsg( "getAllCalendars: Exception caught\n" );
aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 );
DBG_ERRORFILE( aMsg.GetBuffer() );
#else
(void)e;
#endif
}
return ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Calendar >(0);
}
::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Currency2 > LocaleDataWrapper::getAllCurrencies() const
{
try
{
if ( xLD.is() )
return xLD->getAllCurrencies2( getLocale() );
}
catch ( Exception& e )
{
#ifdef DBG_UTIL
ByteString aMsg( "getAllCurrencies: Exception caught\n" );
aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 );
DBG_ERRORFILE( aMsg.GetBuffer() );
#else
(void)e;
#endif
}
return ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Currency2 >(0);
}
::com::sun::star::uno::Sequence< ::com::sun::star::i18n::FormatElement > LocaleDataWrapper::getAllFormats() const
{
try
{
if ( xLD.is() )
return xLD->getAllFormats( getLocale() );
}
catch ( Exception& e )
{
#ifdef DBG_UTIL
ByteString aMsg( "getAllFormats: Exception caught\n" );
aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 );
DBG_ERRORFILE( aMsg.GetBuffer() );
#else
(void)e;
#endif
}
return ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::FormatElement >(0);
}
::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Implementation > LocaleDataWrapper::getCollatorImplementations() const
{
try
{
if ( xLD.is() )
return xLD->getCollatorImplementations( getLocale() );
}
catch ( Exception& e )
{
#ifdef DBG_UTIL
ByteString aMsg( "getCollatorImplementations: Exception caught\n" );
aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 );
DBG_ERRORFILE( aMsg.GetBuffer() );
#else
(void)e;
#endif
}
return ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Implementation >(0);
}
::com::sun::star::uno::Sequence< ::rtl::OUString > LocaleDataWrapper::getTransliterations() const
{
try
{
if ( xLD.is() )
return xLD->getTransliterations( getLocale() );
}
catch ( Exception& e )
{
#ifdef DBG_UTIL
ByteString aMsg( "getTransliterations: Exception caught\n" );
aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 );
DBG_ERRORFILE( aMsg.GetBuffer() );
#else
(void)e;
#endif
}
return ::com::sun::star::uno::Sequence< ::rtl::OUString >(0);
}
::com::sun::star::i18n::ForbiddenCharacters LocaleDataWrapper::getForbiddenCharacters() const
{
try
{
if ( xLD.is() )
return xLD->getForbiddenCharacters( getLocale() );
}
catch ( Exception& e )
{
#ifdef DBG_UTIL
ByteString aMsg( "getForbiddenCharacters: Exception caught\n" );
aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 );
DBG_ERRORFILE( aMsg.GetBuffer() );
#else
(void)e;
#endif
}
return ::com::sun::star::i18n::ForbiddenCharacters();
}
::com::sun::star::uno::Sequence< ::rtl::OUString > LocaleDataWrapper::getReservedWord() const
{
try
{
if ( xLD.is() )
return xLD->getReservedWord( getLocale() );
}
catch ( Exception& e )
{
#ifdef DBG_UTIL
ByteString aMsg( "getReservedWord: Exception caught\n" );
aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 );
DBG_ERRORFILE( aMsg.GetBuffer() );
#else
(void)e;
#endif
}
return ::com::sun::star::uno::Sequence< ::rtl::OUString >(0);
}
::com::sun::star::uno::Sequence< ::com::sun::star::lang::Locale > LocaleDataWrapper::getAllInstalledLocaleNames() const
{
uno::Sequence< lang::Locale > &rInstalledLocales = InstalledLocales::get();
if ( rInstalledLocales.getLength() )
return rInstalledLocales;
try
{
if ( xLD.is() )
rInstalledLocales = xLD->getAllInstalledLocaleNames();
}
catch ( Exception& e )
{
#ifdef DBG_UTIL
ByteString aMsg( "getAllInstalledLocaleNames: Exception caught\n" );
aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 );
DBG_ERRORFILE( aMsg.GetBuffer() );
#else
(void)e;
#endif
}
return rInstalledLocales;
}
// --- Impl and helpers ----------------------------------------------------
// static
::com::sun::star::uno::Sequence< ::com::sun::star::lang::Locale > LocaleDataWrapper::getInstalledLocaleNames()
{
const uno::Sequence< lang::Locale > &rInstalledLocales =
InstalledLocales::get();
if ( !rInstalledLocales.getLength() )
{
LocaleDataWrapper aLDW( ::comphelper::getProcessServiceFactory(), lang::Locale() );
aLDW.getAllInstalledLocaleNames();
}
return rInstalledLocales;
}
// static
::com::sun::star::uno::Sequence< sal_uInt16 > LocaleDataWrapper::getInstalledLanguageTypes()
{
uno::Sequence< sal_uInt16 > &rInstalledLanguageTypes =
InstalledLanguageTypes::get();
if ( rInstalledLanguageTypes.getLength() )
return rInstalledLanguageTypes;
::com::sun::star::uno::Sequence< ::com::sun::star::lang::Locale > xLoc =
getInstalledLocaleNames();
sal_Int32 nCount = xLoc.getLength();
::com::sun::star::uno::Sequence< sal_uInt16 > xLang( nCount );
sal_Int32 nLanguages = 0;
for ( sal_Int32 i=0; i<nCount; i++ )
{
String aDebugLocale;
if (areChecksEnabled())
{
aDebugLocale = xLoc[i].Language;
if ( xLoc[i].Country.getLength() )
{
aDebugLocale += '_';
aDebugLocale += String( xLoc[i].Country);
if ( xLoc[i].Variant.getLength() )
{
aDebugLocale += '_';
aDebugLocale += String( xLoc[i].Variant);
}
}
}
if ( xLoc[i].Variant.getLength() )
{
if (areChecksEnabled())
{
String aMsg( RTL_CONSTASCII_USTRINGPARAM(
"LocaleDataWrapper::getInstalledLanguageTypes: Variants not supported, locale\n"));
aMsg += aDebugLocale;
outputCheckMessage( aMsg );
}
continue;
}
LanguageType eLang = MsLangId::convertLocaleToLanguage( xLoc[i] );
// In checks, exclude known problems because no MS-LCID defined.
if (areChecksEnabled() && eLang == LANGUAGE_DONTKNOW
// && !aDebugLocale.EqualsAscii( "br_AE" ) // ?!? Breton in United Arabic Emirates
)
{
String aMsg( RTL_CONSTASCII_USTRINGPARAM(
"ConvertIsoNamesToLanguage: unknown MS-LCID for locale\n"));
aMsg += aDebugLocale;
outputCheckMessage( aMsg );
}
switch ( eLang )
{
case LANGUAGE_NORWEGIAN : // no_NO, not Bokmal (nb_NO), not Nynorsk (nn_NO)
eLang = LANGUAGE_DONTKNOW; // don't offer "Unknown" language
break;
}
if ( eLang != LANGUAGE_DONTKNOW )
{
rtl::OUString aLanguage, aCountry;
MsLangId::convertLanguageToIsoNames( eLang, aLanguage, aCountry );
if ( xLoc[i].Language != aLanguage ||
xLoc[i].Country != aCountry )
{
// In checks, exclude known problems because no MS-LCID defined
// and default for Language found.
if ( areChecksEnabled()
&& !aDebugLocale.EqualsAscii( "ar_SD" ) // Sudan/ar
&& !aDebugLocale.EqualsAscii( "en_CB" ) // Carribean is not a country
// && !aDebugLocale.EqualsAscii( "en_BG" ) // ?!? Bulgaria/en
// && !aDebugLocale.EqualsAscii( "es_BR" ) // ?!? Brazil/es
)
{
String aMsg( RTL_CONSTASCII_USTRINGPARAM(
"ConvertIsoNamesToLanguage/ConvertLanguageToIsoNames: ambiguous locale (MS-LCID?)\n"));
aMsg += aDebugLocale;
aMsg.AppendAscii( RTL_CONSTASCII_STRINGPARAM( " -> 0x" ) );
aMsg += String::CreateFromInt32( eLang, 16 );
aMsg.AppendAscii( RTL_CONSTASCII_STRINGPARAM( " -> " ) );
aMsg += String( aLanguage);
if ( aCountry.getLength() )
{
aMsg += '_';
aMsg += String( aCountry);
}
outputCheckMessage( aMsg );
}
eLang = LANGUAGE_DONTKNOW;
}
}
if ( eLang != LANGUAGE_DONTKNOW )
xLang[ nLanguages++ ] = eLang;
}
if ( nLanguages < nCount )
xLang.realloc( nLanguages );
rInstalledLanguageTypes = xLang;
return rInstalledLanguageTypes;
}
const String& LocaleDataWrapper::getOneLocaleItem( sal_Int16 nItem ) const
{
::utl::ReadWriteGuard aGuard( aMutex );
if ( nItem >= LocaleItem::COUNT )
{
DBG_ERRORFILE( "getOneLocaleItem: bounds" );
return aLocaleItem[0];
}
if ( aLocaleItem[nItem].Len() == 0 )
{ // no cached content
aGuard.changeReadToWrite();
((LocaleDataWrapper*)this)->getOneLocaleItemImpl( nItem );
}
return aLocaleItem[nItem];
}
void LocaleDataWrapper::getOneLocaleItemImpl( sal_Int16 nItem )
{
if ( !bLocaleDataItemValid )
{
aLocaleDataItem = getLocaleItem();
bLocaleDataItemValid = sal_True;
}
switch ( nItem )
{
case LocaleItem::DATE_SEPARATOR :
aLocaleItem[nItem] = aLocaleDataItem.dateSeparator;
break;
case LocaleItem::THOUSAND_SEPARATOR :
aLocaleItem[nItem] = aLocaleDataItem.thousandSeparator;
break;
case LocaleItem::DECIMAL_SEPARATOR :
aLocaleItem[nItem] = aLocaleDataItem.decimalSeparator;
break;
case LocaleItem::TIME_SEPARATOR :
aLocaleItem[nItem] = aLocaleDataItem.timeSeparator;
break;
case LocaleItem::TIME_100SEC_SEPARATOR :
aLocaleItem[nItem] = aLocaleDataItem.time100SecSeparator;
break;
case LocaleItem::LIST_SEPARATOR :
aLocaleItem[nItem] = aLocaleDataItem.listSeparator;
break;
case LocaleItem::SINGLE_QUOTATION_START :
aLocaleItem[nItem] = aLocaleDataItem.quotationStart;
break;
case LocaleItem::SINGLE_QUOTATION_END :
aLocaleItem[nItem] = aLocaleDataItem.quotationEnd;
break;
case LocaleItem::DOUBLE_QUOTATION_START :
aLocaleItem[nItem] = aLocaleDataItem.doubleQuotationStart;
break;
case LocaleItem::DOUBLE_QUOTATION_END :
aLocaleItem[nItem] = aLocaleDataItem.doubleQuotationEnd;
break;
case LocaleItem::MEASUREMENT_SYSTEM :
aLocaleItem[nItem] = aLocaleDataItem.measurementSystem;
break;
case LocaleItem::TIME_AM :
aLocaleItem[nItem] = aLocaleDataItem.timeAM;
break;
case LocaleItem::TIME_PM :
aLocaleItem[nItem] = aLocaleDataItem.timePM;
break;
case LocaleItem::LONG_DATE_DAY_OF_WEEK_SEPARATOR :
aLocaleItem[nItem] = aLocaleDataItem.LongDateDayOfWeekSeparator;
break;
case LocaleItem::LONG_DATE_DAY_SEPARATOR :
aLocaleItem[nItem] = aLocaleDataItem.LongDateDaySeparator;
break;
case LocaleItem::LONG_DATE_MONTH_SEPARATOR :
aLocaleItem[nItem] = aLocaleDataItem.LongDateMonthSeparator;
break;
case LocaleItem::LONG_DATE_YEAR_SEPARATOR :
aLocaleItem[nItem] = aLocaleDataItem.LongDateYearSeparator;
break;
default:
DBG_ERRORFILE( "getOneLocaleItemImpl: which one?" );
}
}
void LocaleDataWrapper::getOneReservedWordImpl( sal_Int16 nWord )
{
if ( !bReservedWordValid )
{
aReservedWordSeq = getReservedWord();
bReservedWordValid = sal_True;
}
DBG_ASSERT( nWord < aReservedWordSeq.getLength(), "getOneReservedWordImpl: which one?" );
if ( nWord < aReservedWordSeq.getLength() )
aReservedWord[nWord] = aReservedWordSeq[nWord];
}
const String& LocaleDataWrapper::getOneReservedWord( sal_Int16 nWord ) const
{
::utl::ReadWriteGuard aGuard( aMutex );
if ( nWord < 0 || nWord >= reservedWords::COUNT )
{
DBG_ERRORFILE( "getOneReservedWord: bounds" );
nWord = reservedWords::FALSE_WORD;
}
if ( aReservedWord[nWord].Len() == 0 )
{ // no cached content
aGuard.changeReadToWrite();
((LocaleDataWrapper*)this)->getOneReservedWordImpl( nWord );
}
return aReservedWord[nWord];
}
MeasurementSystem LocaleDataWrapper::mapMeasurementStringToEnum( const String& rMS ) const
{
//! TODO: could be cached too
if ( rMS.EqualsIgnoreCaseAscii( "metric" ) )
return MEASURE_METRIC;
//! TODO: other measurement systems? => extend enum MeasurementSystem
return MEASURE_US;
}
void LocaleDataWrapper::getDefaultCalendarImpl()
{
if (!xDefaultCalendar)
{
Sequence< Calendar > xCals = getAllCalendars();
sal_Int32 nCount = xCals.getLength();
sal_Int32 nDef = 0;
if (nCount > 1)
{
const Calendar* pArr = xCals.getArray();
for (sal_Int32 i=0; i<nCount; ++i)
{
if (pArr[i].Default)
{
nDef = i;
break;
}
}
}
xDefaultCalendar.reset( new Calendar( xCals[nDef]));
}
}
const ::boost::shared_ptr< ::com::sun::star::i18n::Calendar > LocaleDataWrapper::getDefaultCalendar() const
{
::utl::ReadWriteGuard aGuard( aMutex );
if (!xDefaultCalendar)
{ // no cached content
aGuard.changeReadToWrite();
((LocaleDataWrapper*)this)->getDefaultCalendarImpl();
}
return xDefaultCalendar;
}
const ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::CalendarItem > LocaleDataWrapper::getDefaultCalendarDays() const
{
return getDefaultCalendar()->Days;
}
const ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::CalendarItem > LocaleDataWrapper::getDefaultCalendarMonths() const
{
return getDefaultCalendar()->Months;
}
// --- currencies -----------------------------------------------------
const String& LocaleDataWrapper::getCurrSymbol() const
{
::utl::ReadWriteGuard aGuard( aMutex );
if ( !aCurrSymbol.Len() )
{
aGuard.changeReadToWrite();
((LocaleDataWrapper*)this)->getCurrSymbolsImpl();
}
return aCurrSymbol;
}
const String& LocaleDataWrapper::getCurrBankSymbol() const
{
::utl::ReadWriteGuard aGuard( aMutex );
if ( !aCurrBankSymbol.Len() )
{
aGuard.changeReadToWrite();
((LocaleDataWrapper*)this)->getCurrSymbolsImpl();
}
return aCurrBankSymbol;
}
sal_uInt16 LocaleDataWrapper::getCurrPositiveFormat() const
{
::utl::ReadWriteGuard aGuard( aMutex );
if ( nCurrPositiveFormat == nCurrFormatInvalid )
{
aGuard.changeReadToWrite();
((LocaleDataWrapper*)this)->getCurrFormatsImpl();
}
return nCurrPositiveFormat;
}
sal_uInt16 LocaleDataWrapper::getCurrNegativeFormat() const
{
::utl::ReadWriteGuard aGuard( aMutex );
if ( nCurrNegativeFormat == nCurrFormatInvalid )
{
aGuard.changeReadToWrite();
((LocaleDataWrapper*)this)->getCurrFormatsImpl();
}
return nCurrNegativeFormat;
}
sal_uInt16 LocaleDataWrapper::getCurrDigits() const
{
::utl::ReadWriteGuard aGuard( aMutex );
if ( nCurrDigits == nCurrFormatInvalid )
{
aGuard.changeReadToWrite();
((LocaleDataWrapper*)this)->getCurrSymbolsImpl();
}
return nCurrDigits;
}
void LocaleDataWrapper::getCurrSymbolsImpl()
{
Sequence< Currency2 > aCurrSeq = getAllCurrencies();
sal_Int32 nCnt = aCurrSeq.getLength();
Currency2 const * const pCurrArr = aCurrSeq.getArray();
sal_Int32 nElem;
for ( nElem = 0; nElem < nCnt; nElem++ )
{
if ( pCurrArr[nElem].Default )
break;
}
if ( nElem >= nCnt )
{
if (areChecksEnabled())
{
String aMsg( RTL_CONSTASCII_USTRINGPARAM(
"LocaleDataWrapper::getCurrSymbolsImpl: no default currency"));
outputCheckMessage( appendLocaleInfo( aMsg ) );
}
nElem = 0;
if ( nElem >= nCnt )
{
if (areChecksEnabled())
outputCheckMessage( String( RTL_CONSTASCII_USTRINGPARAM(
"LocaleDataWrapper::getCurrSymbolsImpl: no currency at all, using ShellsAndPebbles")));
aCurrSymbol.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "ShellsAndPebbles" ) );
aCurrBankSymbol = aCurrSymbol;
nCurrPositiveFormat = nCurrNegativeFormat = nCurrFormatDefault;
nCurrDigits = 2;
return ;
}
}
aCurrSymbol = pCurrArr[nElem].Symbol;
aCurrBankSymbol = pCurrArr[nElem].BankSymbol;
nCurrDigits = pCurrArr[nElem].DecimalPlaces;
}
void LocaleDataWrapper::scanCurrFormatImpl( const String& rCode,
xub_StrLen nStart, xub_StrLen& nSign, xub_StrLen& nPar,
xub_StrLen& nNum, xub_StrLen& nBlank, xub_StrLen& nSym )
{
nSign = nPar = nNum = nBlank = nSym = STRING_NOTFOUND;
const sal_Unicode* const pStr = rCode.GetBuffer();
const sal_Unicode* const pStop = pStr + rCode.Len();
const sal_Unicode* p = pStr + nStart;
int nInSection = 0;
sal_Bool bQuote = sal_False;
while ( p < pStop )
{
if ( bQuote )
{
if ( *p == '"' && *(p-1) != '\\' )
bQuote = sal_False;
}
else
{
switch ( *p )
{
case '"' :
if ( pStr == p || *(p-1) != '\\' )
bQuote = sal_True;
break;
case '-' :
if ( !nInSection && nSign == STRING_NOTFOUND )
nSign = (xub_StrLen)(p - pStr);
break;
case '(' :
if ( !nInSection && nPar == STRING_NOTFOUND )
nPar = (xub_StrLen)(p - pStr);
break;
case '0' :
case '#' :
if ( !nInSection && nNum == STRING_NOTFOUND )
nNum = (xub_StrLen)(p - pStr);
break;
case '[' :
nInSection++;
break;
case ']' :
if ( nInSection )
{
nInSection--;
if ( !nInSection && nBlank == STRING_NOTFOUND
&& nSym != STRING_NOTFOUND && p < pStop-1 && *(p+1) == ' ' )
nBlank = (xub_StrLen)(p - pStr + 1);
}
break;
case '$' :
if ( nSym == STRING_NOTFOUND && nInSection && *(p-1) == '[' )
{
nSym = (xub_StrLen)(p - pStr + 1);
if ( nNum != STRING_NOTFOUND && *(p-2) == ' ' )
nBlank = (xub_StrLen)(p - pStr - 2);
}
break;
case ';' :
if ( !nInSection )
p = pStop;
break;
default:
if ( !nInSection && nSym == STRING_NOTFOUND && rCode.Equals( aCurrSymbol, (xub_StrLen)(p-pStr), aCurrSymbol.Len() ) )
{ // currency symbol not surrounded by [$...]
nSym = (xub_StrLen)(p - pStr);
if ( nBlank == STRING_NOTFOUND && pStr < p && *(p-1) == ' ' )
nBlank = (xub_StrLen)(p - pStr - 1);
p += aCurrSymbol.Len() - 1;
if ( nBlank == STRING_NOTFOUND && p < pStop-2 && *(p+2) == ' ' )
nBlank = (xub_StrLen)(p - pStr + 2);
}
}
}
p++;
}
}
void LocaleDataWrapper::getCurrFormatsImpl()
{
NumberFormatCodeWrapper aNumberFormatCode( xSMgr, getLocale() );
uno::Sequence< NumberFormatCode > aFormatSeq
= aNumberFormatCode.getAllFormatCode( KNumberFormatUsage::CURRENCY );
sal_Int32 nCnt = aFormatSeq.getLength();
if ( !nCnt )
{ // bad luck
if (areChecksEnabled())
{
String aMsg( RTL_CONSTASCII_USTRINGPARAM(
"LocaleDataWrapper::getCurrFormatsImpl: no currency formats"));
outputCheckMessage( appendLocaleInfo( aMsg ) );
}
nCurrPositiveFormat = nCurrNegativeFormat = nCurrFormatDefault;
return ;
}
// find a negative code (medium preferred) and a default (medium preferred) (not necessarily the same)
NumberFormatCode const * const pFormatArr = aFormatSeq.getArray();
sal_Int32 nElem, nDef, nNeg, nMedium;
nDef = nNeg = nMedium = -1;
for ( nElem = 0; nElem < nCnt; nElem++ )
{
if ( pFormatArr[nElem].Type == KNumberFormatType::MEDIUM )
{
if ( pFormatArr[nElem].Default )
{
nDef = nElem;
nMedium = nElem;
if ( pFormatArr[nElem].Code.indexOf( ';' ) >= 0 )
nNeg = nElem;
}
else
{
if ( (nNeg == -1 || nMedium == -1) && pFormatArr[nElem].Code.indexOf( ';' ) >= 0 )
nNeg = nElem;
if ( nMedium == -1 )
nMedium = nElem;
}
}
else
{
if ( nDef == -1 && pFormatArr[nElem].Default )
nDef = nElem;
if ( nNeg == -1 && pFormatArr[nElem].Code.indexOf( ';' ) >= 0 )
nNeg = nElem;
}
}
// make sure it's loaded
getCurrSymbol();
xub_StrLen nSign, nPar, nNum, nBlank, nSym;
// positive format
nElem = (nDef >= 0 ? nDef : (nNeg >= 0 ? nNeg : 0));
scanCurrFormatImpl( pFormatArr[nElem].Code, 0, nSign, nPar, nNum, nBlank, nSym );
if (areChecksEnabled() && (nNum == STRING_NOTFOUND || nSym == STRING_NOTFOUND))
{
String aMsg( RTL_CONSTASCII_USTRINGPARAM(
"LocaleDataWrapper::getCurrFormatsImpl: CurrPositiveFormat?"));
outputCheckMessage( appendLocaleInfo( aMsg ) );
}
if ( nBlank == STRING_NOTFOUND )
{
if ( nSym < nNum )
nCurrPositiveFormat = 0; // $1
else
nCurrPositiveFormat = 1; // 1$
}
else
{
if ( nSym < nNum )
nCurrPositiveFormat = 2; // $ 1
else
nCurrPositiveFormat = 3; // 1 $
}
// negative format
if ( nNeg < 0 )
nCurrNegativeFormat = nCurrFormatDefault;
else
{
const ::rtl::OUString& rCode = pFormatArr[nNeg].Code;
xub_StrLen nDelim = (xub_StrLen)rCode.indexOf( ';' );
scanCurrFormatImpl( rCode, nDelim+1, nSign, nPar, nNum, nBlank, nSym );
if (areChecksEnabled() && (nNum == STRING_NOTFOUND ||
nSym == STRING_NOTFOUND || (nPar == STRING_NOTFOUND &&
nSign == STRING_NOTFOUND)))
{
String aMsg( RTL_CONSTASCII_USTRINGPARAM(
"LocaleDataWrapper::getCurrFormatsImpl: CurrNegativeFormat?"));
outputCheckMessage( appendLocaleInfo( aMsg ) );
}
if ( nBlank == STRING_NOTFOUND )
{
if ( nSym < nNum )
{
if ( nPar < nSym )
nCurrNegativeFormat = 0; // ($1)
else if ( nSign < nSym )
nCurrNegativeFormat = 1; // -$1
else if ( nNum < nSign )
nCurrNegativeFormat = 3; // $1-
else
nCurrNegativeFormat = 2; // $-1
}
else
{
if ( nPar < nNum )
nCurrNegativeFormat = 4; // (1$)
else if ( nSign < nNum )
nCurrNegativeFormat = 5; // -1$
else if ( nSym < nSign )
nCurrNegativeFormat = 7; // 1$-
else
nCurrNegativeFormat = 6; // 1-$
}
}
else
{
if ( nSym < nNum )
{
if ( nPar < nSym )
nCurrNegativeFormat = 14; // ($ 1)
else if ( nSign < nSym )
nCurrNegativeFormat = 9; // -$ 1
else if ( nNum < nSign )
nCurrNegativeFormat = 12; // $ 1-
else
nCurrNegativeFormat = 11; // $ -1
}
else
{
if ( nPar < nNum )
nCurrNegativeFormat = 15; // (1 $)
else if ( nSign < nNum )
nCurrNegativeFormat = 8; // -1 $
else if ( nSym < nSign )
nCurrNegativeFormat = 10; // 1 $-
else
nCurrNegativeFormat = 13; // 1- $
}
}
}
}
// --- date -----------------------------------------------------------
DateFormat LocaleDataWrapper::getDateFormat() const
{
::utl::ReadWriteGuard aGuard( aMutex );
if ( nDateFormat == nDateFormatInvalid )
{
aGuard.changeReadToWrite();
((LocaleDataWrapper*)this)->getDateFormatsImpl();
}
return (DateFormat) nDateFormat;
}
DateFormat LocaleDataWrapper::getLongDateFormat() const
{
::utl::ReadWriteGuard aGuard( aMutex );
if ( nLongDateFormat == nDateFormatInvalid )
{
aGuard.changeReadToWrite();
((LocaleDataWrapper*)this)->getDateFormatsImpl();
}
return (DateFormat) nLongDateFormat;
}
DateFormat LocaleDataWrapper::scanDateFormatImpl( const String& rCode )
{
// Only some european versions were translated, the ones with different
// keyword combinations are:
// English DMY, German TMJ, Spanish DMA, French JMA, Italian GMA,
// Dutch DMJ, Finnish PKV
// default is English keywords for every other language
xub_StrLen nDay = rCode.Search( 'D' );
xub_StrLen nMonth = rCode.Search( 'M' );
xub_StrLen nYear = rCode.Search( 'Y' );
if ( nDay == STRING_NOTFOUND || nMonth == STRING_NOTFOUND || nYear == STRING_NOTFOUND )
{ // This algorithm assumes that all three parts (DMY) are present
if ( nMonth == STRING_NOTFOUND )
{ // only Finnish has something else than 'M' for month
nMonth = rCode.Search( 'K' );
if ( nMonth != STRING_NOTFOUND )
{
nDay = rCode.Search( 'P' );
nYear = rCode.Search( 'V' );
}
}
else if ( nDay == STRING_NOTFOUND )
{ // We have a month 'M' if we reach this branch.
// Possible languages containing 'M' but no 'D':
// German, French, Italian
nDay = rCode.Search( 'T' ); // German
if ( nDay != STRING_NOTFOUND )
nYear = rCode.Search( 'J' );
else
{
nYear = rCode.Search( 'A' ); // French, Italian
if ( nYear != STRING_NOTFOUND )
{
nDay = rCode.Search( 'J' ); // French
if ( nDay == STRING_NOTFOUND )
nDay = rCode.Search( 'G' ); // Italian
}
}
}
else
{ // We have a month 'M' and a day 'D'.
// Possible languages containing 'D' and 'M' but not 'Y':
// Spanish, Dutch
nYear = rCode.Search( 'A' ); // Spanish
if ( nYear == STRING_NOTFOUND )
nYear = rCode.Search( 'J' ); // Dutch
}
if ( nDay == STRING_NOTFOUND || nMonth == STRING_NOTFOUND || nYear == STRING_NOTFOUND )
{
if (areChecksEnabled())
{
String aMsg( RTL_CONSTASCII_USTRINGPARAM(
"LocaleDataWrapper::scanDateFormat: not all DMY present"));
outputCheckMessage( appendLocaleInfo( aMsg ) );
}
if ( nDay == STRING_NOTFOUND )
nDay = rCode.Len();
if ( nMonth == STRING_NOTFOUND )
nMonth = rCode.Len();
if ( nYear == STRING_NOTFOUND )
nYear = rCode.Len();
}
}
// compare with <= because each position may equal rCode.Len()
if ( nDay <= nMonth && nMonth <= nYear )
return DMY; // also if every position equals rCode.Len()
else if ( nMonth <= nDay && nDay <= nYear )
return MDY;
else if ( nYear <= nMonth && nMonth <= nDay )
return YMD;
else
{
if (areChecksEnabled())
{
String aMsg( RTL_CONSTASCII_USTRINGPARAM(
"LocaleDataWrapper::scanDateFormat: no magic applyable"));
outputCheckMessage( appendLocaleInfo( aMsg ) );
}
return DMY;
}
}
void LocaleDataWrapper::getDateFormatsImpl()
{
NumberFormatCodeWrapper aNumberFormatCode( xSMgr, getLocale() );
uno::Sequence< NumberFormatCode > aFormatSeq
= aNumberFormatCode.getAllFormatCode( KNumberFormatUsage::DATE );
sal_Int32 nCnt = aFormatSeq.getLength();
if ( !nCnt )
{ // bad luck
if (areChecksEnabled())
{
String aMsg( RTL_CONSTASCII_USTRINGPARAM(
"LocaleDataWrapper::getDateFormatsImpl: no date formats"));
outputCheckMessage( appendLocaleInfo( aMsg ) );
}
nDateFormat = nLongDateFormat = DMY;
return ;
}
// find the edit (21), a default (medium preferred),
// a medium (default preferred), and a long (default preferred)
NumberFormatCode const * const pFormatArr = aFormatSeq.getArray();
sal_Int32 nElem, nEdit, nDef, nMedium, nLong;
nEdit = nDef = nMedium = nLong = -1;
for ( nElem = 0; nElem < nCnt; nElem++ )
{
if ( nEdit == -1 && pFormatArr[nElem].Index == NumberFormatIndex::DATE_SYS_DDMMYYYY )
nEdit = nElem;
if ( nDef == -1 && pFormatArr[nElem].Default )
nDef = nElem;
switch ( pFormatArr[nElem].Type )
{
case KNumberFormatType::MEDIUM :
{
if ( pFormatArr[nElem].Default )
{
nDef = nElem;
nMedium = nElem;
}
else if ( nMedium == -1 )
nMedium = nElem;
}
break;
case KNumberFormatType::LONG :
{
if ( pFormatArr[nElem].Default )
nLong = nElem;
else if ( nLong == -1 )
nLong = nElem;
}
break;
}
}
if ( nEdit == -1 )
{
if (areChecksEnabled())
{
String aMsg( RTL_CONSTASCII_USTRINGPARAM(
"LocaleDataWrapper::getDateFormatsImpl: no edit"));
outputCheckMessage( appendLocaleInfo( aMsg ) );
}
if ( nDef == -1 )
{
if (areChecksEnabled())
{
String aMsg( RTL_CONSTASCII_USTRINGPARAM(
"LocaleDataWrapper::getDateFormatsImpl: no default"));
outputCheckMessage( appendLocaleInfo( aMsg ) );
}
if ( nMedium != -1 )
nDef = nMedium;
else if ( nLong != -1 )
nDef = nLong;
else
nDef = 0;
}
nEdit = nDef;
}
DateFormat nDF = scanDateFormatImpl( pFormatArr[nEdit].Code );
if ( pFormatArr[nEdit].Type == KNumberFormatType::LONG )
{ // normally this is not the case
nLongDateFormat = nDateFormat = nDF;
}
else
{
nDateFormat = nDF;
if ( nLong == -1 )
nLongDateFormat = nDF;
else
nLongDateFormat = scanDateFormatImpl( pFormatArr[nLong].Code );
}
}
// --- digit grouping -------------------------------------------------
void LocaleDataWrapper::getDigitGroupingImpl()
{
/* TODO: This is a very simplified grouping setup that only serves its
* current purpose for Indian locales. A free-form flexible one would
* obtain grouping from locale data where it could be specified using, for
* example, codes like #,### and #,##,### that would generate the integer
* sequence. Needed additional API and a locale data element.
*/
if (!aGrouping.getLength())
{
aGrouping.realloc(3); // room for {3,2,0}
aGrouping[0] = 0; // invalidate
}
if (!aGrouping[0])
{
i18n::LanguageCountryInfo aLCInfo( getLanguageCountryInfo());
if (aLCInfo.Country.equalsIgnoreAsciiCaseAscii( "IN") || // India
aLCInfo.Country.equalsIgnoreAsciiCaseAscii( "BT")) // Bhutan
{
aGrouping[0] = 3;
aGrouping[1] = 2;
aGrouping[2] = 0;
}
else
{
aGrouping[0] = 3;
aGrouping[1] = 0;
}
}
}
const ::com::sun::star::uno::Sequence< sal_Int32 > LocaleDataWrapper::getDigitGrouping() const
{
::utl::ReadWriteGuard aGuard( aMutex );
if (!aGrouping.getLength() || aGrouping[0] == 0)
{ // no cached content
aGuard.changeReadToWrite();
((LocaleDataWrapper*)this)->getDigitGroupingImpl();
}
return aGrouping;
}
// --- simple number formatting helpers -------------------------------
// The ImplAdd... methods are taken from class International and modified to
// suit the needs.
static sal_Unicode* ImplAddUNum( sal_Unicode* pBuf, sal_uInt64 nNumber )
{
// fill temp buffer with digits
sal_Unicode aTempBuf[64];
sal_Unicode* pTempBuf = aTempBuf;
do
{
*pTempBuf = (sal_Unicode)(nNumber % 10) + '0';
pTempBuf++;
nNumber /= 10;
}
while ( nNumber );
// copy temp buffer to buffer passed
do
{
pTempBuf--;
*pBuf = *pTempBuf;
pBuf++;
}
while ( pTempBuf != aTempBuf );
return pBuf;
}
static sal_Unicode* ImplAddUNum( sal_Unicode* pBuf, sal_uInt64 nNumber, int nMinLen )
{
// fill temp buffer with digits
sal_Unicode aTempBuf[64];
sal_Unicode* pTempBuf = aTempBuf;
do
{
*pTempBuf = (sal_Unicode)(nNumber % 10) + '0';
pTempBuf++;
nNumber /= 10;
nMinLen--;
}
while ( nNumber );
// fill with zeros up to the minimal length
while ( nMinLen > 0 )
{
*pBuf = '0';
pBuf++;
nMinLen--;
}
// copy temp buffer to real buffer
do
{
pTempBuf--;
*pBuf = *pTempBuf;
pBuf++;
}
while ( pTempBuf != aTempBuf );
return pBuf;
}
static sal_Unicode* ImplAdd2UNum( sal_Unicode* pBuf, sal_uInt16 nNumber, int bLeading )
{
DBG_ASSERT( nNumber < 100, "ImplAdd2UNum() - Number >= 100" );
if ( nNumber < 10 )
{
if ( bLeading )
{
*pBuf = '0';
pBuf++;
}
*pBuf = nNumber + '0';
}
else
{
sal_uInt16 nTemp = nNumber % 10;
nNumber /= 10;
*pBuf = nNumber + '0';
pBuf++;
*pBuf = nTemp + '0';
}
pBuf++;
return pBuf;
}
inline sal_Unicode* ImplAddString( sal_Unicode* pBuf, const String& rStr )
{
if ( rStr.Len() == 1 )
*pBuf++ = rStr.GetChar(0);
else if ( rStr.Len() == 0 )
;
else
{
memcpy( pBuf, rStr.GetBuffer(), rStr.Len() * sizeof(sal_Unicode) );
pBuf += rStr.Len();
}
return pBuf;
}
inline sal_Unicode* ImplAddString( sal_Unicode* pBuf, sal_Unicode c )
{
*pBuf = c;
pBuf++;
return pBuf;
}
inline sal_Unicode* ImplAddString( sal_Unicode* pBuf, const sal_Unicode* pCopyBuf, xub_StrLen nLen )
{
memcpy( pBuf, pCopyBuf, nLen * sizeof(sal_Unicode) );
return pBuf + nLen;
}
sal_Unicode* LocaleDataWrapper::ImplAddFormatNum( sal_Unicode* pBuf,
sal_Int64 nNumber, sal_uInt16 nDecimals, sal_Bool bUseThousandSep,
sal_Bool bTrailingZeros ) const
{
sal_Unicode aNumBuf[64];
sal_Unicode* pNumBuf;
sal_uInt16 nNumLen;
sal_uInt16 i = 0;
sal_Bool bNeg;
// negative number
if ( nNumber < 0 )
{
nNumber *= -1;
bNeg = sal_True;
*pBuf = '-';
pBuf++;
}
else
bNeg = sal_False;
// convert number
pNumBuf = ImplAddUNum( aNumBuf, (sal_uInt64)nNumber );
nNumLen = (sal_uInt16)(sal_uLong)(pNumBuf-aNumBuf);
pNumBuf = aNumBuf;
if ( nNumLen <= nDecimals )
{
// strip .0 in decimals?
if ( !nNumber && !bTrailingZeros )
{
*pBuf = '0';
pBuf++;
}
else
{
// LeadingZero, insert 0
if ( isNumLeadingZero() )
{
*pBuf = '0';
pBuf++;
}
// append decimal separator
pBuf = ImplAddString( pBuf, getNumDecimalSep() );
// fill with zeros
while ( i < (nDecimals-nNumLen) )
{
*pBuf = '0';
pBuf++;
i++;
}
// append decimals
while ( nNumLen )
{
*pBuf = *pNumBuf;
pBuf++;
pNumBuf++;
nNumLen--;
}
}
}
else
{
const String& rThoSep = getNumThousandSep();
// copy number to buffer (excluding decimals)
sal_uInt16 nNumLen2 = nNumLen-nDecimals;
uno::Sequence< sal_Bool > aGroupPos;
if (bUseThousandSep)
aGroupPos = utl::DigitGroupingIterator::createForwardSequence(
nNumLen2, getDigitGrouping());
for ( ; i < nNumLen2; ++i )
{
*pBuf = *pNumBuf;
pBuf++;
pNumBuf++;
// add thousand separator?
if ( bUseThousandSep && aGroupPos[i] )
pBuf = ImplAddString( pBuf, rThoSep );
}
// append decimals
if ( nDecimals )
{
pBuf = ImplAddString( pBuf, getNumDecimalSep() );
sal_Bool bNullEnd = sal_True;
while ( i < nNumLen )
{
if ( *pNumBuf != '0' )
bNullEnd = sal_False;
*pBuf = *pNumBuf;
pBuf++;
pNumBuf++;
i++;
}
// strip .0 in decimals?
if ( bNullEnd && !bTrailingZeros )
pBuf -= nDecimals+1;
}
}
return pBuf;
}
// --- simple date and time formatting --------------------------------
String LocaleDataWrapper::getDate( const Date& rDate ) const
{
::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nBlockCritical );
//!TODO: leading zeros et al
sal_Unicode aBuf[128];
sal_Unicode* pBuf = aBuf;
sal_uInt16 nDay = rDate.GetDay();
sal_uInt16 nMonth = rDate.GetMonth();
sal_uInt16 nYear = rDate.GetYear();
sal_uInt16 nYearLen;
if ( sal_True /* IsDateCentury() */ )
nYearLen = 4;
else
{
nYearLen = 2;
nYear %= 100;
}
switch ( getDateFormat() )
{
case DMY :
pBuf = ImplAdd2UNum( pBuf, nDay, sal_True /* IsDateDayLeadingZero() */ );
pBuf = ImplAddString( pBuf, getDateSep() );
pBuf = ImplAdd2UNum( pBuf, nMonth, sal_True /* IsDateMonthLeadingZero() */ );
pBuf = ImplAddString( pBuf, getDateSep() );
pBuf = ImplAddUNum( pBuf, nYear, nYearLen );
break;
case MDY :
pBuf = ImplAdd2UNum( pBuf, nMonth, sal_True /* IsDateMonthLeadingZero() */ );
pBuf = ImplAddString( pBuf, getDateSep() );
pBuf = ImplAdd2UNum( pBuf, nDay, sal_True /* IsDateDayLeadingZero() */ );
pBuf = ImplAddString( pBuf, getDateSep() );
pBuf = ImplAddUNum( pBuf, nYear, nYearLen );
break;
default:
pBuf = ImplAddUNum( pBuf, nYear, nYearLen );
pBuf = ImplAddString( pBuf, getDateSep() );
pBuf = ImplAdd2UNum( pBuf, nMonth, sal_True /* IsDateMonthLeadingZero() */ );
pBuf = ImplAddString( pBuf, getDateSep() );
pBuf = ImplAdd2UNum( pBuf, nDay, sal_True /* IsDateDayLeadingZero() */ );
}
return String( aBuf, (xub_StrLen)(sal_uLong)(pBuf-aBuf) );
}
String LocaleDataWrapper::getTime( const Time& rTime, sal_Bool bSec, sal_Bool b100Sec ) const
{
::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nBlockCritical );
//!TODO: leading zeros et al
sal_Unicode aBuf[128];
sal_Unicode* pBuf = aBuf;
sal_uInt16 nHour = rTime.GetHour();
sal_Bool bHour12 = sal_False; //!TODO: AM/PM from default time format code
if ( bHour12 )
{
nHour %= 12;
// 0:00 -> 12:00
if ( !nHour )
nHour = 12;
}
else
nHour %= 24;
pBuf = ImplAdd2UNum( pBuf, nHour, sal_True /* IsTimeLeadingZero() */ );
pBuf = ImplAddString( pBuf, getTimeSep() );
pBuf = ImplAdd2UNum( pBuf, rTime.GetMin(), sal_True );
if ( bSec )
{
pBuf = ImplAddString( pBuf, getTimeSep() );
pBuf = ImplAdd2UNum( pBuf, rTime.GetSec(), sal_True );
if ( b100Sec )
{
pBuf = ImplAddString( pBuf, getTime100SecSep() );
pBuf = ImplAdd2UNum( pBuf, rTime.Get100Sec(), sal_True );
}
}
String aStr( aBuf, (xub_StrLen)(sal_uLong)(pBuf-aBuf) );
if ( bHour12 )
{
if ( (rTime.GetHour() % 24) >= 12 )
aStr += getTimePM();
else
aStr += getTimeAM();
}
#if 0
//!TODO: do we need a time string? like "o'clock" or "Uhr" or similar
else
aStr += getTimeStr();
#endif
return aStr;
}
String LocaleDataWrapper::getLongDate( const Date& rDate, CalendarWrapper& rCal,
sal_Int16 nDisplayDayOfWeek, sal_Bool bDayOfMonthWithLeadingZero,
sal_Int16 nDisplayMonth, sal_Bool bTwoDigitYear ) const
{
::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nBlockCritical );
using namespace ::com::sun::star::i18n;
sal_Unicode aBuf[20];
sal_Unicode* pBuf;
String aStr;
sal_Int16 nVal;
rCal.setGregorianDateTime( rDate );
// day of week
nVal = rCal.getValue( CalendarFieldIndex::DAY_OF_WEEK );
aStr += rCal.getDisplayName( CalendarDisplayIndex::DAY, nVal, nDisplayDayOfWeek );
aStr += getLongDateDayOfWeekSep();
// day of month
nVal = rCal.getValue( CalendarFieldIndex::DAY_OF_MONTH );
pBuf = ImplAdd2UNum( aBuf, nVal, bDayOfMonthWithLeadingZero );
String aDay( aBuf, (xub_StrLen)(sal_uLong)(pBuf-aBuf) );
// month of year
nVal = rCal.getValue( CalendarFieldIndex::MONTH );
String aMonth( rCal.getDisplayName( CalendarDisplayIndex::MONTH, nVal, nDisplayMonth ) );
// year
nVal = rCal.getValue( CalendarFieldIndex::YEAR );
if ( bTwoDigitYear )
pBuf = ImplAddUNum( aBuf, nVal % 100, 2 );
else
pBuf = ImplAddUNum( aBuf, nVal );
String aYear( aBuf, (xub_StrLen)(sal_uLong)(pBuf-aBuf) );
// concatenate
switch ( getLongDateFormat() )
{
case DMY :
aStr += aDay;
aStr += getLongDateDaySep();
aStr += aMonth;
aStr += getLongDateMonthSep();
aStr += aYear;
break;
case MDY :
aStr += aMonth;
aStr += getLongDateMonthSep();
aStr += aDay;
aStr += getLongDateDaySep();
aStr += aYear;
break;
default: // YMD
aStr += aYear;
aStr += getLongDateYearSep();
aStr += aMonth;
aStr += getLongDateMonthSep();
aStr += aDay;
}
return aStr;
}
String LocaleDataWrapper::getDuration( const Time& rTime, sal_Bool bSec, sal_Bool b100Sec ) const
{
::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nBlockCritical );
sal_Unicode aBuf[128];
sal_Unicode* pBuf = aBuf;
if ( rTime < Time( 0 ) )
pBuf = ImplAddString( pBuf, ' ' );
if ( sal_True /* IsTimeLeadingZero() */ )
pBuf = ImplAddUNum( pBuf, rTime.GetHour(), 2 );
else
pBuf = ImplAddUNum( pBuf, rTime.GetHour() );
pBuf = ImplAddString( pBuf, getTimeSep() );
pBuf = ImplAdd2UNum( pBuf, rTime.GetMin(), sal_True );
if ( bSec )
{
pBuf = ImplAddString( pBuf, getTimeSep() );
pBuf = ImplAdd2UNum( pBuf, rTime.GetSec(), sal_True );
if ( b100Sec )
{
pBuf = ImplAddString( pBuf, getTime100SecSep() );
pBuf = ImplAdd2UNum( pBuf, rTime.Get100Sec(), sal_True );
}
}
return String( aBuf, (xub_StrLen)(sal_uLong)(pBuf-aBuf) );
}
// --- simple number formatting ---------------------------------------
inline size_t ImplGetNumberStringLengthGuess( const LocaleDataWrapper& rLoc, sal_uInt16 nDecimals )
{
// approximately 3.2 bits per digit
const size_t nDig = ((sizeof(sal_Int64) * 8) / 3) + 1;
// digits, separators (pessimized for insane "every digit may be grouped"), leading zero, sign
size_t nGuess = ((nDecimals < nDig) ?
(((nDig - nDecimals) * rLoc.getNumThousandSep().Len()) + nDig) :
nDecimals) + rLoc.getNumDecimalSep().Len() + 3;
return nGuess;
}
String LocaleDataWrapper::getNum( sal_Int64 nNumber, sal_uInt16 nDecimals,
sal_Bool bUseThousandSep, sal_Bool bTrailingZeros ) const
{
::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nBlockCritical );
sal_Unicode aBuf[128]; // big enough for 64-bit long and crazy grouping
// check if digits and separators will fit into fixed buffer or allocate
size_t nGuess = ImplGetNumberStringLengthGuess( *this, nDecimals );
sal_Unicode* const pBuffer = (nGuess < 118 ? aBuf :
new sal_Unicode[nGuess + 16]);
sal_Unicode* pBuf = ImplAddFormatNum( pBuffer, nNumber, nDecimals,
bUseThousandSep, bTrailingZeros );
String aStr( pBuffer, (xub_StrLen)(sal_uLong)(pBuf-pBuffer) );
if ( pBuffer != aBuf )
delete [] pBuffer;
return aStr;
}
String LocaleDataWrapper::getCurr( sal_Int64 nNumber, sal_uInt16 nDecimals,
const String& rCurrencySymbol, sal_Bool bUseThousandSep ) const
{
::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nBlockCritical );
sal_Unicode aBuf[192];
sal_Unicode aNumBuf[128]; // big enough for 64-bit long and crazy grouping
sal_Unicode cZeroChar = getCurrZeroChar();
// check if digits and separators will fit into fixed buffer or allocate
size_t nGuess = ImplGetNumberStringLengthGuess( *this, nDecimals );
sal_Unicode* const pNumBuffer = (nGuess < 118 ? aNumBuf :
new sal_Unicode[nGuess + 16]);
sal_Unicode* const pBuffer =
((size_t(rCurrencySymbol.Len()) + nGuess + 20) < sizeof(aBuf)/sizeof(aBuf[0]) ? aBuf :
new sal_Unicode[ rCurrencySymbol.Len() + nGuess + 20 ]);
sal_Unicode* pBuf = pBuffer;
sal_Bool bNeg;
if ( nNumber < 0 )
{
bNeg = sal_True;
nNumber *= -1;
}
else
bNeg = sal_False;
// convert number
sal_Unicode* pEndNumBuf = ImplAddFormatNum( pNumBuffer, nNumber, nDecimals,
bUseThousandSep, sal_True );
xub_StrLen nNumLen = (xub_StrLen)(sal_uLong)(pEndNumBuf-pNumBuffer);
// replace zeros with zero character
if ( (cZeroChar != '0') && nDecimals /* && IsNumTrailingZeros() */ )
{
sal_Unicode* pTempBuf;
sal_uInt16 i;
sal_Bool bZero = sal_True;
pTempBuf = pNumBuffer+nNumLen-nDecimals;
i = 0;
do
{
if ( *pTempBuf != '0' )
{
bZero = sal_False;
break;
}
pTempBuf++;
i++;
}
while ( i < nDecimals );
if ( bZero )
{
pTempBuf = pNumBuffer+nNumLen-nDecimals;
i = 0;
do
{
*pTempBuf = cZeroChar;
pTempBuf++;
i++;
}
while ( i < nDecimals );
}
}
if ( !bNeg )
{
switch( getCurrPositiveFormat() )
{
case 0:
pBuf = ImplAddString( pBuf, rCurrencySymbol );
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
break;
case 1:
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
pBuf = ImplAddString( pBuf, rCurrencySymbol );
break;
case 2:
pBuf = ImplAddString( pBuf, rCurrencySymbol );
pBuf = ImplAddString( pBuf, ' ' );
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
break;
case 3:
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
pBuf = ImplAddString( pBuf, ' ' );
pBuf = ImplAddString( pBuf, rCurrencySymbol );
break;
}
}
else
{
switch( getCurrNegativeFormat() )
{
case 0:
pBuf = ImplAddString( pBuf, '(' );
pBuf = ImplAddString( pBuf, rCurrencySymbol );
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
pBuf = ImplAddString( pBuf, ')' );
break;
case 1:
pBuf = ImplAddString( pBuf, '-' );
pBuf = ImplAddString( pBuf, rCurrencySymbol );
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
break;
case 2:
pBuf = ImplAddString( pBuf, rCurrencySymbol );
pBuf = ImplAddString( pBuf, '-' );
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
break;
case 3:
pBuf = ImplAddString( pBuf, rCurrencySymbol );
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
pBuf = ImplAddString( pBuf, '-' );
break;
case 4:
pBuf = ImplAddString( pBuf, '(' );
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
pBuf = ImplAddString( pBuf, rCurrencySymbol );
pBuf = ImplAddString( pBuf, ')' );
break;
case 5:
pBuf = ImplAddString( pBuf, '-' );
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
pBuf = ImplAddString( pBuf, rCurrencySymbol );
break;
case 6:
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
pBuf = ImplAddString( pBuf, '-' );
pBuf = ImplAddString( pBuf, rCurrencySymbol );
break;
case 7:
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
pBuf = ImplAddString( pBuf, rCurrencySymbol );
pBuf = ImplAddString( pBuf, '-' );
break;
case 8:
pBuf = ImplAddString( pBuf, '-' );
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
pBuf = ImplAddString( pBuf, ' ' );
pBuf = ImplAddString( pBuf, rCurrencySymbol );
break;
case 9:
pBuf = ImplAddString( pBuf, '-' );
pBuf = ImplAddString( pBuf, rCurrencySymbol );
pBuf = ImplAddString( pBuf, ' ' );
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
break;
case 10:
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
pBuf = ImplAddString( pBuf, ' ' );
pBuf = ImplAddString( pBuf, rCurrencySymbol );
pBuf = ImplAddString( pBuf, '-' );
break;
case 11:
pBuf = ImplAddString( pBuf, rCurrencySymbol );
pBuf = ImplAddString( pBuf, ' ' );
pBuf = ImplAddString( pBuf, '-' );
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
break;
case 12:
pBuf = ImplAddString( pBuf, rCurrencySymbol );
pBuf = ImplAddString( pBuf, ' ' );
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
pBuf = ImplAddString( pBuf, '-' );
break;
case 13:
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
pBuf = ImplAddString( pBuf, '-' );
pBuf = ImplAddString( pBuf, ' ' );
pBuf = ImplAddString( pBuf, rCurrencySymbol );
break;
case 14:
pBuf = ImplAddString( pBuf, '(' );
pBuf = ImplAddString( pBuf, rCurrencySymbol );
pBuf = ImplAddString( pBuf, ' ' );
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
pBuf = ImplAddString( pBuf, ')' );
break;
case 15:
pBuf = ImplAddString( pBuf, '(' );
pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
pBuf = ImplAddString( pBuf, ' ' );
pBuf = ImplAddString( pBuf, rCurrencySymbol );
pBuf = ImplAddString( pBuf, ')' );
break;
}
}
String aNumber( pBuffer, (xub_StrLen)(sal_uLong)(pBuf-pBuffer) );
if ( pBuffer != aBuf )
delete [] pBuffer;
if ( pNumBuffer != aNumBuf )
delete [] pNumBuffer;
return aNumber;
}
// --- mixed ----------------------------------------------------------
::com::sun::star::lang::Locale LocaleDataWrapper::getLoadedLocale() const
{
LanguageCountryInfo aLCInfo = getLanguageCountryInfo();
return lang::Locale( aLCInfo.Language, aLCInfo.Country, aLCInfo.Variant );
}
String& LocaleDataWrapper::appendLocaleInfo( String& rDebugMsg ) const
{
::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nBlockCritical );
rDebugMsg += '\n';
rDebugMsg += String( aLocale.Language);
rDebugMsg += '_';
rDebugMsg += String( aLocale.Country);
rDebugMsg.AppendAscii( RTL_CONSTASCII_STRINGPARAM( " requested\n" ) );
lang::Locale aLoaded = getLoadedLocale();
rDebugMsg += String( aLoaded.Language);
rDebugMsg += '_';
rDebugMsg += String( aLoaded.Country);
rDebugMsg.AppendAscii( RTL_CONSTASCII_STRINGPARAM( " loaded" ) );
return rDebugMsg;
}
// static
void LocaleDataWrapper::outputCheckMessage( const String& rMsg )
{
outputCheckMessage( ByteString( rMsg, RTL_TEXTENCODING_UTF8).GetBuffer());
}
// static
void LocaleDataWrapper::outputCheckMessage( const char* pStr )
{
fprintf( stderr, "\n%s\n", pStr);
fflush( stderr);
DBG_ERROR( pStr);
}
// static
void LocaleDataWrapper::evaluateLocaleDataChecking()
{
// Using the rtl_Instance template here wouldn't solve all threaded write
// accesses, since we want to assign the result to the static member
// variable and would need to dereference the pointer returned and assign
// the value unguarded. This is the same pattern manually coded.
sal_uInt8 nCheck = nLocaleDataChecking;
if (!nCheck)
{
::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex());
nCheck = nLocaleDataChecking;
if (!nCheck)
{
#ifdef DBG_UTIL
nCheck = 1;
#else
const char* pEnv = getenv( "OOO_ENABLE_LOCALE_DATA_CHECKS");
if (pEnv && (pEnv[0] == 'Y' || pEnv[0] == 'y' || pEnv[0] == '1'))
nCheck = 1;
else
nCheck = 2;
#endif
OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
nLocaleDataChecking = nCheck;
}
}
else {
OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
}
}