]>
Commit | Line | Data |
---|---|---|
b75a7d8f A |
1 | /* |
2 | ********************************************************************** | |
46f4442e | 3 | * Copyright (C) 1997-2008, International Business Machines |
b75a7d8f A |
4 | * Corporation and others. All Rights Reserved. |
5 | ********************************************************************** | |
6 | * | |
7 | * File locid.cpp | |
8 | * | |
9 | * Created by: Richard Gillam | |
10 | * | |
11 | * Modification History: | |
12 | * | |
13 | * Date Name Description | |
374ca955 | 14 | * 02/11/97 aliu Changed gLocPath to fgDataDirectory and added |
b75a7d8f | 15 | * methods to get and set it. |
374ca955 | 16 | * 04/02/97 aliu Made operator!= inline; fixed return value |
b75a7d8f A |
17 | * of getName(). |
18 | * 04/15/97 aliu Cleanup for AIX/Win32. | |
19 | * 04/24/97 aliu Numerous changes per code review. | |
20 | * 08/18/98 stephen Changed getDisplayName() | |
21 | * Added SIMPLIFIED_CHINESE, TRADITIONAL_CHINESE | |
22 | * Added getISOCountries(), getISOLanguages(), | |
23 | * getLanguagesForCountry() | |
24 | * 03/16/99 bertrand rehaul. | |
25 | * 07/21/99 stephen Added U_CFUNC setDefault | |
26 | * 11/09/99 weiv Added const char * getName() const; | |
27 | * 04/12/00 srl removing unicodestring api's and cached hash code | |
28 | * 08/10/01 grhoten Change the static Locales to accessor functions | |
29 | ****************************************************************************** | |
30 | */ | |
31 | ||
32 | ||
33 | #include "unicode/locid.h" | |
34 | #include "unicode/uloc.h" | |
35 | #include "umutex.h" | |
36 | #include "uassert.h" | |
37 | #include "cmemory.h" | |
38 | #include "cstring.h" | |
39 | #include "uhash.h" | |
40 | #include "ucln_cmn.h" | |
41 | ||
374ca955 A |
42 | #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) |
43 | ||
46f4442e | 44 | static U_NAMESPACE_QUALIFIER Locale* availableLocaleList = NULL; |
b75a7d8f A |
45 | static int32_t availableLocaleListCount; |
46 | typedef enum ELocalePos { | |
47 | eENGLISH, | |
48 | eFRENCH, | |
49 | eGERMAN, | |
50 | eITALIAN, | |
51 | eJAPANESE, | |
52 | eKOREAN, | |
53 | eCHINESE, | |
54 | ||
55 | eFRANCE, | |
56 | eGERMANY, | |
57 | eITALY, | |
58 | eJAPAN, | |
59 | eKOREA, | |
60 | eCHINA, /* Alias for PRC */ | |
61 | eTAIWAN, | |
62 | eUK, | |
63 | eUS, | |
64 | eCANADA, | |
65 | eCANADA_FRENCH, | |
66 | ||
67 | ||
68 | //eDEFAULT, | |
69 | eMAX_LOCALES | |
70 | } ELocalePos; | |
71 | ||
374ca955 A |
72 | U_CFUNC int32_t locale_getKeywords(const char *localeID, |
73 | char prev, | |
74 | char *keywords, int32_t keywordCapacity, | |
75 | char *values, int32_t valuesCapacity, int32_t *valLen, | |
76 | UBool valuesToo, | |
77 | UErrorCode *status); | |
78 | ||
46f4442e A |
79 | static U_NAMESPACE_QUALIFIER Locale *gLocaleCache = NULL; |
80 | static U_NAMESPACE_QUALIFIER Locale *gDefaultLocale = NULL; | |
81 | static UHashtable *gDefaultLocalesHashT = NULL; | |
374ca955 A |
82 | |
83 | U_CDECL_BEGIN | |
84 | // | |
85 | // Deleter function for Locales owned by the default Locale hash table/ | |
86 | // | |
87 | static void U_CALLCONV | |
88 | deleteLocale(void *obj) { | |
46f4442e | 89 | delete (U_NAMESPACE_QUALIFIER Locale *) obj; |
374ca955 | 90 | } |
b75a7d8f | 91 | |
374ca955 | 92 | static UBool U_CALLCONV locale_cleanup(void) |
b75a7d8f A |
93 | { |
94 | U_NAMESPACE_USE | |
95 | ||
96 | if (availableLocaleList) { | |
97 | delete []availableLocaleList; | |
98 | availableLocaleList = NULL; | |
99 | } | |
100 | availableLocaleListCount = 0; | |
101 | ||
102 | if (gLocaleCache) { | |
103 | delete [] gLocaleCache; | |
104 | gLocaleCache = NULL; | |
105 | } | |
374ca955 A |
106 | |
107 | if (gDefaultLocalesHashT) { | |
108 | uhash_close(gDefaultLocalesHashT); // Automatically deletes all elements, using deleter func. | |
109 | gDefaultLocalesHashT = NULL; | |
b75a7d8f | 110 | } |
46f4442e A |
111 | else if (gDefaultLocale) { |
112 | // The cache wasn't created, and only one default locale was created. | |
113 | delete gDefaultLocale; | |
114 | } | |
374ca955 A |
115 | gDefaultLocale = NULL; |
116 | ||
b75a7d8f A |
117 | return TRUE; |
118 | } | |
374ca955 | 119 | U_CDECL_END |
b75a7d8f A |
120 | |
121 | U_NAMESPACE_BEGIN | |
374ca955 A |
122 | // |
123 | // locale_set_default_internal. | |
124 | // | |
b75a7d8f A |
125 | void locale_set_default_internal(const char *id) |
126 | { | |
374ca955 A |
127 | UErrorCode status = U_ZERO_ERROR; |
128 | UBool canonicalize = FALSE; | |
129 | ||
130 | // If given a NULL string for the locale id, grab the default | |
131 | // name from the system. | |
132 | // (Different from most other locale APIs, where a null name means use | |
133 | // the current ICU default locale.) | |
134 | if (id == NULL) { | |
b75a7d8f A |
135 | umtx_lock(NULL); |
136 | id = uprv_getDefaultLocaleID(); | |
137 | umtx_unlock(NULL); | |
374ca955 A |
138 | canonicalize = TRUE; // always canonicalize host ID |
139 | } | |
140 | ||
141 | // put the locale id into a canonical form, | |
142 | // in preparation for looking up this locale in the hash table of | |
143 | // already-created locale objects. | |
144 | // | |
145 | status = U_ZERO_ERROR; | |
146 | char localeNameBuf[512]; | |
147 | ||
148 | if (canonicalize) { | |
149 | uloc_canonicalize(id, localeNameBuf, sizeof(localeNameBuf)-1, &status); | |
150 | } else { | |
151 | uloc_getName(id, localeNameBuf, sizeof(localeNameBuf)-1, &status); | |
b75a7d8f | 152 | } |
374ca955 A |
153 | localeNameBuf[sizeof(localeNameBuf)-1] = 0; // Force null termination in event of |
154 | // a long name filling the buffer. | |
155 | // (long names are truncated.) | |
b75a7d8f | 156 | |
374ca955 | 157 | // Lazy creation of the hash table itself, if needed. |
46f4442e A |
158 | UBool isOnlyLocale; |
159 | UMTX_CHECK(NULL, (gDefaultLocale == NULL), isOnlyLocale); | |
160 | if (isOnlyLocale) { | |
161 | // We haven't seen this locale id before. | |
162 | // Create a new Locale object for it. | |
163 | Locale *newFirstDefault = new Locale(Locale::eBOGUS); | |
164 | if (newFirstDefault == NULL) { | |
165 | // No way to report errors from here. | |
166 | return; | |
167 | } | |
168 | newFirstDefault->init(localeNameBuf, FALSE); | |
169 | umtx_lock(NULL); | |
170 | if (gDefaultLocale == NULL) { | |
171 | gDefaultLocale = newFirstDefault; // Assignment to gDefaultLocale must happen inside mutex | |
172 | newFirstDefault = NULL; | |
173 | ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); | |
174 | } | |
175 | // Else some other thread raced us through here, and set the new Locale. | |
176 | // Use the hash table next. | |
177 | umtx_unlock(NULL); | |
178 | if (newFirstDefault == NULL) { | |
179 | // We were successful in setting the locale, and we were the first one to set it. | |
180 | return; | |
181 | } | |
182 | // else start using the hash table. | |
183 | } | |
184 | ||
185 | // Lazy creation of the hash table itself, if needed. | |
186 | UBool hashTableNeedsInit; | |
187 | UMTX_CHECK(NULL, (gDefaultLocalesHashT == NULL), hashTableNeedsInit); | |
374ca955 A |
188 | if (hashTableNeedsInit) { |
189 | status = U_ZERO_ERROR; | |
73c04bcf | 190 | UHashtable *tHashTable = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); |
374ca955 A |
191 | if (U_FAILURE(status)) { |
192 | return; | |
193 | } | |
194 | uhash_setValueDeleter(tHashTable, deleteLocale); | |
195 | umtx_lock(NULL); | |
196 | if (gDefaultLocalesHashT == NULL) { | |
197 | gDefaultLocalesHashT = tHashTable; | |
198 | ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); | |
374ca955 | 199 | } else { |
374ca955 | 200 | uhash_close(tHashTable); |
46f4442e | 201 | hashTableNeedsInit = FALSE; |
374ca955 | 202 | } |
46f4442e | 203 | umtx_unlock(NULL); |
374ca955 A |
204 | } |
205 | ||
206 | // Hash table lookup, key is the locale full name | |
207 | umtx_lock(NULL); | |
208 | Locale *newDefault = (Locale *)uhash_get(gDefaultLocalesHashT, localeNameBuf); | |
209 | if (newDefault != NULL) { | |
210 | // We have the requested locale in the hash table already. | |
211 | // Just set it as default. Inside the mutex lock, for those troublesome processors. | |
212 | gDefaultLocale = newDefault; | |
213 | umtx_unlock(NULL); | |
214 | } else { | |
215 | umtx_unlock(NULL); | |
216 | // We haven't seen this locale id before. | |
217 | // Create a new Locale object for it. | |
218 | newDefault = new Locale(Locale::eBOGUS); | |
219 | if (newDefault == NULL) { | |
220 | // No way to report errors from here. | |
221 | return; | |
222 | } | |
223 | newDefault->init(localeNameBuf, FALSE); | |
224 | ||
225 | // Add newly created Locale to the hash table of default Locales | |
226 | const char *key = newDefault->getName(); | |
227 | U_ASSERT(uprv_strcmp(key, localeNameBuf) == 0); | |
228 | umtx_lock(NULL); | |
46f4442e | 229 | Locale *hashTableVal = (Locale *)uhash_get(gDefaultLocalesHashT, key); |
374ca955 | 230 | if (hashTableVal == NULL) { |
46f4442e A |
231 | if (hashTableNeedsInit) { |
232 | // This is the second request to set the locale. | |
233 | // Cache the first one. | |
234 | uhash_put(gDefaultLocalesHashT, (void *)gDefaultLocale->getName(), gDefaultLocale, &status); | |
235 | } | |
374ca955 A |
236 | uhash_put(gDefaultLocalesHashT, (void *)key, newDefault, &status); |
237 | gDefaultLocale = newDefault; | |
374ca955 A |
238 | // ignore errors from hash table insert. (Couldn't do anything anyway) |
239 | // We can still set the default Locale, | |
240 | // it just wont be cached, and will eventually leak. | |
241 | } else { | |
242 | // Some other thread raced us through here, and got the new Locale | |
243 | // into the hash table before us. Use that one. | |
244 | gDefaultLocale = hashTableVal; // Assignment to gDefaultLocale must happen inside mutex | |
374ca955 A |
245 | delete newDefault; |
246 | } | |
46f4442e | 247 | umtx_unlock(NULL); |
374ca955 | 248 | } |
b75a7d8f A |
249 | } |
250 | U_NAMESPACE_END | |
251 | ||
252 | /* sfb 07/21/99 */ | |
253 | U_CFUNC void | |
254 | locale_set_default(const char *id) | |
255 | { | |
256 | U_NAMESPACE_USE | |
257 | locale_set_default_internal(id); | |
258 | } | |
259 | /* end */ | |
260 | ||
261 | U_CFUNC const char * | |
262 | locale_get_default(void) | |
263 | { | |
264 | U_NAMESPACE_USE | |
265 | ||
266 | return Locale::getDefault().getName(); | |
267 | } | |
268 | ||
269 | ||
270 | U_NAMESPACE_BEGIN | |
271 | ||
46f4442e A |
272 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Locale) |
273 | ||
b75a7d8f A |
274 | /*Character separating the posix id fields*/ |
275 | // '_' | |
276 | // In the platform codepage. | |
277 | #define SEP_CHAR '_' | |
278 | ||
279 | Locale::~Locale() | |
374ca955 | 280 | { |
b75a7d8f | 281 | /*if fullName is on the heap, we free it*/ |
374ca955 | 282 | if (fullName != fullNameBuffer) |
b75a7d8f A |
283 | { |
284 | uprv_free(fullName); | |
285 | fullName = NULL; | |
286 | } | |
374ca955 A |
287 | if (baseName && baseName != baseNameBuffer) { |
288 | uprv_free(baseName); | |
289 | baseName = NULL; | |
290 | } | |
b75a7d8f A |
291 | } |
292 | ||
293 | Locale::Locale() | |
374ca955 | 294 | : UObject(), fullName(fullNameBuffer), baseName(NULL) |
b75a7d8f | 295 | { |
374ca955 | 296 | init(NULL, FALSE); |
b75a7d8f A |
297 | } |
298 | ||
374ca955 A |
299 | /* |
300 | * Internal constructor to allow construction of a locale object with | |
301 | * NO side effects. (Default constructor tries to get | |
302 | * the default locale.) | |
303 | */ | |
304 | Locale::Locale(Locale::ELocaleType) | |
305 | : UObject(), fullName(fullNameBuffer), baseName(NULL) | |
b75a7d8f A |
306 | { |
307 | setToBogus(); | |
308 | } | |
309 | ||
310 | ||
374ca955 A |
311 | Locale::Locale( const char * newLanguage, |
312 | const char * newCountry, | |
313 | const char * newVariant, | |
314 | const char * newKeywords) | |
315 | : UObject(), fullName(fullNameBuffer), baseName(NULL) | |
b75a7d8f A |
316 | { |
317 | if( (newLanguage==NULL) && (newCountry == NULL) && (newVariant == NULL) ) | |
318 | { | |
374ca955 | 319 | init(NULL, FALSE); /* shortcut */ |
b75a7d8f A |
320 | } |
321 | else | |
322 | { | |
323 | char togo_stack[ULOC_FULLNAME_CAPACITY]; | |
324 | char *togo; | |
325 | char *togo_heap = NULL; | |
326 | int32_t size = 0; | |
327 | int32_t lsize = 0; | |
328 | int32_t csize = 0; | |
329 | int32_t vsize = 0; | |
374ca955 | 330 | int32_t ksize = 0; |
b75a7d8f A |
331 | char *p; |
332 | ||
333 | // Calculate the size of the resulting string. | |
334 | ||
335 | // Language | |
336 | if ( newLanguage != NULL ) | |
337 | { | |
338 | lsize = (int32_t)uprv_strlen(newLanguage); | |
339 | size = lsize; | |
340 | } | |
341 | ||
342 | // _Country | |
343 | if ( newCountry != NULL ) | |
344 | { | |
345 | csize = (int32_t)uprv_strlen(newCountry); | |
346 | size += csize; | |
347 | } | |
348 | ||
349 | // _Variant | |
350 | if ( newVariant != NULL ) | |
351 | { | |
352 | // remove leading _'s | |
353 | while(newVariant[0] == SEP_CHAR) | |
354 | { | |
355 | newVariant++; | |
356 | } | |
374ca955 | 357 | |
b75a7d8f A |
358 | // remove trailing _'s |
359 | vsize = (int32_t)uprv_strlen(newVariant); | |
360 | while( (vsize>1) && (newVariant[vsize-1] == SEP_CHAR) ) | |
361 | { | |
362 | vsize--; | |
363 | } | |
364 | } | |
365 | ||
366 | if( vsize > 0 ) | |
367 | { | |
368 | size += vsize; | |
369 | } | |
370 | ||
371 | // Separator rules: | |
372 | if ( vsize > 0 ) | |
373 | { | |
374ca955 | 374 | size += 2; // at least: __v |
b75a7d8f A |
375 | } |
376 | else if ( csize > 0 ) | |
377 | { | |
374ca955 A |
378 | size += 1; // at least: _v |
379 | } | |
380 | ||
381 | if ( newKeywords != NULL) | |
382 | { | |
383 | ksize = (int32_t)uprv_strlen(newKeywords); | |
384 | size += ksize + 1; | |
b75a7d8f A |
385 | } |
386 | ||
374ca955 | 387 | |
b75a7d8f A |
388 | // NOW we have the full locale string.. |
389 | ||
390 | /*if the whole string is longer than our internal limit, we need | |
391 | to go to the heap for temporary buffers*/ | |
73c04bcf | 392 | if (size >= ULOC_FULLNAME_CAPACITY) |
b75a7d8f A |
393 | { |
394 | togo_heap = (char *)uprv_malloc(sizeof(char)*(size+1)); | |
46f4442e A |
395 | // If togo_heap could not be created, initialize with default settings. |
396 | if (togo_heap == NULL) { | |
397 | init(NULL, FALSE); | |
398 | } | |
b75a7d8f A |
399 | togo = togo_heap; |
400 | } | |
401 | else | |
402 | { | |
403 | togo = togo_stack; | |
404 | } | |
405 | ||
406 | togo[0] = 0; | |
407 | ||
408 | // Now, copy it back. | |
409 | p = togo; | |
410 | if ( lsize != 0 ) | |
411 | { | |
412 | uprv_strcpy(p, newLanguage); | |
413 | p += lsize; | |
414 | } | |
415 | ||
416 | if ( ( vsize != 0 ) || (csize != 0) ) // at least: __v | |
417 | { // ^ | |
418 | *p++ = SEP_CHAR; | |
419 | } | |
420 | ||
421 | if ( csize != 0 ) | |
374ca955 | 422 | { |
b75a7d8f A |
423 | uprv_strcpy(p, newCountry); |
424 | p += csize; | |
425 | } | |
426 | ||
427 | if ( vsize != 0) | |
428 | { | |
429 | *p++ = SEP_CHAR; // at least: __v | |
430 | ||
374ca955 | 431 | uprv_strncpy(p, newVariant, vsize); // Must use strncpy because |
b75a7d8f A |
432 | p += vsize; // of trimming (above). |
433 | *p = 0; // terminate | |
434 | } | |
435 | ||
374ca955 A |
436 | if ( ksize != 0) |
437 | { | |
438 | if (uprv_strchr(newKeywords, '=')) { | |
439 | *p++ = '@'; /* keyword parsing */ | |
440 | } | |
441 | else { | |
442 | *p++ = '_'; /* Variant parsing with a script */ | |
443 | if ( vsize == 0) { | |
444 | *p++ = '_'; /* No country found */ | |
445 | } | |
446 | } | |
447 | uprv_strcpy(p, newKeywords); | |
448 | p += ksize; | |
449 | } | |
450 | ||
b75a7d8f A |
451 | // Parse it, because for example 'language' might really be a complete |
452 | // string. | |
374ca955 | 453 | init(togo, FALSE); |
b75a7d8f A |
454 | |
455 | if (togo_heap) { | |
456 | uprv_free(togo_heap); | |
457 | } | |
458 | } | |
459 | } | |
460 | ||
461 | Locale::Locale(const Locale &other) | |
374ca955 | 462 | : UObject(other), fullName(fullNameBuffer), baseName(NULL) |
b75a7d8f A |
463 | { |
464 | *this = other; | |
465 | } | |
466 | ||
467 | Locale &Locale::operator=(const Locale &other) | |
468 | { | |
469 | if (this == &other) { | |
470 | return *this; | |
471 | } | |
472 | ||
473 | if (&other == NULL) { | |
474 | this->setToBogus(); | |
475 | return *this; | |
476 | } | |
477 | ||
478 | /* Free our current storage */ | |
479 | if(fullName != fullNameBuffer) { | |
480 | uprv_free(fullName); | |
481 | fullName = fullNameBuffer; | |
482 | } | |
483 | ||
484 | /* Allocate the full name if necessary */ | |
485 | if(other.fullName != other.fullNameBuffer) { | |
486 | fullName = (char *)uprv_malloc(sizeof(char)*(uprv_strlen(other.fullName)+1)); | |
46f4442e A |
487 | if (fullName == NULL) { |
488 | return *this; | |
489 | } | |
b75a7d8f | 490 | } |
b75a7d8f A |
491 | /* Copy the full name */ |
492 | uprv_strcpy(fullName, other.fullName); | |
493 | ||
374ca955 A |
494 | /* baseName is the cached result of getBaseName. if 'other' has a |
495 | baseName and it fits in baseNameBuffer, then copy it. otherwise set | |
496 | it to NULL, and let the user lazy-create it (in getBaseName) if they | |
497 | want it. */ | |
498 | if(baseName && baseName != baseNameBuffer) { | |
499 | uprv_free(baseName); | |
500 | } | |
501 | baseName = NULL; | |
502 | ||
503 | if(other.baseName == other.baseNameBuffer) { | |
504 | uprv_strcpy(baseNameBuffer, other.baseNameBuffer); | |
505 | baseName = baseNameBuffer; | |
506 | } | |
507 | ||
b75a7d8f A |
508 | /* Copy the language and country fields */ |
509 | uprv_strcpy(language, other.language); | |
374ca955 | 510 | uprv_strcpy(script, other.script); |
b75a7d8f A |
511 | uprv_strcpy(country, other.country); |
512 | ||
513 | /* The variantBegin is an offset into fullName, just copy it */ | |
514 | variantBegin = other.variantBegin; | |
515 | fIsBogus = other.fIsBogus; | |
516 | return *this; | |
517 | } | |
518 | ||
374ca955 A |
519 | Locale * |
520 | Locale::clone() const { | |
521 | return new Locale(*this); | |
522 | } | |
523 | ||
b75a7d8f A |
524 | UBool |
525 | Locale::operator==( const Locale& other) const | |
526 | { | |
527 | return (uprv_strcmp(other.fullName, fullName) == 0); | |
528 | } | |
529 | ||
530 | /*This function initializes a Locale from a C locale ID*/ | |
374ca955 | 531 | Locale& Locale::init(const char* localeID, UBool canonicalize) |
b75a7d8f A |
532 | { |
533 | fIsBogus = FALSE; | |
534 | /* Free our current storage */ | |
535 | if(fullName != fullNameBuffer) { | |
536 | uprv_free(fullName); | |
537 | fullName = fullNameBuffer; | |
538 | } | |
539 | ||
374ca955 A |
540 | if(baseName && baseName != baseNameBuffer) { |
541 | uprv_free(baseName); | |
542 | baseName = NULL; | |
543 | } | |
544 | ||
b75a7d8f A |
545 | // not a loop: |
546 | // just an easy way to have a common error-exit | |
547 | // without goto and without another function | |
548 | do { | |
374ca955 A |
549 | char *separator; |
550 | char *field[5] = {0}; | |
551 | int32_t fieldLen[5] = {0}; | |
552 | int32_t fieldIdx; | |
553 | int32_t variantField; | |
b75a7d8f A |
554 | int32_t length; |
555 | UErrorCode err; | |
556 | ||
557 | if(localeID == NULL) { | |
558 | // not an error, just set the default locale | |
559 | return *this = getDefault(); | |
560 | } | |
561 | ||
374ca955 A |
562 | /* preset all fields to empty */ |
563 | language[0] = script[0] = country[0] = 0; | |
564 | ||
b75a7d8f A |
565 | // "canonicalize" the locale ID to ICU/Java format |
566 | err = U_ZERO_ERROR; | |
374ca955 A |
567 | length = canonicalize ? |
568 | uloc_canonicalize(localeID, fullName, sizeof(fullNameBuffer), &err) : | |
569 | uloc_getName(localeID, fullName, sizeof(fullNameBuffer), &err); | |
570 | ||
571 | if(err == U_BUFFER_OVERFLOW_ERROR || length >= (int32_t)sizeof(fullNameBuffer)) { | |
b75a7d8f A |
572 | /*Go to heap for the fullName if necessary*/ |
573 | fullName = (char *)uprv_malloc(sizeof(char)*(length + 1)); | |
574 | if(fullName == 0) { | |
575 | fullName = fullNameBuffer; | |
576 | break; // error: out of memory | |
577 | } | |
578 | err = U_ZERO_ERROR; | |
374ca955 A |
579 | length = canonicalize ? |
580 | uloc_canonicalize(localeID, fullName, length+1, &err) : | |
581 | uloc_getName(localeID, fullName, length+1, &err); | |
b75a7d8f A |
582 | } |
583 | if(U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) { | |
584 | /* should never occur */ | |
585 | break; | |
586 | } | |
587 | ||
374ca955 | 588 | variantBegin = length; |
b75a7d8f | 589 | |
374ca955 A |
590 | /* after uloc_getName/canonicalize() we know that only '_' are separators */ |
591 | separator = field[0] = fullName; | |
592 | fieldIdx = 1; | |
593 | while ((separator = uprv_strchr(field[fieldIdx-1], SEP_CHAR)) && fieldIdx < (int32_t)(sizeof(field)/sizeof(field[0]))-1) { | |
594 | field[fieldIdx] = separator + 1; | |
73c04bcf | 595 | fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]); |
374ca955 A |
596 | fieldIdx++; |
597 | } | |
598 | // variant may contain @foo or .foo POSIX cruft; remove it | |
599 | separator = uprv_strchr(field[fieldIdx-1], '@'); | |
600 | char* sep2 = uprv_strchr(field[fieldIdx-1], '.'); | |
601 | if (separator!=NULL || sep2!=NULL) { | |
602 | if (separator==NULL || (sep2!=NULL && separator > sep2)) { | |
603 | separator = sep2; | |
b75a7d8f | 604 | } |
73c04bcf | 605 | fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]); |
b75a7d8f | 606 | } else { |
374ca955 A |
607 | fieldLen[fieldIdx-1] = length - (int32_t)(field[fieldIdx-1] - fullName); |
608 | } | |
609 | ||
610 | if (fieldLen[0] >= (int32_t)(sizeof(language)) | |
611 | || (fieldLen[1] == 4 && fieldLen[2] >= (int32_t)(sizeof(country))) | |
612 | || (fieldLen[1] != 4 && fieldLen[1] >= (int32_t)(sizeof(country)))) | |
613 | { | |
614 | break; // error: one of the fields is too long | |
615 | } | |
616 | ||
617 | variantField = 2; /* Usually the 2nd one, except when a script is used. */ | |
618 | if (fieldLen[0] > 0) { | |
619 | /* We have a language */ | |
620 | uprv_memcpy(language, fullName, fieldLen[0]); | |
621 | language[fieldLen[0]] = 0; | |
622 | } | |
623 | if (fieldLen[1] == 4) { | |
624 | /* We have at least a script */ | |
625 | uprv_memcpy(script, field[1], fieldLen[1]); | |
626 | script[fieldLen[1]] = 0; | |
627 | variantField = 3; | |
628 | if (fieldLen[2] > 0) { | |
629 | /* We have a country */ | |
630 | uprv_memcpy(country, field[2], fieldLen[2]); | |
631 | country[fieldLen[2]] = 0; | |
b75a7d8f | 632 | } |
374ca955 A |
633 | } |
634 | else if (fieldLen[1] > 0) { | |
635 | /* We have a country and no script */ | |
636 | uprv_memcpy(country, field[1], fieldLen[1]); | |
637 | country[fieldLen[1]] = 0; | |
638 | } | |
639 | if (variantField > 0 && fieldLen[variantField] > 0) { | |
640 | /* We have a variant */ | |
641 | variantBegin = (int32_t)(field[variantField] - fullName); | |
b75a7d8f A |
642 | } |
643 | ||
644 | // successful end of init() | |
645 | return *this; | |
73c04bcf | 646 | } while(0); /*loop doesn't iterate*/ |
b75a7d8f A |
647 | |
648 | // when an error occurs, then set this object to "bogus" (there is no UErrorCode here) | |
649 | setToBogus(); | |
650 | ||
651 | return *this; | |
652 | } | |
653 | ||
654 | int32_t | |
374ca955 | 655 | Locale::hashCode() const |
b75a7d8f A |
656 | { |
657 | UHashTok hashKey; | |
658 | hashKey.pointer = fullName; | |
659 | return uhash_hashChars(hashKey); | |
660 | } | |
661 | ||
374ca955 | 662 | void |
b75a7d8f | 663 | Locale::setToBogus() { |
374ca955 A |
664 | /* Free our current storage */ |
665 | if(fullName != fullNameBuffer) { | |
666 | uprv_free(fullName); | |
667 | fullName = fullNameBuffer; | |
668 | } | |
669 | *fullNameBuffer = 0; | |
670 | *language = 0; | |
671 | *script = 0; | |
672 | *country = 0; | |
673 | fIsBogus = TRUE; | |
b75a7d8f A |
674 | } |
675 | ||
374ca955 A |
676 | const Locale& U_EXPORT2 |
677 | Locale::getDefault() | |
b75a7d8f | 678 | { |
374ca955 | 679 | const Locale *retLocale; |
46f4442e | 680 | UMTX_CHECK(NULL, gDefaultLocale, retLocale); |
374ca955 A |
681 | if (retLocale == NULL) { |
682 | locale_set_default_internal(NULL); | |
683 | umtx_lock(NULL); | |
684 | // Need a mutex in case some other thread set a new | |
685 | // default inbetween when we set and when we get the new default. For | |
686 | // processors with weak memory coherency, we might not otherwise see all | |
687 | // of the newly created new default locale. | |
688 | retLocale = gDefaultLocale; | |
689 | umtx_unlock(NULL); | |
b75a7d8f | 690 | } |
374ca955 | 691 | return *retLocale; |
b75a7d8f A |
692 | } |
693 | ||
374ca955 A |
694 | |
695 | ||
696 | void U_EXPORT2 | |
697 | Locale::setDefault( const Locale& newLocale, | |
698 | UErrorCode& status) | |
b75a7d8f | 699 | { |
374ca955 | 700 | if (U_FAILURE(status)) { |
b75a7d8f | 701 | return; |
374ca955 A |
702 | } |
703 | ||
704 | /* Set the default from the full name string of the supplied locale. | |
705 | * This is a convenient way to access the default locale caching mechanisms. | |
706 | */ | |
707 | const char *localeID = newLocale.getName(); | |
708 | locale_set_default_internal(localeID); | |
b75a7d8f A |
709 | } |
710 | ||
374ca955 | 711 | Locale U_EXPORT2 |
b75a7d8f A |
712 | Locale::createFromName (const char *name) |
713 | { | |
714 | if (name) { | |
374ca955 A |
715 | Locale l(""); |
716 | l.init(name, FALSE); | |
b75a7d8f A |
717 | return l; |
718 | } | |
719 | else { | |
720 | return getDefault(); | |
721 | } | |
722 | } | |
723 | ||
374ca955 A |
724 | Locale U_EXPORT2 |
725 | Locale::createCanonical(const char* name) { | |
726 | Locale loc(""); | |
727 | loc.init(name, TRUE); | |
728 | return loc; | |
729 | } | |
b75a7d8f A |
730 | |
731 | const char * | |
732 | Locale::getISO3Language() const | |
733 | { | |
734 | return uloc_getISO3Language(fullName); | |
735 | } | |
736 | ||
737 | ||
738 | const char * | |
739 | Locale::getISO3Country() const | |
740 | { | |
741 | return uloc_getISO3Country(fullName); | |
742 | } | |
743 | ||
744 | /** | |
745 | * Return the LCID value as specified in the "LocaleID" resource for this | |
746 | * locale. The LocaleID must be expressed as a hexadecimal number, from | |
747 | * one to four digits. If the LocaleID resource is not present, or is | |
748 | * in an incorrect format, 0 is returned. The LocaleID is for use in | |
749 | * Windows (it is an LCID), but is available on all platforms. | |
750 | */ | |
374ca955 | 751 | uint32_t |
b75a7d8f A |
752 | Locale::getLCID() const |
753 | { | |
754 | return uloc_getLCID(fullName); | |
755 | } | |
756 | ||
374ca955 | 757 | UnicodeString& |
b75a7d8f A |
758 | Locale::getDisplayLanguage(UnicodeString& dispLang) const |
759 | { | |
760 | return this->getDisplayLanguage(getDefault(), dispLang); | |
761 | } | |
762 | ||
763 | /*We cannot make any assumptions on the size of the output display strings | |
764 | * Yet, since we are calling through to a C API, we need to set limits on | |
765 | * buffer size. For all the following getDisplay functions we first attempt | |
766 | * to fill up a stack allocated buffer. If it is to small we heap allocated | |
767 | * the exact buffer we need copy it to the UnicodeString and delete it*/ | |
768 | ||
769 | UnicodeString& | |
770 | Locale::getDisplayLanguage(const Locale &displayLocale, | |
771 | UnicodeString &result) const { | |
772 | UChar *buffer; | |
773 | UErrorCode errorCode=U_ZERO_ERROR; | |
774 | int32_t length; | |
775 | ||
776 | buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); | |
777 | if(buffer==0) { | |
778 | result.truncate(0); | |
779 | return result; | |
780 | } | |
781 | ||
782 | length=uloc_getDisplayLanguage(fullName, displayLocale.fullName, | |
783 | buffer, result.getCapacity(), | |
784 | &errorCode); | |
73c04bcf | 785 | result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
b75a7d8f A |
786 | |
787 | if(errorCode==U_BUFFER_OVERFLOW_ERROR) { | |
788 | buffer=result.getBuffer(length); | |
789 | if(buffer==0) { | |
790 | result.truncate(0); | |
791 | return result; | |
792 | } | |
793 | errorCode=U_ZERO_ERROR; | |
794 | length=uloc_getDisplayLanguage(fullName, displayLocale.fullName, | |
795 | buffer, result.getCapacity(), | |
796 | &errorCode); | |
73c04bcf | 797 | result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
b75a7d8f A |
798 | } |
799 | ||
800 | return result; | |
801 | } | |
802 | ||
374ca955 A |
803 | UnicodeString& |
804 | Locale::getDisplayScript(UnicodeString& dispScript) const | |
805 | { | |
806 | return this->getDisplayScript(getDefault(), dispScript); | |
807 | } | |
808 | ||
809 | UnicodeString& | |
810 | Locale::getDisplayScript(const Locale &displayLocale, | |
811 | UnicodeString &result) const { | |
812 | UChar *buffer; | |
813 | UErrorCode errorCode=U_ZERO_ERROR; | |
814 | int32_t length; | |
815 | ||
816 | buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); | |
817 | if(buffer==0) { | |
818 | result.truncate(0); | |
819 | return result; | |
820 | } | |
821 | ||
822 | length=uloc_getDisplayScript(fullName, displayLocale.fullName, | |
823 | buffer, result.getCapacity(), | |
824 | &errorCode); | |
73c04bcf | 825 | result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
374ca955 A |
826 | |
827 | if(errorCode==U_BUFFER_OVERFLOW_ERROR) { | |
828 | buffer=result.getBuffer(length); | |
829 | if(buffer==0) { | |
830 | result.truncate(0); | |
831 | return result; | |
832 | } | |
833 | errorCode=U_ZERO_ERROR; | |
834 | length=uloc_getDisplayScript(fullName, displayLocale.fullName, | |
835 | buffer, result.getCapacity(), | |
836 | &errorCode); | |
73c04bcf | 837 | result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
374ca955 A |
838 | } |
839 | ||
840 | return result; | |
841 | } | |
842 | ||
843 | UnicodeString& | |
b75a7d8f A |
844 | Locale::getDisplayCountry(UnicodeString& dispCntry) const |
845 | { | |
846 | return this->getDisplayCountry(getDefault(), dispCntry); | |
847 | } | |
848 | ||
849 | UnicodeString& | |
850 | Locale::getDisplayCountry(const Locale &displayLocale, | |
851 | UnicodeString &result) const { | |
852 | UChar *buffer; | |
853 | UErrorCode errorCode=U_ZERO_ERROR; | |
854 | int32_t length; | |
855 | ||
856 | buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); | |
857 | if(buffer==0) { | |
858 | result.truncate(0); | |
859 | return result; | |
860 | } | |
861 | ||
862 | length=uloc_getDisplayCountry(fullName, displayLocale.fullName, | |
863 | buffer, result.getCapacity(), | |
864 | &errorCode); | |
73c04bcf | 865 | result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
b75a7d8f A |
866 | |
867 | if(errorCode==U_BUFFER_OVERFLOW_ERROR) { | |
868 | buffer=result.getBuffer(length); | |
869 | if(buffer==0) { | |
870 | result.truncate(0); | |
871 | return result; | |
872 | } | |
873 | errorCode=U_ZERO_ERROR; | |
874 | length=uloc_getDisplayCountry(fullName, displayLocale.fullName, | |
875 | buffer, result.getCapacity(), | |
876 | &errorCode); | |
73c04bcf | 877 | result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
b75a7d8f A |
878 | } |
879 | ||
880 | return result; | |
881 | } | |
882 | ||
374ca955 | 883 | UnicodeString& |
b75a7d8f A |
884 | Locale::getDisplayVariant(UnicodeString& dispVar) const |
885 | { | |
886 | return this->getDisplayVariant(getDefault(), dispVar); | |
887 | } | |
888 | ||
889 | UnicodeString& | |
890 | Locale::getDisplayVariant(const Locale &displayLocale, | |
891 | UnicodeString &result) const { | |
892 | UChar *buffer; | |
893 | UErrorCode errorCode=U_ZERO_ERROR; | |
894 | int32_t length; | |
895 | ||
896 | buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); | |
897 | if(buffer==0) { | |
898 | result.truncate(0); | |
899 | return result; | |
900 | } | |
901 | ||
902 | length=uloc_getDisplayVariant(fullName, displayLocale.fullName, | |
903 | buffer, result.getCapacity(), | |
904 | &errorCode); | |
73c04bcf | 905 | result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
b75a7d8f A |
906 | |
907 | if(errorCode==U_BUFFER_OVERFLOW_ERROR) { | |
908 | buffer=result.getBuffer(length); | |
909 | if(buffer==0) { | |
910 | result.truncate(0); | |
911 | return result; | |
912 | } | |
913 | errorCode=U_ZERO_ERROR; | |
914 | length=uloc_getDisplayVariant(fullName, displayLocale.fullName, | |
915 | buffer, result.getCapacity(), | |
916 | &errorCode); | |
73c04bcf | 917 | result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
b75a7d8f A |
918 | } |
919 | ||
920 | return result; | |
921 | } | |
922 | ||
374ca955 | 923 | UnicodeString& |
b75a7d8f A |
924 | Locale::getDisplayName( UnicodeString& name ) const |
925 | { | |
926 | return this->getDisplayName(getDefault(), name); | |
927 | } | |
928 | ||
929 | UnicodeString& | |
930 | Locale::getDisplayName(const Locale &displayLocale, | |
931 | UnicodeString &result) const { | |
932 | UChar *buffer; | |
933 | UErrorCode errorCode=U_ZERO_ERROR; | |
934 | int32_t length; | |
935 | ||
936 | buffer=result.getBuffer(ULOC_FULLNAME_CAPACITY); | |
937 | if(buffer==0) { | |
938 | result.truncate(0); | |
939 | return result; | |
940 | } | |
941 | ||
942 | length=uloc_getDisplayName(fullName, displayLocale.fullName, | |
943 | buffer, result.getCapacity(), | |
944 | &errorCode); | |
73c04bcf | 945 | result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
b75a7d8f A |
946 | |
947 | if(errorCode==U_BUFFER_OVERFLOW_ERROR) { | |
948 | buffer=result.getBuffer(length); | |
949 | if(buffer==0) { | |
950 | result.truncate(0); | |
951 | return result; | |
952 | } | |
953 | errorCode=U_ZERO_ERROR; | |
954 | length=uloc_getDisplayName(fullName, displayLocale.fullName, | |
955 | buffer, result.getCapacity(), | |
956 | &errorCode); | |
73c04bcf | 957 | result.releaseBuffer(U_SUCCESS(errorCode) ? length : 0); |
b75a7d8f A |
958 | } |
959 | ||
960 | return result; | |
961 | } | |
374ca955 A |
962 | const Locale* U_EXPORT2 |
963 | Locale::getAvailableLocales(int32_t& count) | |
b75a7d8f A |
964 | { |
965 | // for now, there is a hardcoded list, so just walk through that list and set it up. | |
46f4442e A |
966 | UBool needInit; |
967 | UMTX_CHECK(NULL, availableLocaleList == NULL, needInit); | |
374ca955 | 968 | |
b75a7d8f A |
969 | if (needInit) { |
970 | int32_t locCount = uloc_countAvailable(); | |
971 | Locale *newLocaleList = 0; | |
972 | if(locCount) { | |
973 | newLocaleList = new Locale[locCount]; | |
974 | } | |
975 | if (newLocaleList == NULL) { | |
73c04bcf | 976 | count = 0; |
b75a7d8f A |
977 | return NULL; |
978 | } | |
374ca955 | 979 | |
b75a7d8f | 980 | count = locCount; |
374ca955 | 981 | |
b75a7d8f A |
982 | while(--locCount >= 0) { |
983 | newLocaleList[locCount].setFromPOSIXID(uloc_getAvailable(locCount)); | |
984 | } | |
374ca955 | 985 | |
b75a7d8f A |
986 | umtx_lock(NULL); |
987 | if(availableLocaleList == 0) { | |
988 | availableLocaleListCount = count; | |
989 | availableLocaleList = newLocaleList; | |
990 | newLocaleList = NULL; | |
374ca955 | 991 | ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); |
b75a7d8f A |
992 | } |
993 | umtx_unlock(NULL); | |
994 | delete []newLocaleList; | |
995 | } | |
996 | count = availableLocaleListCount; | |
997 | return availableLocaleList; | |
998 | } | |
999 | ||
374ca955 | 1000 | const char* const* U_EXPORT2 Locale::getISOCountries() |
b75a7d8f A |
1001 | { |
1002 | return uloc_getISOCountries(); | |
1003 | } | |
1004 | ||
374ca955 | 1005 | const char* const* U_EXPORT2 Locale::getISOLanguages() |
b75a7d8f A |
1006 | { |
1007 | return uloc_getISOLanguages(); | |
1008 | } | |
1009 | ||
374ca955 | 1010 | // Set the locale's data based on a posix id. |
b75a7d8f A |
1011 | void Locale::setFromPOSIXID(const char *posixID) |
1012 | { | |
374ca955 | 1013 | init(posixID, TRUE); |
b75a7d8f A |
1014 | } |
1015 | ||
374ca955 | 1016 | const Locale & U_EXPORT2 |
b75a7d8f A |
1017 | Locale::getEnglish(void) |
1018 | { | |
1019 | return getLocale(eENGLISH); | |
1020 | } | |
1021 | ||
374ca955 | 1022 | const Locale & U_EXPORT2 |
b75a7d8f A |
1023 | Locale::getFrench(void) |
1024 | { | |
1025 | return getLocale(eFRENCH); | |
1026 | } | |
1027 | ||
374ca955 | 1028 | const Locale & U_EXPORT2 |
b75a7d8f A |
1029 | Locale::getGerman(void) |
1030 | { | |
1031 | return getLocale(eGERMAN); | |
1032 | } | |
1033 | ||
374ca955 | 1034 | const Locale & U_EXPORT2 |
b75a7d8f A |
1035 | Locale::getItalian(void) |
1036 | { | |
1037 | return getLocale(eITALIAN); | |
1038 | } | |
1039 | ||
374ca955 | 1040 | const Locale & U_EXPORT2 |
b75a7d8f A |
1041 | Locale::getJapanese(void) |
1042 | { | |
1043 | return getLocale(eJAPANESE); | |
1044 | } | |
1045 | ||
374ca955 | 1046 | const Locale & U_EXPORT2 |
b75a7d8f A |
1047 | Locale::getKorean(void) |
1048 | { | |
1049 | return getLocale(eKOREAN); | |
1050 | } | |
1051 | ||
374ca955 | 1052 | const Locale & U_EXPORT2 |
b75a7d8f A |
1053 | Locale::getChinese(void) |
1054 | { | |
1055 | return getLocale(eCHINESE); | |
1056 | } | |
1057 | ||
374ca955 | 1058 | const Locale & U_EXPORT2 |
b75a7d8f A |
1059 | Locale::getSimplifiedChinese(void) |
1060 | { | |
1061 | return getLocale(eCHINA); | |
1062 | } | |
1063 | ||
374ca955 | 1064 | const Locale & U_EXPORT2 |
b75a7d8f A |
1065 | Locale::getTraditionalChinese(void) |
1066 | { | |
1067 | return getLocale(eTAIWAN); | |
1068 | } | |
1069 | ||
1070 | ||
374ca955 | 1071 | const Locale & U_EXPORT2 |
b75a7d8f A |
1072 | Locale::getFrance(void) |
1073 | { | |
1074 | return getLocale(eFRANCE); | |
1075 | } | |
1076 | ||
374ca955 | 1077 | const Locale & U_EXPORT2 |
b75a7d8f A |
1078 | Locale::getGermany(void) |
1079 | { | |
1080 | return getLocale(eGERMANY); | |
1081 | } | |
1082 | ||
374ca955 | 1083 | const Locale & U_EXPORT2 |
b75a7d8f A |
1084 | Locale::getItaly(void) |
1085 | { | |
1086 | return getLocale(eITALY); | |
1087 | } | |
1088 | ||
374ca955 | 1089 | const Locale & U_EXPORT2 |
b75a7d8f A |
1090 | Locale::getJapan(void) |
1091 | { | |
1092 | return getLocale(eJAPAN); | |
1093 | } | |
1094 | ||
374ca955 | 1095 | const Locale & U_EXPORT2 |
b75a7d8f A |
1096 | Locale::getKorea(void) |
1097 | { | |
1098 | return getLocale(eKOREA); | |
1099 | } | |
1100 | ||
374ca955 | 1101 | const Locale & U_EXPORT2 |
b75a7d8f A |
1102 | Locale::getChina(void) |
1103 | { | |
1104 | return getLocale(eCHINA); | |
1105 | } | |
1106 | ||
374ca955 | 1107 | const Locale & U_EXPORT2 |
b75a7d8f A |
1108 | Locale::getPRC(void) |
1109 | { | |
1110 | return getLocale(eCHINA); | |
1111 | } | |
1112 | ||
374ca955 | 1113 | const Locale & U_EXPORT2 |
b75a7d8f A |
1114 | Locale::getTaiwan(void) |
1115 | { | |
1116 | return getLocale(eTAIWAN); | |
1117 | } | |
1118 | ||
374ca955 | 1119 | const Locale & U_EXPORT2 |
b75a7d8f A |
1120 | Locale::getUK(void) |
1121 | { | |
1122 | return getLocale(eUK); | |
1123 | } | |
1124 | ||
374ca955 | 1125 | const Locale & U_EXPORT2 |
b75a7d8f A |
1126 | Locale::getUS(void) |
1127 | { | |
1128 | return getLocale(eUS); | |
1129 | } | |
1130 | ||
374ca955 | 1131 | const Locale & U_EXPORT2 |
b75a7d8f A |
1132 | Locale::getCanada(void) |
1133 | { | |
1134 | return getLocale(eCANADA); | |
1135 | } | |
1136 | ||
374ca955 | 1137 | const Locale & U_EXPORT2 |
b75a7d8f A |
1138 | Locale::getCanadaFrench(void) |
1139 | { | |
1140 | return getLocale(eCANADA_FRENCH); | |
1141 | } | |
1142 | ||
1143 | const Locale & | |
1144 | Locale::getLocale(int locid) | |
1145 | { | |
1146 | Locale *localeCache = getLocaleCache(); | |
73c04bcf | 1147 | U_ASSERT((locid < eMAX_LOCALES)&&(locid>=0)); |
b75a7d8f A |
1148 | if (localeCache == NULL) { |
1149 | // Failure allocating the locale cache. | |
1150 | // The best we can do is return a NULL reference. | |
1151 | locid = 0; | |
1152 | } | |
73c04bcf | 1153 | return localeCache[locid]; /*operating on NULL*/ |
b75a7d8f A |
1154 | } |
1155 | ||
1156 | /* | |
1157 | This function is defined this way in order to get around static | |
1158 | initialization and static destruction. | |
1159 | */ | |
1160 | Locale * | |
1161 | Locale::getLocaleCache(void) | |
1162 | { | |
1163 | umtx_lock(NULL); | |
1164 | UBool needInit = (gLocaleCache == NULL); | |
1165 | umtx_unlock(NULL); | |
374ca955 | 1166 | |
b75a7d8f | 1167 | if (needInit) { |
374ca955 | 1168 | Locale *tLocaleCache = new Locale[(int)eMAX_LOCALES]; |
b75a7d8f A |
1169 | if (tLocaleCache == NULL) { |
1170 | return NULL; | |
1171 | } | |
1172 | tLocaleCache[eENGLISH] = Locale("en"); | |
1173 | tLocaleCache[eFRENCH] = Locale("fr"); | |
1174 | tLocaleCache[eGERMAN] = Locale("de"); | |
1175 | tLocaleCache[eITALIAN] = Locale("it"); | |
1176 | tLocaleCache[eJAPANESE] = Locale("ja"); | |
1177 | tLocaleCache[eKOREAN] = Locale("ko"); | |
1178 | tLocaleCache[eCHINESE] = Locale("zh"); | |
1179 | tLocaleCache[eFRANCE] = Locale("fr", "FR"); | |
1180 | tLocaleCache[eGERMANY] = Locale("de", "DE"); | |
1181 | tLocaleCache[eITALY] = Locale("it", "IT"); | |
1182 | tLocaleCache[eJAPAN] = Locale("ja", "JP"); | |
1183 | tLocaleCache[eKOREA] = Locale("ko", "KR"); | |
1184 | tLocaleCache[eCHINA] = Locale("zh", "CN"); | |
1185 | tLocaleCache[eTAIWAN] = Locale("zh", "TW"); | |
1186 | tLocaleCache[eUK] = Locale("en", "GB"); | |
1187 | tLocaleCache[eUS] = Locale("en", "US"); | |
1188 | tLocaleCache[eCANADA] = Locale("en", "CA"); | |
1189 | tLocaleCache[eCANADA_FRENCH] = Locale("fr", "CA"); | |
374ca955 | 1190 | |
b75a7d8f A |
1191 | umtx_lock(NULL); |
1192 | if (gLocaleCache == NULL) { | |
1193 | gLocaleCache = tLocaleCache; | |
1194 | tLocaleCache = NULL; | |
374ca955 | 1195 | ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); |
b75a7d8f A |
1196 | } |
1197 | umtx_unlock(NULL); | |
1198 | if (tLocaleCache) { | |
1199 | delete [] tLocaleCache; // Fancy array delete will destruct each member. | |
1200 | } | |
1201 | } | |
1202 | return gLocaleCache; | |
1203 | } | |
1204 | ||
374ca955 A |
1205 | class KeywordEnumeration : public StringEnumeration { |
1206 | private: | |
1207 | char *keywords; | |
1208 | char *current; | |
1209 | int32_t length; | |
1210 | UnicodeString currUSKey; | |
1211 | static const char fgClassID;/* Warning this is used beyond the typical RTTI usage. */ | |
1212 | ||
1213 | public: | |
1214 | static UClassID U_EXPORT2 getStaticClassID(void) { return (UClassID)&fgClassID; } | |
1215 | virtual UClassID getDynamicClassID(void) const { return getStaticClassID(); } | |
1216 | public: | |
1217 | KeywordEnumeration(const char *keys, int32_t keywordLen, int32_t currentIndex, UErrorCode &status) | |
1218 | : keywords((char *)&fgClassID), current((char *)&fgClassID), length(0) { | |
1219 | if(U_SUCCESS(status) && keywordLen != 0) { | |
1220 | if(keys == NULL || keywordLen < 0) { | |
1221 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
1222 | } else { | |
1223 | keywords = (char *)uprv_malloc(keywordLen+1); | |
1224 | if (keywords == NULL) { | |
1225 | status = U_MEMORY_ALLOCATION_ERROR; | |
1226 | } | |
1227 | else { | |
1228 | uprv_memcpy(keywords, keys, keywordLen); | |
1229 | keywords[keywordLen] = 0; | |
1230 | current = keywords + currentIndex; | |
1231 | length = keywordLen; | |
1232 | } | |
1233 | } | |
1234 | } | |
1235 | } | |
1236 | ||
1237 | virtual ~KeywordEnumeration() { | |
1238 | uprv_free(keywords); | |
1239 | } | |
1240 | ||
1241 | virtual StringEnumeration * clone() const | |
1242 | { | |
1243 | UErrorCode status = U_ZERO_ERROR; | |
1244 | return new KeywordEnumeration(keywords, length, (int32_t)(current - keywords), status); | |
1245 | } | |
1246 | ||
1247 | virtual int32_t count(UErrorCode &/*status*/) const { | |
1248 | char *kw = keywords; | |
1249 | int32_t result = 0; | |
1250 | while(*kw) { | |
1251 | result++; | |
1252 | kw += uprv_strlen(kw)+1; | |
1253 | } | |
1254 | return result; | |
1255 | } | |
1256 | ||
1257 | virtual const char* next(int32_t* resultLength, UErrorCode& status) { | |
1258 | const char* result; | |
1259 | int32_t len; | |
1260 | if(U_SUCCESS(status) && *current != 0) { | |
1261 | result = current; | |
73c04bcf | 1262 | len = (int32_t)uprv_strlen(current); |
374ca955 A |
1263 | current += len+1; |
1264 | if(resultLength != NULL) { | |
1265 | *resultLength = len; | |
1266 | } | |
1267 | } else { | |
1268 | if(resultLength != NULL) { | |
1269 | *resultLength = 0; | |
1270 | } | |
1271 | result = NULL; | |
1272 | } | |
1273 | return result; | |
1274 | } | |
1275 | ||
1276 | virtual const UnicodeString* snext(UErrorCode& status) { | |
1277 | int32_t resultLength = 0; | |
1278 | const char *s = next(&resultLength, status); | |
1279 | return setChars(s, resultLength, status); | |
1280 | } | |
1281 | ||
1282 | virtual void reset(UErrorCode& /*status*/) { | |
1283 | current = keywords; | |
1284 | } | |
1285 | }; | |
1286 | ||
1287 | const char KeywordEnumeration::fgClassID = '\0'; | |
1288 | ||
1289 | StringEnumeration * | |
1290 | Locale::createKeywords(UErrorCode &status) const | |
1291 | { | |
1292 | char keywords[256]; | |
1293 | int32_t keywordCapacity = 256; | |
1294 | StringEnumeration *result = NULL; | |
1295 | ||
1296 | const char* variantStart = uprv_strchr(fullName, '@'); | |
1297 | const char* assignment = uprv_strchr(fullName, '='); | |
1298 | if(variantStart) { | |
1299 | if(assignment > variantStart) { | |
1300 | int32_t keyLen = locale_getKeywords(variantStart+1, '@', keywords, keywordCapacity, NULL, 0, NULL, FALSE, &status); | |
1301 | if(keyLen) { | |
1302 | result = new KeywordEnumeration(keywords, keyLen, 0, status); | |
1303 | } | |
1304 | } else { | |
1305 | status = U_INVALID_FORMAT_ERROR; | |
1306 | } | |
1307 | } | |
1308 | return result; | |
1309 | } | |
1310 | ||
1311 | int32_t | |
1312 | Locale::getKeywordValue(const char* keywordName, char *buffer, int32_t bufLen, UErrorCode &status) const | |
1313 | { | |
1314 | return uloc_getKeywordValue(fullName, keywordName, buffer, bufLen, &status); | |
1315 | } | |
1316 | ||
1317 | const char * | |
1318 | Locale::getBaseName() const | |
1319 | { | |
1320 | // lazy init | |
1321 | UErrorCode status = U_ZERO_ERROR; | |
1322 | // semantically const | |
1323 | if(baseName == 0) { | |
1324 | ((Locale *)this)->baseName = ((Locale *)this)->baseNameBuffer; | |
1325 | int32_t baseNameSize = uloc_getBaseName(fullName, baseName, ULOC_FULLNAME_CAPACITY, &status); | |
1326 | if(baseNameSize >= ULOC_FULLNAME_CAPACITY) { | |
1327 | ((Locale *)this)->baseName = (char *)uprv_malloc(sizeof(char) * baseNameSize + 1); | |
46f4442e A |
1328 | if (baseName == NULL) { |
1329 | return baseName; | |
1330 | } | |
374ca955 A |
1331 | uloc_getBaseName(fullName, baseName, baseNameSize+1, &status); |
1332 | } | |
1333 | baseName[baseNameSize] = 0; | |
1334 | } | |
1335 | return baseName; | |
1336 | } | |
1337 | ||
1338 | ||
b75a7d8f A |
1339 | //eof |
1340 | U_NAMESPACE_END |