1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_unotools.hxx" 30 31 #include <string.h> // memcpy() 32 #include <stdio.h> // fprintf(), stderr 33 34 #include <unotools/localedatawrapper.hxx> 35 #include <unotools/numberformatcodewrapper.hxx> 36 #include <unotools/calendarwrapper.hxx> 37 #include <unotools/digitgroupingiterator.hxx> 38 #include <tools/string.hxx> 39 #include <tools/debug.hxx> 40 #include <i18npool/mslangid.hxx> 41 42 #ifndef _COMPHELPER_COMPONENTFACTORY_HXX_ 43 #include <comphelper/componentfactory.hxx> 44 #endif 45 #include <unotools/processfactory.hxx> 46 #include <com/sun/star/uno/XInterface.hpp> 47 #include <com/sun/star/lang/XMultiServiceFactory.hpp> 48 #include <com/sun/star/i18n/KNumberFormatUsage.hpp> 49 #include <com/sun/star/i18n/KNumberFormatType.hpp> 50 #include <com/sun/star/i18n/CalendarFieldIndex.hpp> 51 #include <com/sun/star/i18n/CalendarDisplayIndex.hpp> 52 53 #ifndef _COM_SUN_STAR_I18N_NUMBERFORMATINDEX_HPP_ 54 #include <com/sun/star/i18n/NumberFormatIndex.hdl> 55 #endif 56 #include <rtl/instance.hxx> 57 58 #define LOCALEDATA_LIBRARYNAME "i18npool" 59 #define LOCALEDATA_SERVICENAME "com.sun.star.i18n.LocaleData" 60 61 static const int nDateFormatInvalid = -1; 62 static const sal_uInt16 nCurrFormatInvalid = 0xffff; 63 static const sal_uInt16 nCurrFormatDefault = 0; 64 65 using namespace ::com::sun::star; 66 using namespace ::com::sun::star::i18n; 67 using namespace ::com::sun::star::uno; 68 69 namespace 70 { 71 struct InstalledLocales 72 : public rtl::Static< 73 uno::Sequence< lang::Locale >, InstalledLocales > 74 {}; 75 76 struct InstalledLanguageTypes 77 : public rtl::Static< 78 uno::Sequence< sal_uInt16 >, InstalledLanguageTypes > 79 {}; 80 } 81 82 sal_uInt8 LocaleDataWrapper::nLocaleDataChecking = 0; 83 84 LocaleDataWrapper::LocaleDataWrapper( 85 const Reference< lang::XMultiServiceFactory > & xSF, 86 const lang::Locale& rLocale 87 ) 88 : 89 xSMgr( xSF ), 90 bLocaleDataItemValid( sal_False ), 91 bReservedWordValid( sal_False ) 92 { 93 setLocale( rLocale ); 94 if ( xSMgr.is() ) 95 { 96 try 97 { 98 xLD = Reference< XLocaleData2 > ( xSMgr->createInstance( 99 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( LOCALEDATA_SERVICENAME ) ) ), 100 uno::UNO_QUERY ); 101 } 102 catch ( Exception& e ) 103 { 104 #ifdef DBG_UTIL 105 ByteString aMsg( "LocaleDataWrapper ctor: Exception caught\n" ); 106 aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 ); 107 DBG_ERRORFILE( aMsg.GetBuffer() ); 108 #else 109 (void)e; 110 #endif 111 } 112 } 113 else 114 { // try to get an instance somehow 115 DBG_ERRORFILE( "LocaleDataWrapper: no service manager, trying own" ); 116 try 117 { 118 Reference< XInterface > xI = ::comphelper::getComponentInstance( 119 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( LLCF_LIBNAME( LOCALEDATA_LIBRARYNAME ) ) ), 120 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( LOCALEDATA_SERVICENAME ) ) ); 121 if ( xI.is() ) 122 { 123 Any x = xI->queryInterface( ::getCppuType((const Reference< XLocaleData2 >*)0) ); 124 x >>= xLD; 125 } 126 } 127 catch ( Exception& e ) 128 { 129 #ifdef DBG_UTIL 130 ByteString aMsg( "getComponentInstance: Exception caught\n" ); 131 aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 ); 132 DBG_ERRORFILE( aMsg.GetBuffer() ); 133 #else 134 (void)e; 135 #endif 136 } 137 } 138 } 139 140 141 LocaleDataWrapper::~LocaleDataWrapper() 142 { 143 } 144 145 146 void LocaleDataWrapper::setLocale( const ::com::sun::star::lang::Locale& rLocale ) 147 { 148 ::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nCriticalChange ); 149 aLocale = rLocale; 150 invalidateData(); 151 } 152 153 154 const ::com::sun::star::lang::Locale& LocaleDataWrapper::getLocale() const 155 { 156 ::utl::ReadWriteGuard aGuard( aMutex ); 157 return aLocale; 158 } 159 160 161 void LocaleDataWrapper::invalidateData() 162 { 163 aCurrSymbol.Erase(); 164 aCurrBankSymbol.Erase(); 165 nDateFormat = nLongDateFormat = nDateFormatInvalid; 166 nCurrPositiveFormat = nCurrNegativeFormat = nCurrDigits = nCurrFormatInvalid; 167 if ( bLocaleDataItemValid ) 168 { 169 for ( sal_Int32 j=0; j<LocaleItem::COUNT; j++ ) 170 { 171 aLocaleItem[j].Erase(); 172 } 173 bLocaleDataItemValid = sal_False; 174 } 175 if ( bReservedWordValid ) 176 { 177 for ( sal_Int16 j=0; j<reservedWords::COUNT; j++ ) 178 { 179 aReservedWord[j].Erase(); 180 } 181 bReservedWordValid = sal_False; 182 } 183 xDefaultCalendar.reset(); 184 if (aGrouping.getLength()) 185 aGrouping[0] = 0; 186 // dummies 187 cCurrZeroChar = '0'; 188 } 189 190 191 ::com::sun::star::i18n::LanguageCountryInfo LocaleDataWrapper::getLanguageCountryInfo() const 192 { 193 try 194 { 195 if ( xLD.is() ) 196 return xLD->getLanguageCountryInfo( getLocale() ); 197 } 198 catch ( Exception& e ) 199 { 200 #ifdef DBG_UTIL 201 ByteString aMsg( "getLanguageCountryInfo: Exception caught\n" ); 202 aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 ); 203 DBG_ERRORFILE( aMsg.GetBuffer() ); 204 #else 205 (void)e; 206 #endif 207 } 208 return ::com::sun::star::i18n::LanguageCountryInfo(); 209 } 210 211 212 ::com::sun::star::i18n::LocaleDataItem LocaleDataWrapper::getLocaleItem() const 213 { 214 try 215 { 216 if ( xLD.is() ) 217 return xLD->getLocaleItem( getLocale() ); 218 } 219 catch ( Exception& e ) 220 { 221 #ifdef DBG_UTIL 222 ByteString aMsg( "getLocaleItem: Exception caught\n" ); 223 aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 ); 224 DBG_ERRORFILE( aMsg.GetBuffer() ); 225 #else 226 (void)e; 227 #endif 228 } 229 return ::com::sun::star::i18n::LocaleDataItem(); 230 } 231 232 233 ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Calendar > LocaleDataWrapper::getAllCalendars() const 234 { 235 try 236 { 237 if ( xLD.is() ) 238 return xLD->getAllCalendars( getLocale() ); 239 } 240 catch ( Exception& e ) 241 { 242 #ifdef DBG_UTIL 243 ByteString aMsg( "getAllCalendars: Exception caught\n" ); 244 aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 ); 245 DBG_ERRORFILE( aMsg.GetBuffer() ); 246 #else 247 (void)e; 248 #endif 249 } 250 return ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Calendar >(0); 251 } 252 253 254 ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Currency2 > LocaleDataWrapper::getAllCurrencies() const 255 { 256 try 257 { 258 if ( xLD.is() ) 259 return xLD->getAllCurrencies2( getLocale() ); 260 } 261 catch ( Exception& e ) 262 { 263 #ifdef DBG_UTIL 264 ByteString aMsg( "getAllCurrencies: Exception caught\n" ); 265 aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 ); 266 DBG_ERRORFILE( aMsg.GetBuffer() ); 267 #else 268 (void)e; 269 #endif 270 } 271 return ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Currency2 >(0); 272 } 273 274 275 ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::FormatElement > LocaleDataWrapper::getAllFormats() const 276 { 277 try 278 { 279 if ( xLD.is() ) 280 return xLD->getAllFormats( getLocale() ); 281 } 282 catch ( Exception& e ) 283 { 284 #ifdef DBG_UTIL 285 ByteString aMsg( "getAllFormats: Exception caught\n" ); 286 aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 ); 287 DBG_ERRORFILE( aMsg.GetBuffer() ); 288 #else 289 (void)e; 290 #endif 291 } 292 return ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::FormatElement >(0); 293 } 294 295 296 ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Implementation > LocaleDataWrapper::getCollatorImplementations() const 297 { 298 try 299 { 300 if ( xLD.is() ) 301 return xLD->getCollatorImplementations( getLocale() ); 302 } 303 catch ( Exception& e ) 304 { 305 #ifdef DBG_UTIL 306 ByteString aMsg( "getCollatorImplementations: Exception caught\n" ); 307 aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 ); 308 DBG_ERRORFILE( aMsg.GetBuffer() ); 309 #else 310 (void)e; 311 #endif 312 } 313 return ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Implementation >(0); 314 } 315 316 317 ::com::sun::star::uno::Sequence< ::rtl::OUString > LocaleDataWrapper::getTransliterations() const 318 { 319 try 320 { 321 if ( xLD.is() ) 322 return xLD->getTransliterations( getLocale() ); 323 } 324 catch ( Exception& e ) 325 { 326 #ifdef DBG_UTIL 327 ByteString aMsg( "getTransliterations: Exception caught\n" ); 328 aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 ); 329 DBG_ERRORFILE( aMsg.GetBuffer() ); 330 #else 331 (void)e; 332 #endif 333 } 334 return ::com::sun::star::uno::Sequence< ::rtl::OUString >(0); 335 } 336 337 338 ::com::sun::star::i18n::ForbiddenCharacters LocaleDataWrapper::getForbiddenCharacters() const 339 { 340 try 341 { 342 if ( xLD.is() ) 343 return xLD->getForbiddenCharacters( getLocale() ); 344 } 345 catch ( Exception& e ) 346 { 347 #ifdef DBG_UTIL 348 ByteString aMsg( "getForbiddenCharacters: Exception caught\n" ); 349 aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 ); 350 DBG_ERRORFILE( aMsg.GetBuffer() ); 351 #else 352 (void)e; 353 #endif 354 } 355 return ::com::sun::star::i18n::ForbiddenCharacters(); 356 } 357 358 359 ::com::sun::star::uno::Sequence< ::rtl::OUString > LocaleDataWrapper::getReservedWord() const 360 { 361 try 362 { 363 if ( xLD.is() ) 364 return xLD->getReservedWord( getLocale() ); 365 } 366 catch ( Exception& e ) 367 { 368 #ifdef DBG_UTIL 369 ByteString aMsg( "getReservedWord: Exception caught\n" ); 370 aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 ); 371 DBG_ERRORFILE( aMsg.GetBuffer() ); 372 #else 373 (void)e; 374 #endif 375 } 376 return ::com::sun::star::uno::Sequence< ::rtl::OUString >(0); 377 } 378 379 380 ::com::sun::star::uno::Sequence< ::com::sun::star::lang::Locale > LocaleDataWrapper::getAllInstalledLocaleNames() const 381 { 382 uno::Sequence< lang::Locale > &rInstalledLocales = InstalledLocales::get(); 383 384 if ( rInstalledLocales.getLength() ) 385 return rInstalledLocales; 386 387 try 388 { 389 if ( xLD.is() ) 390 rInstalledLocales = xLD->getAllInstalledLocaleNames(); 391 } 392 catch ( Exception& e ) 393 { 394 #ifdef DBG_UTIL 395 ByteString aMsg( "getAllInstalledLocaleNames: Exception caught\n" ); 396 aMsg += ByteString( String( e.Message ), RTL_TEXTENCODING_UTF8 ); 397 DBG_ERRORFILE( aMsg.GetBuffer() ); 398 #else 399 (void)e; 400 #endif 401 } 402 return rInstalledLocales; 403 } 404 405 406 // --- Impl and helpers ---------------------------------------------------- 407 408 // static 409 ::com::sun::star::uno::Sequence< ::com::sun::star::lang::Locale > LocaleDataWrapper::getInstalledLocaleNames() 410 { 411 const uno::Sequence< lang::Locale > &rInstalledLocales = 412 InstalledLocales::get(); 413 414 if ( !rInstalledLocales.getLength() ) 415 { 416 LocaleDataWrapper aLDW( ::comphelper::getProcessServiceFactory(), lang::Locale() ); 417 aLDW.getAllInstalledLocaleNames(); 418 } 419 return rInstalledLocales; 420 } 421 422 // static 423 ::com::sun::star::uno::Sequence< sal_uInt16 > LocaleDataWrapper::getInstalledLanguageTypes() 424 { 425 uno::Sequence< sal_uInt16 > &rInstalledLanguageTypes = 426 InstalledLanguageTypes::get(); 427 428 if ( rInstalledLanguageTypes.getLength() ) 429 return rInstalledLanguageTypes; 430 431 ::com::sun::star::uno::Sequence< ::com::sun::star::lang::Locale > xLoc = 432 getInstalledLocaleNames(); 433 sal_Int32 nCount = xLoc.getLength(); 434 ::com::sun::star::uno::Sequence< sal_uInt16 > xLang( nCount ); 435 sal_Int32 nLanguages = 0; 436 for ( sal_Int32 i=0; i<nCount; i++ ) 437 { 438 String aDebugLocale; 439 if (areChecksEnabled()) 440 { 441 aDebugLocale = xLoc[i].Language; 442 if ( xLoc[i].Country.getLength() ) 443 { 444 aDebugLocale += '_'; 445 aDebugLocale += String( xLoc[i].Country); 446 if ( xLoc[i].Variant.getLength() ) 447 { 448 aDebugLocale += '_'; 449 aDebugLocale += String( xLoc[i].Variant); 450 } 451 } 452 } 453 454 if ( xLoc[i].Variant.getLength() ) 455 { 456 if (areChecksEnabled()) 457 { 458 String aMsg( RTL_CONSTASCII_USTRINGPARAM( 459 "LocaleDataWrapper::getInstalledLanguageTypes: Variants not supported, locale\n")); 460 aMsg += aDebugLocale; 461 outputCheckMessage( aMsg ); 462 } 463 continue; 464 } 465 LanguageType eLang = MsLangId::convertLocaleToLanguage( xLoc[i] ); 466 467 // In checks, exclude known problems because no MS-LCID defined. 468 if (areChecksEnabled() && eLang == LANGUAGE_DONTKNOW 469 // && !aDebugLocale.EqualsAscii( "br_AE" ) // ?!? Breton in United Arabic Emirates 470 ) 471 { 472 String aMsg( RTL_CONSTASCII_USTRINGPARAM( 473 "ConvertIsoNamesToLanguage: unknown MS-LCID for locale\n")); 474 aMsg += aDebugLocale; 475 outputCheckMessage( aMsg ); 476 } 477 478 switch ( eLang ) 479 { 480 case LANGUAGE_NORWEGIAN : // no_NO, not Bokmal (nb_NO), not Nynorsk (nn_NO) 481 eLang = LANGUAGE_DONTKNOW; // don't offer "Unknown" language 482 break; 483 } 484 if ( eLang != LANGUAGE_DONTKNOW ) 485 { 486 rtl::OUString aLanguage, aCountry; 487 MsLangId::convertLanguageToIsoNames( eLang, aLanguage, aCountry ); 488 if ( xLoc[i].Language != aLanguage || 489 xLoc[i].Country != aCountry ) 490 { 491 // In checks, exclude known problems because no MS-LCID defined 492 // and default for Language found. 493 if ( areChecksEnabled() 494 && !aDebugLocale.EqualsAscii( "ar_SD" ) // Sudan/ar 495 && !aDebugLocale.EqualsAscii( "en_CB" ) // Carribean is not a country 496 // && !aDebugLocale.EqualsAscii( "en_BG" ) // ?!? Bulgaria/en 497 // && !aDebugLocale.EqualsAscii( "es_BR" ) // ?!? Brazil/es 498 ) 499 { 500 String aMsg( RTL_CONSTASCII_USTRINGPARAM( 501 "ConvertIsoNamesToLanguage/ConvertLanguageToIsoNames: ambiguous locale (MS-LCID?)\n")); 502 aMsg += aDebugLocale; 503 aMsg.AppendAscii( RTL_CONSTASCII_STRINGPARAM( " -> 0x" ) ); 504 aMsg += String::CreateFromInt32( eLang, 16 ); 505 aMsg.AppendAscii( RTL_CONSTASCII_STRINGPARAM( " -> " ) ); 506 aMsg += String( aLanguage); 507 if ( aCountry.getLength() ) 508 { 509 aMsg += '_'; 510 aMsg += String( aCountry); 511 } 512 outputCheckMessage( aMsg ); 513 } 514 eLang = LANGUAGE_DONTKNOW; 515 } 516 } 517 if ( eLang != LANGUAGE_DONTKNOW ) 518 xLang[ nLanguages++ ] = eLang; 519 } 520 if ( nLanguages < nCount ) 521 xLang.realloc( nLanguages ); 522 rInstalledLanguageTypes = xLang; 523 524 return rInstalledLanguageTypes; 525 } 526 527 const String& LocaleDataWrapper::getOneLocaleItem( sal_Int16 nItem ) const 528 { 529 ::utl::ReadWriteGuard aGuard( aMutex ); 530 if ( nItem >= LocaleItem::COUNT ) 531 { 532 DBG_ERRORFILE( "getOneLocaleItem: bounds" ); 533 return aLocaleItem[0]; 534 } 535 if ( aLocaleItem[nItem].Len() == 0 ) 536 { // no cached content 537 aGuard.changeReadToWrite(); 538 ((LocaleDataWrapper*)this)->getOneLocaleItemImpl( nItem ); 539 } 540 return aLocaleItem[nItem]; 541 } 542 543 544 void LocaleDataWrapper::getOneLocaleItemImpl( sal_Int16 nItem ) 545 { 546 if ( !bLocaleDataItemValid ) 547 { 548 aLocaleDataItem = getLocaleItem(); 549 bLocaleDataItemValid = sal_True; 550 } 551 switch ( nItem ) 552 { 553 case LocaleItem::DATE_SEPARATOR : 554 aLocaleItem[nItem] = aLocaleDataItem.dateSeparator; 555 break; 556 case LocaleItem::THOUSAND_SEPARATOR : 557 aLocaleItem[nItem] = aLocaleDataItem.thousandSeparator; 558 break; 559 case LocaleItem::DECIMAL_SEPARATOR : 560 aLocaleItem[nItem] = aLocaleDataItem.decimalSeparator; 561 break; 562 case LocaleItem::TIME_SEPARATOR : 563 aLocaleItem[nItem] = aLocaleDataItem.timeSeparator; 564 break; 565 case LocaleItem::TIME_100SEC_SEPARATOR : 566 aLocaleItem[nItem] = aLocaleDataItem.time100SecSeparator; 567 break; 568 case LocaleItem::LIST_SEPARATOR : 569 aLocaleItem[nItem] = aLocaleDataItem.listSeparator; 570 break; 571 case LocaleItem::SINGLE_QUOTATION_START : 572 aLocaleItem[nItem] = aLocaleDataItem.quotationStart; 573 break; 574 case LocaleItem::SINGLE_QUOTATION_END : 575 aLocaleItem[nItem] = aLocaleDataItem.quotationEnd; 576 break; 577 case LocaleItem::DOUBLE_QUOTATION_START : 578 aLocaleItem[nItem] = aLocaleDataItem.doubleQuotationStart; 579 break; 580 case LocaleItem::DOUBLE_QUOTATION_END : 581 aLocaleItem[nItem] = aLocaleDataItem.doubleQuotationEnd; 582 break; 583 case LocaleItem::MEASUREMENT_SYSTEM : 584 aLocaleItem[nItem] = aLocaleDataItem.measurementSystem; 585 break; 586 case LocaleItem::TIME_AM : 587 aLocaleItem[nItem] = aLocaleDataItem.timeAM; 588 break; 589 case LocaleItem::TIME_PM : 590 aLocaleItem[nItem] = aLocaleDataItem.timePM; 591 break; 592 case LocaleItem::LONG_DATE_DAY_OF_WEEK_SEPARATOR : 593 aLocaleItem[nItem] = aLocaleDataItem.LongDateDayOfWeekSeparator; 594 break; 595 case LocaleItem::LONG_DATE_DAY_SEPARATOR : 596 aLocaleItem[nItem] = aLocaleDataItem.LongDateDaySeparator; 597 break; 598 case LocaleItem::LONG_DATE_MONTH_SEPARATOR : 599 aLocaleItem[nItem] = aLocaleDataItem.LongDateMonthSeparator; 600 break; 601 case LocaleItem::LONG_DATE_YEAR_SEPARATOR : 602 aLocaleItem[nItem] = aLocaleDataItem.LongDateYearSeparator; 603 break; 604 default: 605 DBG_ERRORFILE( "getOneLocaleItemImpl: which one?" ); 606 } 607 } 608 609 610 void LocaleDataWrapper::getOneReservedWordImpl( sal_Int16 nWord ) 611 { 612 if ( !bReservedWordValid ) 613 { 614 aReservedWordSeq = getReservedWord(); 615 bReservedWordValid = sal_True; 616 } 617 DBG_ASSERT( nWord < aReservedWordSeq.getLength(), "getOneReservedWordImpl: which one?" ); 618 if ( nWord < aReservedWordSeq.getLength() ) 619 aReservedWord[nWord] = aReservedWordSeq[nWord]; 620 } 621 622 623 const String& LocaleDataWrapper::getOneReservedWord( sal_Int16 nWord ) const 624 { 625 ::utl::ReadWriteGuard aGuard( aMutex ); 626 if ( nWord < 0 || nWord >= reservedWords::COUNT ) 627 { 628 DBG_ERRORFILE( "getOneReservedWord: bounds" ); 629 nWord = reservedWords::FALSE_WORD; 630 } 631 if ( aReservedWord[nWord].Len() == 0 ) 632 { // no cached content 633 aGuard.changeReadToWrite(); 634 ((LocaleDataWrapper*)this)->getOneReservedWordImpl( nWord ); 635 } 636 return aReservedWord[nWord]; 637 } 638 639 640 MeasurementSystem LocaleDataWrapper::mapMeasurementStringToEnum( const String& rMS ) const 641 { 642 //! TODO: could be cached too 643 if ( rMS.EqualsIgnoreCaseAscii( "metric" ) ) 644 return MEASURE_METRIC; 645 //! TODO: other measurement systems? => extend enum MeasurementSystem 646 return MEASURE_US; 647 } 648 649 650 void LocaleDataWrapper::getDefaultCalendarImpl() 651 { 652 if (!xDefaultCalendar) 653 { 654 Sequence< Calendar > xCals = getAllCalendars(); 655 sal_Int32 nCount = xCals.getLength(); 656 sal_Int32 nDef = 0; 657 if (nCount > 1) 658 { 659 const Calendar* pArr = xCals.getArray(); 660 for (sal_Int32 i=0; i<nCount; ++i) 661 { 662 if (pArr[i].Default) 663 { 664 nDef = i; 665 break; 666 } 667 } 668 } 669 xDefaultCalendar.reset( new Calendar( xCals[nDef])); 670 } 671 } 672 673 674 const ::boost::shared_ptr< ::com::sun::star::i18n::Calendar > LocaleDataWrapper::getDefaultCalendar() const 675 { 676 ::utl::ReadWriteGuard aGuard( aMutex ); 677 if (!xDefaultCalendar) 678 { // no cached content 679 aGuard.changeReadToWrite(); 680 ((LocaleDataWrapper*)this)->getDefaultCalendarImpl(); 681 } 682 return xDefaultCalendar; 683 } 684 685 686 const ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::CalendarItem > LocaleDataWrapper::getDefaultCalendarDays() const 687 { 688 return getDefaultCalendar()->Days; 689 } 690 691 692 const ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::CalendarItem > LocaleDataWrapper::getDefaultCalendarMonths() const 693 { 694 return getDefaultCalendar()->Months; 695 } 696 697 698 // --- currencies ----------------------------------------------------- 699 700 const String& LocaleDataWrapper::getCurrSymbol() const 701 { 702 ::utl::ReadWriteGuard aGuard( aMutex ); 703 if ( !aCurrSymbol.Len() ) 704 { 705 aGuard.changeReadToWrite(); 706 ((LocaleDataWrapper*)this)->getCurrSymbolsImpl(); 707 } 708 return aCurrSymbol; 709 } 710 711 712 const String& LocaleDataWrapper::getCurrBankSymbol() const 713 { 714 ::utl::ReadWriteGuard aGuard( aMutex ); 715 if ( !aCurrBankSymbol.Len() ) 716 { 717 aGuard.changeReadToWrite(); 718 ((LocaleDataWrapper*)this)->getCurrSymbolsImpl(); 719 } 720 return aCurrBankSymbol; 721 } 722 723 724 sal_uInt16 LocaleDataWrapper::getCurrPositiveFormat() const 725 { 726 ::utl::ReadWriteGuard aGuard( aMutex ); 727 if ( nCurrPositiveFormat == nCurrFormatInvalid ) 728 { 729 aGuard.changeReadToWrite(); 730 ((LocaleDataWrapper*)this)->getCurrFormatsImpl(); 731 } 732 return nCurrPositiveFormat; 733 } 734 735 736 sal_uInt16 LocaleDataWrapper::getCurrNegativeFormat() const 737 { 738 ::utl::ReadWriteGuard aGuard( aMutex ); 739 if ( nCurrNegativeFormat == nCurrFormatInvalid ) 740 { 741 aGuard.changeReadToWrite(); 742 ((LocaleDataWrapper*)this)->getCurrFormatsImpl(); 743 } 744 return nCurrNegativeFormat; 745 } 746 747 748 sal_uInt16 LocaleDataWrapper::getCurrDigits() const 749 { 750 ::utl::ReadWriteGuard aGuard( aMutex ); 751 if ( nCurrDigits == nCurrFormatInvalid ) 752 { 753 aGuard.changeReadToWrite(); 754 ((LocaleDataWrapper*)this)->getCurrSymbolsImpl(); 755 } 756 return nCurrDigits; 757 } 758 759 760 void LocaleDataWrapper::getCurrSymbolsImpl() 761 { 762 Sequence< Currency2 > aCurrSeq = getAllCurrencies(); 763 sal_Int32 nCnt = aCurrSeq.getLength(); 764 Currency2 const * const pCurrArr = aCurrSeq.getArray(); 765 sal_Int32 nElem; 766 for ( nElem = 0; nElem < nCnt; nElem++ ) 767 { 768 if ( pCurrArr[nElem].Default ) 769 break; 770 } 771 if ( nElem >= nCnt ) 772 { 773 if (areChecksEnabled()) 774 { 775 String aMsg( RTL_CONSTASCII_USTRINGPARAM( 776 "LocaleDataWrapper::getCurrSymbolsImpl: no default currency")); 777 outputCheckMessage( appendLocaleInfo( aMsg ) ); 778 } 779 nElem = 0; 780 if ( nElem >= nCnt ) 781 { 782 if (areChecksEnabled()) 783 outputCheckMessage( String( RTL_CONSTASCII_USTRINGPARAM( 784 "LocaleDataWrapper::getCurrSymbolsImpl: no currency at all, using ShellsAndPebbles"))); 785 aCurrSymbol.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "ShellsAndPebbles" ) ); 786 aCurrBankSymbol = aCurrSymbol; 787 nCurrPositiveFormat = nCurrNegativeFormat = nCurrFormatDefault; 788 nCurrDigits = 2; 789 return ; 790 } 791 } 792 aCurrSymbol = pCurrArr[nElem].Symbol; 793 aCurrBankSymbol = pCurrArr[nElem].BankSymbol; 794 nCurrDigits = pCurrArr[nElem].DecimalPlaces; 795 } 796 797 798 void LocaleDataWrapper::scanCurrFormatImpl( const String& rCode, 799 xub_StrLen nStart, xub_StrLen& nSign, xub_StrLen& nPar, 800 xub_StrLen& nNum, xub_StrLen& nBlank, xub_StrLen& nSym ) 801 { 802 nSign = nPar = nNum = nBlank = nSym = STRING_NOTFOUND; 803 const sal_Unicode* const pStr = rCode.GetBuffer(); 804 const sal_Unicode* const pStop = pStr + rCode.Len(); 805 const sal_Unicode* p = pStr + nStart; 806 int nInSection = 0; 807 sal_Bool bQuote = sal_False; 808 while ( p < pStop ) 809 { 810 if ( bQuote ) 811 { 812 if ( *p == '"' && *(p-1) != '\\' ) 813 bQuote = sal_False; 814 } 815 else 816 { 817 switch ( *p ) 818 { 819 case '"' : 820 if ( pStr == p || *(p-1) != '\\' ) 821 bQuote = sal_True; 822 break; 823 case '-' : 824 if ( !nInSection && nSign == STRING_NOTFOUND ) 825 nSign = (xub_StrLen)(p - pStr); 826 break; 827 case '(' : 828 if ( !nInSection && nPar == STRING_NOTFOUND ) 829 nPar = (xub_StrLen)(p - pStr); 830 break; 831 case '0' : 832 case '#' : 833 if ( !nInSection && nNum == STRING_NOTFOUND ) 834 nNum = (xub_StrLen)(p - pStr); 835 break; 836 case '[' : 837 nInSection++; 838 break; 839 case ']' : 840 if ( nInSection ) 841 { 842 nInSection--; 843 if ( !nInSection && nBlank == STRING_NOTFOUND 844 && nSym != STRING_NOTFOUND && p < pStop-1 && *(p+1) == ' ' ) 845 nBlank = (xub_StrLen)(p - pStr + 1); 846 } 847 break; 848 case '$' : 849 if ( nSym == STRING_NOTFOUND && nInSection && *(p-1) == '[' ) 850 { 851 nSym = (xub_StrLen)(p - pStr + 1); 852 if ( nNum != STRING_NOTFOUND && *(p-2) == ' ' ) 853 nBlank = (xub_StrLen)(p - pStr - 2); 854 } 855 break; 856 case ';' : 857 if ( !nInSection ) 858 p = pStop; 859 break; 860 default: 861 if ( !nInSection && nSym == STRING_NOTFOUND && rCode.Equals( aCurrSymbol, (xub_StrLen)(p-pStr), aCurrSymbol.Len() ) ) 862 { // currency symbol not surrounded by [$...] 863 nSym = (xub_StrLen)(p - pStr); 864 if ( nBlank == STRING_NOTFOUND && pStr < p && *(p-1) == ' ' ) 865 nBlank = (xub_StrLen)(p - pStr - 1); 866 p += aCurrSymbol.Len() - 1; 867 if ( nBlank == STRING_NOTFOUND && p < pStop-2 && *(p+2) == ' ' ) 868 nBlank = (xub_StrLen)(p - pStr + 2); 869 } 870 } 871 } 872 p++; 873 } 874 } 875 876 877 void LocaleDataWrapper::getCurrFormatsImpl() 878 { 879 NumberFormatCodeWrapper aNumberFormatCode( xSMgr, getLocale() ); 880 uno::Sequence< NumberFormatCode > aFormatSeq 881 = aNumberFormatCode.getAllFormatCode( KNumberFormatUsage::CURRENCY ); 882 sal_Int32 nCnt = aFormatSeq.getLength(); 883 if ( !nCnt ) 884 { // bad luck 885 if (areChecksEnabled()) 886 { 887 String aMsg( RTL_CONSTASCII_USTRINGPARAM( 888 "LocaleDataWrapper::getCurrFormatsImpl: no currency formats")); 889 outputCheckMessage( appendLocaleInfo( aMsg ) ); 890 } 891 nCurrPositiveFormat = nCurrNegativeFormat = nCurrFormatDefault; 892 return ; 893 } 894 // find a negative code (medium preferred) and a default (medium preferred) (not necessarily the same) 895 NumberFormatCode const * const pFormatArr = aFormatSeq.getArray(); 896 sal_Int32 nElem, nDef, nNeg, nMedium; 897 nDef = nNeg = nMedium = -1; 898 for ( nElem = 0; nElem < nCnt; nElem++ ) 899 { 900 if ( pFormatArr[nElem].Type == KNumberFormatType::MEDIUM ) 901 { 902 if ( pFormatArr[nElem].Default ) 903 { 904 nDef = nElem; 905 nMedium = nElem; 906 if ( pFormatArr[nElem].Code.indexOf( ';' ) >= 0 ) 907 nNeg = nElem; 908 } 909 else 910 { 911 if ( (nNeg == -1 || nMedium == -1) && pFormatArr[nElem].Code.indexOf( ';' ) >= 0 ) 912 nNeg = nElem; 913 if ( nMedium == -1 ) 914 nMedium = nElem; 915 } 916 } 917 else 918 { 919 if ( nDef == -1 && pFormatArr[nElem].Default ) 920 nDef = nElem; 921 if ( nNeg == -1 && pFormatArr[nElem].Code.indexOf( ';' ) >= 0 ) 922 nNeg = nElem; 923 } 924 } 925 926 // make sure it's loaded 927 getCurrSymbol(); 928 929 xub_StrLen nSign, nPar, nNum, nBlank, nSym; 930 931 // positive format 932 nElem = (nDef >= 0 ? nDef : (nNeg >= 0 ? nNeg : 0)); 933 scanCurrFormatImpl( pFormatArr[nElem].Code, 0, nSign, nPar, nNum, nBlank, nSym ); 934 if (areChecksEnabled() && (nNum == STRING_NOTFOUND || nSym == STRING_NOTFOUND)) 935 { 936 String aMsg( RTL_CONSTASCII_USTRINGPARAM( 937 "LocaleDataWrapper::getCurrFormatsImpl: CurrPositiveFormat?")); 938 outputCheckMessage( appendLocaleInfo( aMsg ) ); 939 } 940 if ( nBlank == STRING_NOTFOUND ) 941 { 942 if ( nSym < nNum ) 943 nCurrPositiveFormat = 0; // $1 944 else 945 nCurrPositiveFormat = 1; // 1$ 946 } 947 else 948 { 949 if ( nSym < nNum ) 950 nCurrPositiveFormat = 2; // $ 1 951 else 952 nCurrPositiveFormat = 3; // 1 $ 953 } 954 955 // negative format 956 if ( nNeg < 0 ) 957 nCurrNegativeFormat = nCurrFormatDefault; 958 else 959 { 960 const ::rtl::OUString& rCode = pFormatArr[nNeg].Code; 961 xub_StrLen nDelim = (xub_StrLen)rCode.indexOf( ';' ); 962 scanCurrFormatImpl( rCode, nDelim+1, nSign, nPar, nNum, nBlank, nSym ); 963 if (areChecksEnabled() && (nNum == STRING_NOTFOUND || 964 nSym == STRING_NOTFOUND || (nPar == STRING_NOTFOUND && 965 nSign == STRING_NOTFOUND))) 966 { 967 String aMsg( RTL_CONSTASCII_USTRINGPARAM( 968 "LocaleDataWrapper::getCurrFormatsImpl: CurrNegativeFormat?")); 969 outputCheckMessage( appendLocaleInfo( aMsg ) ); 970 } 971 if ( nBlank == STRING_NOTFOUND ) 972 { 973 if ( nSym < nNum ) 974 { 975 if ( nPar < nSym ) 976 nCurrNegativeFormat = 0; // ($1) 977 else if ( nSign < nSym ) 978 nCurrNegativeFormat = 1; // -$1 979 else if ( nNum < nSign ) 980 nCurrNegativeFormat = 3; // $1- 981 else 982 nCurrNegativeFormat = 2; // $-1 983 } 984 else 985 { 986 if ( nPar < nNum ) 987 nCurrNegativeFormat = 4; // (1$) 988 else if ( nSign < nNum ) 989 nCurrNegativeFormat = 5; // -1$ 990 else if ( nSym < nSign ) 991 nCurrNegativeFormat = 7; // 1$- 992 else 993 nCurrNegativeFormat = 6; // 1-$ 994 } 995 } 996 else 997 { 998 if ( nSym < nNum ) 999 { 1000 if ( nPar < nSym ) 1001 nCurrNegativeFormat = 14; // ($ 1) 1002 else if ( nSign < nSym ) 1003 nCurrNegativeFormat = 9; // -$ 1 1004 else if ( nNum < nSign ) 1005 nCurrNegativeFormat = 12; // $ 1- 1006 else 1007 nCurrNegativeFormat = 11; // $ -1 1008 } 1009 else 1010 { 1011 if ( nPar < nNum ) 1012 nCurrNegativeFormat = 15; // (1 $) 1013 else if ( nSign < nNum ) 1014 nCurrNegativeFormat = 8; // -1 $ 1015 else if ( nSym < nSign ) 1016 nCurrNegativeFormat = 10; // 1 $- 1017 else 1018 nCurrNegativeFormat = 13; // 1- $ 1019 } 1020 } 1021 } 1022 } 1023 1024 1025 // --- date ----------------------------------------------------------- 1026 1027 DateFormat LocaleDataWrapper::getDateFormat() const 1028 { 1029 ::utl::ReadWriteGuard aGuard( aMutex ); 1030 if ( nDateFormat == nDateFormatInvalid ) 1031 { 1032 aGuard.changeReadToWrite(); 1033 ((LocaleDataWrapper*)this)->getDateFormatsImpl(); 1034 } 1035 return (DateFormat) nDateFormat; 1036 } 1037 1038 1039 DateFormat LocaleDataWrapper::getLongDateFormat() const 1040 { 1041 ::utl::ReadWriteGuard aGuard( aMutex ); 1042 if ( nLongDateFormat == nDateFormatInvalid ) 1043 { 1044 aGuard.changeReadToWrite(); 1045 ((LocaleDataWrapper*)this)->getDateFormatsImpl(); 1046 } 1047 return (DateFormat) nLongDateFormat; 1048 } 1049 1050 1051 DateFormat LocaleDataWrapper::scanDateFormatImpl( const String& rCode ) 1052 { 1053 // Only some european versions were translated, the ones with different 1054 // keyword combinations are: 1055 // English DMY, German TMJ, Spanish DMA, French JMA, Italian GMA, 1056 // Dutch DMJ, Finnish PKV 1057 1058 // default is English keywords for every other language 1059 xub_StrLen nDay = rCode.Search( 'D' ); 1060 xub_StrLen nMonth = rCode.Search( 'M' ); 1061 xub_StrLen nYear = rCode.Search( 'Y' ); 1062 if ( nDay == STRING_NOTFOUND || nMonth == STRING_NOTFOUND || nYear == STRING_NOTFOUND ) 1063 { // This algorithm assumes that all three parts (DMY) are present 1064 if ( nMonth == STRING_NOTFOUND ) 1065 { // only Finnish has something else than 'M' for month 1066 nMonth = rCode.Search( 'K' ); 1067 if ( nMonth != STRING_NOTFOUND ) 1068 { 1069 nDay = rCode.Search( 'P' ); 1070 nYear = rCode.Search( 'V' ); 1071 } 1072 } 1073 else if ( nDay == STRING_NOTFOUND ) 1074 { // We have a month 'M' if we reach this branch. 1075 // Possible languages containing 'M' but no 'D': 1076 // German, French, Italian 1077 nDay = rCode.Search( 'T' ); // German 1078 if ( nDay != STRING_NOTFOUND ) 1079 nYear = rCode.Search( 'J' ); 1080 else 1081 { 1082 nYear = rCode.Search( 'A' ); // French, Italian 1083 if ( nYear != STRING_NOTFOUND ) 1084 { 1085 nDay = rCode.Search( 'J' ); // French 1086 if ( nDay == STRING_NOTFOUND ) 1087 nDay = rCode.Search( 'G' ); // Italian 1088 } 1089 } 1090 } 1091 else 1092 { // We have a month 'M' and a day 'D'. 1093 // Possible languages containing 'D' and 'M' but not 'Y': 1094 // Spanish, Dutch 1095 nYear = rCode.Search( 'A' ); // Spanish 1096 if ( nYear == STRING_NOTFOUND ) 1097 nYear = rCode.Search( 'J' ); // Dutch 1098 } 1099 if ( nDay == STRING_NOTFOUND || nMonth == STRING_NOTFOUND || nYear == STRING_NOTFOUND ) 1100 { 1101 if (areChecksEnabled()) 1102 { 1103 String aMsg( RTL_CONSTASCII_USTRINGPARAM( 1104 "LocaleDataWrapper::scanDateFormat: not all DMY present")); 1105 outputCheckMessage( appendLocaleInfo( aMsg ) ); 1106 } 1107 if ( nDay == STRING_NOTFOUND ) 1108 nDay = rCode.Len(); 1109 if ( nMonth == STRING_NOTFOUND ) 1110 nMonth = rCode.Len(); 1111 if ( nYear == STRING_NOTFOUND ) 1112 nYear = rCode.Len(); 1113 } 1114 } 1115 // compare with <= because each position may equal rCode.Len() 1116 if ( nDay <= nMonth && nMonth <= nYear ) 1117 return DMY; // also if every position equals rCode.Len() 1118 else if ( nMonth <= nDay && nDay <= nYear ) 1119 return MDY; 1120 else if ( nYear <= nMonth && nMonth <= nDay ) 1121 return YMD; 1122 else 1123 { 1124 if (areChecksEnabled()) 1125 { 1126 String aMsg( RTL_CONSTASCII_USTRINGPARAM( 1127 "LocaleDataWrapper::scanDateFormat: no magic applyable")); 1128 outputCheckMessage( appendLocaleInfo( aMsg ) ); 1129 } 1130 return DMY; 1131 } 1132 } 1133 1134 1135 void LocaleDataWrapper::getDateFormatsImpl() 1136 { 1137 NumberFormatCodeWrapper aNumberFormatCode( xSMgr, getLocale() ); 1138 uno::Sequence< NumberFormatCode > aFormatSeq 1139 = aNumberFormatCode.getAllFormatCode( KNumberFormatUsage::DATE ); 1140 sal_Int32 nCnt = aFormatSeq.getLength(); 1141 if ( !nCnt ) 1142 { // bad luck 1143 if (areChecksEnabled()) 1144 { 1145 String aMsg( RTL_CONSTASCII_USTRINGPARAM( 1146 "LocaleDataWrapper::getDateFormatsImpl: no date formats")); 1147 outputCheckMessage( appendLocaleInfo( aMsg ) ); 1148 } 1149 nDateFormat = nLongDateFormat = DMY; 1150 return ; 1151 } 1152 // find the edit (21), a default (medium preferred), 1153 // a medium (default preferred), and a long (default preferred) 1154 NumberFormatCode const * const pFormatArr = aFormatSeq.getArray(); 1155 sal_Int32 nElem, nEdit, nDef, nMedium, nLong; 1156 nEdit = nDef = nMedium = nLong = -1; 1157 for ( nElem = 0; nElem < nCnt; nElem++ ) 1158 { 1159 if ( nEdit == -1 && pFormatArr[nElem].Index == NumberFormatIndex::DATE_SYS_DDMMYYYY ) 1160 nEdit = nElem; 1161 if ( nDef == -1 && pFormatArr[nElem].Default ) 1162 nDef = nElem; 1163 switch ( pFormatArr[nElem].Type ) 1164 { 1165 case KNumberFormatType::MEDIUM : 1166 { 1167 if ( pFormatArr[nElem].Default ) 1168 { 1169 nDef = nElem; 1170 nMedium = nElem; 1171 } 1172 else if ( nMedium == -1 ) 1173 nMedium = nElem; 1174 } 1175 break; 1176 case KNumberFormatType::LONG : 1177 { 1178 if ( pFormatArr[nElem].Default ) 1179 nLong = nElem; 1180 else if ( nLong == -1 ) 1181 nLong = nElem; 1182 } 1183 break; 1184 } 1185 } 1186 if ( nEdit == -1 ) 1187 { 1188 if (areChecksEnabled()) 1189 { 1190 String aMsg( RTL_CONSTASCII_USTRINGPARAM( 1191 "LocaleDataWrapper::getDateFormatsImpl: no edit")); 1192 outputCheckMessage( appendLocaleInfo( aMsg ) ); 1193 } 1194 if ( nDef == -1 ) 1195 { 1196 if (areChecksEnabled()) 1197 { 1198 String aMsg( RTL_CONSTASCII_USTRINGPARAM( 1199 "LocaleDataWrapper::getDateFormatsImpl: no default")); 1200 outputCheckMessage( appendLocaleInfo( aMsg ) ); 1201 } 1202 if ( nMedium != -1 ) 1203 nDef = nMedium; 1204 else if ( nLong != -1 ) 1205 nDef = nLong; 1206 else 1207 nDef = 0; 1208 } 1209 nEdit = nDef; 1210 } 1211 DateFormat nDF = scanDateFormatImpl( pFormatArr[nEdit].Code ); 1212 if ( pFormatArr[nEdit].Type == KNumberFormatType::LONG ) 1213 { // normally this is not the case 1214 nLongDateFormat = nDateFormat = nDF; 1215 } 1216 else 1217 { 1218 nDateFormat = nDF; 1219 if ( nLong == -1 ) 1220 nLongDateFormat = nDF; 1221 else 1222 nLongDateFormat = scanDateFormatImpl( pFormatArr[nLong].Code ); 1223 } 1224 } 1225 1226 1227 // --- digit grouping ------------------------------------------------- 1228 1229 void LocaleDataWrapper::getDigitGroupingImpl() 1230 { 1231 /* TODO: This is a very simplified grouping setup that only serves its 1232 * current purpose for Indian locales. A free-form flexible one would 1233 * obtain grouping from locale data where it could be specified using, for 1234 * example, codes like #,### and #,##,### that would generate the integer 1235 * sequence. Needed additional API and a locale data element. 1236 */ 1237 1238 if (!aGrouping.getLength()) 1239 { 1240 aGrouping.realloc(3); // room for {3,2,0} 1241 aGrouping[0] = 0; // invalidate 1242 } 1243 if (!aGrouping[0]) 1244 { 1245 i18n::LanguageCountryInfo aLCInfo( getLanguageCountryInfo()); 1246 if (aLCInfo.Country.equalsIgnoreAsciiCaseAscii( "IN") || // India 1247 aLCInfo.Country.equalsIgnoreAsciiCaseAscii( "BT")) // Bhutan 1248 { 1249 aGrouping[0] = 3; 1250 aGrouping[1] = 2; 1251 aGrouping[2] = 0; 1252 } 1253 else 1254 { 1255 aGrouping[0] = 3; 1256 aGrouping[1] = 0; 1257 } 1258 } 1259 } 1260 1261 1262 const ::com::sun::star::uno::Sequence< sal_Int32 > LocaleDataWrapper::getDigitGrouping() const 1263 { 1264 ::utl::ReadWriteGuard aGuard( aMutex ); 1265 if (!aGrouping.getLength() || aGrouping[0] == 0) 1266 { // no cached content 1267 aGuard.changeReadToWrite(); 1268 ((LocaleDataWrapper*)this)->getDigitGroupingImpl(); 1269 } 1270 return aGrouping; 1271 } 1272 1273 1274 // --- simple number formatting helpers ------------------------------- 1275 1276 // The ImplAdd... methods are taken from class International and modified to 1277 // suit the needs. 1278 1279 static sal_Unicode* ImplAddUNum( sal_Unicode* pBuf, sal_uInt64 nNumber ) 1280 { 1281 // fill temp buffer with digits 1282 sal_Unicode aTempBuf[64]; 1283 sal_Unicode* pTempBuf = aTempBuf; 1284 do 1285 { 1286 *pTempBuf = (sal_Unicode)(nNumber % 10) + '0'; 1287 pTempBuf++; 1288 nNumber /= 10; 1289 } 1290 while ( nNumber ); 1291 1292 // copy temp buffer to buffer passed 1293 do 1294 { 1295 pTempBuf--; 1296 *pBuf = *pTempBuf; 1297 pBuf++; 1298 } 1299 while ( pTempBuf != aTempBuf ); 1300 1301 return pBuf; 1302 } 1303 1304 1305 static sal_Unicode* ImplAddUNum( sal_Unicode* pBuf, sal_uInt64 nNumber, int nMinLen ) 1306 { 1307 // fill temp buffer with digits 1308 sal_Unicode aTempBuf[64]; 1309 sal_Unicode* pTempBuf = aTempBuf; 1310 do 1311 { 1312 *pTempBuf = (sal_Unicode)(nNumber % 10) + '0'; 1313 pTempBuf++; 1314 nNumber /= 10; 1315 nMinLen--; 1316 } 1317 while ( nNumber ); 1318 1319 // fill with zeros up to the minimal length 1320 while ( nMinLen > 0 ) 1321 { 1322 *pBuf = '0'; 1323 pBuf++; 1324 nMinLen--; 1325 } 1326 1327 // copy temp buffer to real buffer 1328 do 1329 { 1330 pTempBuf--; 1331 *pBuf = *pTempBuf; 1332 pBuf++; 1333 } 1334 while ( pTempBuf != aTempBuf ); 1335 1336 return pBuf; 1337 } 1338 1339 1340 static sal_Unicode* ImplAdd2UNum( sal_Unicode* pBuf, sal_uInt16 nNumber, int bLeading ) 1341 { 1342 DBG_ASSERT( nNumber < 100, "ImplAdd2UNum() - Number >= 100" ); 1343 1344 if ( nNumber < 10 ) 1345 { 1346 if ( bLeading ) 1347 { 1348 *pBuf = '0'; 1349 pBuf++; 1350 } 1351 *pBuf = nNumber + '0'; 1352 } 1353 else 1354 { 1355 sal_uInt16 nTemp = nNumber % 10; 1356 nNumber /= 10; 1357 *pBuf = nNumber + '0'; 1358 pBuf++; 1359 *pBuf = nTemp + '0'; 1360 } 1361 1362 pBuf++; 1363 return pBuf; 1364 } 1365 1366 1367 inline sal_Unicode* ImplAddString( sal_Unicode* pBuf, const String& rStr ) 1368 { 1369 if ( rStr.Len() == 1 ) 1370 *pBuf++ = rStr.GetChar(0); 1371 else if ( rStr.Len() == 0 ) 1372 ; 1373 else 1374 { 1375 memcpy( pBuf, rStr.GetBuffer(), rStr.Len() * sizeof(sal_Unicode) ); 1376 pBuf += rStr.Len(); 1377 } 1378 return pBuf; 1379 } 1380 1381 1382 inline sal_Unicode* ImplAddString( sal_Unicode* pBuf, sal_Unicode c ) 1383 { 1384 *pBuf = c; 1385 pBuf++; 1386 return pBuf; 1387 } 1388 1389 1390 inline sal_Unicode* ImplAddString( sal_Unicode* pBuf, const sal_Unicode* pCopyBuf, xub_StrLen nLen ) 1391 { 1392 memcpy( pBuf, pCopyBuf, nLen * sizeof(sal_Unicode) ); 1393 return pBuf + nLen; 1394 } 1395 1396 1397 sal_Unicode* LocaleDataWrapper::ImplAddFormatNum( sal_Unicode* pBuf, 1398 sal_Int64 nNumber, sal_uInt16 nDecimals, sal_Bool bUseThousandSep, 1399 sal_Bool bTrailingZeros ) const 1400 { 1401 sal_Unicode aNumBuf[64]; 1402 sal_Unicode* pNumBuf; 1403 sal_uInt16 nNumLen; 1404 sal_uInt16 i = 0; 1405 sal_Bool bNeg; 1406 1407 // negative number 1408 if ( nNumber < 0 ) 1409 { 1410 nNumber *= -1; 1411 bNeg = sal_True; 1412 *pBuf = '-'; 1413 pBuf++; 1414 } 1415 else 1416 bNeg = sal_False; 1417 1418 // convert number 1419 pNumBuf = ImplAddUNum( aNumBuf, (sal_uInt64)nNumber ); 1420 nNumLen = (sal_uInt16)(sal_uLong)(pNumBuf-aNumBuf); 1421 pNumBuf = aNumBuf; 1422 1423 if ( nNumLen <= nDecimals ) 1424 { 1425 // strip .0 in decimals? 1426 if ( !nNumber && !bTrailingZeros ) 1427 { 1428 *pBuf = '0'; 1429 pBuf++; 1430 } 1431 else 1432 { 1433 // LeadingZero, insert 0 1434 if ( isNumLeadingZero() ) 1435 { 1436 *pBuf = '0'; 1437 pBuf++; 1438 } 1439 1440 // append decimal separator 1441 pBuf = ImplAddString( pBuf, getNumDecimalSep() ); 1442 1443 // fill with zeros 1444 while ( i < (nDecimals-nNumLen) ) 1445 { 1446 *pBuf = '0'; 1447 pBuf++; 1448 i++; 1449 } 1450 1451 // append decimals 1452 while ( nNumLen ) 1453 { 1454 *pBuf = *pNumBuf; 1455 pBuf++; 1456 pNumBuf++; 1457 nNumLen--; 1458 } 1459 } 1460 } 1461 else 1462 { 1463 const String& rThoSep = getNumThousandSep(); 1464 1465 // copy number to buffer (excluding decimals) 1466 sal_uInt16 nNumLen2 = nNumLen-nDecimals; 1467 uno::Sequence< sal_Bool > aGroupPos; 1468 if (bUseThousandSep) 1469 aGroupPos = utl::DigitGroupingIterator::createForwardSequence( 1470 nNumLen2, getDigitGrouping()); 1471 for ( ; i < nNumLen2; ++i ) 1472 { 1473 *pBuf = *pNumBuf; 1474 pBuf++; 1475 pNumBuf++; 1476 1477 // add thousand separator? 1478 if ( bUseThousandSep && aGroupPos[i] ) 1479 pBuf = ImplAddString( pBuf, rThoSep ); 1480 } 1481 1482 // append decimals 1483 if ( nDecimals ) 1484 { 1485 pBuf = ImplAddString( pBuf, getNumDecimalSep() ); 1486 1487 sal_Bool bNullEnd = sal_True; 1488 while ( i < nNumLen ) 1489 { 1490 if ( *pNumBuf != '0' ) 1491 bNullEnd = sal_False; 1492 1493 *pBuf = *pNumBuf; 1494 pBuf++; 1495 pNumBuf++; 1496 i++; 1497 } 1498 1499 // strip .0 in decimals? 1500 if ( bNullEnd && !bTrailingZeros ) 1501 pBuf -= nDecimals+1; 1502 } 1503 } 1504 1505 return pBuf; 1506 } 1507 1508 1509 // --- simple date and time formatting -------------------------------- 1510 1511 String LocaleDataWrapper::getDate( const Date& rDate ) const 1512 { 1513 ::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nBlockCritical ); 1514 //!TODO: leading zeros et al 1515 sal_Unicode aBuf[128]; 1516 sal_Unicode* pBuf = aBuf; 1517 sal_uInt16 nDay = rDate.GetDay(); 1518 sal_uInt16 nMonth = rDate.GetMonth(); 1519 sal_uInt16 nYear = rDate.GetYear(); 1520 sal_uInt16 nYearLen; 1521 1522 if ( sal_True /* IsDateCentury() */ ) 1523 nYearLen = 4; 1524 else 1525 { 1526 nYearLen = 2; 1527 nYear %= 100; 1528 } 1529 1530 switch ( getDateFormat() ) 1531 { 1532 case DMY : 1533 pBuf = ImplAdd2UNum( pBuf, nDay, sal_True /* IsDateDayLeadingZero() */ ); 1534 pBuf = ImplAddString( pBuf, getDateSep() ); 1535 pBuf = ImplAdd2UNum( pBuf, nMonth, sal_True /* IsDateMonthLeadingZero() */ ); 1536 pBuf = ImplAddString( pBuf, getDateSep() ); 1537 pBuf = ImplAddUNum( pBuf, nYear, nYearLen ); 1538 break; 1539 case MDY : 1540 pBuf = ImplAdd2UNum( pBuf, nMonth, sal_True /* IsDateMonthLeadingZero() */ ); 1541 pBuf = ImplAddString( pBuf, getDateSep() ); 1542 pBuf = ImplAdd2UNum( pBuf, nDay, sal_True /* IsDateDayLeadingZero() */ ); 1543 pBuf = ImplAddString( pBuf, getDateSep() ); 1544 pBuf = ImplAddUNum( pBuf, nYear, nYearLen ); 1545 break; 1546 default: 1547 pBuf = ImplAddUNum( pBuf, nYear, nYearLen ); 1548 pBuf = ImplAddString( pBuf, getDateSep() ); 1549 pBuf = ImplAdd2UNum( pBuf, nMonth, sal_True /* IsDateMonthLeadingZero() */ ); 1550 pBuf = ImplAddString( pBuf, getDateSep() ); 1551 pBuf = ImplAdd2UNum( pBuf, nDay, sal_True /* IsDateDayLeadingZero() */ ); 1552 } 1553 1554 return String( aBuf, (xub_StrLen)(sal_uLong)(pBuf-aBuf) ); 1555 } 1556 1557 1558 String LocaleDataWrapper::getTime( const Time& rTime, sal_Bool bSec, sal_Bool b100Sec ) const 1559 { 1560 ::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nBlockCritical ); 1561 //!TODO: leading zeros et al 1562 sal_Unicode aBuf[128]; 1563 sal_Unicode* pBuf = aBuf; 1564 sal_uInt16 nHour = rTime.GetHour(); 1565 sal_Bool bHour12 = sal_False; //!TODO: AM/PM from default time format code 1566 1567 if ( bHour12 ) 1568 { 1569 nHour %= 12; 1570 // 0:00 -> 12:00 1571 if ( !nHour ) 1572 nHour = 12; 1573 } 1574 else 1575 nHour %= 24; 1576 1577 pBuf = ImplAdd2UNum( pBuf, nHour, sal_True /* IsTimeLeadingZero() */ ); 1578 pBuf = ImplAddString( pBuf, getTimeSep() ); 1579 pBuf = ImplAdd2UNum( pBuf, rTime.GetMin(), sal_True ); 1580 if ( bSec ) 1581 { 1582 pBuf = ImplAddString( pBuf, getTimeSep() ); 1583 pBuf = ImplAdd2UNum( pBuf, rTime.GetSec(), sal_True ); 1584 1585 if ( b100Sec ) 1586 { 1587 pBuf = ImplAddString( pBuf, getTime100SecSep() ); 1588 pBuf = ImplAdd2UNum( pBuf, rTime.Get100Sec(), sal_True ); 1589 } 1590 } 1591 1592 String aStr( aBuf, (xub_StrLen)(sal_uLong)(pBuf-aBuf) ); 1593 1594 if ( bHour12 ) 1595 { 1596 if ( (rTime.GetHour() % 24) >= 12 ) 1597 aStr += getTimePM(); 1598 else 1599 aStr += getTimeAM(); 1600 } 1601 #if 0 1602 //!TODO: do we need a time string? like "o'clock" or "Uhr" or similar 1603 else 1604 aStr += getTimeStr(); 1605 #endif 1606 1607 return aStr; 1608 } 1609 1610 1611 String LocaleDataWrapper::getLongDate( const Date& rDate, CalendarWrapper& rCal, 1612 sal_Int16 nDisplayDayOfWeek, sal_Bool bDayOfMonthWithLeadingZero, 1613 sal_Int16 nDisplayMonth, sal_Bool bTwoDigitYear ) const 1614 { 1615 ::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nBlockCritical ); 1616 using namespace ::com::sun::star::i18n; 1617 sal_Unicode aBuf[20]; 1618 sal_Unicode* pBuf; 1619 String aStr; 1620 sal_Int16 nVal; 1621 rCal.setGregorianDateTime( rDate ); 1622 // day of week 1623 nVal = rCal.getValue( CalendarFieldIndex::DAY_OF_WEEK ); 1624 aStr += rCal.getDisplayName( CalendarDisplayIndex::DAY, nVal, nDisplayDayOfWeek ); 1625 aStr += getLongDateDayOfWeekSep(); 1626 // day of month 1627 nVal = rCal.getValue( CalendarFieldIndex::DAY_OF_MONTH ); 1628 pBuf = ImplAdd2UNum( aBuf, nVal, bDayOfMonthWithLeadingZero ); 1629 String aDay( aBuf, (xub_StrLen)(sal_uLong)(pBuf-aBuf) ); 1630 // month of year 1631 nVal = rCal.getValue( CalendarFieldIndex::MONTH ); 1632 String aMonth( rCal.getDisplayName( CalendarDisplayIndex::MONTH, nVal, nDisplayMonth ) ); 1633 // year 1634 nVal = rCal.getValue( CalendarFieldIndex::YEAR ); 1635 if ( bTwoDigitYear ) 1636 pBuf = ImplAddUNum( aBuf, nVal % 100, 2 ); 1637 else 1638 pBuf = ImplAddUNum( aBuf, nVal ); 1639 String aYear( aBuf, (xub_StrLen)(sal_uLong)(pBuf-aBuf) ); 1640 // concatenate 1641 switch ( getLongDateFormat() ) 1642 { 1643 case DMY : 1644 aStr += aDay; 1645 aStr += getLongDateDaySep(); 1646 aStr += aMonth; 1647 aStr += getLongDateMonthSep(); 1648 aStr += aYear; 1649 break; 1650 case MDY : 1651 aStr += aMonth; 1652 aStr += getLongDateMonthSep(); 1653 aStr += aDay; 1654 aStr += getLongDateDaySep(); 1655 aStr += aYear; 1656 break; 1657 default: // YMD 1658 aStr += aYear; 1659 aStr += getLongDateYearSep(); 1660 aStr += aMonth; 1661 aStr += getLongDateMonthSep(); 1662 aStr += aDay; 1663 } 1664 return aStr; 1665 } 1666 1667 1668 String LocaleDataWrapper::getDuration( const Time& rTime, sal_Bool bSec, sal_Bool b100Sec ) const 1669 { 1670 ::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nBlockCritical ); 1671 sal_Unicode aBuf[128]; 1672 sal_Unicode* pBuf = aBuf; 1673 1674 if ( rTime < Time( 0 ) ) 1675 pBuf = ImplAddString( pBuf, ' ' ); 1676 1677 if ( sal_True /* IsTimeLeadingZero() */ ) 1678 pBuf = ImplAddUNum( pBuf, rTime.GetHour(), 2 ); 1679 else 1680 pBuf = ImplAddUNum( pBuf, rTime.GetHour() ); 1681 pBuf = ImplAddString( pBuf, getTimeSep() ); 1682 pBuf = ImplAdd2UNum( pBuf, rTime.GetMin(), sal_True ); 1683 if ( bSec ) 1684 { 1685 pBuf = ImplAddString( pBuf, getTimeSep() ); 1686 pBuf = ImplAdd2UNum( pBuf, rTime.GetSec(), sal_True ); 1687 1688 if ( b100Sec ) 1689 { 1690 pBuf = ImplAddString( pBuf, getTime100SecSep() ); 1691 pBuf = ImplAdd2UNum( pBuf, rTime.Get100Sec(), sal_True ); 1692 } 1693 } 1694 1695 return String( aBuf, (xub_StrLen)(sal_uLong)(pBuf-aBuf) ); 1696 } 1697 1698 1699 // --- simple number formatting --------------------------------------- 1700 1701 inline size_t ImplGetNumberStringLengthGuess( const LocaleDataWrapper& rLoc, sal_uInt16 nDecimals ) 1702 { 1703 // approximately 3.2 bits per digit 1704 const size_t nDig = ((sizeof(sal_Int64) * 8) / 3) + 1; 1705 // digits, separators (pessimized for insane "every digit may be grouped"), leading zero, sign 1706 size_t nGuess = ((nDecimals < nDig) ? 1707 (((nDig - nDecimals) * rLoc.getNumThousandSep().Len()) + nDig) : 1708 nDecimals) + rLoc.getNumDecimalSep().Len() + 3; 1709 return nGuess; 1710 } 1711 1712 1713 String LocaleDataWrapper::getNum( sal_Int64 nNumber, sal_uInt16 nDecimals, 1714 sal_Bool bUseThousandSep, sal_Bool bTrailingZeros ) const 1715 { 1716 ::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nBlockCritical ); 1717 sal_Unicode aBuf[128]; // big enough for 64-bit long and crazy grouping 1718 // check if digits and separators will fit into fixed buffer or allocate 1719 size_t nGuess = ImplGetNumberStringLengthGuess( *this, nDecimals ); 1720 sal_Unicode* const pBuffer = (nGuess < 118 ? aBuf : 1721 new sal_Unicode[nGuess + 16]); 1722 1723 sal_Unicode* pBuf = ImplAddFormatNum( pBuffer, nNumber, nDecimals, 1724 bUseThousandSep, bTrailingZeros ); 1725 String aStr( pBuffer, (xub_StrLen)(sal_uLong)(pBuf-pBuffer) ); 1726 1727 if ( pBuffer != aBuf ) 1728 delete [] pBuffer; 1729 return aStr; 1730 } 1731 1732 1733 String LocaleDataWrapper::getCurr( sal_Int64 nNumber, sal_uInt16 nDecimals, 1734 const String& rCurrencySymbol, sal_Bool bUseThousandSep ) const 1735 { 1736 ::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nBlockCritical ); 1737 sal_Unicode aBuf[192]; 1738 sal_Unicode aNumBuf[128]; // big enough for 64-bit long and crazy grouping 1739 sal_Unicode cZeroChar = getCurrZeroChar(); 1740 1741 // check if digits and separators will fit into fixed buffer or allocate 1742 size_t nGuess = ImplGetNumberStringLengthGuess( *this, nDecimals ); 1743 sal_Unicode* const pNumBuffer = (nGuess < 118 ? aNumBuf : 1744 new sal_Unicode[nGuess + 16]); 1745 1746 sal_Unicode* const pBuffer = 1747 ((size_t(rCurrencySymbol.Len()) + nGuess + 20) < sizeof(aBuf)/sizeof(aBuf[0]) ? aBuf : 1748 new sal_Unicode[ rCurrencySymbol.Len() + nGuess + 20 ]); 1749 sal_Unicode* pBuf = pBuffer; 1750 1751 sal_Bool bNeg; 1752 if ( nNumber < 0 ) 1753 { 1754 bNeg = sal_True; 1755 nNumber *= -1; 1756 } 1757 else 1758 bNeg = sal_False; 1759 1760 // convert number 1761 sal_Unicode* pEndNumBuf = ImplAddFormatNum( pNumBuffer, nNumber, nDecimals, 1762 bUseThousandSep, sal_True ); 1763 xub_StrLen nNumLen = (xub_StrLen)(sal_uLong)(pEndNumBuf-pNumBuffer); 1764 1765 // replace zeros with zero character 1766 if ( (cZeroChar != '0') && nDecimals /* && IsNumTrailingZeros() */ ) 1767 { 1768 sal_Unicode* pTempBuf; 1769 sal_uInt16 i; 1770 sal_Bool bZero = sal_True; 1771 1772 pTempBuf = pNumBuffer+nNumLen-nDecimals; 1773 i = 0; 1774 do 1775 { 1776 if ( *pTempBuf != '0' ) 1777 { 1778 bZero = sal_False; 1779 break; 1780 } 1781 1782 pTempBuf++; 1783 i++; 1784 } 1785 while ( i < nDecimals ); 1786 1787 if ( bZero ) 1788 { 1789 pTempBuf = pNumBuffer+nNumLen-nDecimals; 1790 i = 0; 1791 do 1792 { 1793 *pTempBuf = cZeroChar; 1794 pTempBuf++; 1795 i++; 1796 } 1797 while ( i < nDecimals ); 1798 } 1799 } 1800 1801 if ( !bNeg ) 1802 { 1803 switch( getCurrPositiveFormat() ) 1804 { 1805 case 0: 1806 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1807 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1808 break; 1809 case 1: 1810 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1811 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1812 break; 1813 case 2: 1814 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1815 pBuf = ImplAddString( pBuf, ' ' ); 1816 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1817 break; 1818 case 3: 1819 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1820 pBuf = ImplAddString( pBuf, ' ' ); 1821 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1822 break; 1823 } 1824 } 1825 else 1826 { 1827 switch( getCurrNegativeFormat() ) 1828 { 1829 case 0: 1830 pBuf = ImplAddString( pBuf, '(' ); 1831 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1832 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1833 pBuf = ImplAddString( pBuf, ')' ); 1834 break; 1835 case 1: 1836 pBuf = ImplAddString( pBuf, '-' ); 1837 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1838 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1839 break; 1840 case 2: 1841 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1842 pBuf = ImplAddString( pBuf, '-' ); 1843 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1844 break; 1845 case 3: 1846 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1847 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1848 pBuf = ImplAddString( pBuf, '-' ); 1849 break; 1850 case 4: 1851 pBuf = ImplAddString( pBuf, '(' ); 1852 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1853 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1854 pBuf = ImplAddString( pBuf, ')' ); 1855 break; 1856 case 5: 1857 pBuf = ImplAddString( pBuf, '-' ); 1858 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1859 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1860 break; 1861 case 6: 1862 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1863 pBuf = ImplAddString( pBuf, '-' ); 1864 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1865 break; 1866 case 7: 1867 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1868 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1869 pBuf = ImplAddString( pBuf, '-' ); 1870 break; 1871 case 8: 1872 pBuf = ImplAddString( pBuf, '-' ); 1873 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1874 pBuf = ImplAddString( pBuf, ' ' ); 1875 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1876 break; 1877 case 9: 1878 pBuf = ImplAddString( pBuf, '-' ); 1879 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1880 pBuf = ImplAddString( pBuf, ' ' ); 1881 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1882 break; 1883 case 10: 1884 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1885 pBuf = ImplAddString( pBuf, ' ' ); 1886 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1887 pBuf = ImplAddString( pBuf, '-' ); 1888 break; 1889 case 11: 1890 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1891 pBuf = ImplAddString( pBuf, ' ' ); 1892 pBuf = ImplAddString( pBuf, '-' ); 1893 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1894 break; 1895 case 12: 1896 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1897 pBuf = ImplAddString( pBuf, ' ' ); 1898 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1899 pBuf = ImplAddString( pBuf, '-' ); 1900 break; 1901 case 13: 1902 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1903 pBuf = ImplAddString( pBuf, '-' ); 1904 pBuf = ImplAddString( pBuf, ' ' ); 1905 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1906 break; 1907 case 14: 1908 pBuf = ImplAddString( pBuf, '(' ); 1909 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1910 pBuf = ImplAddString( pBuf, ' ' ); 1911 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1912 pBuf = ImplAddString( pBuf, ')' ); 1913 break; 1914 case 15: 1915 pBuf = ImplAddString( pBuf, '(' ); 1916 pBuf = ImplAddString( pBuf, pNumBuffer, nNumLen ); 1917 pBuf = ImplAddString( pBuf, ' ' ); 1918 pBuf = ImplAddString( pBuf, rCurrencySymbol ); 1919 pBuf = ImplAddString( pBuf, ')' ); 1920 break; 1921 } 1922 } 1923 1924 String aNumber( pBuffer, (xub_StrLen)(sal_uLong)(pBuf-pBuffer) ); 1925 1926 if ( pBuffer != aBuf ) 1927 delete [] pBuffer; 1928 if ( pNumBuffer != aNumBuf ) 1929 delete [] pNumBuffer; 1930 1931 return aNumber; 1932 } 1933 1934 1935 // --- mixed ---------------------------------------------------------- 1936 1937 ::com::sun::star::lang::Locale LocaleDataWrapper::getLoadedLocale() const 1938 { 1939 LanguageCountryInfo aLCInfo = getLanguageCountryInfo(); 1940 return lang::Locale( aLCInfo.Language, aLCInfo.Country, aLCInfo.Variant ); 1941 } 1942 1943 1944 String& LocaleDataWrapper::appendLocaleInfo( String& rDebugMsg ) const 1945 { 1946 ::utl::ReadWriteGuard aGuard( aMutex, ::utl::ReadWriteGuardMode::nBlockCritical ); 1947 rDebugMsg += '\n'; 1948 rDebugMsg += String( aLocale.Language); 1949 rDebugMsg += '_'; 1950 rDebugMsg += String( aLocale.Country); 1951 rDebugMsg.AppendAscii( RTL_CONSTASCII_STRINGPARAM( " requested\n" ) ); 1952 lang::Locale aLoaded = getLoadedLocale(); 1953 rDebugMsg += String( aLoaded.Language); 1954 rDebugMsg += '_'; 1955 rDebugMsg += String( aLoaded.Country); 1956 rDebugMsg.AppendAscii( RTL_CONSTASCII_STRINGPARAM( " loaded" ) ); 1957 return rDebugMsg; 1958 } 1959 1960 1961 // static 1962 void LocaleDataWrapper::outputCheckMessage( const String& rMsg ) 1963 { 1964 outputCheckMessage( ByteString( rMsg, RTL_TEXTENCODING_UTF8).GetBuffer()); 1965 } 1966 1967 1968 // static 1969 void LocaleDataWrapper::outputCheckMessage( const char* pStr ) 1970 { 1971 fprintf( stderr, "\n%s\n", pStr); 1972 fflush( stderr); 1973 DBG_ERROR( pStr); 1974 } 1975 1976 1977 // static 1978 void LocaleDataWrapper::evaluateLocaleDataChecking() 1979 { 1980 // Using the rtl_Instance template here wouldn't solve all threaded write 1981 // accesses, since we want to assign the result to the static member 1982 // variable and would need to dereference the pointer returned and assign 1983 // the value unguarded. This is the same pattern manually coded. 1984 sal_uInt8 nCheck = nLocaleDataChecking; 1985 if (!nCheck) 1986 { 1987 ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex()); 1988 nCheck = nLocaleDataChecking; 1989 if (!nCheck) 1990 { 1991 #ifdef DBG_UTIL 1992 nCheck = 1; 1993 #else 1994 const char* pEnv = getenv( "OOO_ENABLE_LOCALE_DATA_CHECKS"); 1995 if (pEnv && (pEnv[0] == 'Y' || pEnv[0] == 'y' || pEnv[0] == '1')) 1996 nCheck = 1; 1997 else 1998 nCheck = 2; 1999 #endif 2000 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); 2001 nLocaleDataChecking = nCheck; 2002 } 2003 } 2004 else { 2005 OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); 2006 } 2007 } 2008