1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <string.h> // memcpy()
22 #include <stdio.h> // fprintf(), stderr
24 #include <unotools/localedatawrapper.hxx>
25 #include <unotools/numberformatcodewrapper.hxx>
26 #include <unotools/calendarwrapper.hxx>
27 #include <unotools/digitgroupingiterator.hxx>
28 #include <tools/string.hxx>
29 #include <tools/debug.hxx>
30 #include <i18npool/languagetag.hxx>
32 #include "instance.hxx"
33 #include <com/sun/star/i18n/KNumberFormatUsage.hpp>
34 #include <com/sun/star/i18n/KNumberFormatType.hpp>
35 #include <com/sun/star/i18n/LocaleData.hpp>
36 #include <com/sun/star/i18n/CalendarFieldIndex.hpp>
37 #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
38 #include <com/sun/star/i18n/NumberFormatIndex.hpp>
40 #include <comphelper/processfactory.hxx>
41 #include <rtl/instance.hxx>
42 #include <rtl/ustrbuf.hxx>
43 #include <sal/macros.h>
45 static const int nDateFormatInvalid
= -1;
46 static const sal_uInt16 nCurrFormatInvalid
= 0xffff;
47 static const sal_uInt16 nCurrFormatDefault
= 0;
49 using namespace ::com::sun::star
;
50 using namespace ::com::sun::star::i18n
;
51 using namespace ::com::sun::star::uno
;
55 struct InstalledLocales
57 uno::Sequence
< lang::Locale
>, InstalledLocales
>
60 struct InstalledLanguageTypes
62 uno::Sequence
< sal_uInt16
>, InstalledLanguageTypes
>
66 sal_uInt8
LocaleDataWrapper::nLocaleDataChecking
= 0;
68 LocaleDataWrapper::LocaleDataWrapper(
69 const Reference
< uno::XComponentContext
> & rxContext
,
70 const LanguageTag
& rLanguageTag
73 m_xContext( rxContext
),
74 xLD( LocaleData::create(rxContext
) ),
75 maLanguageTag( rLanguageTag
),
76 bLocaleDataItemValid( sal_False
),
77 bReservedWordValid( sal_False
)
82 LocaleDataWrapper::LocaleDataWrapper(
83 const LanguageTag
& rLanguageTag
86 m_xContext( comphelper::getProcessComponentContext() ),
87 xLD( LocaleData::create(m_xContext
) ),
88 maLanguageTag( rLanguageTag
),
89 bLocaleDataItemValid( sal_False
),
90 bReservedWordValid( sal_False
)
95 LocaleDataWrapper::~LocaleDataWrapper()
100 void LocaleDataWrapper::setLanguageTag( const LanguageTag
& rLanguageTag
)
102 ::utl::ReadWriteGuard
aGuard( aMutex
, ::utl::ReadWriteGuardMode::nCriticalChange
);
103 maLanguageTag
= rLanguageTag
;
108 const LanguageTag
& LocaleDataWrapper::getLanguageTag() const
110 ::utl::ReadWriteGuard
aGuard( aMutex
);
111 return maLanguageTag
;
115 const ::com::sun::star::lang::Locale
& LocaleDataWrapper::getMyLocale() const
117 ::utl::ReadWriteGuard
aGuard( aMutex
);
118 return maLanguageTag
.getLocale();
122 void LocaleDataWrapper::invalidateData()
124 aCurrSymbol
= rtl::OUString();
125 aCurrBankSymbol
= rtl::OUString();
126 nDateFormat
= nLongDateFormat
= nDateFormatInvalid
;
127 nCurrPositiveFormat
= nCurrNegativeFormat
= nCurrDigits
= nCurrFormatInvalid
;
128 if ( bLocaleDataItemValid
)
130 for (sal_Int32 j
=0; j
<LocaleItem::COUNT
; ++j
)
131 aLocaleItem
[j
] = rtl::OUString();
132 bLocaleDataItemValid
= sal_False
;
134 if ( bReservedWordValid
)
136 for ( sal_Int16 j
=0; j
<reservedWords::COUNT
; ++j
)
137 aReservedWord
[j
] = rtl::OUString();
138 bReservedWordValid
= sal_False
;
140 xDefaultCalendar
.reset();
141 if (aGrouping
.getLength())
143 if (aDateAcceptancePatterns
.getLength())
144 aDateAcceptancePatterns
= Sequence
<OUString
>();
150 ::com::sun::star::i18n::LanguageCountryInfo
LocaleDataWrapper::getLanguageCountryInfo() const
154 return xLD
->getLanguageCountryInfo( getMyLocale() );
156 catch (const Exception
& e
)
158 SAL_WARN( "unotools.i18n", "getLanguageCountryInfo: Exception caught " << e
.Message
);
160 return ::com::sun::star::i18n::LanguageCountryInfo();
164 ::com::sun::star::i18n::LocaleDataItem
LocaleDataWrapper::getLocaleItem() const
168 return xLD
->getLocaleItem( getMyLocale() );
170 catch (const Exception
& e
)
172 SAL_WARN( "unotools.i18n", "getLocaleItem: Exception caught " << e
.Message
);
174 return ::com::sun::star::i18n::LocaleDataItem();
178 ::com::sun::star::uno::Sequence
< ::com::sun::star::i18n::Currency2
> LocaleDataWrapper::getAllCurrencies() const
182 return xLD
->getAllCurrencies2( getMyLocale() );
184 catch (const Exception
& e
)
186 SAL_WARN( "unotools.i18n", "getAllCurrencies: Exception caught " << e
.Message
);
188 return ::com::sun::star::uno::Sequence
< ::com::sun::star::i18n::Currency2
>(0);
192 ::com::sun::star::uno::Sequence
< ::com::sun::star::i18n::FormatElement
> LocaleDataWrapper::getAllFormats() const
196 return xLD
->getAllFormats( getMyLocale() );
198 catch (const Exception
& e
)
200 SAL_WARN( "unotools.i18n", "getAllFormats: Exception caught " << e
.Message
);
202 return ::com::sun::star::uno::Sequence
< ::com::sun::star::i18n::FormatElement
>(0);
206 ::com::sun::star::i18n::ForbiddenCharacters
LocaleDataWrapper::getForbiddenCharacters() const
210 return xLD
->getForbiddenCharacters( getMyLocale() );
212 catch (const Exception
& e
)
214 SAL_WARN( "unotools.i18n", "getForbiddenCharacters: Exception caught " << e
.Message
);
216 return ::com::sun::star::i18n::ForbiddenCharacters();
220 ::com::sun::star::uno::Sequence
< ::rtl::OUString
> LocaleDataWrapper::getReservedWord() const
224 return xLD
->getReservedWord( getMyLocale() );
226 catch ( const Exception
& e
)
228 SAL_WARN( "unotools.i18n", "getReservedWord: Exception caught " << e
.Message
);
230 return ::com::sun::star::uno::Sequence
< ::rtl::OUString
>(0);
234 ::com::sun::star::uno::Sequence
< ::com::sun::star::lang::Locale
> LocaleDataWrapper::getAllInstalledLocaleNames() const
236 uno::Sequence
< lang::Locale
> &rInstalledLocales
= InstalledLocales::get();
238 if ( rInstalledLocales
.getLength() )
239 return rInstalledLocales
;
243 rInstalledLocales
= xLD
->getAllInstalledLocaleNames();
245 catch ( const Exception
& e
)
247 SAL_WARN( "unotools.i18n", "getAllInstalledLocaleNames: Exception caught " << e
.Message
);
249 return rInstalledLocales
;
253 // --- Impl and helpers ----------------------------------------------------
256 ::com::sun::star::uno::Sequence
< ::com::sun::star::lang::Locale
> LocaleDataWrapper::getInstalledLocaleNames()
258 const uno::Sequence
< lang::Locale
> &rInstalledLocales
=
259 InstalledLocales::get();
261 if ( !rInstalledLocales
.getLength() )
263 LocaleDataWrapper
aLDW( ::comphelper::getProcessComponentContext(), LanguageTag( lang::Locale()) );
264 aLDW
.getAllInstalledLocaleNames();
266 return rInstalledLocales
;
270 ::com::sun::star::uno::Sequence
< sal_uInt16
> LocaleDataWrapper::getInstalledLanguageTypes()
272 uno::Sequence
< sal_uInt16
> &rInstalledLanguageTypes
=
273 InstalledLanguageTypes::get();
275 if ( rInstalledLanguageTypes
.getLength() )
276 return rInstalledLanguageTypes
;
278 ::com::sun::star::uno::Sequence
< ::com::sun::star::lang::Locale
> xLoc
=
279 getInstalledLocaleNames();
280 sal_Int32 nCount
= xLoc
.getLength();
281 ::com::sun::star::uno::Sequence
< sal_uInt16
> xLang( nCount
);
282 sal_Int32 nLanguages
= 0;
283 for ( sal_Int32 i
=0; i
<nCount
; i
++ )
286 if (areChecksEnabled())
288 /* FIXME-BCP47: handle language tags! */
289 aDebugLocale
= xLoc
[i
].Language
;
290 if ( !xLoc
[i
].Country
.isEmpty() )
293 aDebugLocale
+= String( xLoc
[i
].Country
);
294 if ( !xLoc
[i
].Variant
.isEmpty() )
297 aDebugLocale
+= String( xLoc
[i
].Variant
);
302 if ( !xLoc
[i
].Variant
.isEmpty() )
304 if (areChecksEnabled())
306 rtl::OUStringBuffer
aMsg("LocaleDataWrapper::getInstalledLanguageTypes: Variants not supported, locale\n");
307 aMsg
.append(aDebugLocale
);
308 outputCheckMessage(aMsg
.makeStringAndClear());
312 LanguageTag
aLanguageTag( xLoc
[i
] );
313 LanguageType eLang
= aLanguageTag
.getLanguageType();
315 // In checks, exclude known problems because no MS-LCID defined.
316 if (areChecksEnabled() && eLang
== LANGUAGE_DONTKNOW
)
318 rtl::OUStringBuffer
aMsg("ConvertIsoNamesToLanguage: unknown MS-LCID for locale\n");
319 aMsg
.append(aDebugLocale
);
320 outputCheckMessage(aMsg
.makeStringAndClear());
325 case LANGUAGE_NORWEGIAN
: // no_NO, not Bokmal (nb_NO), not Nynorsk (nn_NO)
326 eLang
= LANGUAGE_DONTKNOW
; // don't offer "Unknown" language
329 if ( eLang
!= LANGUAGE_DONTKNOW
)
331 LanguageTag
aBackLanguageTag( eLang
);
332 if ( aLanguageTag
!= aBackLanguageTag
)
334 // In checks, exclude known problems because no MS-LCID defined
335 // and default for Language found.
336 if ( areChecksEnabled()
337 && !aDebugLocale
.EqualsAscii( "ar-SD" ) // Sudan/ar
338 && !aDebugLocale
.EqualsAscii( "en-CB" ) // Carribean is not a country
339 // && !aDebugLocale.EqualsAscii( "en-BG" ) // ?!? Bulgaria/en
340 // && !aDebugLocale.EqualsAscii( "es-BR" ) // ?!? Brazil/es
343 rtl::OUStringBuffer aMsg
;
344 aMsg
.appendAscii(RTL_CONSTASCII_STRINGPARAM(
345 "ConvertIsoNamesToLanguage/ConvertLanguageToIsoNames: ambiguous locale (MS-LCID?)\n"));
346 aMsg
.append(aDebugLocale
);
347 aMsg
.appendAscii(RTL_CONSTASCII_STRINGPARAM( " -> 0x"));
348 aMsg
.append(static_cast<sal_Int32
>(eLang
), 16);
349 aMsg
.appendAscii(RTL_CONSTASCII_STRINGPARAM( " -> "));
350 aMsg
.append(aBackLanguageTag
.getBcp47());
351 outputCheckMessage( aMsg
.makeStringAndClear() );
353 eLang
= LANGUAGE_DONTKNOW
;
356 if ( eLang
!= LANGUAGE_DONTKNOW
)
357 xLang
[ nLanguages
++ ] = eLang
;
359 if ( nLanguages
< nCount
)
360 xLang
.realloc( nLanguages
);
361 rInstalledLanguageTypes
= xLang
;
363 return rInstalledLanguageTypes
;
366 const rtl::OUString
& LocaleDataWrapper::getOneLocaleItem( sal_Int16 nItem
) const
368 ::utl::ReadWriteGuard
aGuard( aMutex
);
369 if ( nItem
>= LocaleItem::COUNT
)
371 SAL_WARN( "unotools.i18n", "getOneLocaleItem: bounds" );
372 return aLocaleItem
[0];
374 if (aLocaleItem
[nItem
].isEmpty())
375 { // no cached content
376 aGuard
.changeReadToWrite();
377 ((LocaleDataWrapper
*)this)->getOneLocaleItemImpl( nItem
);
379 return aLocaleItem
[nItem
];
383 void LocaleDataWrapper::getOneLocaleItemImpl( sal_Int16 nItem
)
385 if ( !bLocaleDataItemValid
)
387 aLocaleDataItem
= getLocaleItem();
388 bLocaleDataItemValid
= sal_True
;
392 case LocaleItem::DATE_SEPARATOR
:
393 aLocaleItem
[nItem
] = aLocaleDataItem
.dateSeparator
;
395 case LocaleItem::THOUSAND_SEPARATOR
:
396 aLocaleItem
[nItem
] = aLocaleDataItem
.thousandSeparator
;
398 case LocaleItem::DECIMAL_SEPARATOR
:
399 aLocaleItem
[nItem
] = aLocaleDataItem
.decimalSeparator
;
401 case LocaleItem::TIME_SEPARATOR
:
402 aLocaleItem
[nItem
] = aLocaleDataItem
.timeSeparator
;
404 case LocaleItem::TIME_100SEC_SEPARATOR
:
405 aLocaleItem
[nItem
] = aLocaleDataItem
.time100SecSeparator
;
407 case LocaleItem::LIST_SEPARATOR
:
408 aLocaleItem
[nItem
] = aLocaleDataItem
.listSeparator
;
410 case LocaleItem::SINGLE_QUOTATION_START
:
411 aLocaleItem
[nItem
] = aLocaleDataItem
.quotationStart
;
413 case LocaleItem::SINGLE_QUOTATION_END
:
414 aLocaleItem
[nItem
] = aLocaleDataItem
.quotationEnd
;
416 case LocaleItem::DOUBLE_QUOTATION_START
:
417 aLocaleItem
[nItem
] = aLocaleDataItem
.doubleQuotationStart
;
419 case LocaleItem::DOUBLE_QUOTATION_END
:
420 aLocaleItem
[nItem
] = aLocaleDataItem
.doubleQuotationEnd
;
422 case LocaleItem::MEASUREMENT_SYSTEM
:
423 aLocaleItem
[nItem
] = aLocaleDataItem
.measurementSystem
;
425 case LocaleItem::TIME_AM
:
426 aLocaleItem
[nItem
] = aLocaleDataItem
.timeAM
;
428 case LocaleItem::TIME_PM
:
429 aLocaleItem
[nItem
] = aLocaleDataItem
.timePM
;
431 case LocaleItem::LONG_DATE_DAY_OF_WEEK_SEPARATOR
:
432 aLocaleItem
[nItem
] = aLocaleDataItem
.LongDateDayOfWeekSeparator
;
434 case LocaleItem::LONG_DATE_DAY_SEPARATOR
:
435 aLocaleItem
[nItem
] = aLocaleDataItem
.LongDateDaySeparator
;
437 case LocaleItem::LONG_DATE_MONTH_SEPARATOR
:
438 aLocaleItem
[nItem
] = aLocaleDataItem
.LongDateMonthSeparator
;
440 case LocaleItem::LONG_DATE_YEAR_SEPARATOR
:
441 aLocaleItem
[nItem
] = aLocaleDataItem
.LongDateYearSeparator
;
444 SAL_WARN( "unotools.i18n", "getOneLocaleItemImpl: which one?" );
449 void LocaleDataWrapper::getOneReservedWordImpl( sal_Int16 nWord
)
451 if ( !bReservedWordValid
)
453 aReservedWordSeq
= getReservedWord();
454 bReservedWordValid
= sal_True
;
456 DBG_ASSERT( nWord
< aReservedWordSeq
.getLength(), "getOneReservedWordImpl: which one?" );
457 if ( nWord
< aReservedWordSeq
.getLength() )
458 aReservedWord
[nWord
] = aReservedWordSeq
[nWord
];
462 const rtl::OUString
& LocaleDataWrapper::getOneReservedWord( sal_Int16 nWord
) const
464 ::utl::ReadWriteGuard
aGuard( aMutex
);
465 if ( nWord
< 0 || nWord
>= reservedWords::COUNT
)
467 SAL_WARN( "unotools.i18n", "getOneReservedWord: bounds" );
468 nWord
= reservedWords::FALSE_WORD
;
470 if (aReservedWord
[nWord
].isEmpty())
471 { // no cached content
472 aGuard
.changeReadToWrite();
473 ((LocaleDataWrapper
*)this)->getOneReservedWordImpl( nWord
);
475 return aReservedWord
[nWord
];
479 MeasurementSystem
LocaleDataWrapper::mapMeasurementStringToEnum( const rtl::OUString
& rMS
) const
481 //! TODO: could be cached too
482 if ( rMS
.equalsIgnoreAsciiCase( "metric" ) )
483 return MEASURE_METRIC
;
484 //! TODO: other measurement systems? => extend enum MeasurementSystem
489 void LocaleDataWrapper::getDefaultCalendarImpl()
491 if (!xDefaultCalendar
)
493 Sequence
< Calendar2
> xCals
= getAllCalendars();
494 sal_Int32 nCount
= xCals
.getLength();
498 const Calendar2
* pArr
= xCals
.getArray();
499 for (sal_Int32 i
=0; i
<nCount
; ++i
)
508 xDefaultCalendar
.reset( new Calendar2( xCals
[nDef
]));
513 const ::boost::shared_ptr
< ::com::sun::star::i18n::Calendar2
> LocaleDataWrapper::getDefaultCalendar() const
515 ::utl::ReadWriteGuard
aGuard( aMutex
);
516 if (!xDefaultCalendar
)
517 { // no cached content
518 aGuard
.changeReadToWrite();
519 ((LocaleDataWrapper
*)this)->getDefaultCalendarImpl();
521 return xDefaultCalendar
;
525 const ::com::sun::star::uno::Sequence
< ::com::sun::star::i18n::CalendarItem2
> LocaleDataWrapper::getDefaultCalendarDays() const
527 return getDefaultCalendar()->Days
;
531 const ::com::sun::star::uno::Sequence
< ::com::sun::star::i18n::CalendarItem2
> LocaleDataWrapper::getDefaultCalendarMonths() const
533 return getDefaultCalendar()->Months
;
537 // --- currencies -----------------------------------------------------
539 const rtl::OUString
& LocaleDataWrapper::getCurrSymbol() const
541 ::utl::ReadWriteGuard
aGuard( aMutex
);
542 if (aCurrSymbol
.isEmpty())
544 aGuard
.changeReadToWrite();
545 ((LocaleDataWrapper
*)this)->getCurrSymbolsImpl();
551 const rtl::OUString
& LocaleDataWrapper::getCurrBankSymbol() const
553 ::utl::ReadWriteGuard
aGuard( aMutex
);
554 if (aCurrBankSymbol
.isEmpty())
556 aGuard
.changeReadToWrite();
557 ((LocaleDataWrapper
*)this)->getCurrSymbolsImpl();
559 return aCurrBankSymbol
;
563 sal_uInt16
LocaleDataWrapper::getCurrPositiveFormat() const
565 ::utl::ReadWriteGuard
aGuard( aMutex
);
566 if ( nCurrPositiveFormat
== nCurrFormatInvalid
)
568 aGuard
.changeReadToWrite();
569 ((LocaleDataWrapper
*)this)->getCurrFormatsImpl();
571 return nCurrPositiveFormat
;
575 sal_uInt16
LocaleDataWrapper::getCurrNegativeFormat() const
577 ::utl::ReadWriteGuard
aGuard( aMutex
);
578 if ( nCurrNegativeFormat
== nCurrFormatInvalid
)
580 aGuard
.changeReadToWrite();
581 ((LocaleDataWrapper
*)this)->getCurrFormatsImpl();
583 return nCurrNegativeFormat
;
587 sal_uInt16
LocaleDataWrapper::getCurrDigits() const
589 ::utl::ReadWriteGuard
aGuard( aMutex
);
590 if ( nCurrDigits
== nCurrFormatInvalid
)
592 aGuard
.changeReadToWrite();
593 ((LocaleDataWrapper
*)this)->getCurrSymbolsImpl();
599 void LocaleDataWrapper::getCurrSymbolsImpl()
601 Sequence
< Currency2
> aCurrSeq
= getAllCurrencies();
602 sal_Int32 nCnt
= aCurrSeq
.getLength();
603 Currency2
const * const pCurrArr
= aCurrSeq
.getArray();
605 for ( nElem
= 0; nElem
< nCnt
; nElem
++ )
607 if ( pCurrArr
[nElem
].Default
)
612 if (areChecksEnabled())
614 rtl::OUString
aMsg( RTL_CONSTASCII_USTRINGPARAM(
615 "LocaleDataWrapper::getCurrSymbolsImpl: no default currency"));
616 outputCheckMessage( appendLocaleInfo( aMsg
) );
621 if (areChecksEnabled())
622 outputCheckMessage(rtl::OUString("LocaleDataWrapper::getCurrSymbolsImpl: no currency at all, using ShellsAndPebbles"));
623 aCurrSymbol
= rtl::OUString("ShellsAndPebbles");
624 aCurrBankSymbol
= aCurrSymbol
;
625 nCurrPositiveFormat
= nCurrNegativeFormat
= nCurrFormatDefault
;
630 aCurrSymbol
= pCurrArr
[nElem
].Symbol
;
631 aCurrBankSymbol
= pCurrArr
[nElem
].BankSymbol
;
632 nCurrDigits
= pCurrArr
[nElem
].DecimalPlaces
;
636 void LocaleDataWrapper::scanCurrFormatImpl( const rtl::OUString
& rCode
,
637 sal_Int32 nStart
, sal_Int32
& nSign
, sal_Int32
& nPar
,
638 sal_Int32
& nNum
, sal_Int32
& nBlank
, sal_Int32
& nSym
)
640 nSign
= nPar
= nNum
= nBlank
= nSym
= -1;
641 const sal_Unicode
* const pStr
= rCode
.getStr();
642 const sal_Unicode
* const pStop
= pStr
+ rCode
.getLength();
643 const sal_Unicode
* p
= pStr
+ nStart
;
645 sal_Bool bQuote
= sal_False
;
650 if ( *p
== '"' && *(p
-1) != '\\' )
658 if ( pStr
== p
|| *(p
-1) != '\\' )
662 if (!nInSection
&& nSign
== -1)
666 if (!nInSection
&& nPar
== -1)
671 if (!nInSection
&& nNum
== -1)
681 if (!nInSection
&& nBlank
== -1
682 && nSym
!= -1 && p
< pStop
-1 && *(p
+1) == ' ' )
683 nBlank
= p
- pStr
+ 1;
687 if (nSym
== -1 && nInSection
&& *(p
-1) == '[')
690 if (nNum
!= -1 && *(p
-2) == ' ')
691 nBlank
= p
- pStr
- 2;
699 if (!nInSection
&& nSym
== -1 && String(rCode
).Equals( aCurrSymbol
, (xub_StrLen
)(p
-pStr
), aCurrSymbol
.getLength()))
700 { // currency symbol not surrounded by [$...]
702 if (nBlank
== -1 && pStr
< p
&& *(p
-1) == ' ')
703 nBlank
= p
- pStr
- 1;
704 p
+= aCurrSymbol
.getLength() - 1;
705 if (nBlank
== -1 && p
< pStop
-2 && *(p
+2) == ' ')
706 nBlank
= p
- pStr
+ 2;
714 void LocaleDataWrapper::getCurrFormatsImpl()
716 NumberFormatCodeWrapper
aNumberFormatCode( m_xContext
, getMyLocale() );
717 uno::Sequence
< NumberFormatCode
> aFormatSeq
718 = aNumberFormatCode
.getAllFormatCode( KNumberFormatUsage::CURRENCY
);
719 sal_Int32 nCnt
= aFormatSeq
.getLength();
722 if (areChecksEnabled())
724 rtl::OUString
aMsg( RTL_CONSTASCII_USTRINGPARAM(
725 "LocaleDataWrapper::getCurrFormatsImpl: no currency formats"));
726 outputCheckMessage( appendLocaleInfo( aMsg
) );
728 nCurrPositiveFormat
= nCurrNegativeFormat
= nCurrFormatDefault
;
731 // find a negative code (medium preferred) and a default (medium preferred) (not necessarily the same)
732 NumberFormatCode
const * const pFormatArr
= aFormatSeq
.getArray();
733 sal_Int32 nElem
, nDef
, nNeg
, nMedium
;
734 nDef
= nNeg
= nMedium
= -1;
735 for ( nElem
= 0; nElem
< nCnt
; nElem
++ )
737 if ( pFormatArr
[nElem
].Type
== KNumberFormatType::MEDIUM
)
739 if ( pFormatArr
[nElem
].Default
)
743 if ( pFormatArr
[nElem
].Code
.indexOf( ';' ) >= 0 )
748 if ( (nNeg
== -1 || nMedium
== -1) && pFormatArr
[nElem
].Code
.indexOf( ';' ) >= 0 )
756 if ( nDef
== -1 && pFormatArr
[nElem
].Default
)
758 if ( nNeg
== -1 && pFormatArr
[nElem
].Code
.indexOf( ';' ) >= 0 )
763 // make sure it's loaded
766 sal_Int32 nSign
, nPar
, nNum
, nBlank
, nSym
;
769 nElem
= (nDef
>= 0 ? nDef
: (nNeg
>= 0 ? nNeg
: 0));
770 scanCurrFormatImpl( pFormatArr
[nElem
].Code
, 0, nSign
, nPar
, nNum
, nBlank
, nSym
);
771 if (areChecksEnabled() && (nNum
== -1 || nSym
== -1))
773 rtl::OUString
aMsg( RTL_CONSTASCII_USTRINGPARAM(
774 "LocaleDataWrapper::getCurrFormatsImpl: CurrPositiveFormat?"));
775 outputCheckMessage( appendLocaleInfo( aMsg
) );
780 nCurrPositiveFormat
= 0; // $1
782 nCurrPositiveFormat
= 1; // 1$
787 nCurrPositiveFormat
= 2; // $ 1
789 nCurrPositiveFormat
= 3; // 1 $
794 nCurrNegativeFormat
= nCurrFormatDefault
;
797 const ::rtl::OUString
& rCode
= pFormatArr
[nNeg
].Code
;
798 sal_Int32 nDelim
= rCode
.indexOf(';');
799 scanCurrFormatImpl( rCode
, nDelim
+1, nSign
, nPar
, nNum
, nBlank
, nSym
);
800 if (areChecksEnabled() && (nNum
== -1 || nSym
== -1 || (nPar
== -1 && nSign
== -1)))
802 rtl::OUString
aMsg( RTL_CONSTASCII_USTRINGPARAM(
803 "LocaleDataWrapper::getCurrFormatsImpl: CurrNegativeFormat?"));
804 outputCheckMessage( appendLocaleInfo( aMsg
) );
806 // NOTE: one of nPar or nSign are allowed to be -1
811 if ( -1 < nPar
&& nPar
< nSym
)
812 nCurrNegativeFormat
= 0; // ($1)
813 else if ( -1 < nSign
&& nSign
< nSym
)
814 nCurrNegativeFormat
= 1; // -$1
815 else if ( nNum
< nSign
)
816 nCurrNegativeFormat
= 3; // $1-
818 nCurrNegativeFormat
= 2; // $-1
822 if ( -1 < nPar
&& nPar
< nNum
)
823 nCurrNegativeFormat
= 4; // (1$)
824 else if ( -1 < nSign
&& nSign
< nNum
)
825 nCurrNegativeFormat
= 5; // -1$
826 else if ( nSym
< nSign
)
827 nCurrNegativeFormat
= 7; // 1$-
829 nCurrNegativeFormat
= 6; // 1-$
836 if ( -1 < nPar
&& nPar
< nSym
)
837 nCurrNegativeFormat
= 14; // ($ 1)
838 else if ( -1 < nSign
&& nSign
< nSym
)
839 nCurrNegativeFormat
= 9; // -$ 1
840 else if ( nNum
< nSign
)
841 nCurrNegativeFormat
= 12; // $ 1-
843 nCurrNegativeFormat
= 11; // $ -1
847 if ( -1 < nPar
&& nPar
< nNum
)
848 nCurrNegativeFormat
= 15; // (1 $)
849 else if ( -1 < nSign
&& nSign
< nNum
)
850 nCurrNegativeFormat
= 8; // -1 $
851 else if ( nSym
< nSign
)
852 nCurrNegativeFormat
= 10; // 1 $-
854 nCurrNegativeFormat
= 13; // 1- $
861 // --- date -----------------------------------------------------------
863 DateFormat
LocaleDataWrapper::getDateFormat() const
865 ::utl::ReadWriteGuard
aGuard( aMutex
);
866 if ( nDateFormat
== nDateFormatInvalid
)
868 aGuard
.changeReadToWrite();
869 ((LocaleDataWrapper
*)this)->getDateFormatsImpl();
871 return (DateFormat
) nDateFormat
;
875 DateFormat
LocaleDataWrapper::getLongDateFormat() const
877 ::utl::ReadWriteGuard
aGuard( aMutex
);
878 if ( nLongDateFormat
== nDateFormatInvalid
)
880 aGuard
.changeReadToWrite();
881 ((LocaleDataWrapper
*)this)->getDateFormatsImpl();
883 return (DateFormat
) nLongDateFormat
;
887 DateFormat
LocaleDataWrapper::scanDateFormatImpl( const rtl::OUString
& rCode
)
889 // Only some european versions were translated, the ones with different
890 // keyword combinations are:
891 // English DMY, German TMJ, Spanish DMA, French JMA, Italian GMA,
892 // Dutch DMJ, Finnish PKV
894 // default is English keywords for every other language
895 sal_Int32 nDay
= rCode
.indexOf('D');
896 sal_Int32 nMonth
= rCode
.indexOf('M');
897 sal_Int32 nYear
= rCode
.indexOf('Y');
898 if (nDay
== -1 || nMonth
== -1 || nYear
== -1)
899 { // This algorithm assumes that all three parts (DMY) are present
901 { // only Finnish has something else than 'M' for month
902 nMonth
= rCode
.indexOf('K');
905 nDay
= rCode
.indexOf('P');
906 nYear
= rCode
.indexOf('V');
910 { // We have a month 'M' if we reach this branch.
911 // Possible languages containing 'M' but no 'D':
912 // German, French, Italian
913 nDay
= rCode
.indexOf('T'); // German
915 nYear
= rCode
.indexOf('J');
918 nYear
= rCode
.indexOf('A'); // French, Italian
921 nDay
= rCode
.indexOf('J'); // French
923 nDay
= rCode
.indexOf('G'); // Italian
928 { // We have a month 'M' and a day 'D'.
929 // Possible languages containing 'D' and 'M' but not 'Y':
931 nYear
= rCode
.indexOf('A'); // Spanish
933 nYear
= rCode
.indexOf('J'); // Dutch
935 if (nDay
== -1 || nMonth
== -1 || nYear
== -1)
937 if (areChecksEnabled())
939 rtl::OUString
aMsg( RTL_CONSTASCII_USTRINGPARAM(
940 "LocaleDataWrapper::scanDateFormat: not all DMY present"));
941 outputCheckMessage( appendLocaleInfo( aMsg
) );
944 nDay
= rCode
.getLength();
946 nMonth
= rCode
.getLength();
948 nYear
= rCode
.getLength();
951 // compare with <= because each position may equal rCode.getLength()
952 if ( nDay
<= nMonth
&& nMonth
<= nYear
)
953 return DMY
; // also if every position equals rCode.getLength()
954 else if ( nMonth
<= nDay
&& nDay
<= nYear
)
956 else if ( nYear
<= nMonth
&& nMonth
<= nDay
)
960 if (areChecksEnabled())
962 rtl::OUString
aMsg( RTL_CONSTASCII_USTRINGPARAM(
963 "LocaleDataWrapper::scanDateFormat: no magic applyable"));
964 outputCheckMessage( appendLocaleInfo( aMsg
) );
971 void LocaleDataWrapper::getDateFormatsImpl()
973 NumberFormatCodeWrapper
aNumberFormatCode( m_xContext
, getMyLocale() );
974 uno::Sequence
< NumberFormatCode
> aFormatSeq
975 = aNumberFormatCode
.getAllFormatCode( KNumberFormatUsage::DATE
);
976 sal_Int32 nCnt
= aFormatSeq
.getLength();
979 if (areChecksEnabled())
981 rtl::OUString
aMsg( RTL_CONSTASCII_USTRINGPARAM(
982 "LocaleDataWrapper::getDateFormatsImpl: no date formats"));
983 outputCheckMessage( appendLocaleInfo( aMsg
) );
985 nDateFormat
= nLongDateFormat
= DMY
;
988 // find the edit (21), a default (medium preferred),
989 // a medium (default preferred), and a long (default preferred)
990 NumberFormatCode
const * const pFormatArr
= aFormatSeq
.getArray();
991 sal_Int32 nElem
, nEdit
, nDef
, nMedium
, nLong
;
992 nEdit
= nDef
= nMedium
= nLong
= -1;
993 for ( nElem
= 0; nElem
< nCnt
; nElem
++ )
995 if ( nEdit
== -1 && pFormatArr
[nElem
].Index
== NumberFormatIndex::DATE_SYS_DDMMYYYY
)
997 if ( nDef
== -1 && pFormatArr
[nElem
].Default
)
999 switch ( pFormatArr
[nElem
].Type
)
1001 case KNumberFormatType::MEDIUM
:
1003 if ( pFormatArr
[nElem
].Default
)
1008 else if ( nMedium
== -1 )
1012 case KNumberFormatType::LONG
:
1014 if ( pFormatArr
[nElem
].Default
)
1016 else if ( nLong
== -1 )
1024 if (areChecksEnabled())
1026 rtl::OUString
aMsg( RTL_CONSTASCII_USTRINGPARAM(
1027 "LocaleDataWrapper::getDateFormatsImpl: no edit"));
1028 outputCheckMessage( appendLocaleInfo( aMsg
) );
1032 if (areChecksEnabled())
1034 rtl::OUString
aMsg( RTL_CONSTASCII_USTRINGPARAM(
1035 "LocaleDataWrapper::getDateFormatsImpl: no default"));
1036 outputCheckMessage( appendLocaleInfo( aMsg
) );
1038 if ( nMedium
!= -1 )
1040 else if ( nLong
!= -1 )
1047 DateFormat nDF
= scanDateFormatImpl( pFormatArr
[nEdit
].Code
);
1048 if ( pFormatArr
[nEdit
].Type
== KNumberFormatType::LONG
)
1049 { // normally this is not the case
1050 nLongDateFormat
= nDateFormat
= nDF
;
1056 nLongDateFormat
= nDF
;
1058 nLongDateFormat
= scanDateFormatImpl( pFormatArr
[nLong
].Code
);
1063 // --- digit grouping -------------------------------------------------
1065 void LocaleDataWrapper::getDigitGroupingImpl()
1067 /* TODO: This is a very simplified grouping setup that only serves its
1068 * current purpose for Indian locales. A free-form flexible one would
1069 * obtain grouping from locale data where it could be specified using, for
1070 * example, codes like #,### and #,##,### that would generate the integer
1071 * sequence. Needed additional API and a locale data element.
1074 if (!aGrouping
.getLength())
1076 aGrouping
.realloc(3); // room for {3,2,0}
1077 aGrouping
[0] = 0; // invalidate
1081 i18n::LanguageCountryInfo
aLCInfo( getLanguageCountryInfo());
1082 if (aLCInfo
.Country
.equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("IN")) || // India
1083 aLCInfo
.Country
.equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("BT")) ) // Bhutan
1098 const ::com::sun::star::uno::Sequence
< sal_Int32
> LocaleDataWrapper::getDigitGrouping() const
1100 ::utl::ReadWriteGuard
aGuard( aMutex
);
1101 if (!aGrouping
.getLength() || aGrouping
[0] == 0)
1102 { // no cached content
1103 aGuard
.changeReadToWrite();
1104 ((LocaleDataWrapper
*)this)->getDigitGroupingImpl();
1110 // --- simple number formatting helpers -------------------------------
1112 // The ImplAdd... methods are taken from class International and modified to
1115 static sal_Unicode
* ImplAddUNum( sal_Unicode
* pBuf
, sal_uInt64 nNumber
)
1117 // fill temp buffer with digits
1118 sal_Unicode aTempBuf
[64];
1119 sal_Unicode
* pTempBuf
= aTempBuf
;
1122 *pTempBuf
= (sal_Unicode
)(nNumber
% 10) + '0';
1128 // copy temp buffer to buffer passed
1135 while ( pTempBuf
!= aTempBuf
);
1141 static sal_Unicode
* ImplAddUNum( sal_Unicode
* pBuf
, sal_uInt64 nNumber
, int nMinLen
)
1143 // fill temp buffer with digits
1144 sal_Unicode aTempBuf
[64];
1145 sal_Unicode
* pTempBuf
= aTempBuf
;
1148 *pTempBuf
= (sal_Unicode
)(nNumber
% 10) + '0';
1155 // fill with zeros up to the minimal length
1156 while ( nMinLen
> 0 )
1163 // copy temp buffer to real buffer
1170 while ( pTempBuf
!= aTempBuf
);
1176 static sal_Unicode
* ImplAdd2UNum( sal_Unicode
* pBuf
, sal_uInt16 nNumber
, int bLeading
)
1178 DBG_ASSERT( nNumber
< 100, "ImplAdd2UNum() - Number >= 100" );
1187 *pBuf
= nNumber
+ '0';
1191 sal_uInt16 nTemp
= nNumber
% 10;
1193 *pBuf
= nNumber
+ '0';
1195 *pBuf
= nTemp
+ '0';
1203 inline sal_Unicode
* ImplAddString( sal_Unicode
* pBuf
, const rtl::OUString
& rStr
)
1205 if ( rStr
.getLength() == 1 )
1207 else if (rStr
.isEmpty())
1211 memcpy( pBuf
, rStr
.getStr(), rStr
.getLength() * sizeof(sal_Unicode
) );
1212 pBuf
+= rStr
.getLength();
1218 inline sal_Unicode
* ImplAddString( sal_Unicode
* pBuf
, sal_Unicode c
)
1226 inline sal_Unicode
* ImplAddString( sal_Unicode
* pBuf
, const sal_Unicode
* pCopyBuf
, xub_StrLen nLen
)
1228 memcpy( pBuf
, pCopyBuf
, nLen
* sizeof(sal_Unicode
) );
1233 sal_Unicode
* LocaleDataWrapper::ImplAddFormatNum( sal_Unicode
* pBuf
,
1234 sal_Int64 nNumber
, sal_uInt16 nDecimals
, sal_Bool bUseThousandSep
,
1235 sal_Bool bTrailingZeros
) const
1237 sal_Unicode aNumBuf
[64];
1238 sal_Unicode
* pNumBuf
;
1251 pNumBuf
= ImplAddUNum( aNumBuf
, (sal_uInt64
)nNumber
);
1252 nNumLen
= (sal_uInt16
)(sal_uLong
)(pNumBuf
-aNumBuf
);
1255 if ( nNumLen
<= nDecimals
)
1257 // strip .0 in decimals?
1258 if ( !nNumber
&& !bTrailingZeros
)
1265 // LeadingZero, insert 0
1266 if ( isNumLeadingZero() )
1272 // append decimal separator
1273 pBuf
= ImplAddString( pBuf
, getNumDecimalSep() );
1276 while ( i
< (nDecimals
-nNumLen
) )
1295 const rtl::OUString
& rThoSep
= getNumThousandSep();
1297 // copy number to buffer (excluding decimals)
1298 sal_uInt16 nNumLen2
= nNumLen
-nDecimals
;
1299 uno::Sequence
< sal_Bool
> aGroupPos
;
1300 if (bUseThousandSep
)
1301 aGroupPos
= utl::DigitGroupingIterator::createForwardSequence(
1302 nNumLen2
, getDigitGrouping());
1303 for ( ; i
< nNumLen2
; ++i
)
1309 // add thousand separator?
1310 if ( bUseThousandSep
&& aGroupPos
[i
] )
1311 pBuf
= ImplAddString( pBuf
, rThoSep
);
1317 pBuf
= ImplAddString( pBuf
, getNumDecimalSep() );
1319 sal_Bool bNullEnd
= sal_True
;
1320 while ( i
< nNumLen
)
1322 if ( *pNumBuf
!= '0' )
1323 bNullEnd
= sal_False
;
1331 // strip .0 in decimals?
1332 if ( bNullEnd
&& !bTrailingZeros
)
1333 pBuf
-= nDecimals
+1;
1341 // --- simple date and time formatting --------------------------------
1343 rtl::OUString
LocaleDataWrapper::getDate( const Date
& rDate
) const
1345 ::utl::ReadWriteGuard
aGuard( aMutex
, ::utl::ReadWriteGuardMode::nBlockCritical
);
1346 //!TODO: leading zeros et al
1347 sal_Unicode aBuf
[128];
1348 sal_Unicode
* pBuf
= aBuf
;
1349 sal_uInt16 nDay
= rDate
.GetDay();
1350 sal_uInt16 nMonth
= rDate
.GetMonth();
1351 sal_uInt16 nYear
= rDate
.GetYear();
1352 sal_uInt16 nYearLen
;
1354 if ( sal_True
/* IsDateCentury() */ )
1362 switch ( getDateFormat() )
1365 pBuf
= ImplAdd2UNum( pBuf
, nDay
, sal_True
/* IsDateDayLeadingZero() */ );
1366 pBuf
= ImplAddString( pBuf
, getDateSep() );
1367 pBuf
= ImplAdd2UNum( pBuf
, nMonth
, sal_True
/* IsDateMonthLeadingZero() */ );
1368 pBuf
= ImplAddString( pBuf
, getDateSep() );
1369 pBuf
= ImplAddUNum( pBuf
, nYear
, nYearLen
);
1372 pBuf
= ImplAdd2UNum( pBuf
, nMonth
, sal_True
/* IsDateMonthLeadingZero() */ );
1373 pBuf
= ImplAddString( pBuf
, getDateSep() );
1374 pBuf
= ImplAdd2UNum( pBuf
, nDay
, sal_True
/* IsDateDayLeadingZero() */ );
1375 pBuf
= ImplAddString( pBuf
, getDateSep() );
1376 pBuf
= ImplAddUNum( pBuf
, nYear
, nYearLen
);
1379 pBuf
= ImplAddUNum( pBuf
, nYear
, nYearLen
);
1380 pBuf
= ImplAddString( pBuf
, getDateSep() );
1381 pBuf
= ImplAdd2UNum( pBuf
, nMonth
, sal_True
/* IsDateMonthLeadingZero() */ );
1382 pBuf
= ImplAddString( pBuf
, getDateSep() );
1383 pBuf
= ImplAdd2UNum( pBuf
, nDay
, sal_True
/* IsDateDayLeadingZero() */ );
1386 return rtl::OUString(aBuf
, pBuf
-aBuf
);
1390 rtl::OUString
LocaleDataWrapper::getTime( const Time
& rTime
, sal_Bool bSec
, sal_Bool b100Sec
) const
1392 ::utl::ReadWriteGuard
aGuard( aMutex
, ::utl::ReadWriteGuardMode::nBlockCritical
);
1393 //!TODO: leading zeros et al
1394 sal_Unicode aBuf
[128];
1395 sal_Unicode
* pBuf
= aBuf
;
1396 sal_uInt16 nHour
= rTime
.GetHour();
1397 sal_Bool bHour12
= sal_False
; //!TODO: AM/PM from default time format code
1409 pBuf
= ImplAdd2UNum( pBuf
, nHour
, sal_True
/* IsTimeLeadingZero() */ );
1410 pBuf
= ImplAddString( pBuf
, getTimeSep() );
1411 pBuf
= ImplAdd2UNum( pBuf
, rTime
.GetMin(), sal_True
);
1414 pBuf
= ImplAddString( pBuf
, getTimeSep() );
1415 pBuf
= ImplAdd2UNum( pBuf
, rTime
.GetSec(), sal_True
);
1419 pBuf
= ImplAddString( pBuf
, getTime100SecSep() );
1420 pBuf
= ImplAdd2UNum( pBuf
, rTime
.Get100Sec(), sal_True
);
1424 rtl::OUString
aStr(aBuf
, pBuf
- aBuf
);
1428 if ( (rTime
.GetHour() % 24) >= 12 )
1429 aStr
+= getTimePM();
1431 aStr
+= getTimeAM();
1438 rtl::OUString
LocaleDataWrapper::getLongDate( const Date
& rDate
, CalendarWrapper
& rCal
,
1439 sal_Int16 nDisplayDayOfWeek
, sal_Bool bDayOfMonthWithLeadingZero
,
1440 sal_Int16 nDisplayMonth
, sal_Bool bTwoDigitYear
) const
1442 ::utl::ReadWriteGuard
aGuard( aMutex
, ::utl::ReadWriteGuardMode::nBlockCritical
);
1443 using namespace ::com::sun::star::i18n
;
1444 sal_Unicode aBuf
[20];
1448 rCal
.setGregorianDateTime( rDate
);
1450 nVal
= rCal
.getValue( CalendarFieldIndex::DAY_OF_WEEK
);
1451 aStr
+= rCal
.getDisplayName( CalendarDisplayIndex::DAY
, nVal
, nDisplayDayOfWeek
);
1452 aStr
+= getLongDateDayOfWeekSep();
1454 nVal
= rCal
.getValue( CalendarFieldIndex::DAY_OF_MONTH
);
1455 pBuf
= ImplAdd2UNum( aBuf
, nVal
, bDayOfMonthWithLeadingZero
);
1456 rtl::OUString
aDay(aBuf
, pBuf
-aBuf
);
1458 nVal
= rCal
.getValue( CalendarFieldIndex::MONTH
);
1459 rtl::OUString
aMonth( rCal
.getDisplayName( CalendarDisplayIndex::MONTH
, nVal
, nDisplayMonth
) );
1461 nVal
= rCal
.getValue( CalendarFieldIndex::YEAR
);
1462 if ( bTwoDigitYear
)
1463 pBuf
= ImplAddUNum( aBuf
, nVal
% 100, 2 );
1465 pBuf
= ImplAddUNum( aBuf
, nVal
);
1466 rtl::OUString
aYear(aBuf
, pBuf
-aBuf
);
1468 switch ( getLongDateFormat() )
1472 aStr
+= getLongDateDaySep();
1474 aStr
+= getLongDateMonthSep();
1479 aStr
+= getLongDateMonthSep();
1481 aStr
+= getLongDateDaySep();
1486 aStr
+= getLongDateYearSep();
1488 aStr
+= getLongDateMonthSep();
1495 rtl::OUString
LocaleDataWrapper::getDuration( const Time
& rTime
, sal_Bool bSec
, sal_Bool b100Sec
) const
1497 ::utl::ReadWriteGuard
aGuard( aMutex
, ::utl::ReadWriteGuardMode::nBlockCritical
);
1498 sal_Unicode aBuf
[128];
1499 sal_Unicode
* pBuf
= aBuf
;
1501 if ( rTime
< Time( 0 ) )
1502 pBuf
= ImplAddString( pBuf
, ' ' );
1504 if ( sal_True
/* IsTimeLeadingZero() */ )
1505 pBuf
= ImplAddUNum( pBuf
, rTime
.GetHour(), 2 );
1507 pBuf
= ImplAddUNum( pBuf
, rTime
.GetHour() );
1508 pBuf
= ImplAddString( pBuf
, getTimeSep() );
1509 pBuf
= ImplAdd2UNum( pBuf
, rTime
.GetMin(), sal_True
);
1512 pBuf
= ImplAddString( pBuf
, getTimeSep() );
1513 pBuf
= ImplAdd2UNum( pBuf
, rTime
.GetSec(), sal_True
);
1517 pBuf
= ImplAddString( pBuf
, getTime100SecSep() );
1518 pBuf
= ImplAdd2UNum( pBuf
, rTime
.Get100Sec(), sal_True
);
1522 return rtl::OUString(aBuf
, pBuf
-aBuf
);
1526 // --- simple number formatting ---------------------------------------
1528 inline size_t ImplGetNumberStringLengthGuess( const LocaleDataWrapper
& rLoc
, sal_uInt16 nDecimals
)
1530 // approximately 3.2 bits per digit
1531 const size_t nDig
= ((sizeof(sal_Int64
) * 8) / 3) + 1;
1532 // digits, separators (pessimized for insane "every digit may be grouped"), leading zero, sign
1533 size_t nGuess
= ((nDecimals
< nDig
) ?
1534 (((nDig
- nDecimals
) * rLoc
.getNumThousandSep().getLength()) + nDig
) :
1535 nDecimals
) + rLoc
.getNumDecimalSep().getLength() + 3;
1540 rtl::OUString
LocaleDataWrapper::getNum( sal_Int64 nNumber
, sal_uInt16 nDecimals
,
1541 sal_Bool bUseThousandSep
, sal_Bool bTrailingZeros
) const
1543 ::utl::ReadWriteGuard
aGuard( aMutex
, ::utl::ReadWriteGuardMode::nBlockCritical
);
1544 sal_Unicode aBuf
[128]; // big enough for 64-bit long and crazy grouping
1545 // check if digits and separators will fit into fixed buffer or allocate
1546 size_t nGuess
= ImplGetNumberStringLengthGuess( *this, nDecimals
);
1547 sal_Unicode
* const pBuffer
= (nGuess
< 118 ? aBuf
:
1548 new sal_Unicode
[nGuess
+ 16]);
1550 sal_Unicode
* pBuf
= ImplAddFormatNum( pBuffer
, nNumber
, nDecimals
,
1551 bUseThousandSep
, bTrailingZeros
);
1552 rtl::OUString
aStr(pBuffer
, pBuf
-pBuffer
);
1554 if ( pBuffer
!= aBuf
)
1559 rtl::OUString
LocaleDataWrapper::getCurr( sal_Int64 nNumber
, sal_uInt16 nDecimals
,
1560 const rtl::OUString
& rCurrencySymbol
, sal_Bool bUseThousandSep
) const
1562 ::utl::ReadWriteGuard
aGuard( aMutex
, ::utl::ReadWriteGuardMode::nBlockCritical
);
1563 sal_Unicode aBuf
[192];
1564 sal_Unicode aNumBuf
[128]; // big enough for 64-bit long and crazy grouping
1565 sal_Unicode cZeroChar
= getCurrZeroChar();
1567 // check if digits and separators will fit into fixed buffer or allocate
1568 size_t nGuess
= ImplGetNumberStringLengthGuess( *this, nDecimals
);
1569 sal_Unicode
* const pNumBuffer
= (nGuess
< 118 ? aNumBuf
:
1570 new sal_Unicode
[nGuess
+ 16]);
1572 sal_Unicode
* const pBuffer
=
1573 ((size_t(rCurrencySymbol
.getLength()) + nGuess
+ 20) < SAL_N_ELEMENTS(aBuf
) ? aBuf
:
1574 new sal_Unicode
[ rCurrencySymbol
.getLength() + nGuess
+ 20 ]);
1575 sal_Unicode
* pBuf
= pBuffer
;
1587 sal_Unicode
* pEndNumBuf
= ImplAddFormatNum( pNumBuffer
, nNumber
, nDecimals
,
1588 bUseThousandSep
, sal_True
);
1589 xub_StrLen nNumLen
= (xub_StrLen
)(sal_uLong
)(pEndNumBuf
-pNumBuffer
);
1591 // replace zeros with zero character
1592 if ( (cZeroChar
!= '0') && nDecimals
/* && IsNumTrailingZeros() */ )
1594 sal_Unicode
* pTempBuf
;
1596 sal_Bool bZero
= sal_True
;
1598 pTempBuf
= pNumBuffer
+nNumLen
-nDecimals
;
1602 if ( *pTempBuf
!= '0' )
1611 while ( i
< nDecimals
);
1615 pTempBuf
= pNumBuffer
+nNumLen
-nDecimals
;
1619 *pTempBuf
= cZeroChar
;
1623 while ( i
< nDecimals
);
1629 switch( getCurrPositiveFormat() )
1632 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1633 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1636 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1637 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1640 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1641 pBuf
= ImplAddString( pBuf
, ' ' );
1642 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1645 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1646 pBuf
= ImplAddString( pBuf
, ' ' );
1647 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1653 switch( getCurrNegativeFormat() )
1656 pBuf
= ImplAddString( pBuf
, '(' );
1657 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1658 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1659 pBuf
= ImplAddString( pBuf
, ')' );
1662 pBuf
= ImplAddString( pBuf
, '-' );
1663 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1664 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1667 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1668 pBuf
= ImplAddString( pBuf
, '-' );
1669 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1672 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1673 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1674 pBuf
= ImplAddString( pBuf
, '-' );
1677 pBuf
= ImplAddString( pBuf
, '(' );
1678 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1679 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1680 pBuf
= ImplAddString( pBuf
, ')' );
1683 pBuf
= ImplAddString( pBuf
, '-' );
1684 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1685 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1688 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1689 pBuf
= ImplAddString( pBuf
, '-' );
1690 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1693 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1694 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1695 pBuf
= ImplAddString( pBuf
, '-' );
1698 pBuf
= ImplAddString( pBuf
, '-' );
1699 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1700 pBuf
= ImplAddString( pBuf
, ' ' );
1701 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1704 pBuf
= ImplAddString( pBuf
, '-' );
1705 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1706 pBuf
= ImplAddString( pBuf
, ' ' );
1707 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1710 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1711 pBuf
= ImplAddString( pBuf
, ' ' );
1712 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1713 pBuf
= ImplAddString( pBuf
, '-' );
1716 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1717 pBuf
= ImplAddString( pBuf
, ' ' );
1718 pBuf
= ImplAddString( pBuf
, '-' );
1719 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1722 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1723 pBuf
= ImplAddString( pBuf
, ' ' );
1724 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1725 pBuf
= ImplAddString( pBuf
, '-' );
1728 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1729 pBuf
= ImplAddString( pBuf
, '-' );
1730 pBuf
= ImplAddString( pBuf
, ' ' );
1731 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1734 pBuf
= ImplAddString( pBuf
, '(' );
1735 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1736 pBuf
= ImplAddString( pBuf
, ' ' );
1737 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1738 pBuf
= ImplAddString( pBuf
, ')' );
1741 pBuf
= ImplAddString( pBuf
, '(' );
1742 pBuf
= ImplAddString( pBuf
, pNumBuffer
, nNumLen
);
1743 pBuf
= ImplAddString( pBuf
, ' ' );
1744 pBuf
= ImplAddString( pBuf
, rCurrencySymbol
);
1745 pBuf
= ImplAddString( pBuf
, ')' );
1750 rtl::OUString
aNumber(pBuffer
, pBuf
-pBuffer
);
1752 if ( pBuffer
!= aBuf
)
1754 if ( pNumBuffer
!= aNumBuf
)
1755 delete [] pNumBuffer
;
1761 // --- mixed ----------------------------------------------------------
1763 LanguageTag
LocaleDataWrapper::getLoadedLanguageTag() const
1765 LanguageCountryInfo aLCInfo
= getLanguageCountryInfo();
1766 return LanguageTag( lang::Locale( aLCInfo
.Language
, aLCInfo
.Country
, aLCInfo
.Variant
));
1770 rtl::OUString
LocaleDataWrapper::appendLocaleInfo(const rtl::OUString
& rDebugMsg
) const
1772 ::utl::ReadWriteGuard
aGuard( aMutex
, ::utl::ReadWriteGuardMode::nBlockCritical
);
1773 rtl::OUStringBuffer
aDebugMsg(rDebugMsg
);
1774 aDebugMsg
.append(static_cast<sal_Unicode
>('\n'));
1775 aDebugMsg
.append(maLanguageTag
.getBcp47());
1776 aDebugMsg
.appendAscii(RTL_CONSTASCII_STRINGPARAM(" requested\n"));
1777 LanguageTag aLoaded
= getLoadedLanguageTag();
1778 aDebugMsg
.append(aLoaded
.getBcp47());
1779 aDebugMsg
.appendAscii(RTL_CONSTASCII_STRINGPARAM(" loaded"));
1780 return aDebugMsg
.makeStringAndClear();
1785 void LocaleDataWrapper::outputCheckMessage( const rtl::OUString
& rMsg
)
1787 outputCheckMessage(rtl::OUStringToOString(rMsg
, RTL_TEXTENCODING_UTF8
).getStr());
1792 void LocaleDataWrapper::outputCheckMessage( const char* pStr
)
1794 fprintf( stderr
, "\n%s\n", pStr
);
1796 OSL_TRACE("%s", pStr
);
1801 void LocaleDataWrapper::evaluateLocaleDataChecking()
1803 // Using the rtl_Instance template here wouldn't solve all threaded write
1804 // accesses, since we want to assign the result to the static member
1805 // variable and would need to dereference the pointer returned and assign
1806 // the value unguarded. This is the same pattern manually coded.
1807 sal_uInt8 nCheck
= nLocaleDataChecking
;
1810 ::osl::MutexGuard
aGuard( ::osl::Mutex::getGlobalMutex());
1811 nCheck
= nLocaleDataChecking
;
1817 const char* pEnv
= getenv( "OOO_ENABLE_LOCALE_DATA_CHECKS");
1818 if (pEnv
&& (pEnv
[0] == 'Y' || pEnv
[0] == 'y' || pEnv
[0] == '1'))
1823 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
1824 nLocaleDataChecking
= nCheck
;
1828 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
1833 // --- XLocaleData3 ----------------------------------------------------------
1835 ::com::sun::star::uno::Sequence
< ::com::sun::star::i18n::Calendar2
> LocaleDataWrapper::getAllCalendars() const
1839 return xLD
->getAllCalendars2( getMyLocale() );
1841 catch (const Exception
& e
)
1843 SAL_WARN( "unotools.i18n", "getAllCalendars: Exception caught " << e
.Message
);
1845 return ::com::sun::star::uno::Sequence
< ::com::sun::star::i18n::Calendar2
>(0);
1849 // --- XLocaleData4 ----------------------------------------------------------
1851 ::com::sun::star::uno::Sequence
< ::rtl::OUString
> LocaleDataWrapper::getDateAcceptancePatterns() const
1853 ::utl::ReadWriteGuard
aGuard( aMutex
);
1855 if (aDateAcceptancePatterns
.getLength())
1856 return aDateAcceptancePatterns
;
1858 aGuard
.changeReadToWrite();
1862 const_cast<LocaleDataWrapper
*>(this)->aDateAcceptancePatterns
=
1863 xLD
->getDateAcceptancePatterns( getMyLocale() );
1864 return aDateAcceptancePatterns
;
1866 catch (const Exception
& e
)
1868 SAL_WARN( "unotools.i18n", "getDateAcceptancePatterns: Exception caught " << e
.Message
);
1870 return ::com::sun::star::uno::Sequence
< ::rtl::OUString
>(0);
1873 // --- Override layer --------------------------------------------------------
1875 void LocaleDataWrapper::setDateAcceptancePatterns(
1876 const ::com::sun::star::uno::Sequence
< ::rtl::OUString
> & rPatterns
)
1878 ::utl::ReadWriteGuard
aGuard( aMutex
, ::utl::ReadWriteGuardMode::nWrite
);
1880 if (!aDateAcceptancePatterns
.getLength() || !rPatterns
.getLength())
1884 aDateAcceptancePatterns
= xLD
->getDateAcceptancePatterns( getMyLocale() );
1886 catch (const Exception
& e
)
1888 SAL_WARN( "unotools.i18n", "setDateAcceptancePatterns: Exception caught " << e
.Message
);
1890 if (!rPatterns
.getLength())
1891 return; // just a reset
1892 if (!aDateAcceptancePatterns
.getLength())
1894 aDateAcceptancePatterns
= rPatterns
;
1899 // Never overwrite the locale's full date pattern! The first.
1900 if (aDateAcceptancePatterns
[0] == rPatterns
[0])
1901 aDateAcceptancePatterns
= rPatterns
; // sane
1904 // Copy existing full date pattern and append the sequence passed.
1905 /* TODO: could check for duplicates and shrink target sequence */
1906 Sequence
< OUString
> aTmp( rPatterns
.getLength() + 1 );
1907 OUString
* pArray1
= aTmp
.getArray();
1908 const OUString
* pArray2
= rPatterns
.getConstArray();
1909 pArray1
[0] = aDateAcceptancePatterns
[0];
1910 for (sal_Int32 i
=0; i
< rPatterns
.getLength(); ++i
)
1911 pArray1
[i
+1] = pArray2
[i
];
1912 aDateAcceptancePatterns
= aTmp
;
1916 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */