Version 4.0.0.1, tag libreoffice-4.0.0.1
[LibreOffice.git] / unotools / source / i18n / localedatawrapper.cxx
blob8e283b6f9d09443c41caed1d2bde360bace9f7fe
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
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;
53 namespace
55 struct InstalledLocales
56 : public rtl::Static<
57 uno::Sequence< lang::Locale >, InstalledLocales >
58 {};
60 struct InstalledLanguageTypes
61 : public rtl::Static<
62 uno::Sequence< sal_uInt16 >, InstalledLanguageTypes >
63 {};
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 )
79 invalidateData();
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 )
92 invalidateData();
95 LocaleDataWrapper::~LocaleDataWrapper()
100 void LocaleDataWrapper::setLanguageTag( const LanguageTag& rLanguageTag )
102 ::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nCriticalChange );
103 maLanguageTag = rLanguageTag;
104 invalidateData();
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())
142 aGrouping[0] = 0;
143 if (aDateAcceptancePatterns.getLength())
144 aDateAcceptancePatterns = Sequence<OUString>();
145 // dummies
146 cCurrZeroChar = '0';
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 ----------------------------------------------------
255 // static
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;
269 // static
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++ )
285 String aDebugLocale;
286 if (areChecksEnabled())
288 /* FIXME-BCP47: handle language tags! */
289 aDebugLocale = xLoc[i].Language;
290 if ( !xLoc[i].Country.isEmpty() )
292 aDebugLocale += '-';
293 aDebugLocale += String( xLoc[i].Country);
294 if ( !xLoc[i].Variant.isEmpty() )
296 aDebugLocale += '-';
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());
310 continue;
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());
323 switch ( eLang )
325 case LANGUAGE_NORWEGIAN : // no_NO, not Bokmal (nb_NO), not Nynorsk (nn_NO)
326 eLang = LANGUAGE_DONTKNOW; // don't offer "Unknown" language
327 break;
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;
390 switch ( nItem )
392 case LocaleItem::DATE_SEPARATOR :
393 aLocaleItem[nItem] = aLocaleDataItem.dateSeparator;
394 break;
395 case LocaleItem::THOUSAND_SEPARATOR :
396 aLocaleItem[nItem] = aLocaleDataItem.thousandSeparator;
397 break;
398 case LocaleItem::DECIMAL_SEPARATOR :
399 aLocaleItem[nItem] = aLocaleDataItem.decimalSeparator;
400 break;
401 case LocaleItem::TIME_SEPARATOR :
402 aLocaleItem[nItem] = aLocaleDataItem.timeSeparator;
403 break;
404 case LocaleItem::TIME_100SEC_SEPARATOR :
405 aLocaleItem[nItem] = aLocaleDataItem.time100SecSeparator;
406 break;
407 case LocaleItem::LIST_SEPARATOR :
408 aLocaleItem[nItem] = aLocaleDataItem.listSeparator;
409 break;
410 case LocaleItem::SINGLE_QUOTATION_START :
411 aLocaleItem[nItem] = aLocaleDataItem.quotationStart;
412 break;
413 case LocaleItem::SINGLE_QUOTATION_END :
414 aLocaleItem[nItem] = aLocaleDataItem.quotationEnd;
415 break;
416 case LocaleItem::DOUBLE_QUOTATION_START :
417 aLocaleItem[nItem] = aLocaleDataItem.doubleQuotationStart;
418 break;
419 case LocaleItem::DOUBLE_QUOTATION_END :
420 aLocaleItem[nItem] = aLocaleDataItem.doubleQuotationEnd;
421 break;
422 case LocaleItem::MEASUREMENT_SYSTEM :
423 aLocaleItem[nItem] = aLocaleDataItem.measurementSystem;
424 break;
425 case LocaleItem::TIME_AM :
426 aLocaleItem[nItem] = aLocaleDataItem.timeAM;
427 break;
428 case LocaleItem::TIME_PM :
429 aLocaleItem[nItem] = aLocaleDataItem.timePM;
430 break;
431 case LocaleItem::LONG_DATE_DAY_OF_WEEK_SEPARATOR :
432 aLocaleItem[nItem] = aLocaleDataItem.LongDateDayOfWeekSeparator;
433 break;
434 case LocaleItem::LONG_DATE_DAY_SEPARATOR :
435 aLocaleItem[nItem] = aLocaleDataItem.LongDateDaySeparator;
436 break;
437 case LocaleItem::LONG_DATE_MONTH_SEPARATOR :
438 aLocaleItem[nItem] = aLocaleDataItem.LongDateMonthSeparator;
439 break;
440 case LocaleItem::LONG_DATE_YEAR_SEPARATOR :
441 aLocaleItem[nItem] = aLocaleDataItem.LongDateYearSeparator;
442 break;
443 default:
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
485 return MEASURE_US;
489 void LocaleDataWrapper::getDefaultCalendarImpl()
491 if (!xDefaultCalendar)
493 Sequence< Calendar2 > xCals = getAllCalendars();
494 sal_Int32 nCount = xCals.getLength();
495 sal_Int32 nDef = 0;
496 if (nCount > 1)
498 const Calendar2* pArr = xCals.getArray();
499 for (sal_Int32 i=0; i<nCount; ++i)
501 if (pArr[i].Default)
503 nDef = i;
504 break;
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();
547 return aCurrSymbol;
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();
595 return nCurrDigits;
599 void LocaleDataWrapper::getCurrSymbolsImpl()
601 Sequence< Currency2 > aCurrSeq = getAllCurrencies();
602 sal_Int32 nCnt = aCurrSeq.getLength();
603 Currency2 const * const pCurrArr = aCurrSeq.getArray();
604 sal_Int32 nElem;
605 for ( nElem = 0; nElem < nCnt; nElem++ )
607 if ( pCurrArr[nElem].Default )
608 break;
610 if ( nElem >= nCnt )
612 if (areChecksEnabled())
614 rtl::OUString aMsg( RTL_CONSTASCII_USTRINGPARAM(
615 "LocaleDataWrapper::getCurrSymbolsImpl: no default currency"));
616 outputCheckMessage( appendLocaleInfo( aMsg ) );
618 nElem = 0;
619 if ( nElem >= nCnt )
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;
626 nCurrDigits = 2;
627 return ;
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;
644 int nInSection = 0;
645 sal_Bool bQuote = sal_False;
646 while ( p < pStop )
648 if ( bQuote )
650 if ( *p == '"' && *(p-1) != '\\' )
651 bQuote = sal_False;
653 else
655 switch ( *p )
657 case '"' :
658 if ( pStr == p || *(p-1) != '\\' )
659 bQuote = sal_True;
660 break;
661 case '-' :
662 if (!nInSection && nSign == -1)
663 nSign = p - pStr;
664 break;
665 case '(' :
666 if (!nInSection && nPar == -1)
667 nPar = p - pStr;
668 break;
669 case '0' :
670 case '#' :
671 if (!nInSection && nNum == -1)
672 nNum = p - pStr;
673 break;
674 case '[' :
675 nInSection++;
676 break;
677 case ']' :
678 if ( nInSection )
680 nInSection--;
681 if (!nInSection && nBlank == -1
682 && nSym != -1 && p < pStop-1 && *(p+1) == ' ' )
683 nBlank = p - pStr + 1;
685 break;
686 case '$' :
687 if (nSym == -1 && nInSection && *(p-1) == '[')
689 nSym = p - pStr + 1;
690 if (nNum != -1 && *(p-2) == ' ')
691 nBlank = p - pStr - 2;
693 break;
694 case ';' :
695 if ( !nInSection )
696 p = pStop;
697 break;
698 default:
699 if (!nInSection && nSym == -1 && String(rCode).Equals( aCurrSymbol, (xub_StrLen)(p-pStr), aCurrSymbol.getLength()))
700 { // currency symbol not surrounded by [$...]
701 nSym = p - pStr;
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;
710 p++;
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();
720 if ( !nCnt )
721 { // bad luck
722 if (areChecksEnabled())
724 rtl::OUString aMsg( RTL_CONSTASCII_USTRINGPARAM(
725 "LocaleDataWrapper::getCurrFormatsImpl: no currency formats"));
726 outputCheckMessage( appendLocaleInfo( aMsg ) );
728 nCurrPositiveFormat = nCurrNegativeFormat = nCurrFormatDefault;
729 return ;
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 )
741 nDef = nElem;
742 nMedium = nElem;
743 if ( pFormatArr[nElem].Code.indexOf( ';' ) >= 0 )
744 nNeg = nElem;
746 else
748 if ( (nNeg == -1 || nMedium == -1) && pFormatArr[nElem].Code.indexOf( ';' ) >= 0 )
749 nNeg = nElem;
750 if ( nMedium == -1 )
751 nMedium = nElem;
754 else
756 if ( nDef == -1 && pFormatArr[nElem].Default )
757 nDef = nElem;
758 if ( nNeg == -1 && pFormatArr[nElem].Code.indexOf( ';' ) >= 0 )
759 nNeg = nElem;
763 // make sure it's loaded
764 getCurrSymbol();
766 sal_Int32 nSign, nPar, nNum, nBlank, nSym;
768 // positive format
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 ) );
777 if (nBlank == -1)
779 if ( nSym < nNum )
780 nCurrPositiveFormat = 0; // $1
781 else
782 nCurrPositiveFormat = 1; // 1$
784 else
786 if ( nSym < nNum )
787 nCurrPositiveFormat = 2; // $ 1
788 else
789 nCurrPositiveFormat = 3; // 1 $
792 // negative format
793 if ( nNeg < 0 )
794 nCurrNegativeFormat = nCurrFormatDefault;
795 else
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
807 if (nBlank == -1)
809 if ( nSym < nNum )
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-
817 else
818 nCurrNegativeFormat = 2; // $-1
820 else
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$-
828 else
829 nCurrNegativeFormat = 6; // 1-$
832 else
834 if ( nSym < nNum )
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-
842 else
843 nCurrNegativeFormat = 11; // $ -1
845 else
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 $-
853 else
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
900 if (nMonth == -1)
901 { // only Finnish has something else than 'M' for month
902 nMonth = rCode.indexOf('K');
903 if (nMonth != -1)
905 nDay = rCode.indexOf('P');
906 nYear = rCode.indexOf('V');
909 else if (nDay == -1)
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
914 if (nDay != -1)
915 nYear = rCode.indexOf('J');
916 else
918 nYear = rCode.indexOf('A'); // French, Italian
919 if (nYear != -1)
921 nDay = rCode.indexOf('J'); // French
922 if (nDay == -1)
923 nDay = rCode.indexOf('G'); // Italian
927 else
928 { // We have a month 'M' and a day 'D'.
929 // Possible languages containing 'D' and 'M' but not 'Y':
930 // Spanish, Dutch
931 nYear = rCode.indexOf('A'); // Spanish
932 if (nYear == -1)
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 ) );
943 if (nDay == -1)
944 nDay = rCode.getLength();
945 if (nMonth == -1)
946 nMonth = rCode.getLength();
947 if (nYear == -1)
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 )
955 return MDY;
956 else if ( nYear <= nMonth && nMonth <= nDay )
957 return YMD;
958 else
960 if (areChecksEnabled())
962 rtl::OUString aMsg( RTL_CONSTASCII_USTRINGPARAM(
963 "LocaleDataWrapper::scanDateFormat: no magic applyable"));
964 outputCheckMessage( appendLocaleInfo( aMsg ) );
966 return DMY;
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();
977 if ( !nCnt )
978 { // bad luck
979 if (areChecksEnabled())
981 rtl::OUString aMsg( RTL_CONSTASCII_USTRINGPARAM(
982 "LocaleDataWrapper::getDateFormatsImpl: no date formats"));
983 outputCheckMessage( appendLocaleInfo( aMsg ) );
985 nDateFormat = nLongDateFormat = DMY;
986 return ;
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 )
996 nEdit = nElem;
997 if ( nDef == -1 && pFormatArr[nElem].Default )
998 nDef = nElem;
999 switch ( pFormatArr[nElem].Type )
1001 case KNumberFormatType::MEDIUM :
1003 if ( pFormatArr[nElem].Default )
1005 nDef = nElem;
1006 nMedium = nElem;
1008 else if ( nMedium == -1 )
1009 nMedium = nElem;
1011 break;
1012 case KNumberFormatType::LONG :
1014 if ( pFormatArr[nElem].Default )
1015 nLong = nElem;
1016 else if ( nLong == -1 )
1017 nLong = nElem;
1019 break;
1022 if ( nEdit == -1 )
1024 if (areChecksEnabled())
1026 rtl::OUString aMsg( RTL_CONSTASCII_USTRINGPARAM(
1027 "LocaleDataWrapper::getDateFormatsImpl: no edit"));
1028 outputCheckMessage( appendLocaleInfo( aMsg ) );
1030 if ( nDef == -1 )
1032 if (areChecksEnabled())
1034 rtl::OUString aMsg( RTL_CONSTASCII_USTRINGPARAM(
1035 "LocaleDataWrapper::getDateFormatsImpl: no default"));
1036 outputCheckMessage( appendLocaleInfo( aMsg ) );
1038 if ( nMedium != -1 )
1039 nDef = nMedium;
1040 else if ( nLong != -1 )
1041 nDef = nLong;
1042 else
1043 nDef = 0;
1045 nEdit = nDef;
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;
1052 else
1054 nDateFormat = nDF;
1055 if ( nLong == -1 )
1056 nLongDateFormat = nDF;
1057 else
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
1079 if (!aGrouping[0])
1081 i18n::LanguageCountryInfo aLCInfo( getLanguageCountryInfo());
1082 if (aLCInfo.Country.equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("IN")) || // India
1083 aLCInfo.Country.equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("BT")) ) // Bhutan
1085 aGrouping[0] = 3;
1086 aGrouping[1] = 2;
1087 aGrouping[2] = 0;
1089 else
1091 aGrouping[0] = 3;
1092 aGrouping[1] = 0;
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();
1106 return aGrouping;
1110 // --- simple number formatting helpers -------------------------------
1112 // The ImplAdd... methods are taken from class International and modified to
1113 // suit the needs.
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';
1123 pTempBuf++;
1124 nNumber /= 10;
1126 while ( nNumber );
1128 // copy temp buffer to buffer passed
1131 pTempBuf--;
1132 *pBuf = *pTempBuf;
1133 pBuf++;
1135 while ( pTempBuf != aTempBuf );
1137 return pBuf;
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';
1149 pTempBuf++;
1150 nNumber /= 10;
1151 nMinLen--;
1153 while ( nNumber );
1155 // fill with zeros up to the minimal length
1156 while ( nMinLen > 0 )
1158 *pBuf = '0';
1159 pBuf++;
1160 nMinLen--;
1163 // copy temp buffer to real buffer
1166 pTempBuf--;
1167 *pBuf = *pTempBuf;
1168 pBuf++;
1170 while ( pTempBuf != aTempBuf );
1172 return pBuf;
1176 static sal_Unicode* ImplAdd2UNum( sal_Unicode* pBuf, sal_uInt16 nNumber, int bLeading )
1178 DBG_ASSERT( nNumber < 100, "ImplAdd2UNum() - Number >= 100" );
1180 if ( nNumber < 10 )
1182 if ( bLeading )
1184 *pBuf = '0';
1185 pBuf++;
1187 *pBuf = nNumber + '0';
1189 else
1191 sal_uInt16 nTemp = nNumber % 10;
1192 nNumber /= 10;
1193 *pBuf = nNumber + '0';
1194 pBuf++;
1195 *pBuf = nTemp + '0';
1198 pBuf++;
1199 return pBuf;
1203 inline sal_Unicode* ImplAddString( sal_Unicode* pBuf, const rtl::OUString& rStr )
1205 if ( rStr.getLength() == 1 )
1206 *pBuf++ = rStr[0];
1207 else if (rStr.isEmpty())
1209 else
1211 memcpy( pBuf, rStr.getStr(), rStr.getLength() * sizeof(sal_Unicode) );
1212 pBuf += rStr.getLength();
1214 return pBuf;
1218 inline sal_Unicode* ImplAddString( sal_Unicode* pBuf, sal_Unicode c )
1220 *pBuf = c;
1221 pBuf++;
1222 return pBuf;
1226 inline sal_Unicode* ImplAddString( sal_Unicode* pBuf, const sal_Unicode* pCopyBuf, xub_StrLen nLen )
1228 memcpy( pBuf, pCopyBuf, nLen * sizeof(sal_Unicode) );
1229 return pBuf + nLen;
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;
1239 sal_uInt16 nNumLen;
1240 sal_uInt16 i = 0;
1242 // negative number
1243 if ( nNumber < 0 )
1245 nNumber *= -1;
1246 *pBuf = '-';
1247 pBuf++;
1250 // convert number
1251 pNumBuf = ImplAddUNum( aNumBuf, (sal_uInt64)nNumber );
1252 nNumLen = (sal_uInt16)(sal_uLong)(pNumBuf-aNumBuf);
1253 pNumBuf = aNumBuf;
1255 if ( nNumLen <= nDecimals )
1257 // strip .0 in decimals?
1258 if ( !nNumber && !bTrailingZeros )
1260 *pBuf = '0';
1261 pBuf++;
1263 else
1265 // LeadingZero, insert 0
1266 if ( isNumLeadingZero() )
1268 *pBuf = '0';
1269 pBuf++;
1272 // append decimal separator
1273 pBuf = ImplAddString( pBuf, getNumDecimalSep() );
1275 // fill with zeros
1276 while ( i < (nDecimals-nNumLen) )
1278 *pBuf = '0';
1279 pBuf++;
1280 i++;
1283 // append decimals
1284 while ( nNumLen )
1286 *pBuf = *pNumBuf;
1287 pBuf++;
1288 pNumBuf++;
1289 nNumLen--;
1293 else
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 )
1305 *pBuf = *pNumBuf;
1306 pBuf++;
1307 pNumBuf++;
1309 // add thousand separator?
1310 if ( bUseThousandSep && aGroupPos[i] )
1311 pBuf = ImplAddString( pBuf, rThoSep );
1314 // append decimals
1315 if ( nDecimals )
1317 pBuf = ImplAddString( pBuf, getNumDecimalSep() );
1319 sal_Bool bNullEnd = sal_True;
1320 while ( i < nNumLen )
1322 if ( *pNumBuf != '0' )
1323 bNullEnd = sal_False;
1325 *pBuf = *pNumBuf;
1326 pBuf++;
1327 pNumBuf++;
1328 i++;
1331 // strip .0 in decimals?
1332 if ( bNullEnd && !bTrailingZeros )
1333 pBuf -= nDecimals+1;
1337 return pBuf;
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() */ )
1355 nYearLen = 4;
1356 else
1358 nYearLen = 2;
1359 nYear %= 100;
1362 switch ( getDateFormat() )
1364 case DMY :
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 );
1370 break;
1371 case MDY :
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 );
1377 break;
1378 default:
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
1399 if ( bHour12 )
1401 nHour %= 12;
1402 // 0:00 -> 12:00
1403 if ( !nHour )
1404 nHour = 12;
1406 else
1407 nHour %= 24;
1409 pBuf = ImplAdd2UNum( pBuf, nHour, sal_True /* IsTimeLeadingZero() */ );
1410 pBuf = ImplAddString( pBuf, getTimeSep() );
1411 pBuf = ImplAdd2UNum( pBuf, rTime.GetMin(), sal_True );
1412 if ( bSec )
1414 pBuf = ImplAddString( pBuf, getTimeSep() );
1415 pBuf = ImplAdd2UNum( pBuf, rTime.GetSec(), sal_True );
1417 if ( b100Sec )
1419 pBuf = ImplAddString( pBuf, getTime100SecSep() );
1420 pBuf = ImplAdd2UNum( pBuf, rTime.Get100Sec(), sal_True );
1424 rtl::OUString aStr(aBuf, pBuf - aBuf);
1426 if ( bHour12 )
1428 if ( (rTime.GetHour() % 24) >= 12 )
1429 aStr += getTimePM();
1430 else
1431 aStr += getTimeAM();
1434 return aStr;
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];
1445 sal_Unicode* pBuf;
1446 String aStr;
1447 sal_Int16 nVal;
1448 rCal.setGregorianDateTime( rDate );
1449 // day of week
1450 nVal = rCal.getValue( CalendarFieldIndex::DAY_OF_WEEK );
1451 aStr += rCal.getDisplayName( CalendarDisplayIndex::DAY, nVal, nDisplayDayOfWeek );
1452 aStr += getLongDateDayOfWeekSep();
1453 // day of month
1454 nVal = rCal.getValue( CalendarFieldIndex::DAY_OF_MONTH );
1455 pBuf = ImplAdd2UNum( aBuf, nVal, bDayOfMonthWithLeadingZero );
1456 rtl::OUString aDay(aBuf, pBuf-aBuf);
1457 // month of year
1458 nVal = rCal.getValue( CalendarFieldIndex::MONTH );
1459 rtl::OUString aMonth( rCal.getDisplayName( CalendarDisplayIndex::MONTH, nVal, nDisplayMonth ) );
1460 // year
1461 nVal = rCal.getValue( CalendarFieldIndex::YEAR );
1462 if ( bTwoDigitYear )
1463 pBuf = ImplAddUNum( aBuf, nVal % 100, 2 );
1464 else
1465 pBuf = ImplAddUNum( aBuf, nVal );
1466 rtl::OUString aYear(aBuf, pBuf-aBuf);
1467 // concatenate
1468 switch ( getLongDateFormat() )
1470 case DMY :
1471 aStr += aDay;
1472 aStr += getLongDateDaySep();
1473 aStr += aMonth;
1474 aStr += getLongDateMonthSep();
1475 aStr += aYear;
1476 break;
1477 case MDY :
1478 aStr += aMonth;
1479 aStr += getLongDateMonthSep();
1480 aStr += aDay;
1481 aStr += getLongDateDaySep();
1482 aStr += aYear;
1483 break;
1484 default: // YMD
1485 aStr += aYear;
1486 aStr += getLongDateYearSep();
1487 aStr += aMonth;
1488 aStr += getLongDateMonthSep();
1489 aStr += aDay;
1491 return aStr;
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 );
1506 else
1507 pBuf = ImplAddUNum( pBuf, rTime.GetHour() );
1508 pBuf = ImplAddString( pBuf, getTimeSep() );
1509 pBuf = ImplAdd2UNum( pBuf, rTime.GetMin(), sal_True );
1510 if ( bSec )
1512 pBuf = ImplAddString( pBuf, getTimeSep() );
1513 pBuf = ImplAdd2UNum( pBuf, rTime.GetSec(), sal_True );
1515 if ( b100Sec )
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;
1536 return nGuess;
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 )
1555 delete [] pBuffer;
1556 return aStr;
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;
1577 sal_Bool bNeg;
1578 if ( nNumber < 0 )
1580 bNeg = sal_True;
1581 nNumber *= -1;
1583 else
1584 bNeg = sal_False;
1586 // convert number
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;
1595 sal_uInt16 i;
1596 sal_Bool bZero = sal_True;
1598 pTempBuf = pNumBuffer+nNumLen-nDecimals;
1599 i = 0;
1602 if ( *pTempBuf != '0' )
1604 bZero = sal_False;
1605 break;
1608 pTempBuf++;
1609 i++;
1611 while ( i < nDecimals );
1613 if ( bZero )
1615 pTempBuf = pNumBuffer+nNumLen-nDecimals;
1616 i = 0;
1619 *pTempBuf = cZeroChar;
1620 pTempBuf++;
1621 i++;
1623 while ( i < nDecimals );
1627 if ( !bNeg )
1629 switch( getCurrPositiveFormat() )
1631 case 0:
1632 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1633 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1634 break;
1635 case 1:
1636 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1637 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1638 break;
1639 case 2:
1640 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1641 pBuf = ImplAddString( pBuf, ' ' );
1642 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1643 break;
1644 case 3:
1645 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1646 pBuf = ImplAddString( pBuf, ' ' );
1647 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1648 break;
1651 else
1653 switch( getCurrNegativeFormat() )
1655 case 0:
1656 pBuf = ImplAddString( pBuf, '(' );
1657 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1658 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1659 pBuf = ImplAddString( pBuf, ')' );
1660 break;
1661 case 1:
1662 pBuf = ImplAddString( pBuf, '-' );
1663 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1664 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1665 break;
1666 case 2:
1667 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1668 pBuf = ImplAddString( pBuf, '-' );
1669 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1670 break;
1671 case 3:
1672 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1673 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1674 pBuf = ImplAddString( pBuf, '-' );
1675 break;
1676 case 4:
1677 pBuf = ImplAddString( pBuf, '(' );
1678 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1679 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1680 pBuf = ImplAddString( pBuf, ')' );
1681 break;
1682 case 5:
1683 pBuf = ImplAddString( pBuf, '-' );
1684 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1685 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1686 break;
1687 case 6:
1688 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1689 pBuf = ImplAddString( pBuf, '-' );
1690 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1691 break;
1692 case 7:
1693 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1694 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1695 pBuf = ImplAddString( pBuf, '-' );
1696 break;
1697 case 8:
1698 pBuf = ImplAddString( pBuf, '-' );
1699 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1700 pBuf = ImplAddString( pBuf, ' ' );
1701 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1702 break;
1703 case 9:
1704 pBuf = ImplAddString( pBuf, '-' );
1705 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1706 pBuf = ImplAddString( pBuf, ' ' );
1707 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1708 break;
1709 case 10:
1710 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1711 pBuf = ImplAddString( pBuf, ' ' );
1712 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1713 pBuf = ImplAddString( pBuf, '-' );
1714 break;
1715 case 11:
1716 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1717 pBuf = ImplAddString( pBuf, ' ' );
1718 pBuf = ImplAddString( pBuf, '-' );
1719 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1720 break;
1721 case 12:
1722 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1723 pBuf = ImplAddString( pBuf, ' ' );
1724 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1725 pBuf = ImplAddString( pBuf, '-' );
1726 break;
1727 case 13:
1728 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1729 pBuf = ImplAddString( pBuf, '-' );
1730 pBuf = ImplAddString( pBuf, ' ' );
1731 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1732 break;
1733 case 14:
1734 pBuf = ImplAddString( pBuf, '(' );
1735 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1736 pBuf = ImplAddString( pBuf, ' ' );
1737 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1738 pBuf = ImplAddString( pBuf, ')' );
1739 break;
1740 case 15:
1741 pBuf = ImplAddString( pBuf, '(' );
1742 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen );
1743 pBuf = ImplAddString( pBuf, ' ' );
1744 pBuf = ImplAddString( pBuf, rCurrencySymbol );
1745 pBuf = ImplAddString( pBuf, ')' );
1746 break;
1750 rtl::OUString aNumber(pBuffer, pBuf-pBuffer);
1752 if ( pBuffer != aBuf )
1753 delete [] pBuffer;
1754 if ( pNumBuffer != aNumBuf )
1755 delete [] pNumBuffer;
1757 return aNumber;
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();
1784 // static
1785 void LocaleDataWrapper::outputCheckMessage( const rtl::OUString& rMsg )
1787 outputCheckMessage(rtl::OUStringToOString(rMsg, RTL_TEXTENCODING_UTF8).getStr());
1791 // static
1792 void LocaleDataWrapper::outputCheckMessage( const char* pStr )
1794 fprintf( stderr, "\n%s\n", pStr);
1795 fflush( stderr);
1796 OSL_TRACE("%s", pStr);
1800 // static
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;
1808 if (!nCheck)
1810 ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex());
1811 nCheck = nLocaleDataChecking;
1812 if (!nCheck)
1814 #ifdef DBG_UTIL
1815 nCheck = 1;
1816 #else
1817 const char* pEnv = getenv( "OOO_ENABLE_LOCALE_DATA_CHECKS");
1818 if (pEnv && (pEnv[0] == 'Y' || pEnv[0] == 'y' || pEnv[0] == '1'))
1819 nCheck = 1;
1820 else
1821 nCheck = 2;
1822 #endif
1823 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
1824 nLocaleDataChecking = nCheck;
1827 else {
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;
1895 return;
1899 // Never overwrite the locale's full date pattern! The first.
1900 if (aDateAcceptancePatterns[0] == rPatterns[0])
1901 aDateAcceptancePatterns = rPatterns; // sane
1902 else
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: */