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