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