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