6a08067902
Change-Id: Ifae7f344e3827875e32afa3cda23c771f5735707 Reviewed-on: https://gerrit.libreoffice.org/4659 Tested-by: LibreOffice gerrit bot <gerrit@libreoffice.org> Reviewed-by: Fridrich Strba <fridrich@documentfoundation.org> Tested-by: Fridrich Strba <fridrich@documentfoundation.org>
1890 lines
58 KiB
C++
1890 lines
58 KiB
C++
/* -*- 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 <string.h> // memcpy()
|
|
#include <stdio.h> // fprintf(), stderr
|
|
#include <string>
|
|
|
|
#include <unotools/localedatawrapper.hxx>
|
|
#include <unotools/numberformatcodewrapper.hxx>
|
|
#include <unotools/calendarwrapper.hxx>
|
|
#include <unotools/digitgroupingiterator.hxx>
|
|
#include <tools/debug.hxx>
|
|
#include <i18nlangtag/languagetag.hxx>
|
|
|
|
#include <com/sun/star/i18n/KNumberFormatUsage.hpp>
|
|
#include <com/sun/star/i18n/KNumberFormatType.hpp>
|
|
#include <com/sun/star/i18n/LocaleData.hpp>
|
|
#include <com/sun/star/i18n/CalendarFieldIndex.hpp>
|
|
#include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
|
|
#include <com/sun/star/i18n/NumberFormatIndex.hpp>
|
|
|
|
#include <comphelper/processfactory.hxx>
|
|
#include <rtl/instance.hxx>
|
|
#include <rtl/ustrbuf.hxx>
|
|
#include <sal/macros.h>
|
|
|
|
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< uno::XComponentContext > & rxContext,
|
|
const LanguageTag& rLanguageTag
|
|
)
|
|
:
|
|
m_xContext( rxContext ),
|
|
xLD( LocaleData::create(rxContext) ),
|
|
maLanguageTag( rLanguageTag ),
|
|
bLocaleDataItemValid( sal_False ),
|
|
bReservedWordValid( sal_False )
|
|
{
|
|
invalidateData();
|
|
}
|
|
|
|
LocaleDataWrapper::LocaleDataWrapper(
|
|
const LanguageTag& rLanguageTag
|
|
)
|
|
:
|
|
m_xContext( comphelper::getProcessComponentContext() ),
|
|
xLD( LocaleData::create(m_xContext) ),
|
|
maLanguageTag( rLanguageTag ),
|
|
bLocaleDataItemValid( sal_False ),
|
|
bReservedWordValid( sal_False )
|
|
{
|
|
invalidateData();
|
|
}
|
|
|
|
LocaleDataWrapper::~LocaleDataWrapper()
|
|
{
|
|
}
|
|
|
|
|
|
void LocaleDataWrapper::setLanguageTag( const LanguageTag& rLanguageTag )
|
|
{
|
|
::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nCriticalChange );
|
|
maLanguageTag = rLanguageTag;
|
|
invalidateData();
|
|
}
|
|
|
|
|
|
const LanguageTag& LocaleDataWrapper::getLanguageTag() const
|
|
{
|
|
::utl::ReadWriteGuard aGuard( aMutex );
|
|
return maLanguageTag;
|
|
}
|
|
|
|
|
|
const ::com::sun::star::lang::Locale& LocaleDataWrapper::getMyLocale() const
|
|
{
|
|
::utl::ReadWriteGuard aGuard( aMutex );
|
|
return maLanguageTag.getLocale();
|
|
}
|
|
|
|
|
|
void LocaleDataWrapper::invalidateData()
|
|
{
|
|
aCurrSymbol = OUString();
|
|
aCurrBankSymbol = OUString();
|
|
nDateFormat = nLongDateFormat = nDateFormatInvalid;
|
|
nCurrPositiveFormat = nCurrNegativeFormat = nCurrDigits = nCurrFormatInvalid;
|
|
if ( bLocaleDataItemValid )
|
|
{
|
|
for (sal_Int32 j=0; j<LocaleItem::COUNT; ++j)
|
|
aLocaleItem[j] = OUString();
|
|
bLocaleDataItemValid = sal_False;
|
|
}
|
|
if ( bReservedWordValid )
|
|
{
|
|
for ( sal_Int16 j=0; j<reservedWords::COUNT; ++j )
|
|
aReservedWord[j] = OUString();
|
|
bReservedWordValid = sal_False;
|
|
}
|
|
xDefaultCalendar.reset();
|
|
if (aGrouping.getLength())
|
|
aGrouping[0] = 0;
|
|
if (aDateAcceptancePatterns.getLength())
|
|
aDateAcceptancePatterns = Sequence<OUString>();
|
|
// dummies
|
|
cCurrZeroChar = '0';
|
|
}
|
|
|
|
|
|
/* FIXME-BCP47: locale data should provide a language tag instead that could be
|
|
* passed on. */
|
|
::com::sun::star::i18n::LanguageCountryInfo LocaleDataWrapper::getLanguageCountryInfo() const
|
|
{
|
|
try
|
|
{
|
|
return xLD->getLanguageCountryInfo( getMyLocale() );
|
|
}
|
|
catch (const Exception& e)
|
|
{
|
|
SAL_WARN( "unotools.i18n", "getLanguageCountryInfo: Exception caught " << e.Message );
|
|
}
|
|
return ::com::sun::star::i18n::LanguageCountryInfo();
|
|
}
|
|
|
|
|
|
::com::sun::star::i18n::LocaleDataItem LocaleDataWrapper::getLocaleItem() const
|
|
{
|
|
try
|
|
{
|
|
return xLD->getLocaleItem( getMyLocale() );
|
|
}
|
|
catch (const Exception& e)
|
|
{
|
|
SAL_WARN( "unotools.i18n", "getLocaleItem: Exception caught " << e.Message );
|
|
}
|
|
return ::com::sun::star::i18n::LocaleDataItem();
|
|
}
|
|
|
|
|
|
::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Currency2 > LocaleDataWrapper::getAllCurrencies() const
|
|
{
|
|
try
|
|
{
|
|
return xLD->getAllCurrencies2( getMyLocale() );
|
|
}
|
|
catch (const Exception& e)
|
|
{
|
|
SAL_WARN( "unotools.i18n", "getAllCurrencies: Exception caught " << e.Message );
|
|
}
|
|
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
|
|
{
|
|
return xLD->getAllFormats( getMyLocale() );
|
|
}
|
|
catch (const Exception& e)
|
|
{
|
|
SAL_WARN( "unotools.i18n", "getAllFormats: Exception caught " << e.Message );
|
|
}
|
|
return ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::FormatElement >(0);
|
|
}
|
|
|
|
|
|
::com::sun::star::i18n::ForbiddenCharacters LocaleDataWrapper::getForbiddenCharacters() const
|
|
{
|
|
try
|
|
{
|
|
return xLD->getForbiddenCharacters( getMyLocale() );
|
|
}
|
|
catch (const Exception& e)
|
|
{
|
|
SAL_WARN( "unotools.i18n", "getForbiddenCharacters: Exception caught " << e.Message );
|
|
}
|
|
return ::com::sun::star::i18n::ForbiddenCharacters();
|
|
}
|
|
|
|
|
|
::com::sun::star::uno::Sequence< OUString > LocaleDataWrapper::getReservedWord() const
|
|
{
|
|
try
|
|
{
|
|
return xLD->getReservedWord( getMyLocale() );
|
|
}
|
|
catch ( const Exception& e )
|
|
{
|
|
SAL_WARN( "unotools.i18n", "getReservedWord: Exception caught " << e.Message );
|
|
}
|
|
return ::com::sun::star::uno::Sequence< 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
|
|
{
|
|
rInstalledLocales = xLD->getAllInstalledLocaleNames();
|
|
}
|
|
catch ( const Exception& e )
|
|
{
|
|
SAL_WARN( "unotools.i18n", "getAllInstalledLocaleNames: Exception caught " << e.Message );
|
|
}
|
|
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::getProcessComponentContext(), LanguageTag( LANGUAGE_SYSTEM) );
|
|
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++ )
|
|
{
|
|
LanguageTag aLanguageTag( xLoc[i] );
|
|
OUString aDebugLocale;
|
|
if (areChecksEnabled())
|
|
{
|
|
aDebugLocale = aLanguageTag.getBcp47( false);
|
|
}
|
|
|
|
LanguageType eLang = aLanguageTag.getLanguageType( false);
|
|
if (areChecksEnabled() && eLang == LANGUAGE_DONTKNOW)
|
|
{
|
|
OUStringBuffer aMsg("ConvertIsoNamesToLanguage: unknown MS-LCID for locale\n");
|
|
aMsg.append(aDebugLocale);
|
|
outputCheckMessage(aMsg.makeStringAndClear());
|
|
}
|
|
|
|
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 )
|
|
{
|
|
LanguageTag aBackLanguageTag( eLang);
|
|
if ( aLanguageTag != aBackLanguageTag )
|
|
{
|
|
// In checks, exclude known problems because no MS-LCID defined
|
|
// and default for Language found.
|
|
if ( areChecksEnabled()
|
|
&& aDebugLocale != "ar-SD" // Sudan/ar
|
|
&& aDebugLocale != "en-CB" // Carribean is not a country
|
|
// && aDebugLocale != "en-BG" // ?!? Bulgaria/en
|
|
// && aDebugLocale != "es-BR" // ?!? Brazil/es
|
|
)
|
|
{
|
|
OUStringBuffer aMsg("ConvertIsoNamesToLanguage/ConvertLanguageToIsoNames: ambiguous locale (MS-LCID?)\n");
|
|
aMsg.append(aDebugLocale);
|
|
aMsg.appendAscii(" -> 0x");
|
|
aMsg.append(static_cast<sal_Int32>(eLang), 16);
|
|
aMsg.appendAscii(" -> ");
|
|
aMsg.append(aBackLanguageTag.getBcp47());
|
|
outputCheckMessage( aMsg.makeStringAndClear() );
|
|
}
|
|
eLang = LANGUAGE_DONTKNOW;
|
|
}
|
|
}
|
|
if ( eLang != LANGUAGE_DONTKNOW )
|
|
xLang[ nLanguages++ ] = eLang;
|
|
}
|
|
if ( nLanguages < nCount )
|
|
xLang.realloc( nLanguages );
|
|
rInstalledLanguageTypes = xLang;
|
|
|
|
return rInstalledLanguageTypes;
|
|
}
|
|
|
|
const OUString& LocaleDataWrapper::getOneLocaleItem( sal_Int16 nItem ) const
|
|
{
|
|
::utl::ReadWriteGuard aGuard( aMutex );
|
|
if ( nItem >= LocaleItem::COUNT )
|
|
{
|
|
SAL_WARN( "unotools.i18n", "getOneLocaleItem: bounds" );
|
|
return aLocaleItem[0];
|
|
}
|
|
if (aLocaleItem[nItem].isEmpty())
|
|
{ // 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:
|
|
SAL_WARN( "unotools.i18n", "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 OUString& LocaleDataWrapper::getOneReservedWord( sal_Int16 nWord ) const
|
|
{
|
|
::utl::ReadWriteGuard aGuard( aMutex );
|
|
if ( nWord < 0 || nWord >= reservedWords::COUNT )
|
|
{
|
|
SAL_WARN( "unotools.i18n", "getOneReservedWord: bounds" );
|
|
nWord = reservedWords::FALSE_WORD;
|
|
}
|
|
if (aReservedWord[nWord].isEmpty())
|
|
{ // no cached content
|
|
aGuard.changeReadToWrite();
|
|
((LocaleDataWrapper*)this)->getOneReservedWordImpl( nWord );
|
|
}
|
|
return aReservedWord[nWord];
|
|
}
|
|
|
|
|
|
MeasurementSystem LocaleDataWrapper::mapMeasurementStringToEnum( const OUString& rMS ) const
|
|
{
|
|
//! TODO: could be cached too
|
|
if ( rMS.equalsIgnoreAsciiCase( "metric" ) )
|
|
return MEASURE_METRIC;
|
|
//! TODO: other measurement systems? => extend enum MeasurementSystem
|
|
return MEASURE_US;
|
|
}
|
|
|
|
|
|
void LocaleDataWrapper::getDefaultCalendarImpl()
|
|
{
|
|
if (!xDefaultCalendar)
|
|
{
|
|
Sequence< Calendar2 > xCals = getAllCalendars();
|
|
sal_Int32 nCount = xCals.getLength();
|
|
sal_Int32 nDef = 0;
|
|
if (nCount > 1)
|
|
{
|
|
const Calendar2* pArr = xCals.getArray();
|
|
for (sal_Int32 i=0; i<nCount; ++i)
|
|
{
|
|
if (pArr[i].Default)
|
|
{
|
|
nDef = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
xDefaultCalendar.reset( new Calendar2( xCals[nDef]));
|
|
}
|
|
}
|
|
|
|
|
|
const ::boost::shared_ptr< ::com::sun::star::i18n::Calendar2 > 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::CalendarItem2 > LocaleDataWrapper::getDefaultCalendarDays() const
|
|
{
|
|
return getDefaultCalendar()->Days;
|
|
}
|
|
|
|
|
|
const ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::CalendarItem2 > LocaleDataWrapper::getDefaultCalendarMonths() const
|
|
{
|
|
return getDefaultCalendar()->Months;
|
|
}
|
|
|
|
|
|
// --- currencies -----------------------------------------------------
|
|
|
|
const OUString& LocaleDataWrapper::getCurrSymbol() const
|
|
{
|
|
::utl::ReadWriteGuard aGuard( aMutex );
|
|
if (aCurrSymbol.isEmpty())
|
|
{
|
|
aGuard.changeReadToWrite();
|
|
((LocaleDataWrapper*)this)->getCurrSymbolsImpl();
|
|
}
|
|
return aCurrSymbol;
|
|
}
|
|
|
|
|
|
const OUString& LocaleDataWrapper::getCurrBankSymbol() const
|
|
{
|
|
::utl::ReadWriteGuard aGuard( aMutex );
|
|
if (aCurrBankSymbol.isEmpty())
|
|
{
|
|
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())
|
|
{
|
|
OUString aMsg( "LocaleDataWrapper::getCurrSymbolsImpl: no default currency" );
|
|
outputCheckMessage( appendLocaleInfo( aMsg ) );
|
|
}
|
|
nElem = 0;
|
|
if ( nElem >= nCnt )
|
|
{
|
|
if (areChecksEnabled())
|
|
outputCheckMessage(OUString("LocaleDataWrapper::getCurrSymbolsImpl: no currency at all, using ShellsAndPebbles"));
|
|
aCurrSymbol = OUString("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 OUString& rCode,
|
|
sal_Int32 nStart, sal_Int32& nSign, sal_Int32& nPar,
|
|
sal_Int32& nNum, sal_Int32& nBlank, sal_Int32& nSym )
|
|
{
|
|
nSign = nPar = nNum = nBlank = nSym = -1;
|
|
const sal_Unicode* const pStr = rCode.getStr();
|
|
const sal_Unicode* const pStop = pStr + rCode.getLength();
|
|
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 == -1)
|
|
nSign = p - pStr;
|
|
break;
|
|
case '(' :
|
|
if (!nInSection && nPar == -1)
|
|
nPar = p - pStr;
|
|
break;
|
|
case '0' :
|
|
case '#' :
|
|
if (!nInSection && nNum == -1)
|
|
nNum = p - pStr;
|
|
break;
|
|
case '[' :
|
|
nInSection++;
|
|
break;
|
|
case ']' :
|
|
if ( nInSection )
|
|
{
|
|
nInSection--;
|
|
if (!nInSection && nBlank == -1
|
|
&& nSym != -1 && p < pStop-1 && *(p+1) == ' ' )
|
|
nBlank = p - pStr + 1;
|
|
}
|
|
break;
|
|
case '$' :
|
|
if (nSym == -1 && nInSection && *(p-1) == '[')
|
|
{
|
|
nSym = p - pStr + 1;
|
|
if (nNum != -1 && *(p-2) == ' ')
|
|
nBlank = p - pStr - 2;
|
|
}
|
|
break;
|
|
case ';' :
|
|
if ( !nInSection )
|
|
p = pStop;
|
|
break;
|
|
default:
|
|
if (!nInSection && nSym == -1 && rCode.match(aCurrSymbol, (sal_Int32)(p - pStr)))
|
|
{ // currency symbol not surrounded by [$...]
|
|
nSym = p - pStr;
|
|
if (nBlank == -1 && pStr < p && *(p-1) == ' ')
|
|
nBlank = p - pStr - 1;
|
|
p += aCurrSymbol.getLength() - 1;
|
|
if (nBlank == -1 && p < pStop-2 && *(p+2) == ' ')
|
|
nBlank = p - pStr + 2;
|
|
}
|
|
}
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
|
|
void LocaleDataWrapper::getCurrFormatsImpl()
|
|
{
|
|
NumberFormatCodeWrapper aNumberFormatCode( m_xContext, getMyLocale() );
|
|
uno::Sequence< NumberFormatCode > aFormatSeq
|
|
= aNumberFormatCode.getAllFormatCode( KNumberFormatUsage::CURRENCY );
|
|
sal_Int32 nCnt = aFormatSeq.getLength();
|
|
if ( !nCnt )
|
|
{ // bad luck
|
|
if (areChecksEnabled())
|
|
{
|
|
OUString aMsg( "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();
|
|
|
|
sal_Int32 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 == -1 || nSym == -1))
|
|
{
|
|
OUString aMsg( "LocaleDataWrapper::getCurrFormatsImpl: CurrPositiveFormat?" );
|
|
outputCheckMessage( appendLocaleInfo( aMsg ) );
|
|
}
|
|
if (nBlank == -1)
|
|
{
|
|
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 OUString& rCode = pFormatArr[nNeg].Code;
|
|
sal_Int32 nDelim = rCode.indexOf(';');
|
|
scanCurrFormatImpl( rCode, nDelim+1, nSign, nPar, nNum, nBlank, nSym );
|
|
if (areChecksEnabled() && (nNum == -1 || nSym == -1 || (nPar == -1 && nSign == -1)))
|
|
{
|
|
OUString aMsg( "LocaleDataWrapper::getCurrFormatsImpl: CurrNegativeFormat?" );
|
|
outputCheckMessage( appendLocaleInfo( aMsg ) );
|
|
}
|
|
// NOTE: one of nPar or nSign are allowed to be -1
|
|
if (nBlank == -1)
|
|
{
|
|
if ( nSym < nNum )
|
|
{
|
|
if ( -1 < nPar && nPar < nSym )
|
|
nCurrNegativeFormat = 0; // ($1)
|
|
else if ( -1 < nSign && nSign < nSym )
|
|
nCurrNegativeFormat = 1; // -$1
|
|
else if ( nNum < nSign )
|
|
nCurrNegativeFormat = 3; // $1-
|
|
else
|
|
nCurrNegativeFormat = 2; // $-1
|
|
}
|
|
else
|
|
{
|
|
if ( -1 < nPar && nPar < nNum )
|
|
nCurrNegativeFormat = 4; // (1$)
|
|
else if ( -1 < nSign && nSign < nNum )
|
|
nCurrNegativeFormat = 5; // -1$
|
|
else if ( nSym < nSign )
|
|
nCurrNegativeFormat = 7; // 1$-
|
|
else
|
|
nCurrNegativeFormat = 6; // 1-$
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( nSym < nNum )
|
|
{
|
|
if ( -1 < nPar && nPar < nSym )
|
|
nCurrNegativeFormat = 14; // ($ 1)
|
|
else if ( -1 < nSign && nSign < nSym )
|
|
nCurrNegativeFormat = 9; // -$ 1
|
|
else if ( nNum < nSign )
|
|
nCurrNegativeFormat = 12; // $ 1-
|
|
else
|
|
nCurrNegativeFormat = 11; // $ -1
|
|
}
|
|
else
|
|
{
|
|
if ( -1 < nPar && nPar < nNum )
|
|
nCurrNegativeFormat = 15; // (1 $)
|
|
else if ( -1 < nSign && 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 OUString& 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
|
|
sal_Int32 nDay = rCode.indexOf('D');
|
|
sal_Int32 nMonth = rCode.indexOf('M');
|
|
sal_Int32 nYear = rCode.indexOf('Y');
|
|
if (nDay == -1 || nMonth == -1 || nYear == -1)
|
|
{ // This algorithm assumes that all three parts (DMY) are present
|
|
if (nMonth == -1)
|
|
{ // only Finnish has something else than 'M' for month
|
|
nMonth = rCode.indexOf('K');
|
|
if (nMonth != -1)
|
|
{
|
|
nDay = rCode.indexOf('P');
|
|
nYear = rCode.indexOf('V');
|
|
}
|
|
}
|
|
else if (nDay == -1)
|
|
{ // We have a month 'M' if we reach this branch.
|
|
// Possible languages containing 'M' but no 'D':
|
|
// German, French, Italian
|
|
nDay = rCode.indexOf('T'); // German
|
|
if (nDay != -1)
|
|
nYear = rCode.indexOf('J');
|
|
else
|
|
{
|
|
nYear = rCode.indexOf('A'); // French, Italian
|
|
if (nYear != -1)
|
|
{
|
|
nDay = rCode.indexOf('J'); // French
|
|
if (nDay == -1)
|
|
nDay = rCode.indexOf('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.indexOf('A'); // Spanish
|
|
if (nYear == -1)
|
|
nYear = rCode.indexOf('J'); // Dutch
|
|
}
|
|
if (nDay == -1 || nMonth == -1 || nYear == -1)
|
|
{
|
|
if (areChecksEnabled())
|
|
{
|
|
OUString aMsg( "LocaleDataWrapper::scanDateFormat: not all DMY present" );
|
|
outputCheckMessage( appendLocaleInfo( aMsg ) );
|
|
}
|
|
if (nDay == -1)
|
|
nDay = rCode.getLength();
|
|
if (nMonth == -1)
|
|
nMonth = rCode.getLength();
|
|
if (nYear == -1)
|
|
nYear = rCode.getLength();
|
|
}
|
|
}
|
|
// compare with <= because each position may equal rCode.getLength()
|
|
if ( nDay <= nMonth && nMonth <= nYear )
|
|
return DMY; // also if every position equals rCode.getLength()
|
|
else if ( nMonth <= nDay && nDay <= nYear )
|
|
return MDY;
|
|
else if ( nYear <= nMonth && nMonth <= nDay )
|
|
return YMD;
|
|
else
|
|
{
|
|
if (areChecksEnabled())
|
|
{
|
|
OUString aMsg( "LocaleDataWrapper::scanDateFormat: no magic applyable" );
|
|
outputCheckMessage( appendLocaleInfo( aMsg ) );
|
|
}
|
|
return DMY;
|
|
}
|
|
}
|
|
|
|
|
|
void LocaleDataWrapper::getDateFormatsImpl()
|
|
{
|
|
NumberFormatCodeWrapper aNumberFormatCode( m_xContext, getMyLocale() );
|
|
uno::Sequence< NumberFormatCode > aFormatSeq
|
|
= aNumberFormatCode.getAllFormatCode( KNumberFormatUsage::DATE );
|
|
sal_Int32 nCnt = aFormatSeq.getLength();
|
|
if ( !nCnt )
|
|
{ // bad luck
|
|
if (areChecksEnabled())
|
|
{
|
|
OUString aMsg( "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())
|
|
{
|
|
OUString aMsg( "LocaleDataWrapper::getDateFormatsImpl: no edit" );
|
|
outputCheckMessage( appendLocaleInfo( aMsg ) );
|
|
}
|
|
if ( nDef == -1 )
|
|
{
|
|
if (areChecksEnabled())
|
|
{
|
|
OUString aMsg( "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.equalsIgnoreAsciiCase("IN") || // India
|
|
aLCInfo.Country.equalsIgnoreAsciiCase("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, bool 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;
|
|
}
|
|
|
|
static sal_Unicode* ImplAdd9UNum( sal_Unicode* pBuf, sal_uInt32 nNumber, bool bLeading )
|
|
{
|
|
DBG_ASSERT( nNumber < 1000000000, "ImplAdd9UNum() - Number >= 1000000000" );
|
|
|
|
std::ostringstream ostr;
|
|
if (bLeading)
|
|
{
|
|
ostr.fill('0');
|
|
ostr.width(9);
|
|
}
|
|
ostr << nNumber;
|
|
std::string aStr = ostr.str();
|
|
for(const char *pAB= aStr.c_str(); *pAB != '\0'; ++pAB, ++pBuf)
|
|
{
|
|
*pBuf = *pAB;
|
|
}
|
|
|
|
return pBuf;
|
|
}
|
|
|
|
inline sal_Unicode* ImplAddString( sal_Unicode* pBuf, const OUString& rStr )
|
|
{
|
|
if ( rStr.getLength() == 1 )
|
|
*pBuf++ = rStr[0];
|
|
else if (rStr.isEmpty())
|
|
;
|
|
else
|
|
{
|
|
memcpy( pBuf, rStr.getStr(), rStr.getLength() * sizeof(sal_Unicode) );
|
|
pBuf += rStr.getLength();
|
|
}
|
|
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;
|
|
|
|
// negative number
|
|
if ( nNumber < 0 )
|
|
{
|
|
nNumber *= -1;
|
|
*pBuf = '-';
|
|
pBuf++;
|
|
}
|
|
|
|
// 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 OUString& 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 --------------------------------
|
|
|
|
OUString 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 OUString(aBuf, pBuf-aBuf);
|
|
}
|
|
|
|
|
|
OUString 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(), true );
|
|
if ( bSec )
|
|
{
|
|
pBuf = ImplAddString( pBuf, getTimeSep() );
|
|
pBuf = ImplAdd2UNum( pBuf, rTime.GetSec(), true );
|
|
|
|
if ( b100Sec )
|
|
{
|
|
pBuf = ImplAddString( pBuf, getTime100SecSep() );
|
|
pBuf = ImplAdd9UNum( pBuf, rTime.GetNanoSec(), true );
|
|
}
|
|
}
|
|
|
|
OUString aStr(aBuf, pBuf - aBuf);
|
|
|
|
if ( bHour12 )
|
|
{
|
|
if ( (rTime.GetHour() % 24) >= 12 )
|
|
aStr += getTimePM();
|
|
else
|
|
aStr += getTimeAM();
|
|
}
|
|
|
|
return aStr;
|
|
}
|
|
|
|
|
|
OUString 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;
|
|
OUString 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 );
|
|
OUString aDay(aBuf, pBuf-aBuf);
|
|
// month of year
|
|
nVal = rCal.getValue( CalendarFieldIndex::MONTH );
|
|
OUString 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 );
|
|
OUString aYear(aBuf, pBuf-aBuf);
|
|
// concatenate
|
|
switch ( getLongDateFormat() )
|
|
{
|
|
case DMY :
|
|
aStr += aDay + getLongDateDaySep() + aMonth + getLongDateMonthSep() + aYear;
|
|
break;
|
|
case MDY :
|
|
aStr += aMonth + getLongDateMonthSep() + aDay + getLongDateDaySep() + aYear;
|
|
break;
|
|
default: // YMD
|
|
aStr += aYear + getLongDateYearSep() + aMonth + getLongDateMonthSep() + aDay;
|
|
}
|
|
return aStr;
|
|
}
|
|
|
|
|
|
OUString 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(), true );
|
|
if ( bSec )
|
|
{
|
|
pBuf = ImplAddString( pBuf, getTimeSep() );
|
|
pBuf = ImplAdd2UNum( pBuf, rTime.GetSec(), true );
|
|
|
|
if ( b100Sec )
|
|
{
|
|
pBuf = ImplAddString( pBuf, getTime100SecSep() );
|
|
pBuf = ImplAdd9UNum( pBuf, rTime.GetNanoSec(), true );
|
|
}
|
|
}
|
|
|
|
return OUString(aBuf, 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().getLength()) + nDig) :
|
|
nDecimals) + rLoc.getNumDecimalSep().getLength() + 3;
|
|
return nGuess;
|
|
}
|
|
|
|
|
|
OUString 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 );
|
|
OUString aStr(pBuffer, pBuf-pBuffer);
|
|
|
|
if ( pBuffer != aBuf )
|
|
delete [] pBuffer;
|
|
return aStr;
|
|
}
|
|
|
|
OUString LocaleDataWrapper::getCurr( sal_Int64 nNumber, sal_uInt16 nDecimals,
|
|
const OUString& 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.getLength()) + nGuess + 20) < SAL_N_ELEMENTS(aBuf) ? aBuf :
|
|
new sal_Unicode[ rCurrencySymbol.getLength() + 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;
|
|
}
|
|
}
|
|
|
|
OUString aNumber(pBuffer, pBuf-pBuffer);
|
|
|
|
if ( pBuffer != aBuf )
|
|
delete [] pBuffer;
|
|
if ( pNumBuffer != aNumBuf )
|
|
delete [] pNumBuffer;
|
|
|
|
return aNumber;
|
|
}
|
|
|
|
|
|
// --- mixed ----------------------------------------------------------
|
|
|
|
LanguageTag LocaleDataWrapper::getLoadedLanguageTag() const
|
|
{
|
|
LanguageCountryInfo aLCInfo = getLanguageCountryInfo();
|
|
return LanguageTag( lang::Locale( aLCInfo.Language, aLCInfo.Country, aLCInfo.Variant ));
|
|
}
|
|
|
|
|
|
OUString LocaleDataWrapper::appendLocaleInfo(const OUString& rDebugMsg) const
|
|
{
|
|
::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nBlockCritical );
|
|
OUStringBuffer aDebugMsg(rDebugMsg);
|
|
aDebugMsg.append(static_cast<sal_Unicode>('\n'));
|
|
aDebugMsg.append(maLanguageTag.getBcp47());
|
|
aDebugMsg.appendAscii(" requested\n");
|
|
LanguageTag aLoaded = getLoadedLanguageTag();
|
|
aDebugMsg.append(aLoaded.getBcp47());
|
|
aDebugMsg.appendAscii(" loaded");
|
|
return aDebugMsg.makeStringAndClear();
|
|
}
|
|
|
|
|
|
// static
|
|
void LocaleDataWrapper::outputCheckMessage( const OUString& rMsg )
|
|
{
|
|
outputCheckMessage(OUStringToOString(rMsg, RTL_TEXTENCODING_UTF8).getStr());
|
|
}
|
|
|
|
|
|
// static
|
|
void LocaleDataWrapper::outputCheckMessage( const char* pStr )
|
|
{
|
|
fprintf( stderr, "\n%s\n", pStr);
|
|
fflush( stderr);
|
|
OSL_TRACE("%s", 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();
|
|
}
|
|
}
|
|
|
|
|
|
// --- XLocaleData3 ----------------------------------------------------------
|
|
|
|
::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Calendar2 > LocaleDataWrapper::getAllCalendars() const
|
|
{
|
|
try
|
|
{
|
|
return xLD->getAllCalendars2( getMyLocale() );
|
|
}
|
|
catch (const Exception& e)
|
|
{
|
|
SAL_WARN( "unotools.i18n", "getAllCalendars: Exception caught " << e.Message );
|
|
}
|
|
return ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Calendar2 >(0);
|
|
}
|
|
|
|
|
|
// --- XLocaleData4 ----------------------------------------------------------
|
|
|
|
::com::sun::star::uno::Sequence< OUString > LocaleDataWrapper::getDateAcceptancePatterns() const
|
|
{
|
|
::utl::ReadWriteGuard aGuard( aMutex );
|
|
|
|
if (aDateAcceptancePatterns.getLength())
|
|
return aDateAcceptancePatterns;
|
|
|
|
aGuard.changeReadToWrite();
|
|
|
|
try
|
|
{
|
|
const_cast<LocaleDataWrapper*>(this)->aDateAcceptancePatterns =
|
|
xLD->getDateAcceptancePatterns( getMyLocale() );
|
|
return aDateAcceptancePatterns;
|
|
}
|
|
catch (const Exception& e)
|
|
{
|
|
SAL_WARN( "unotools.i18n", "getDateAcceptancePatterns: Exception caught " << e.Message );
|
|
}
|
|
return ::com::sun::star::uno::Sequence< OUString >(0);
|
|
}
|
|
|
|
// --- Override layer --------------------------------------------------------
|
|
|
|
void LocaleDataWrapper::setDateAcceptancePatterns(
|
|
const ::com::sun::star::uno::Sequence< OUString > & rPatterns )
|
|
{
|
|
::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nWrite );
|
|
|
|
if (!aDateAcceptancePatterns.getLength() || !rPatterns.getLength())
|
|
{
|
|
try
|
|
{
|
|
aDateAcceptancePatterns = xLD->getDateAcceptancePatterns( getMyLocale() );
|
|
}
|
|
catch (const Exception& e)
|
|
{
|
|
SAL_WARN( "unotools.i18n", "setDateAcceptancePatterns: Exception caught " << e.Message );
|
|
}
|
|
if (!rPatterns.getLength())
|
|
return; // just a reset
|
|
if (!aDateAcceptancePatterns.getLength())
|
|
{
|
|
aDateAcceptancePatterns = rPatterns;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Never overwrite the locale's full date pattern! The first.
|
|
if (aDateAcceptancePatterns[0] == rPatterns[0])
|
|
aDateAcceptancePatterns = rPatterns; // sane
|
|
else
|
|
{
|
|
// Copy existing full date pattern and append the sequence passed.
|
|
/* TODO: could check for duplicates and shrink target sequence */
|
|
Sequence< OUString > aTmp( rPatterns.getLength() + 1 );
|
|
OUString* pArray1 = aTmp.getArray();
|
|
const OUString* pArray2 = rPatterns.getConstArray();
|
|
pArray1[0] = aDateAcceptancePatterns[0];
|
|
for (sal_Int32 i=0; i < rPatterns.getLength(); ++i)
|
|
pArray1[i+1] = pArray2[i];
|
|
aDateAcceptancePatterns = aTmp;
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|