xref: /AOO41X/main/unotools/source/i18n/localedatawrapper.cxx (revision b5088357f810cb81479bbbd0e021cd3c9835ca0d)
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 
LocaleDataWrapper(const Reference<lang::XMultiServiceFactory> & xSF,const lang::Locale & rLocale)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 
~LocaleDataWrapper()137 LocaleDataWrapper::~LocaleDataWrapper()
138 {
139 }
140 
141 
setLocale(const::com::sun::star::lang::Locale & rLocale)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 
getLocale() const150 const ::com::sun::star::lang::Locale& LocaleDataWrapper::getLocale() const
151 {
152     ::utl::ReadWriteGuard aGuard( aMutex );
153     return aLocale;
154 }
155 
156 
invalidateData()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 
getLanguageCountryInfo() const187 ::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 
getLocaleItem() const208 ::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 
getAllCalendars() const229 ::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 
getAllCurrencies() const250 ::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 
getAllFormats() const271 ::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 
getCollatorImplementations() const292 ::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 
getTransliterations() const313 ::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 
getForbiddenCharacters() const334 ::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 
getReservedWord() const355 ::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 
getAllInstalledLocaleNames() const376 ::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
getInstalledLocaleNames()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
getInstalledLanguageTypes()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 
getOneLocaleItem(sal_Int16 nItem) const523 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 
getOneLocaleItemImpl(sal_Int16 nItem)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 
getOneReservedWordImpl(sal_Int16 nWord)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 
getOneReservedWord(sal_Int16 nWord) const619 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 
mapMeasurementStringToEnum(const String & rMS) const636 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 
getDefaultCalendarImpl()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 
getDefaultCalendar() const670 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 
getDefaultCalendarDays() const682 const ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::CalendarItem > LocaleDataWrapper::getDefaultCalendarDays() const
683 {
684     return getDefaultCalendar()->Days;
685 }
686 
687 
getDefaultCalendarMonths() const688 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 
getCurrSymbol() const696 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 
getCurrBankSymbol() const708 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 
getCurrPositiveFormat() const720 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 
getCurrNegativeFormat() const732 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 
getCurrDigits() const744 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 
getCurrSymbolsImpl()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 
scanCurrFormatImpl(const String & rCode,xub_StrLen nStart,xub_StrLen & nSign,xub_StrLen & nPar,xub_StrLen & nNum,xub_StrLen & nBlank,xub_StrLen & nSym)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 
getCurrFormatsImpl()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 
getDateFormat() const1023 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 
getLongDateFormat() const1035 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 
scanDateFormatImpl(const String & rCode)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 
getDateFormatsImpl()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 
getDigitGroupingImpl()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 
getDigitGrouping() const1258 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 
ImplAddUNum(sal_Unicode * pBuf,sal_uInt64 nNumber)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 
ImplAddUNum(sal_Unicode * pBuf,sal_uInt64 nNumber,int nMinLen)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 
ImplAdd2UNum(sal_Unicode * pBuf,sal_uInt16 nNumber,int bLeading)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 
ImplAddString(sal_Unicode * pBuf,const String & rStr)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 
ImplAddString(sal_Unicode * pBuf,sal_Unicode c)1378 inline sal_Unicode* ImplAddString( sal_Unicode* pBuf, sal_Unicode c )
1379 {
1380     *pBuf = c;
1381     pBuf++;
1382     return pBuf;
1383 }
1384 
1385 
ImplAddString(sal_Unicode * pBuf,const sal_Unicode * pCopyBuf,xub_StrLen nLen)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 
ImplAddFormatNum(sal_Unicode * pBuf,sal_Int64 nNumber,sal_uInt16 nDecimals,sal_Bool bUseThousandSep,sal_Bool bTrailingZeros) const1393 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 
getDate(const Date & rDate) const1507 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 
getTime(const Time & rTime,sal_Bool bSec,sal_Bool b100Sec) const1554 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 
getLongDate(const Date & rDate,CalendarWrapper & rCal,sal_Int16 nDisplayDayOfWeek,sal_Bool bDayOfMonthWithLeadingZero,sal_Int16 nDisplayMonth,sal_Bool bTwoDigitYear) const1607 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 
getDuration(const Time & rTime,sal_Bool bSec,sal_Bool b100Sec) const1664 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 
ImplGetNumberStringLengthGuess(const LocaleDataWrapper & rLoc,sal_uInt16 nDecimals)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 
getNum(sal_Int64 nNumber,sal_uInt16 nDecimals,sal_Bool bUseThousandSep,sal_Bool bTrailingZeros) const1709 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 
getCurr(sal_Int64 nNumber,sal_uInt16 nDecimals,const String & rCurrencySymbol,sal_Bool bUseThousandSep) const1729 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 
getLoadedLocale() const1933 ::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 
appendLocaleInfo(String & rDebugMsg) const1940 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
outputCheckMessage(const String & rMsg)1958 void LocaleDataWrapper::outputCheckMessage( const String& rMsg )
1959 {
1960     outputCheckMessage( ByteString( rMsg, RTL_TEXTENCODING_UTF8).GetBuffer());
1961 }
1962 
1963 
1964 // static
outputCheckMessage(const char * pStr)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
evaluateLocaleDataChecking()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