/src/icu/source/common/locid.cpp
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | // © 2016 and later: Unicode, Inc. and others.  | 
2  |  | // License & terms of use: http://www.unicode.org/copyright.html  | 
3  |  | /*  | 
4  |  |  **********************************************************************  | 
5  |  |  *   Copyright (C) 1997-2016, International Business Machines  | 
6  |  |  *   Corporation and others.  All Rights Reserved.  | 
7  |  |  **********************************************************************  | 
8  |  | *  | 
9  |  | * File locid.cpp  | 
10  |  | *  | 
11  |  | * Created by: Richard Gillam  | 
12  |  | *  | 
13  |  | * Modification History:  | 
14  |  | *  | 
15  |  | *   Date        Name        Description  | 
16  |  | *   02/11/97    aliu        Changed gLocPath to fgDataDirectory and added  | 
17  |  | *                           methods to get and set it.  | 
18  |  | *   04/02/97    aliu        Made operator!= inline; fixed return value  | 
19  |  | *                           of getName().  | 
20  |  | *   04/15/97    aliu        Cleanup for AIX/Win32.  | 
21  |  | *   04/24/97    aliu        Numerous changes per code review.  | 
22  |  | *   08/18/98    stephen     Changed getDisplayName()  | 
23  |  | *                           Added SIMPLIFIED_CHINESE, TRADITIONAL_CHINESE  | 
24  |  | *                           Added getISOCountries(), getISOLanguages(),  | 
25  |  | *                           getLanguagesForCountry()  | 
26  |  | *   03/16/99    bertrand    rehaul.  | 
27  |  | *   07/21/99    stephen     Added U_CFUNC setDefault  | 
28  |  | *   11/09/99    weiv        Added const char * getName() const;  | 
29  |  | *   04/12/00    srl         removing unicodestring api's and cached hash code  | 
30  |  | *   08/10/01    grhoten     Change the static Locales to accessor functions  | 
31  |  | ******************************************************************************  | 
32  |  | */  | 
33  |  |  | 
34  |  | #include <utility>  | 
35  |  |  | 
36  |  | #include "unicode/bytestream.h"  | 
37  |  | #include "unicode/locid.h"  | 
38  |  | #include "unicode/localebuilder.h"  | 
39  |  | #include "unicode/strenum.h"  | 
40  |  | #include "unicode/stringpiece.h"  | 
41  |  | #include "unicode/uloc.h"  | 
42  |  | #include "unicode/ures.h"  | 
43  |  |  | 
44  |  | #include "bytesinkutil.h"  | 
45  |  | #include "charstr.h"  | 
46  |  | #include "charstrmap.h"  | 
47  |  | #include "cmemory.h"  | 
48  |  | #include "cstring.h"  | 
49  |  | #include "mutex.h"  | 
50  |  | #include "putilimp.h"  | 
51  |  | #include "uassert.h"  | 
52  |  | #include "ucln_cmn.h"  | 
53  |  | #include "uhash.h"  | 
54  |  | #include "ulocimp.h"  | 
55  |  | #include "umutex.h"  | 
56  |  | #include "uniquecharstr.h"  | 
57  |  | #include "ustr_imp.h"  | 
58  |  | #include "uvector.h"  | 
59  |  |  | 
60  |  | U_CDECL_BEGIN  | 
61  |  | static UBool U_CALLCONV locale_cleanup(void);  | 
62  |  | U_CDECL_END  | 
63  |  |  | 
64  |  | U_NAMESPACE_BEGIN  | 
65  |  |  | 
66  |  | static Locale   *gLocaleCache = NULL;  | 
67  |  | static UInitOnce gLocaleCacheInitOnce = U_INITONCE_INITIALIZER;  | 
68  |  |  | 
69  |  | // gDefaultLocaleMutex protects all access to gDefaultLocalesHashT and gDefaultLocale.  | 
70  |  | static UMutex gDefaultLocaleMutex;  | 
71  |  | static UHashtable *gDefaultLocalesHashT = NULL;  | 
72  |  | static Locale *gDefaultLocale = NULL;  | 
73  |  |  | 
74  |  | /**  | 
75  |  |  * \def ULOC_STRING_LIMIT  | 
76  |  |  * strings beyond this value crash in CharString  | 
77  |  |  */  | 
78  | 0  | #define ULOC_STRING_LIMIT 357913941  | 
79  |  |  | 
80  |  | U_NAMESPACE_END  | 
81  |  |  | 
82  |  | typedef enum ELocalePos { | 
83  |  |     eENGLISH,  | 
84  |  |     eFRENCH,  | 
85  |  |     eGERMAN,  | 
86  |  |     eITALIAN,  | 
87  |  |     eJAPANESE,  | 
88  |  |     eKOREAN,  | 
89  |  |     eCHINESE,  | 
90  |  |  | 
91  |  |     eFRANCE,  | 
92  |  |     eGERMANY,  | 
93  |  |     eITALY,  | 
94  |  |     eJAPAN,  | 
95  |  |     eKOREA,  | 
96  |  |     eCHINA,      /* Alias for PRC */  | 
97  |  |     eTAIWAN,  | 
98  |  |     eUK,  | 
99  |  |     eUS,  | 
100  |  |     eCANADA,  | 
101  |  |     eCANADA_FRENCH,  | 
102  |  |     eROOT,  | 
103  |  |  | 
104  |  |  | 
105  |  |     //eDEFAULT,  | 
106  |  |     eMAX_LOCALES  | 
107  |  | } ELocalePos;  | 
108  |  |  | 
109  |  | U_CDECL_BEGIN  | 
110  |  | //  | 
111  |  | // Deleter function for Locales owned by the default Locale hash table/  | 
112  |  | //  | 
113  |  | static void U_CALLCONV  | 
114  | 0  | deleteLocale(void *obj) { | 
115  | 0  |     delete (icu::Locale *) obj;  | 
116  | 0  | }  | 
117  |  |  | 
118  |  | static UBool U_CALLCONV locale_cleanup(void)  | 
119  | 0  | { | 
120  | 0  |     U_NAMESPACE_USE  | 
121  |  | 
  | 
122  | 0  |     delete [] gLocaleCache;  | 
123  | 0  |     gLocaleCache = NULL;  | 
124  | 0  |     gLocaleCacheInitOnce.reset();  | 
125  |  | 
  | 
126  | 0  |     if (gDefaultLocalesHashT) { | 
127  | 0  |         uhash_close(gDefaultLocalesHashT);   // Automatically deletes all elements, using deleter func.  | 
128  | 0  |         gDefaultLocalesHashT = NULL;  | 
129  | 0  |     }  | 
130  | 0  |     gDefaultLocale = NULL;  | 
131  | 0  |     return TRUE;  | 
132  | 0  | }  | 
133  |  |  | 
134  |  |  | 
135  | 0  | static void U_CALLCONV locale_init(UErrorCode &status) { | 
136  | 0  |     U_NAMESPACE_USE  | 
137  |  | 
  | 
138  | 0  |     U_ASSERT(gLocaleCache == NULL);  | 
139  | 0  |     gLocaleCache = new Locale[(int)eMAX_LOCALES];  | 
140  | 0  |     if (gLocaleCache == NULL) { | 
141  | 0  |         status = U_MEMORY_ALLOCATION_ERROR;  | 
142  | 0  |         return;  | 
143  | 0  |     }  | 
144  | 0  |     ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup);  | 
145  | 0  |     gLocaleCache[eROOT]          = Locale(""); | 
146  | 0  |     gLocaleCache[eENGLISH]       = Locale("en"); | 
147  | 0  |     gLocaleCache[eFRENCH]        = Locale("fr"); | 
148  | 0  |     gLocaleCache[eGERMAN]        = Locale("de"); | 
149  | 0  |     gLocaleCache[eITALIAN]       = Locale("it"); | 
150  | 0  |     gLocaleCache[eJAPANESE]      = Locale("ja"); | 
151  | 0  |     gLocaleCache[eKOREAN]        = Locale("ko"); | 
152  | 0  |     gLocaleCache[eCHINESE]       = Locale("zh"); | 
153  | 0  |     gLocaleCache[eFRANCE]        = Locale("fr", "FR"); | 
154  | 0  |     gLocaleCache[eGERMANY]       = Locale("de", "DE"); | 
155  | 0  |     gLocaleCache[eITALY]         = Locale("it", "IT"); | 
156  | 0  |     gLocaleCache[eJAPAN]         = Locale("ja", "JP"); | 
157  | 0  |     gLocaleCache[eKOREA]         = Locale("ko", "KR"); | 
158  | 0  |     gLocaleCache[eCHINA]         = Locale("zh", "CN"); | 
159  | 0  |     gLocaleCache[eTAIWAN]        = Locale("zh", "TW"); | 
160  | 0  |     gLocaleCache[eUK]            = Locale("en", "GB"); | 
161  | 0  |     gLocaleCache[eUS]            = Locale("en", "US"); | 
162  | 0  |     gLocaleCache[eCANADA]        = Locale("en", "CA"); | 
163  | 0  |     gLocaleCache[eCANADA_FRENCH] = Locale("fr", "CA"); | 
164  | 0  | }  | 
165  |  |  | 
166  |  | U_CDECL_END  | 
167  |  |  | 
168  |  | U_NAMESPACE_BEGIN  | 
169  |  |  | 
170  | 0  | Locale *locale_set_default_internal(const char *id, UErrorCode& status) { | 
171  |  |     // Synchronize this entire function.  | 
172  | 0  |     Mutex lock(&gDefaultLocaleMutex);  | 
173  |  | 
  | 
174  | 0  |     UBool canonicalize = FALSE;  | 
175  |  |  | 
176  |  |     // If given a NULL string for the locale id, grab the default  | 
177  |  |     //   name from the system.  | 
178  |  |     //   (Different from most other locale APIs, where a null name means use  | 
179  |  |     //    the current ICU default locale.)  | 
180  | 0  |     if (id == NULL) { | 
181  | 0  |         id = uprv_getDefaultLocaleID();   // This function not thread safe? TODO: verify.  | 
182  | 0  |         canonicalize = TRUE; // always canonicalize host ID  | 
183  | 0  |     }  | 
184  |  | 
  | 
185  | 0  |     CharString localeNameBuf;  | 
186  | 0  |     { | 
187  | 0  |         CharStringByteSink sink(&localeNameBuf);  | 
188  | 0  |         if (canonicalize) { | 
189  | 0  |             ulocimp_canonicalize(id, sink, &status);  | 
190  | 0  |         } else { | 
191  | 0  |             ulocimp_getName(id, sink, &status);  | 
192  | 0  |         }  | 
193  | 0  |     }  | 
194  |  | 
  | 
195  | 0  |     if (U_FAILURE(status)) { | 
196  | 0  |         return gDefaultLocale;  | 
197  | 0  |     }  | 
198  |  |  | 
199  | 0  |     if (gDefaultLocalesHashT == NULL) { | 
200  | 0  |         gDefaultLocalesHashT = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);  | 
201  | 0  |         if (U_FAILURE(status)) { | 
202  | 0  |             return gDefaultLocale;  | 
203  | 0  |         }  | 
204  | 0  |         uhash_setValueDeleter(gDefaultLocalesHashT, deleteLocale);  | 
205  | 0  |         ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup);  | 
206  | 0  |     }  | 
207  |  |  | 
208  | 0  |     Locale *newDefault = (Locale *)uhash_get(gDefaultLocalesHashT, localeNameBuf.data());  | 
209  | 0  |     if (newDefault == NULL) { | 
210  | 0  |         newDefault = new Locale(Locale::eBOGUS);  | 
211  | 0  |         if (newDefault == NULL) { | 
212  | 0  |             status = U_MEMORY_ALLOCATION_ERROR;  | 
213  | 0  |             return gDefaultLocale;  | 
214  | 0  |         }  | 
215  | 0  |         newDefault->init(localeNameBuf.data(), FALSE);  | 
216  | 0  |         uhash_put(gDefaultLocalesHashT, (char*) newDefault->getName(), newDefault, &status);  | 
217  | 0  |         if (U_FAILURE(status)) { | 
218  | 0  |             return gDefaultLocale;  | 
219  | 0  |         }  | 
220  | 0  |     }  | 
221  | 0  |     gDefaultLocale = newDefault;  | 
222  | 0  |     return gDefaultLocale;  | 
223  | 0  | }  | 
224  |  |  | 
225  |  | U_NAMESPACE_END  | 
226  |  |  | 
227  |  | /* sfb 07/21/99 */  | 
228  |  | U_CFUNC void  | 
229  |  | locale_set_default(const char *id)  | 
230  | 0  | { | 
231  | 0  |     U_NAMESPACE_USE  | 
232  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
233  | 0  |     locale_set_default_internal(id, status);  | 
234  | 0  | }  | 
235  |  | /* end */  | 
236  |  |  | 
237  |  | U_CFUNC const char *  | 
238  |  | locale_get_default(void)  | 
239  | 0  | { | 
240  | 0  |     U_NAMESPACE_USE  | 
241  | 0  |     return Locale::getDefault().getName();  | 
242  | 0  | }  | 
243  |  |  | 
244  |  |  | 
245  |  | U_NAMESPACE_BEGIN  | 
246  |  |  | 
247  |  | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Locale)  | 
248  |  |  | 
249  |  | /*Character separating the posix id fields*/  | 
250  |  | // '_'  | 
251  |  | // In the platform codepage.  | 
252  | 0  | #define SEP_CHAR '_'  | 
253  | 0  | #define NULL_CHAR '\0'  | 
254  |  |  | 
255  |  | Locale::~Locale()  | 
256  | 0  | { | 
257  | 0  |     if ((baseName != fullName) && (baseName != fullNameBuffer)) { | 
258  | 0  |         uprv_free(baseName);  | 
259  | 0  |     }  | 
260  | 0  |     baseName = NULL;  | 
261  |  |     /*if fullName is on the heap, we free it*/  | 
262  | 0  |     if (fullName != fullNameBuffer)  | 
263  | 0  |     { | 
264  | 0  |         uprv_free(fullName);  | 
265  | 0  |         fullName = NULL;  | 
266  | 0  |     }  | 
267  | 0  | }  | 
268  |  |  | 
269  |  | Locale::Locale()  | 
270  | 0  |     : UObject(), fullName(fullNameBuffer), baseName(NULL)  | 
271  | 0  | { | 
272  | 0  |     init(NULL, FALSE);  | 
273  | 0  | }  | 
274  |  |  | 
275  |  | /*  | 
276  |  |  * Internal constructor to allow construction of a locale object with  | 
277  |  |  *   NO side effects.   (Default constructor tries to get  | 
278  |  |  *   the default locale.)  | 
279  |  |  */  | 
280  |  | Locale::Locale(Locale::ELocaleType)  | 
281  | 0  |     : UObject(), fullName(fullNameBuffer), baseName(NULL)  | 
282  | 0  | { | 
283  | 0  |     setToBogus();  | 
284  | 0  | }  | 
285  |  |  | 
286  |  |  | 
287  |  | Locale::Locale( const   char * newLanguage,  | 
288  |  |                 const   char * newCountry,  | 
289  |  |                 const   char * newVariant,  | 
290  |  |                 const   char * newKeywords)  | 
291  | 0  |     : UObject(), fullName(fullNameBuffer), baseName(NULL)  | 
292  | 0  | { | 
293  | 0  |     if( (newLanguage==NULL) && (newCountry == NULL) && (newVariant == NULL) )  | 
294  | 0  |     { | 
295  | 0  |         init(NULL, FALSE); /* shortcut */  | 
296  | 0  |     }  | 
297  | 0  |     else  | 
298  | 0  |     { | 
299  | 0  |         UErrorCode status = U_ZERO_ERROR;  | 
300  | 0  |         int32_t lsize = 0;  | 
301  | 0  |         int32_t csize = 0;  | 
302  | 0  |         int32_t vsize = 0;  | 
303  | 0  |         int32_t ksize = 0;  | 
304  |  |  | 
305  |  |         // Check the sizes of the input strings.  | 
306  |  |  | 
307  |  |         // Language  | 
308  | 0  |         if ( newLanguage != NULL )  | 
309  | 0  |         { | 
310  | 0  |             lsize = (int32_t)uprv_strlen(newLanguage);  | 
311  | 0  |             if ( lsize < 0 || lsize > ULOC_STRING_LIMIT ) { // int32 wrap | 
312  | 0  |                 setToBogus();  | 
313  | 0  |                 return;  | 
314  | 0  |             }  | 
315  | 0  |         }  | 
316  |  |  | 
317  | 0  |         CharString togo(newLanguage, lsize, status); // start with newLanguage  | 
318  |  |  | 
319  |  |         // _Country  | 
320  | 0  |         if ( newCountry != NULL )  | 
321  | 0  |         { | 
322  | 0  |             csize = (int32_t)uprv_strlen(newCountry);  | 
323  | 0  |             if ( csize < 0 || csize > ULOC_STRING_LIMIT ) { // int32 wrap | 
324  | 0  |                 setToBogus();  | 
325  | 0  |                 return;  | 
326  | 0  |             }  | 
327  | 0  |         }  | 
328  |  |  | 
329  |  |         // _Variant  | 
330  | 0  |         if ( newVariant != NULL )  | 
331  | 0  |         { | 
332  |  |             // remove leading _'s  | 
333  | 0  |             while(newVariant[0] == SEP_CHAR)  | 
334  | 0  |             { | 
335  | 0  |                 newVariant++;  | 
336  | 0  |             }  | 
337  |  |  | 
338  |  |             // remove trailing _'s  | 
339  | 0  |             vsize = (int32_t)uprv_strlen(newVariant);  | 
340  | 0  |             if ( vsize < 0 || vsize > ULOC_STRING_LIMIT ) { // int32 wrap | 
341  | 0  |                 setToBogus();  | 
342  | 0  |                 return;  | 
343  | 0  |             }  | 
344  | 0  |             while( (vsize>1) && (newVariant[vsize-1] == SEP_CHAR) )  | 
345  | 0  |             { | 
346  | 0  |                 vsize--;  | 
347  | 0  |             }  | 
348  | 0  |         }  | 
349  |  |  | 
350  | 0  |         if ( newKeywords != NULL)  | 
351  | 0  |         { | 
352  | 0  |             ksize = (int32_t)uprv_strlen(newKeywords);  | 
353  | 0  |             if ( ksize < 0 || ksize > ULOC_STRING_LIMIT ) { | 
354  | 0  |               setToBogus();  | 
355  | 0  |               return;  | 
356  | 0  |             }  | 
357  | 0  |         }  | 
358  |  |  | 
359  |  |         // We've checked the input sizes, now build up the full locale string..  | 
360  |  |  | 
361  |  |         // newLanguage is already copied  | 
362  |  |  | 
363  | 0  |         if ( ( vsize != 0 ) || (csize != 0) )  // at least:  __v  | 
364  | 0  |         {                                      //            ^ | 
365  | 0  |             togo.append(SEP_CHAR, status);  | 
366  | 0  |         }  | 
367  |  | 
  | 
368  | 0  |         if ( csize != 0 )  | 
369  | 0  |         { | 
370  | 0  |             togo.append(newCountry, status);  | 
371  | 0  |         }  | 
372  |  | 
  | 
373  | 0  |         if ( vsize != 0)  | 
374  | 0  |         { | 
375  | 0  |             togo.append(SEP_CHAR, status)  | 
376  | 0  |                 .append(newVariant, vsize, status);  | 
377  | 0  |         }  | 
378  |  | 
  | 
379  | 0  |         if ( ksize != 0)  | 
380  | 0  |         { | 
381  | 0  |             if (uprv_strchr(newKeywords, '=')) { | 
382  | 0  |                 togo.append('@', status); /* keyword parsing */ | 
383  | 0  |             }  | 
384  | 0  |             else { | 
385  | 0  |                 togo.append('_', status); /* Variant parsing with a script */ | 
386  | 0  |                 if ( vsize == 0) { | 
387  | 0  |                     togo.append('_', status); /* No country found */ | 
388  | 0  |                 }  | 
389  | 0  |             }  | 
390  | 0  |             togo.append(newKeywords, status);  | 
391  | 0  |         }  | 
392  |  | 
  | 
393  | 0  |         if (U_FAILURE(status)) { | 
394  |  |             // Something went wrong with appending, etc.  | 
395  | 0  |             setToBogus();  | 
396  | 0  |             return;  | 
397  | 0  |         }  | 
398  |  |         // Parse it, because for example 'language' might really be a complete  | 
399  |  |         // string.  | 
400  | 0  |         init(togo.data(), FALSE);  | 
401  | 0  |     }  | 
402  | 0  | }  | 
403  |  |  | 
404  |  | Locale::Locale(const Locale &other)  | 
405  | 0  |     : UObject(other), fullName(fullNameBuffer), baseName(NULL)  | 
406  | 0  | { | 
407  | 0  |     *this = other;  | 
408  | 0  | }  | 
409  |  |  | 
410  |  | Locale::Locale(Locale&& other) U_NOEXCEPT  | 
411  | 0  |     : UObject(other), fullName(fullNameBuffer), baseName(fullName) { | 
412  | 0  |   *this = std::move(other);  | 
413  | 0  | }  | 
414  |  |  | 
415  | 0  | Locale& Locale::operator=(const Locale& other) { | 
416  | 0  |     if (this == &other) { | 
417  | 0  |         return *this;  | 
418  | 0  |     }  | 
419  |  |  | 
420  | 0  |     setToBogus();  | 
421  |  | 
  | 
422  | 0  |     if (other.fullName == other.fullNameBuffer) { | 
423  | 0  |         uprv_strcpy(fullNameBuffer, other.fullNameBuffer);  | 
424  | 0  |     } else if (other.fullName == nullptr) { | 
425  | 0  |         fullName = nullptr;  | 
426  | 0  |     } else { | 
427  | 0  |         fullName = uprv_strdup(other.fullName);  | 
428  | 0  |         if (fullName == nullptr) return *this;  | 
429  | 0  |     }  | 
430  |  |  | 
431  | 0  |     if (other.baseName == other.fullName) { | 
432  | 0  |         baseName = fullName;  | 
433  | 0  |     } else if (other.baseName != nullptr) { | 
434  | 0  |         baseName = uprv_strdup(other.baseName);  | 
435  | 0  |         if (baseName == nullptr) return *this;  | 
436  | 0  |     }  | 
437  |  |  | 
438  | 0  |     uprv_strcpy(language, other.language);  | 
439  | 0  |     uprv_strcpy(script, other.script);  | 
440  | 0  |     uprv_strcpy(country, other.country);  | 
441  |  | 
  | 
442  | 0  |     variantBegin = other.variantBegin;  | 
443  | 0  |     fIsBogus = other.fIsBogus;  | 
444  |  | 
  | 
445  | 0  |     return *this;  | 
446  | 0  | }  | 
447  |  |  | 
448  | 0  | Locale& Locale::operator=(Locale&& other) U_NOEXCEPT { | 
449  | 0  |     if ((baseName != fullName) && (baseName != fullNameBuffer)) uprv_free(baseName);  | 
450  | 0  |     if (fullName != fullNameBuffer) uprv_free(fullName);  | 
451  |  | 
  | 
452  | 0  |     if (other.fullName == other.fullNameBuffer || other.baseName == other.fullNameBuffer) { | 
453  | 0  |         uprv_strcpy(fullNameBuffer, other.fullNameBuffer);  | 
454  | 0  |     }  | 
455  | 0  |     if (other.fullName == other.fullNameBuffer) { | 
456  | 0  |         fullName = fullNameBuffer;  | 
457  | 0  |     } else { | 
458  | 0  |         fullName = other.fullName;  | 
459  | 0  |     }  | 
460  |  | 
  | 
461  | 0  |     if (other.baseName == other.fullNameBuffer) { | 
462  | 0  |         baseName = fullNameBuffer;  | 
463  | 0  |     } else if (other.baseName == other.fullName) { | 
464  | 0  |         baseName = fullName;  | 
465  | 0  |     } else { | 
466  | 0  |         baseName = other.baseName;  | 
467  | 0  |     }  | 
468  |  | 
  | 
469  | 0  |     uprv_strcpy(language, other.language);  | 
470  | 0  |     uprv_strcpy(script, other.script);  | 
471  | 0  |     uprv_strcpy(country, other.country);  | 
472  |  | 
  | 
473  | 0  |     variantBegin = other.variantBegin;  | 
474  | 0  |     fIsBogus = other.fIsBogus;  | 
475  |  | 
  | 
476  | 0  |     other.baseName = other.fullName = other.fullNameBuffer;  | 
477  |  | 
  | 
478  | 0  |     return *this;  | 
479  | 0  | }  | 
480  |  |  | 
481  |  | Locale *  | 
482  | 0  | Locale::clone() const { | 
483  | 0  |     return new Locale(*this);  | 
484  | 0  | }  | 
485  |  |  | 
486  |  | bool  | 
487  |  | Locale::operator==( const   Locale& other) const  | 
488  | 0  | { | 
489  | 0  |     return (uprv_strcmp(other.fullName, fullName) == 0);  | 
490  | 0  | }  | 
491  |  |  | 
492  |  | namespace { | 
493  |  |  | 
494  |  | UInitOnce gKnownCanonicalizedInitOnce = U_INITONCE_INITIALIZER;  | 
495  |  | UHashtable *gKnownCanonicalized = nullptr;  | 
496  |  |  | 
497  |  | static const char* const KNOWN_CANONICALIZED[] = { | 
498  |  |     "c",  | 
499  |  |     // Commonly used locales known are already canonicalized  | 
500  |  |     "af", "af_ZA", "am", "am_ET", "ar", "ar_001", "as", "as_IN", "az", "az_AZ",  | 
501  |  |     "be", "be_BY", "bg", "bg_BG", "bn", "bn_IN", "bs", "bs_BA", "ca", "ca_ES",  | 
502  |  |     "cs", "cs_CZ", "cy", "cy_GB", "da", "da_DK", "de", "de_DE", "el", "el_GR",  | 
503  |  |     "en", "en_GB", "en_US", "es", "es_419", "es_ES", "et", "et_EE", "eu",  | 
504  |  |     "eu_ES", "fa", "fa_IR", "fi", "fi_FI", "fil", "fil_PH", "fr", "fr_FR",  | 
505  |  |     "ga", "ga_IE", "gl", "gl_ES", "gu", "gu_IN", "he", "he_IL", "hi", "hi_IN",  | 
506  |  |     "hr", "hr_HR", "hu", "hu_HU", "hy", "hy_AM", "id", "id_ID", "is", "is_IS",  | 
507  |  |     "it", "it_IT", "ja", "ja_JP", "jv", "jv_ID", "ka", "ka_GE", "kk", "kk_KZ",  | 
508  |  |     "km", "km_KH", "kn", "kn_IN", "ko", "ko_KR", "ky", "ky_KG", "lo", "lo_LA",  | 
509  |  |     "lt", "lt_LT", "lv", "lv_LV", "mk", "mk_MK", "ml", "ml_IN", "mn", "mn_MN",  | 
510  |  |     "mr", "mr_IN", "ms", "ms_MY", "my", "my_MM", "nb", "nb_NO", "ne", "ne_NP",  | 
511  |  |     "nl", "nl_NL", "no", "or", "or_IN", "pa", "pa_IN", "pl", "pl_PL", "ps", "ps_AF",  | 
512  |  |     "pt", "pt_BR", "pt_PT", "ro", "ro_RO", "ru", "ru_RU", "sd", "sd_IN", "si",  | 
513  |  |     "si_LK", "sk", "sk_SK", "sl", "sl_SI", "so", "so_SO", "sq", "sq_AL", "sr",  | 
514  |  |     "sr_Cyrl_RS", "sr_Latn", "sr_RS", "sv", "sv_SE", "sw", "sw_TZ", "ta",  | 
515  |  |     "ta_IN", "te", "te_IN", "th", "th_TH", "tk", "tk_TM", "tr", "tr_TR", "uk",  | 
516  |  |     "uk_UA", "ur", "ur_PK", "uz", "uz_UZ", "vi", "vi_VN", "yue", "yue_Hant",  | 
517  |  |     "yue_Hant_HK", "yue_HK", "zh", "zh_CN", "zh_Hans", "zh_Hans_CN", "zh_Hant",  | 
518  |  |     "zh_Hant_TW", "zh_TW", "zu", "zu_ZA"  | 
519  |  | };  | 
520  |  |  | 
521  | 0  | static UBool U_CALLCONV cleanupKnownCanonicalized() { | 
522  | 0  |     gKnownCanonicalizedInitOnce.reset();  | 
523  | 0  |     if (gKnownCanonicalized) { uhash_close(gKnownCanonicalized); } | 
524  | 0  |     return TRUE;  | 
525  | 0  | }  | 
526  |  |  | 
527  | 0  | static void U_CALLCONV loadKnownCanonicalized(UErrorCode &status) { | 
528  | 0  |     ucln_common_registerCleanup(UCLN_COMMON_LOCALE_KNOWN_CANONICALIZED,  | 
529  | 0  |                                 cleanupKnownCanonicalized);  | 
530  | 0  |     LocalUHashtablePointer newKnownCanonicalizedMap(  | 
531  | 0  |         uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status));  | 
532  | 0  |     for (int32_t i = 0;  | 
533  | 0  |             U_SUCCESS(status) && i < UPRV_LENGTHOF(KNOWN_CANONICALIZED);  | 
534  | 0  |             i++) { | 
535  | 0  |         uhash_puti(newKnownCanonicalizedMap.getAlias(),  | 
536  | 0  |                    (void*)KNOWN_CANONICALIZED[i],  | 
537  | 0  |                    1, &status);  | 
538  | 0  |     }  | 
539  | 0  |     if (U_FAILURE(status)) { | 
540  | 0  |         return;  | 
541  | 0  |     }  | 
542  |  |  | 
543  | 0  |     gKnownCanonicalized = newKnownCanonicalizedMap.orphan();  | 
544  | 0  | }  | 
545  |  |  | 
546  |  | class AliasData;  | 
547  |  |  | 
548  |  | /**  | 
549  |  |  * A Builder class to build the alias data.  | 
550  |  |  */  | 
551  |  | class AliasDataBuilder { | 
552  |  | public:  | 
553  | 0  |     AliasDataBuilder() { | 
554  | 0  |     }  | 
555  |  |  | 
556  |  |     // Build the AliasData from resource.  | 
557  |  |     AliasData* build(UErrorCode &status);  | 
558  |  |  | 
559  |  | private:  | 
560  |  |     void readAlias(UResourceBundle* alias,  | 
561  |  |                    UniqueCharStrings* strings,  | 
562  |  |                    LocalMemory<const char*>& types,  | 
563  |  |                    LocalMemory<int32_t>& replacementIndexes,  | 
564  |  |                    int32_t &length,  | 
565  |  |                    void (*checkType)(const char* type),  | 
566  |  |                    void (*checkReplacement)(const UnicodeString& replacement),  | 
567  |  |                    UErrorCode &status);  | 
568  |  |  | 
569  |  |     // Read the languageAlias data from alias to  | 
570  |  |     // strings+types+replacementIndexes  | 
571  |  |     // The number of record will be stored into length.  | 
572  |  |     // Allocate length items for types, to store the type field.  | 
573  |  |     // Allocate length items for replacementIndexes,  | 
574  |  |     // to store the index in the strings for the replacement script.  | 
575  |  |     void readLanguageAlias(UResourceBundle* alias,  | 
576  |  |                            UniqueCharStrings* strings,  | 
577  |  |                            LocalMemory<const char*>& types,  | 
578  |  |                            LocalMemory<int32_t>& replacementIndexes,  | 
579  |  |                            int32_t &length,  | 
580  |  |                            UErrorCode &status);  | 
581  |  |  | 
582  |  |     // Read the scriptAlias data from alias to  | 
583  |  |     // strings+types+replacementIndexes  | 
584  |  |     // Allocate length items for types, to store the type field.  | 
585  |  |     // Allocate length items for replacementIndexes,  | 
586  |  |     // to store the index in the strings for the replacement script.  | 
587  |  |     void readScriptAlias(UResourceBundle* alias,  | 
588  |  |                          UniqueCharStrings* strings,  | 
589  |  |                          LocalMemory<const char*>& types,  | 
590  |  |                          LocalMemory<int32_t>& replacementIndexes,  | 
591  |  |                          int32_t &length, UErrorCode &status);  | 
592  |  |  | 
593  |  |     // Read the territoryAlias data from alias to  | 
594  |  |     // strings+types+replacementIndexes  | 
595  |  |     // Allocate length items for types, to store the type field.  | 
596  |  |     // Allocate length items for replacementIndexes,  | 
597  |  |     // to store the index in the strings for the replacement script.  | 
598  |  |     void readTerritoryAlias(UResourceBundle* alias,  | 
599  |  |                             UniqueCharStrings* strings,  | 
600  |  |                             LocalMemory<const char*>& types,  | 
601  |  |                             LocalMemory<int32_t>& replacementIndexes,  | 
602  |  |                             int32_t &length, UErrorCode &status);  | 
603  |  |  | 
604  |  |     // Read the variantAlias data from alias to  | 
605  |  |     // strings+types+replacementIndexes  | 
606  |  |     // Allocate length items for types, to store the type field.  | 
607  |  |     // Allocate length items for replacementIndexes,  | 
608  |  |     // to store the index in the strings for the replacement variant.  | 
609  |  |     void readVariantAlias(UResourceBundle* alias,  | 
610  |  |                           UniqueCharStrings* strings,  | 
611  |  |                           LocalMemory<const char*>& types,  | 
612  |  |                           LocalMemory<int32_t>& replacementIndexes,  | 
613  |  |                           int32_t &length, UErrorCode &status);  | 
614  |  |  | 
615  |  |     // Read the subdivisionAlias data from alias to  | 
616  |  |     // strings+types+replacementIndexes  | 
617  |  |     // Allocate length items for types, to store the type field.  | 
618  |  |     // Allocate length items for replacementIndexes,  | 
619  |  |     // to store the index in the strings for the replacement variant.  | 
620  |  |     void readSubdivisionAlias(UResourceBundle* alias,  | 
621  |  |                           UniqueCharStrings* strings,  | 
622  |  |                           LocalMemory<const char*>& types,  | 
623  |  |                           LocalMemory<int32_t>& replacementIndexes,  | 
624  |  |                           int32_t &length, UErrorCode &status);  | 
625  |  | };  | 
626  |  |  | 
627  |  | /**  | 
628  |  |  * A class to hold the Alias Data.  | 
629  |  |  */  | 
630  |  | class AliasData : public UMemory { | 
631  |  | public:  | 
632  | 0  |     static const AliasData* singleton(UErrorCode& status) { | 
633  | 0  |         if (U_FAILURE(status)) { | 
634  |  |             // Do not get into loadData if the status already has error.  | 
635  | 0  |             return nullptr;  | 
636  | 0  |         }  | 
637  | 0  |         umtx_initOnce(AliasData::gInitOnce, &AliasData::loadData, status);  | 
638  | 0  |         return gSingleton;  | 
639  | 0  |     }  | 
640  |  |  | 
641  | 0  |     const CharStringMap& languageMap() const { return language; } | 
642  | 0  |     const CharStringMap& scriptMap() const { return script; } | 
643  | 0  |     const CharStringMap& territoryMap() const { return territory; } | 
644  | 0  |     const CharStringMap& variantMap() const { return variant; } | 
645  | 0  |     const CharStringMap& subdivisionMap() const { return subdivision; } | 
646  |  |  | 
647  |  |     static void U_CALLCONV loadData(UErrorCode &status);  | 
648  |  |     static UBool U_CALLCONV cleanup();  | 
649  |  |  | 
650  |  |     static UInitOnce gInitOnce;  | 
651  |  |  | 
652  |  | private:  | 
653  |  |     AliasData(CharStringMap languageMap,  | 
654  |  |               CharStringMap scriptMap,  | 
655  |  |               CharStringMap territoryMap,  | 
656  |  |               CharStringMap variantMap,  | 
657  |  |               CharStringMap subdivisionMap,  | 
658  |  |               CharString* strings)  | 
659  | 0  |         : language(std::move(languageMap)),  | 
660  | 0  |           script(std::move(scriptMap)),  | 
661  | 0  |           territory(std::move(territoryMap)),  | 
662  | 0  |           variant(std::move(variantMap)),  | 
663  | 0  |           subdivision(std::move(subdivisionMap)),  | 
664  | 0  |           strings(strings) { | 
665  | 0  |     }  | 
666  |  |  | 
667  | 0  |     ~AliasData() { | 
668  | 0  |         delete strings;  | 
669  | 0  |     }  | 
670  |  |  | 
671  |  |     static const AliasData* gSingleton;  | 
672  |  |  | 
673  |  |     CharStringMap language;  | 
674  |  |     CharStringMap script;  | 
675  |  |     CharStringMap territory;  | 
676  |  |     CharStringMap variant;  | 
677  |  |     CharStringMap subdivision;  | 
678  |  |     CharString* strings;  | 
679  |  |  | 
680  |  |     friend class AliasDataBuilder;  | 
681  |  | };  | 
682  |  |  | 
683  |  |  | 
684  |  | const AliasData* AliasData::gSingleton = nullptr;  | 
685  |  | UInitOnce AliasData::gInitOnce = U_INITONCE_INITIALIZER;  | 
686  |  |  | 
687  |  | UBool U_CALLCONV  | 
688  |  | AliasData::cleanup()  | 
689  | 0  | { | 
690  | 0  |     gInitOnce.reset();  | 
691  | 0  |     delete gSingleton;  | 
692  | 0  |     return TRUE;  | 
693  | 0  | }  | 
694  |  |  | 
695  |  | void  | 
696  |  | AliasDataBuilder::readAlias(  | 
697  |  |         UResourceBundle* alias,  | 
698  |  |         UniqueCharStrings* strings,  | 
699  |  |         LocalMemory<const char*>& types,  | 
700  |  |         LocalMemory<int32_t>& replacementIndexes,  | 
701  |  |         int32_t &length,  | 
702  |  |         void (*checkType)(const char* type),  | 
703  |  |         void (*checkReplacement)(const UnicodeString& replacement),  | 
704  | 0  |         UErrorCode &status) { | 
705  | 0  |     if (U_FAILURE(status)) { | 
706  | 0  |         return;  | 
707  | 0  |     }  | 
708  | 0  |     length = ures_getSize(alias);  | 
709  | 0  |     const char** rawTypes = types.allocateInsteadAndCopy(length);  | 
710  | 0  |     if (rawTypes == nullptr) { | 
711  | 0  |         status = U_MEMORY_ALLOCATION_ERROR;  | 
712  | 0  |         return;  | 
713  | 0  |     }  | 
714  | 0  |     int32_t* rawIndexes = replacementIndexes.allocateInsteadAndCopy(length);  | 
715  | 0  |     if (rawIndexes == nullptr) { | 
716  | 0  |         status = U_MEMORY_ALLOCATION_ERROR;  | 
717  | 0  |         return;  | 
718  | 0  |     }  | 
719  | 0  |     int i = 0;  | 
720  | 0  |     while (ures_hasNext(alias)) { | 
721  | 0  |         LocalUResourceBundlePointer res(  | 
722  | 0  |             ures_getNextResource(alias, nullptr, &status));  | 
723  | 0  |         const char* aliasFrom = ures_getKey(res.getAlias());  | 
724  | 0  |         UnicodeString aliasTo =  | 
725  | 0  |             ures_getUnicodeStringByKey(res.getAlias(), "replacement", &status);  | 
726  |  | 
  | 
727  | 0  |         checkType(aliasFrom);  | 
728  | 0  |         checkReplacement(aliasTo);  | 
729  |  | 
  | 
730  | 0  |         rawTypes[i] = aliasFrom;  | 
731  | 0  |         rawIndexes[i] = strings->add(aliasTo, status);  | 
732  | 0  |         i++;  | 
733  | 0  |     }  | 
734  | 0  | }  | 
735  |  |  | 
736  |  | /**  | 
737  |  |  * Read the languageAlias data from alias to strings+types+replacementIndexes.  | 
738  |  |  * Allocate length items for types, to store the type field. Allocate length  | 
739  |  |  * items for replacementIndexes, to store the index in the strings for the  | 
740  |  |  * replacement language.  | 
741  |  |  */  | 
742  |  | void  | 
743  |  | AliasDataBuilder::readLanguageAlias(  | 
744  |  |         UResourceBundle* alias,  | 
745  |  |         UniqueCharStrings* strings,  | 
746  |  |         LocalMemory<const char*>& types,  | 
747  |  |         LocalMemory<int32_t>& replacementIndexes,  | 
748  |  |         int32_t &length,  | 
749  |  |         UErrorCode &status)  | 
750  | 0  | { | 
751  | 0  |     return readAlias(  | 
752  | 0  |         alias, strings, types, replacementIndexes, length,  | 
753  |  | #if U_DEBUG  | 
754  |  |         [](const char* type) { | 
755  |  |             // Assert the aliasFrom only contains the following possibilities  | 
756  |  |             // language_REGION_variant  | 
757  |  |             // language_REGION  | 
758  |  |             // language_variant  | 
759  |  |             // language  | 
760  |  |             // und_variant  | 
761  |  |             Locale test(type);  | 
762  |  |             // Assert no script in aliasFrom  | 
763  |  |             U_ASSERT(test.getScript()[0] == '\0');  | 
764  |  |             // Assert when language is und, no REGION in aliasFrom.  | 
765  |  |             U_ASSERT(test.getLanguage()[0] != '\0' || test.getCountry()[0] == '\0');  | 
766  |  |         },  | 
767  |  | #else  | 
768  | 0  |         [](const char*) {}, | 
769  | 0  | #endif  | 
770  | 0  |         [](const UnicodeString&) {}, status); | 
771  | 0  | }  | 
772  |  |  | 
773  |  | /**  | 
774  |  |  * Read the scriptAlias data from alias to strings+types+replacementIndexes.  | 
775  |  |  * Allocate length items for types, to store the type field. Allocate length  | 
776  |  |  * items for replacementIndexes, to store the index in the strings for the  | 
777  |  |  * replacement script.  | 
778  |  |  */  | 
779  |  | void  | 
780  |  | AliasDataBuilder::readScriptAlias(  | 
781  |  |         UResourceBundle* alias,  | 
782  |  |         UniqueCharStrings* strings,  | 
783  |  |         LocalMemory<const char*>& types,  | 
784  |  |         LocalMemory<int32_t>& replacementIndexes,  | 
785  |  |         int32_t &length,  | 
786  |  |         UErrorCode &status)  | 
787  | 0  | { | 
788  | 0  |     return readAlias(  | 
789  | 0  |         alias, strings, types, replacementIndexes, length,  | 
790  |  | #if U_DEBUG  | 
791  |  |         [](const char* type) { | 
792  |  |             U_ASSERT(uprv_strlen(type) == 4);  | 
793  |  |         },  | 
794  |  |         [](const UnicodeString& replacement) { | 
795  |  |             U_ASSERT(replacement.length() == 4);  | 
796  |  |         },  | 
797  |  | #else  | 
798  | 0  |         [](const char*) {}, | 
799  | 0  |         [](const UnicodeString&) { }, | 
800  | 0  | #endif  | 
801  | 0  |         status);  | 
802  | 0  | }  | 
803  |  |  | 
804  |  | /**  | 
805  |  |  * Read the territoryAlias data from alias to strings+types+replacementIndexes.  | 
806  |  |  * Allocate length items for types, to store the type field. Allocate length  | 
807  |  |  * items for replacementIndexes, to store the index in the strings for the  | 
808  |  |  * replacement regions.  | 
809  |  |  */  | 
810  |  | void  | 
811  |  | AliasDataBuilder::readTerritoryAlias(  | 
812  |  |         UResourceBundle* alias,  | 
813  |  |         UniqueCharStrings* strings,  | 
814  |  |         LocalMemory<const char*>& types,  | 
815  |  |         LocalMemory<int32_t>& replacementIndexes,  | 
816  |  |         int32_t &length,  | 
817  |  |         UErrorCode &status)  | 
818  | 0  | { | 
819  | 0  |     return readAlias(  | 
820  | 0  |         alias, strings, types, replacementIndexes, length,  | 
821  |  | #if U_DEBUG  | 
822  |  |         [](const char* type) { | 
823  |  |             U_ASSERT(uprv_strlen(type) == 2 || uprv_strlen(type) == 3);  | 
824  |  |         },  | 
825  |  | #else  | 
826  | 0  |         [](const char*) {}, | 
827  | 0  | #endif  | 
828  | 0  |         [](const UnicodeString&) { }, | 
829  | 0  |         status);  | 
830  | 0  | }  | 
831  |  |  | 
832  |  | /**  | 
833  |  |  * Read the variantAlias data from alias to strings+types+replacementIndexes.  | 
834  |  |  * Allocate length items for types, to store the type field. Allocate length  | 
835  |  |  * items for replacementIndexes, to store the index in the strings for the  | 
836  |  |  * replacement variant.  | 
837  |  |  */  | 
838  |  | void  | 
839  |  | AliasDataBuilder::readVariantAlias(  | 
840  |  |         UResourceBundle* alias,  | 
841  |  |         UniqueCharStrings* strings,  | 
842  |  |         LocalMemory<const char*>& types,  | 
843  |  |         LocalMemory<int32_t>& replacementIndexes,  | 
844  |  |         int32_t &length,  | 
845  |  |         UErrorCode &status)  | 
846  | 0  | { | 
847  | 0  |     return readAlias(  | 
848  | 0  |         alias, strings, types, replacementIndexes, length,  | 
849  |  | #if U_DEBUG  | 
850  |  |         [](const char* type) { | 
851  |  |             U_ASSERT(uprv_strlen(type) >= 4 && uprv_strlen(type) <= 8);  | 
852  |  |             U_ASSERT(uprv_strlen(type) != 4 ||  | 
853  |  |                      (type[0] >= '0' && type[0] <= '9'));  | 
854  |  |         },  | 
855  |  |         [](const UnicodeString& replacement) { | 
856  |  |             U_ASSERT(replacement.length() >= 4 && replacement.length() <= 8);  | 
857  |  |             U_ASSERT(replacement.length() != 4 ||  | 
858  |  |                      (replacement.charAt(0) >= u'0' &&  | 
859  |  |                       replacement.charAt(0) <= u'9'));  | 
860  |  |         },  | 
861  |  | #else  | 
862  | 0  |         [](const char*) {}, | 
863  | 0  |         [](const UnicodeString&) { }, | 
864  | 0  | #endif  | 
865  | 0  |         status);  | 
866  | 0  | }  | 
867  |  |  | 
868  |  | /**  | 
869  |  |  * Read the subdivisionAlias data from alias to strings+types+replacementIndexes.  | 
870  |  |  * Allocate length items for types, to store the type field. Allocate length  | 
871  |  |  * items for replacementIndexes, to store the index in the strings for the  | 
872  |  |  * replacement regions.  | 
873  |  |  */  | 
874  |  | void  | 
875  |  | AliasDataBuilder::readSubdivisionAlias(  | 
876  |  |         UResourceBundle* alias,  | 
877  |  |         UniqueCharStrings* strings,  | 
878  |  |         LocalMemory<const char*>& types,  | 
879  |  |         LocalMemory<int32_t>& replacementIndexes,  | 
880  |  |         int32_t &length,  | 
881  |  |         UErrorCode &status)  | 
882  | 0  | { | 
883  | 0  |     return readAlias(  | 
884  | 0  |         alias, strings, types, replacementIndexes, length,  | 
885  |  | #if U_DEBUG  | 
886  |  |         [](const char* type) { | 
887  |  |             U_ASSERT(uprv_strlen(type) >= 3 && uprv_strlen(type) <= 8);  | 
888  |  |         },  | 
889  |  | #else  | 
890  | 0  |         [](const char*) {}, | 
891  | 0  | #endif  | 
892  | 0  |         [](const UnicodeString&) { }, | 
893  | 0  |         status);  | 
894  | 0  | }  | 
895  |  |  | 
896  |  | /**  | 
897  |  |  * Initializes the alias data from the ICU resource bundles. The alias data  | 
898  |  |  * contains alias of language, country, script and variants.  | 
899  |  |  *  | 
900  |  |  * If the alias data has already loaded, then this method simply returns without  | 
901  |  |  * doing anything meaningful.  | 
902  |  |  */  | 
903  |  | void U_CALLCONV  | 
904  |  | AliasData::loadData(UErrorCode &status)  | 
905  | 0  | { | 
906  |  | #ifdef LOCALE_CANONICALIZATION_DEBUG  | 
907  |  |     UDate start = uprv_getRawUTCtime();  | 
908  |  | #endif  // LOCALE_CANONICALIZATION_DEBUG  | 
909  | 0  |     ucln_common_registerCleanup(UCLN_COMMON_LOCALE_ALIAS, cleanup);  | 
910  | 0  |     AliasDataBuilder builder;  | 
911  | 0  |     gSingleton = builder.build(status);  | 
912  |  | #ifdef LOCALE_CANONICALIZATION_DEBUG  | 
913  |  |     UDate end = uprv_getRawUTCtime();  | 
914  |  |     printf("AliasData::loadData took total %f ms\n", end - start); | 
915  |  | #endif  // LOCALE_CANONICALIZATION_DEBUG  | 
916  | 0  | }  | 
917  |  |  | 
918  |  | /**  | 
919  |  |  * Build the alias data from resources.  | 
920  |  |  */  | 
921  |  | AliasData*  | 
922  | 0  | AliasDataBuilder::build(UErrorCode &status) { | 
923  | 0  |     LocalUResourceBundlePointer metadata(  | 
924  | 0  |         ures_openDirect(nullptr, "metadata", &status));  | 
925  | 0  |     LocalUResourceBundlePointer metadataAlias(  | 
926  | 0  |         ures_getByKey(metadata.getAlias(), "alias", nullptr, &status));  | 
927  | 0  |     LocalUResourceBundlePointer languageAlias(  | 
928  | 0  |         ures_getByKey(metadataAlias.getAlias(), "language", nullptr, &status));  | 
929  | 0  |     LocalUResourceBundlePointer scriptAlias(  | 
930  | 0  |         ures_getByKey(metadataAlias.getAlias(), "script", nullptr, &status));  | 
931  | 0  |     LocalUResourceBundlePointer territoryAlias(  | 
932  | 0  |         ures_getByKey(metadataAlias.getAlias(), "territory", nullptr, &status));  | 
933  | 0  |     LocalUResourceBundlePointer variantAlias(  | 
934  | 0  |         ures_getByKey(metadataAlias.getAlias(), "variant", nullptr, &status));  | 
935  | 0  |     LocalUResourceBundlePointer subdivisionAlias(  | 
936  | 0  |         ures_getByKey(metadataAlias.getAlias(), "subdivision", nullptr, &status));  | 
937  |  | 
  | 
938  | 0  |     if (U_FAILURE(status)) { | 
939  | 0  |         return nullptr;  | 
940  | 0  |     }  | 
941  | 0  |     int32_t languagesLength = 0, scriptLength = 0, territoryLength = 0,  | 
942  | 0  |             variantLength = 0, subdivisionLength = 0;  | 
943  |  |  | 
944  |  |     // Read the languageAlias into languageTypes, languageReplacementIndexes  | 
945  |  |     // and strings  | 
946  | 0  |     UniqueCharStrings strings(status);  | 
947  | 0  |     LocalMemory<const char*> languageTypes;  | 
948  | 0  |     LocalMemory<int32_t> languageReplacementIndexes;  | 
949  | 0  |     readLanguageAlias(languageAlias.getAlias(),  | 
950  | 0  |                       &strings,  | 
951  | 0  |                       languageTypes,  | 
952  | 0  |                       languageReplacementIndexes,  | 
953  | 0  |                       languagesLength,  | 
954  | 0  |                       status);  | 
955  |  |  | 
956  |  |     // Read the scriptAlias into scriptTypes, scriptReplacementIndexes  | 
957  |  |     // and strings  | 
958  | 0  |     LocalMemory<const char*> scriptTypes;  | 
959  | 0  |     LocalMemory<int32_t> scriptReplacementIndexes;  | 
960  | 0  |     readScriptAlias(scriptAlias.getAlias(),  | 
961  | 0  |                     &strings,  | 
962  | 0  |                     scriptTypes,  | 
963  | 0  |                     scriptReplacementIndexes,  | 
964  | 0  |                     scriptLength,  | 
965  | 0  |                     status);  | 
966  |  |  | 
967  |  |     // Read the territoryAlias into territoryTypes, territoryReplacementIndexes  | 
968  |  |     // and strings  | 
969  | 0  |     LocalMemory<const char*> territoryTypes;  | 
970  | 0  |     LocalMemory<int32_t> territoryReplacementIndexes;  | 
971  | 0  |     readTerritoryAlias(territoryAlias.getAlias(),  | 
972  | 0  |                        &strings,  | 
973  | 0  |                        territoryTypes,  | 
974  | 0  |                        territoryReplacementIndexes,  | 
975  | 0  |                        territoryLength, status);  | 
976  |  |  | 
977  |  |     // Read the variantAlias into variantTypes, variantReplacementIndexes  | 
978  |  |     // and strings  | 
979  | 0  |     LocalMemory<const char*> variantTypes;  | 
980  | 0  |     LocalMemory<int32_t> variantReplacementIndexes;  | 
981  | 0  |     readVariantAlias(variantAlias.getAlias(),  | 
982  | 0  |                      &strings,  | 
983  | 0  |                      variantTypes,  | 
984  | 0  |                      variantReplacementIndexes,  | 
985  | 0  |                      variantLength, status);  | 
986  |  |  | 
987  |  |     // Read the subdivisionAlias into subdivisionTypes, subdivisionReplacementIndexes  | 
988  |  |     // and strings  | 
989  | 0  |     LocalMemory<const char*> subdivisionTypes;  | 
990  | 0  |     LocalMemory<int32_t> subdivisionReplacementIndexes;  | 
991  | 0  |     readSubdivisionAlias(subdivisionAlias.getAlias(),  | 
992  | 0  |                          &strings,  | 
993  | 0  |                          subdivisionTypes,  | 
994  | 0  |                          subdivisionReplacementIndexes,  | 
995  | 0  |                          subdivisionLength, status);  | 
996  |  | 
  | 
997  | 0  |     if (U_FAILURE(status)) { | 
998  | 0  |         return nullptr;  | 
999  | 0  |     }  | 
1000  |  |  | 
1001  |  |     // We can only use strings after freeze it.  | 
1002  | 0  |     strings.freeze();  | 
1003  |  |  | 
1004  |  |     // Build the languageMap from languageTypes & languageReplacementIndexes  | 
1005  | 0  |     CharStringMap languageMap(490, status);  | 
1006  | 0  |     for (int32_t i = 0; U_SUCCESS(status) && i < languagesLength; i++) { | 
1007  | 0  |         languageMap.put(languageTypes[i],  | 
1008  | 0  |                         strings.get(languageReplacementIndexes[i]),  | 
1009  | 0  |                         status);  | 
1010  | 0  |     }  | 
1011  |  |  | 
1012  |  |     // Build the scriptMap from scriptTypes & scriptReplacementIndexes  | 
1013  | 0  |     CharStringMap scriptMap(1, status);  | 
1014  | 0  |     for (int32_t i = 0; U_SUCCESS(status) && i < scriptLength; i++) { | 
1015  | 0  |         scriptMap.put(scriptTypes[i],  | 
1016  | 0  |                       strings.get(scriptReplacementIndexes[i]),  | 
1017  | 0  |                       status);  | 
1018  | 0  |     }  | 
1019  |  |  | 
1020  |  |     // Build the territoryMap from territoryTypes & territoryReplacementIndexes  | 
1021  | 0  |     CharStringMap territoryMap(650, status);  | 
1022  | 0  |     for (int32_t i = 0; U_SUCCESS(status) && i < territoryLength; i++) { | 
1023  | 0  |         territoryMap.put(territoryTypes[i],  | 
1024  | 0  |                          strings.get(territoryReplacementIndexes[i]),  | 
1025  | 0  |                          status);  | 
1026  | 0  |     }  | 
1027  |  |  | 
1028  |  |     // Build the variantMap from variantTypes & variantReplacementIndexes.  | 
1029  | 0  |     CharStringMap variantMap(2, status);  | 
1030  | 0  |     for (int32_t i = 0; U_SUCCESS(status) && i < variantLength; i++) { | 
1031  | 0  |         variantMap.put(variantTypes[i],  | 
1032  | 0  |                        strings.get(variantReplacementIndexes[i]),  | 
1033  | 0  |                        status);  | 
1034  | 0  |     }  | 
1035  |  |  | 
1036  |  |     // Build the subdivisionMap from subdivisionTypes & subdivisionReplacementIndexes.  | 
1037  | 0  |     CharStringMap subdivisionMap(2, status);  | 
1038  | 0  |     for (int32_t i = 0; U_SUCCESS(status) && i < subdivisionLength; i++) { | 
1039  | 0  |         subdivisionMap.put(subdivisionTypes[i],  | 
1040  | 0  |                        strings.get(subdivisionReplacementIndexes[i]),  | 
1041  | 0  |                        status);  | 
1042  | 0  |     }  | 
1043  |  | 
  | 
1044  | 0  |     if (U_FAILURE(status)) { | 
1045  | 0  |         return nullptr;  | 
1046  | 0  |     }  | 
1047  |  |  | 
1048  |  |     // copy hashtables  | 
1049  | 0  |     auto *data = new AliasData(  | 
1050  | 0  |         std::move(languageMap),  | 
1051  | 0  |         std::move(scriptMap),  | 
1052  | 0  |         std::move(territoryMap),  | 
1053  | 0  |         std::move(variantMap),  | 
1054  | 0  |         std::move(subdivisionMap),  | 
1055  | 0  |         strings.orphanCharStrings());  | 
1056  |  | 
  | 
1057  | 0  |     if (data == nullptr) { | 
1058  | 0  |         status = U_MEMORY_ALLOCATION_ERROR;  | 
1059  | 0  |     }  | 
1060  | 0  |     return data;  | 
1061  | 0  | }  | 
1062  |  |  | 
1063  |  | /**  | 
1064  |  |  * A class that find the replacement values of locale fields by using AliasData.  | 
1065  |  |  */  | 
1066  |  | class AliasReplacer { | 
1067  |  | public:  | 
1068  |  |     AliasReplacer(UErrorCode status) :  | 
1069  | 0  |             language(nullptr), script(nullptr), region(nullptr),  | 
1070  | 0  |             extensions(nullptr), variants(status),  | 
1071  | 0  |             data(nullptr) { | 
1072  | 0  |     }  | 
1073  | 0  |     ~AliasReplacer() { | 
1074  | 0  |     }  | 
1075  |  |  | 
1076  |  |     // Check the fields inside locale, if need to replace fields,  | 
1077  |  |     // place the the replaced locale ID in out and return true.  | 
1078  |  |     // Otherwise return false for no replacement or error.  | 
1079  |  |     bool replace(  | 
1080  |  |         const Locale& locale, CharString& out, UErrorCode& status);  | 
1081  |  |  | 
1082  |  | private:  | 
1083  |  |     const char* language;  | 
1084  |  |     const char* script;  | 
1085  |  |     const char* region;  | 
1086  |  |     const char* extensions;  | 
1087  |  |     UVector variants;  | 
1088  |  |  | 
1089  |  |     const AliasData* data;  | 
1090  |  |  | 
1091  | 0  |     inline bool notEmpty(const char* str) { | 
1092  | 0  |         return str && str[0] != NULL_CHAR;  | 
1093  | 0  |     }  | 
1094  |  |  | 
1095  |  |     /**  | 
1096  |  |      * If replacement is neither null nor empty and input is either null or empty,  | 
1097  |  |      * return replacement.  | 
1098  |  |      * If replacement is neither null nor empty but input is not empty, return input.  | 
1099  |  |      * If replacement is either null or empty and type is either null or empty,  | 
1100  |  |      * return input.  | 
1101  |  |      * Otherwise return null.  | 
1102  |  |      *   replacement     input      type        return  | 
1103  |  |      *    AAA             nullptr    *           AAA  | 
1104  |  |      *    AAA             BBB        *           BBB  | 
1105  |  |      *    nullptr || ""   CCC        nullptr     CCC  | 
1106  |  |      *    nullptr || ""   *          DDD         nullptr  | 
1107  |  |      */  | 
1108  |  |     inline const char* deleteOrReplace(  | 
1109  | 0  |             const char* input, const char* type, const char* replacement) { | 
1110  | 0  |         return notEmpty(replacement) ?  | 
1111  | 0  |             ((input == nullptr) ?  replacement : input) :  | 
1112  | 0  |             ((type == nullptr) ? input  : nullptr);  | 
1113  | 0  |     }  | 
1114  |  |  | 
1115  | 0  |     inline bool same(const char* a, const char* b) { | 
1116  | 0  |         if (a == nullptr && b == nullptr) { | 
1117  | 0  |             return true;  | 
1118  | 0  |         }  | 
1119  | 0  |         if ((a == nullptr && b != nullptr) ||  | 
1120  | 0  |             (a != nullptr && b == nullptr)) { | 
1121  | 0  |           return false;  | 
1122  | 0  |         }  | 
1123  | 0  |         return uprv_strcmp(a, b) == 0;  | 
1124  | 0  |     }  | 
1125  |  |  | 
1126  |  |     // Gather fields and generate locale ID into out.  | 
1127  |  |     CharString& outputToString(CharString& out, UErrorCode status);  | 
1128  |  |  | 
1129  |  |     // Generate the lookup key.  | 
1130  |  |     CharString& generateKey(const char* language, const char* region,  | 
1131  |  |                             const char* variant, CharString& out,  | 
1132  |  |                             UErrorCode status);  | 
1133  |  |  | 
1134  |  |     void parseLanguageReplacement(const char* replacement,  | 
1135  |  |                                   const char*& replaceLanguage,  | 
1136  |  |                                   const char*& replaceScript,  | 
1137  |  |                                   const char*& replaceRegion,  | 
1138  |  |                                   const char*& replaceVariant,  | 
1139  |  |                                   const char*& replaceExtensions,  | 
1140  |  |                                   UVector& toBeFreed,  | 
1141  |  |                                   UErrorCode& status);  | 
1142  |  |  | 
1143  |  |     // Replace by using languageAlias.  | 
1144  |  |     bool replaceLanguage(bool checkLanguage, bool checkRegion,  | 
1145  |  |                          bool checkVariants, UVector& toBeFreed,  | 
1146  |  |                          UErrorCode& status);  | 
1147  |  |  | 
1148  |  |     // Replace by using territoryAlias.  | 
1149  |  |     bool replaceTerritory(UVector& toBeFreed, UErrorCode& status);  | 
1150  |  |  | 
1151  |  |     // Replace by using scriptAlias.  | 
1152  |  |     bool replaceScript(UErrorCode& status);  | 
1153  |  |  | 
1154  |  |     // Replace by using variantAlias.  | 
1155  |  |     bool replaceVariant(UErrorCode& status);  | 
1156  |  |  | 
1157  |  |     // Replace by using subdivisionAlias.  | 
1158  |  |     bool replaceSubdivision(StringPiece subdivision,  | 
1159  |  |                             CharString& output, UErrorCode& status);  | 
1160  |  |  | 
1161  |  |     // Replace transformed extensions.  | 
1162  |  |     bool replaceTransformedExtensions(  | 
1163  |  |         CharString& transformedExtensions, CharString& output, UErrorCode& status);  | 
1164  |  | };  | 
1165  |  |  | 
1166  |  | CharString&  | 
1167  |  | AliasReplacer::generateKey(  | 
1168  |  |         const char* language, const char* region, const char* variant,  | 
1169  |  |         CharString& out, UErrorCode status)  | 
1170  | 0  | { | 
1171  | 0  |     out.append(language, status);  | 
1172  | 0  |     if (notEmpty(region)) { | 
1173  | 0  |         out.append(SEP_CHAR, status)  | 
1174  | 0  |             .append(region, status);  | 
1175  | 0  |     }  | 
1176  | 0  |     if (notEmpty(variant)) { | 
1177  | 0  |        out.append(SEP_CHAR, status)  | 
1178  | 0  |            .append(variant, status);  | 
1179  | 0  |     }  | 
1180  | 0  |     return out;  | 
1181  | 0  | }  | 
1182  |  |  | 
1183  |  | void  | 
1184  |  | AliasReplacer::parseLanguageReplacement(  | 
1185  |  |     const char* replacement,  | 
1186  |  |     const char*& replacedLanguage,  | 
1187  |  |     const char*& replacedScript,  | 
1188  |  |     const char*& replacedRegion,  | 
1189  |  |     const char*& replacedVariant,  | 
1190  |  |     const char*& replacedExtensions,  | 
1191  |  |     UVector& toBeFreed,  | 
1192  |  |     UErrorCode& status)  | 
1193  | 0  | { | 
1194  | 0  |     if (U_FAILURE(status)) { | 
1195  | 0  |         return;  | 
1196  | 0  |     }  | 
1197  | 0  |     replacedScript = replacedRegion = replacedVariant  | 
1198  | 0  |         = replacedExtensions = nullptr;  | 
1199  | 0  |     if (uprv_strchr(replacement, '_') == nullptr) { | 
1200  | 0  |         replacedLanguage = replacement;  | 
1201  |  |         // reach the end, just return it.  | 
1202  | 0  |         return;  | 
1203  | 0  |     }  | 
1204  |  |     // We have multiple field so we have to allocate and parse  | 
1205  | 0  |     CharString* str = new CharString(  | 
1206  | 0  |         replacement, (int32_t)uprv_strlen(replacement), status);  | 
1207  | 0  |     if (U_FAILURE(status)) { | 
1208  | 0  |         return;  | 
1209  | 0  |     }  | 
1210  | 0  |     if (str == nullptr) { | 
1211  | 0  |         status = U_MEMORY_ALLOCATION_ERROR;  | 
1212  | 0  |         return;  | 
1213  | 0  |     }  | 
1214  | 0  |     toBeFreed.addElementX(str, status);  | 
1215  | 0  |     char* data = str->data();  | 
1216  | 0  |     replacedLanguage = (const char*) data;  | 
1217  | 0  |     char* endOfField = uprv_strchr(data, '_');  | 
1218  | 0  |     *endOfField = '\0'; // null terminiate it.  | 
1219  | 0  |     endOfField++;  | 
1220  | 0  |     const char* start = endOfField;  | 
1221  | 0  |     endOfField = (char*) uprv_strchr(start, '_');  | 
1222  | 0  |     size_t len = 0;  | 
1223  | 0  |     if (endOfField == nullptr) { | 
1224  | 0  |         len = uprv_strlen(start);  | 
1225  | 0  |     } else { | 
1226  | 0  |         len = endOfField - start;  | 
1227  | 0  |         *endOfField = '\0'; // null terminiate it.  | 
1228  | 0  |     }  | 
1229  | 0  |     if (len == 4 && uprv_isASCIILetter(*start)) { | 
1230  |  |         // Got a script  | 
1231  | 0  |         replacedScript = start;  | 
1232  | 0  |         if (endOfField == nullptr) { | 
1233  | 0  |             return;  | 
1234  | 0  |         }  | 
1235  | 0  |         start = endOfField++;  | 
1236  | 0  |         endOfField = (char*)uprv_strchr(start, '_');  | 
1237  | 0  |         if (endOfField == nullptr) { | 
1238  | 0  |             len = uprv_strlen(start);  | 
1239  | 0  |         } else { | 
1240  | 0  |             len = endOfField - start;  | 
1241  | 0  |             *endOfField = '\0'; // null terminiate it.  | 
1242  | 0  |         }  | 
1243  | 0  |     }  | 
1244  | 0  |     if (len >= 2 && len <= 3) { | 
1245  |  |         // Got a region  | 
1246  | 0  |         replacedRegion = start;  | 
1247  | 0  |         if (endOfField == nullptr) { | 
1248  | 0  |             return;  | 
1249  | 0  |         }  | 
1250  | 0  |         start = endOfField++;  | 
1251  | 0  |         endOfField = (char*)uprv_strchr(start, '_');  | 
1252  | 0  |         if (endOfField == nullptr) { | 
1253  | 0  |             len = uprv_strlen(start);  | 
1254  | 0  |         } else { | 
1255  | 0  |             len = endOfField - start;  | 
1256  | 0  |             *endOfField = '\0'; // null terminiate it.  | 
1257  | 0  |         }  | 
1258  | 0  |     }  | 
1259  | 0  |     if (len >= 4) { | 
1260  |  |         // Got a variant  | 
1261  | 0  |         replacedVariant = start;  | 
1262  | 0  |         if (endOfField == nullptr) { | 
1263  | 0  |             return;  | 
1264  | 0  |         }  | 
1265  | 0  |         start = endOfField++;  | 
1266  | 0  |     }  | 
1267  | 0  |     replacedExtensions = start;  | 
1268  | 0  | }  | 
1269  |  |  | 
1270  |  | bool  | 
1271  |  | AliasReplacer::replaceLanguage(  | 
1272  |  |         bool checkLanguage, bool checkRegion,  | 
1273  |  |         bool checkVariants, UVector& toBeFreed, UErrorCode& status)  | 
1274  | 0  | { | 
1275  | 0  |     if (U_FAILURE(status)) { | 
1276  | 0  |         return false;  | 
1277  | 0  |     }  | 
1278  | 0  |     if (    (checkRegion && region == nullptr) ||  | 
1279  | 0  |             (checkVariants && variants.size() == 0)) { | 
1280  |  |         // Nothing to search.  | 
1281  | 0  |         return false;  | 
1282  | 0  |     }  | 
1283  | 0  |     int32_t variant_size = checkVariants ? variants.size() : 1;  | 
1284  |  |     // Since we may have more than one variant, we need to loop through them.  | 
1285  | 0  |     const char* searchLanguage = checkLanguage ? language : "und";  | 
1286  | 0  |     const char* searchRegion = checkRegion ? region : nullptr;  | 
1287  | 0  |     const char* searchVariant = nullptr;  | 
1288  | 0  |     for (int32_t variant_index = 0;  | 
1289  | 0  |             variant_index < variant_size;  | 
1290  | 0  |             variant_index++) { | 
1291  | 0  |         if (checkVariants) { | 
1292  | 0  |             U_ASSERT(variant_index < variant_size);  | 
1293  | 0  |             searchVariant = (const char*)(variants.elementAt(variant_index));  | 
1294  | 0  |         }  | 
1295  |  | 
  | 
1296  | 0  |         if (searchVariant != nullptr && uprv_strlen(searchVariant) < 4) { | 
1297  |  |             // Do not consider  ill-formed variant subtag.  | 
1298  | 0  |             searchVariant = nullptr;  | 
1299  | 0  |         }  | 
1300  | 0  |         CharString typeKey;  | 
1301  | 0  |         generateKey(searchLanguage, searchRegion, searchVariant, typeKey,  | 
1302  | 0  |                     status);  | 
1303  | 0  |         if (U_FAILURE(status)) { | 
1304  | 0  |             return false;  | 
1305  | 0  |         }  | 
1306  | 0  |         const char *replacement = data->languageMap().get(typeKey.data());  | 
1307  | 0  |         if (replacement == nullptr) { | 
1308  |  |             // Found no replacement data.  | 
1309  | 0  |             continue;  | 
1310  | 0  |         }  | 
1311  |  |  | 
1312  | 0  |         const char* replacedLanguage = nullptr;  | 
1313  | 0  |         const char* replacedScript = nullptr;  | 
1314  | 0  |         const char* replacedRegion = nullptr;  | 
1315  | 0  |         const char* replacedVariant = nullptr;  | 
1316  | 0  |         const char* replacedExtensions = nullptr;  | 
1317  | 0  |         parseLanguageReplacement(replacement,  | 
1318  | 0  |                                  replacedLanguage,  | 
1319  | 0  |                                  replacedScript,  | 
1320  | 0  |                                  replacedRegion,  | 
1321  | 0  |                                  replacedVariant,  | 
1322  | 0  |                                  replacedExtensions,  | 
1323  | 0  |                                  toBeFreed,  | 
1324  | 0  |                                  status);  | 
1325  | 0  |         replacedLanguage =  | 
1326  | 0  |             (replacedLanguage != nullptr && uprv_strcmp(replacedLanguage, "und") == 0) ?  | 
1327  | 0  |             language : replacedLanguage;  | 
1328  | 0  |         replacedScript = deleteOrReplace(script, nullptr, replacedScript);  | 
1329  | 0  |         replacedRegion = deleteOrReplace(region, searchRegion, replacedRegion);  | 
1330  | 0  |         replacedVariant = deleteOrReplace(  | 
1331  | 0  |             searchVariant, searchVariant, replacedVariant);  | 
1332  |  | 
  | 
1333  | 0  |         if (    same(language, replacedLanguage) &&  | 
1334  | 0  |                 same(script, replacedScript) &&  | 
1335  | 0  |                 same(region, replacedRegion) &&  | 
1336  | 0  |                 same(searchVariant, replacedVariant) &&  | 
1337  | 0  |                 replacedExtensions == nullptr) { | 
1338  |  |             // Replacement produce no changes.  | 
1339  | 0  |             continue;  | 
1340  | 0  |         }  | 
1341  |  |  | 
1342  | 0  |         language = replacedLanguage;  | 
1343  | 0  |         region = replacedRegion;  | 
1344  | 0  |         script = replacedScript;  | 
1345  | 0  |         if (searchVariant != nullptr) { | 
1346  | 0  |             if (notEmpty(replacedVariant)) { | 
1347  | 0  |                 variants.setElementAt((void*)replacedVariant, variant_index);  | 
1348  | 0  |             } else { | 
1349  | 0  |                 variants.removeElementAt(variant_index);  | 
1350  | 0  |             }  | 
1351  | 0  |         }  | 
1352  | 0  |         if (replacedExtensions != nullptr) { | 
1353  |  |             // DO NOTHING  | 
1354  |  |             // UTS35 does not specify what should we do if we have extensions in the  | 
1355  |  |             // replacement. Currently we know only the following 4 "BCP47 LegacyRules" have  | 
1356  |  |             // extensions in them languageAlias:  | 
1357  |  |             //  i_default => en_x_i_default  | 
1358  |  |             //  i_enochian => und_x_i_enochian  | 
1359  |  |             //  i_mingo => see_x_i_mingo  | 
1360  |  |             //  zh_min => nan_x_zh_min  | 
1361  |  |             // But all of them are already changed by code inside ultag_parse() before  | 
1362  |  |             // hitting this code.  | 
1363  | 0  |         }  | 
1364  |  |  | 
1365  |  |         // Something changed by language alias data.  | 
1366  | 0  |         return true;  | 
1367  | 0  |     }  | 
1368  |  |     // Nothing changed by language alias data.  | 
1369  | 0  |     return false;  | 
1370  | 0  | }  | 
1371  |  |  | 
1372  |  | bool  | 
1373  |  | AliasReplacer::replaceTerritory(UVector& toBeFreed, UErrorCode& status)  | 
1374  | 0  | { | 
1375  | 0  |     if (U_FAILURE(status)) { | 
1376  | 0  |         return false;  | 
1377  | 0  |     }  | 
1378  | 0  |     if (region == nullptr) { | 
1379  |  |         // No region to search.  | 
1380  | 0  |         return false;  | 
1381  | 0  |     }  | 
1382  | 0  |     const char *replacement = data->territoryMap().get(region);  | 
1383  | 0  |     if (replacement == nullptr) { | 
1384  |  |         // Found no replacement data for this region.  | 
1385  | 0  |         return false;  | 
1386  | 0  |     }  | 
1387  | 0  |     const char* replacedRegion = replacement;  | 
1388  | 0  |     const char* firstSpace = uprv_strchr(replacement, ' ');  | 
1389  | 0  |     if (firstSpace != nullptr) { | 
1390  |  |         // If there are are more than one region in the replacement.  | 
1391  |  |         // We need to check which one match based on the language.  | 
1392  |  |         // Cannot use nullptr for language because that will construct  | 
1393  |  |         // the default locale, in that case, use "und" to get the correct  | 
1394  |  |         // locale.  | 
1395  | 0  |         Locale l = LocaleBuilder()  | 
1396  | 0  |             .setLanguage(language == nullptr ? "und" : language)  | 
1397  | 0  |             .setScript(script)  | 
1398  | 0  |             .build(status);  | 
1399  | 0  |         l.addLikelySubtags(status);  | 
1400  | 0  |         const char* likelyRegion = l.getCountry();  | 
1401  | 0  |         LocalPointer<CharString> item;  | 
1402  | 0  |         if (likelyRegion != nullptr && uprv_strlen(likelyRegion) > 0) { | 
1403  | 0  |             size_t len = uprv_strlen(likelyRegion);  | 
1404  | 0  |             const char* foundInReplacement = uprv_strstr(replacement,  | 
1405  | 0  |                                                          likelyRegion);  | 
1406  | 0  |             if (foundInReplacement != nullptr) { | 
1407  |  |                 // Assuming the case there are no three letter region code in  | 
1408  |  |                 // the replacement of territoryAlias  | 
1409  | 0  |                 U_ASSERT(foundInReplacement == replacement ||  | 
1410  | 0  |                          *(foundInReplacement-1) == ' ');  | 
1411  | 0  |                 U_ASSERT(foundInReplacement[len] == ' ' ||  | 
1412  | 0  |                          foundInReplacement[len] == '\0');  | 
1413  | 0  |                 item.adoptInsteadAndCheckErrorCode(  | 
1414  | 0  |                     new CharString(foundInReplacement, (int32_t)len, status), status);  | 
1415  | 0  |             }  | 
1416  | 0  |         }  | 
1417  | 0  |         if (item.isNull() && U_SUCCESS(status)) { | 
1418  | 0  |             item.adoptInsteadAndCheckErrorCode(  | 
1419  | 0  |                 new CharString(replacement,  | 
1420  | 0  |                                (int32_t)(firstSpace - replacement), status), status);  | 
1421  | 0  |         }  | 
1422  | 0  |         if (U_FAILURE(status)) { return false; } | 
1423  | 0  |         if (item.isNull()) { | 
1424  | 0  |             status = U_MEMORY_ALLOCATION_ERROR;  | 
1425  | 0  |             return false;  | 
1426  | 0  |         }  | 
1427  | 0  |         replacedRegion = item->data();  | 
1428  | 0  |         toBeFreed.addElementX(item.orphan(), status);  | 
1429  | 0  |     }  | 
1430  | 0  |     U_ASSERT(!same(region, replacedRegion));  | 
1431  | 0  |     region = replacedRegion;  | 
1432  |  |     // The region is changed by data in territory alias.  | 
1433  | 0  |     return true;  | 
1434  | 0  | }  | 
1435  |  |  | 
1436  |  | bool  | 
1437  |  | AliasReplacer::replaceScript(UErrorCode& status)  | 
1438  | 0  | { | 
1439  | 0  |     if (U_FAILURE(status)) { | 
1440  | 0  |         return false;  | 
1441  | 0  |     }  | 
1442  | 0  |     if (script == nullptr) { | 
1443  |  |         // No script to search.  | 
1444  | 0  |         return false;  | 
1445  | 0  |     }  | 
1446  | 0  |     const char *replacement = data->scriptMap().get(script);  | 
1447  | 0  |     if (replacement == nullptr) { | 
1448  |  |         // Found no replacement data for this script.  | 
1449  | 0  |         return false;  | 
1450  | 0  |     }  | 
1451  | 0  |     U_ASSERT(!same(script, replacement));  | 
1452  | 0  |     script = replacement;  | 
1453  |  |     // The script is changed by data in script alias.  | 
1454  | 0  |     return true;  | 
1455  | 0  | }  | 
1456  |  |  | 
1457  |  | bool  | 
1458  |  | AliasReplacer::replaceVariant(UErrorCode& status)  | 
1459  | 0  | { | 
1460  | 0  |     if (U_FAILURE(status)) { | 
1461  | 0  |         return false;  | 
1462  | 0  |     }  | 
1463  |  |     // Since we may have more than one variant, we need to loop through them.  | 
1464  | 0  |     for (int32_t i = 0; i < variants.size(); i++) { | 
1465  | 0  |         const char *variant = (const char*)(variants.elementAt(i));  | 
1466  | 0  |         const char *replacement = data->variantMap().get(variant);  | 
1467  | 0  |         if (replacement == nullptr) { | 
1468  |  |             // Found no replacement data for this variant.  | 
1469  | 0  |             continue;  | 
1470  | 0  |         }  | 
1471  | 0  |         U_ASSERT((uprv_strlen(replacement) >= 5  &&  | 
1472  | 0  |                   uprv_strlen(replacement) <= 8) ||  | 
1473  | 0  |                  (uprv_strlen(replacement) == 4 &&  | 
1474  | 0  |                   replacement[0] >= '0' &&  | 
1475  | 0  |                   replacement[0] <= '9'));  | 
1476  | 0  |         if (!same(variant, replacement)) { | 
1477  | 0  |             variants.setElementAt((void*)replacement, i);  | 
1478  |  |             // Special hack to handle hepburn-heploc => alalc97  | 
1479  | 0  |             if (uprv_strcmp(variant, "heploc") == 0) { | 
1480  | 0  |                 for (int32_t j = 0; j < variants.size(); j++) { | 
1481  | 0  |                      if (uprv_strcmp((const char*)(variants.elementAt(j)),  | 
1482  | 0  |                                      "hepburn") == 0) { | 
1483  | 0  |                          variants.removeElementAt(j);  | 
1484  | 0  |                      }  | 
1485  | 0  |                 }  | 
1486  | 0  |             }  | 
1487  | 0  |             return true;  | 
1488  | 0  |         }  | 
1489  | 0  |     }  | 
1490  | 0  |     return false;  | 
1491  | 0  | }  | 
1492  |  |  | 
1493  |  | bool  | 
1494  |  | AliasReplacer::replaceSubdivision(  | 
1495  |  |     StringPiece subdivision, CharString& output, UErrorCode& status)  | 
1496  | 0  | { | 
1497  | 0  |     if (U_FAILURE(status)) { | 
1498  | 0  |         return false;  | 
1499  | 0  |     }  | 
1500  | 0  |     const char *replacement = data->subdivisionMap().get(subdivision.data());  | 
1501  | 0  |     if (replacement != nullptr) { | 
1502  | 0  |         const char* firstSpace = uprv_strchr(replacement, ' ');  | 
1503  |  |         // Found replacement data for this subdivision.  | 
1504  | 0  |         size_t len = (firstSpace != nullptr) ?  | 
1505  | 0  |             (firstSpace - replacement) : uprv_strlen(replacement);  | 
1506  | 0  |         if (2 <= len && len <= 8) { | 
1507  | 0  |             output.append(replacement, (int32_t)len, status);  | 
1508  | 0  |             if (2 == len) { | 
1509  |  |                 // Add 'zzzz' based on changes to UTS #35 for CLDR-14312.  | 
1510  | 0  |                 output.append("zzzz", 4, status); | 
1511  | 0  |             }  | 
1512  | 0  |         }  | 
1513  | 0  |         return true;  | 
1514  | 0  |     }  | 
1515  | 0  |     return false;  | 
1516  | 0  | }  | 
1517  |  |  | 
1518  |  | bool  | 
1519  |  | AliasReplacer::replaceTransformedExtensions(  | 
1520  |  |     CharString& transformedExtensions, CharString& output, UErrorCode& status)  | 
1521  | 0  | { | 
1522  |  |     // The content of the transformedExtensions will be modified in this  | 
1523  |  |     // function to NULL-terminating (tkey-tvalue) pairs.  | 
1524  | 0  |     if (U_FAILURE(status)) { | 
1525  | 0  |         return false;  | 
1526  | 0  |     }  | 
1527  | 0  |     int32_t len = transformedExtensions.length();  | 
1528  | 0  |     const char* str = transformedExtensions.data();  | 
1529  | 0  |     const char* tkey = ultag_getTKeyStart(str);  | 
1530  | 0  |     int32_t tlangLen = (tkey == str) ? 0 :  | 
1531  | 0  |         ((tkey == nullptr) ? len : static_cast<int32_t>((tkey - str - 1)));  | 
1532  | 0  |     CharStringByteSink sink(&output);  | 
1533  | 0  |     if (tlangLen > 0) { | 
1534  | 0  |         Locale tlang = LocaleBuilder()  | 
1535  | 0  |             .setLanguageTag(StringPiece(str, tlangLen))  | 
1536  | 0  |             .build(status);  | 
1537  | 0  |         tlang.canonicalize(status);  | 
1538  | 0  |         tlang.toLanguageTag(sink, status);  | 
1539  | 0  |         if (U_FAILURE(status)) { | 
1540  | 0  |             return false;  | 
1541  | 0  |         }  | 
1542  | 0  |         T_CString_toLowerCase(output.data());  | 
1543  | 0  |     }  | 
1544  | 0  |     if (tkey != nullptr) { | 
1545  |  |         // We need to sort the tfields by tkey  | 
1546  | 0  |         UVector tfields(status);  | 
1547  | 0  |         if (U_FAILURE(status)) { | 
1548  | 0  |             return false;  | 
1549  | 0  |         }  | 
1550  | 0  |         do { | 
1551  | 0  |             const char* tvalue = uprv_strchr(tkey, '-');  | 
1552  | 0  |             if (tvalue == nullptr) { | 
1553  | 0  |                 status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1554  | 0  |                 return false;  | 
1555  | 0  |             }  | 
1556  | 0  |             const char* nextTKey = ultag_getTKeyStart(tvalue);  | 
1557  | 0  |             if (nextTKey != nullptr) { | 
1558  | 0  |                 *((char*)(nextTKey-1)) = '\0';  // NULL terminate tvalue  | 
1559  | 0  |             }  | 
1560  | 0  |             tfields.insertElementAt((void*)tkey, tfields.size(), status);  | 
1561  | 0  |             if (U_FAILURE(status)) { | 
1562  | 0  |                 return false;  | 
1563  | 0  |             }  | 
1564  | 0  |             tkey = nextTKey;  | 
1565  | 0  |         } while (tkey != nullptr);  | 
1566  | 0  |         tfields.sort([](UElement e1, UElement e2) -> int32_t { | 
1567  | 0  |             return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer);  | 
1568  | 0  |         }, status);  | 
1569  | 0  |         for (int32_t i = 0; i < tfields.size(); i++) { | 
1570  | 0  |              if (output.length() > 0) { | 
1571  | 0  |                  output.append('-', status); | 
1572  | 0  |              }  | 
1573  | 0  |              const char* tfield = (const char*) tfields.elementAt(i);  | 
1574  | 0  |              const char* tvalue = uprv_strchr(tfield, '-');  | 
1575  | 0  |              if (tvalue == nullptr) { | 
1576  | 0  |                  status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1577  | 0  |                  return false;  | 
1578  | 0  |              }  | 
1579  |  |              // Split the "tkey-tvalue" pair string so that we can canonicalize the tvalue.  | 
1580  | 0  |              *((char*)tvalue++) = '\0'; // NULL terminate tkey  | 
1581  | 0  |              output.append(tfield, status).append('-', status); | 
1582  | 0  |              const char* bcpTValue = ulocimp_toBcpType(tfield, tvalue, nullptr, nullptr);  | 
1583  | 0  |              output.append((bcpTValue == nullptr) ? tvalue : bcpTValue, status);  | 
1584  | 0  |         }  | 
1585  | 0  |     }  | 
1586  | 0  |     if (U_FAILURE(status)) { | 
1587  | 0  |         return false;  | 
1588  | 0  |     }  | 
1589  | 0  |     return true;  | 
1590  | 0  | }  | 
1591  |  |  | 
1592  |  | CharString&  | 
1593  |  | AliasReplacer::outputToString(  | 
1594  |  |     CharString& out, UErrorCode status)  | 
1595  | 0  | { | 
1596  | 0  |     out.append(language, status);  | 
1597  | 0  |     if (notEmpty(script)) { | 
1598  | 0  |         out.append(SEP_CHAR, status)  | 
1599  | 0  |             .append(script, status);  | 
1600  | 0  |     }  | 
1601  | 0  |     if (notEmpty(region)) { | 
1602  | 0  |         out.append(SEP_CHAR, status)  | 
1603  | 0  |             .append(region, status);  | 
1604  | 0  |     }  | 
1605  | 0  |     if (variants.size() > 0) { | 
1606  | 0  |         if (!notEmpty(script) && !notEmpty(region)) { | 
1607  | 0  |           out.append(SEP_CHAR, status);  | 
1608  | 0  |         }  | 
1609  | 0  |         variants.sort([](UElement e1, UElement e2) -> int32_t { | 
1610  | 0  |             return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer);  | 
1611  | 0  |         }, status);  | 
1612  | 0  |         int32_t variantsStart = out.length();  | 
1613  | 0  |         for (int32_t i = 0; i < variants.size(); i++) { | 
1614  | 0  |              out.append(SEP_CHAR, status)  | 
1615  | 0  |                  .append((const char*)(variants.elementAt(i)),  | 
1616  | 0  |                          status);  | 
1617  | 0  |         }  | 
1618  | 0  |         T_CString_toUpperCase(out.data() + variantsStart);  | 
1619  | 0  |     }  | 
1620  | 0  |     if (notEmpty(extensions)) { | 
1621  | 0  |         CharString tmp("und_", status); | 
1622  | 0  |         tmp.append(extensions, status);  | 
1623  | 0  |         Locale tmpLocale(tmp.data());  | 
1624  |  |         // only support x extension inside CLDR for now.  | 
1625  | 0  |         U_ASSERT(extensions[0] == 'x');  | 
1626  | 0  |         out.append(tmpLocale.getName() + 1, status);  | 
1627  | 0  |     }  | 
1628  | 0  |     return out;  | 
1629  | 0  | }  | 
1630  |  |  | 
1631  |  | bool  | 
1632  |  | AliasReplacer::replace(const Locale& locale, CharString& out, UErrorCode& status)  | 
1633  | 0  | { | 
1634  | 0  |     data = AliasData::singleton(status);  | 
1635  | 0  |     if (U_FAILURE(status)) { | 
1636  | 0  |         return false;  | 
1637  | 0  |     }  | 
1638  | 0  |     U_ASSERT(data != nullptr);  | 
1639  | 0  |     out.clear();  | 
1640  | 0  |     language = locale.getLanguage();  | 
1641  | 0  |     if (!notEmpty(language)) { | 
1642  | 0  |         language = nullptr;  | 
1643  | 0  |     }  | 
1644  | 0  |     script = locale.getScript();  | 
1645  | 0  |     if (!notEmpty(script)) { | 
1646  | 0  |         script = nullptr;  | 
1647  | 0  |     }  | 
1648  | 0  |     region = locale.getCountry();  | 
1649  | 0  |     if (!notEmpty(region)) { | 
1650  | 0  |         region = nullptr;  | 
1651  | 0  |     }  | 
1652  | 0  |     const char* variantsStr = locale.getVariant();  | 
1653  | 0  |     CharString variantsBuff(variantsStr, -1, status);  | 
1654  | 0  |     if (!variantsBuff.isEmpty()) { | 
1655  | 0  |         if (U_FAILURE(status)) { return false; } | 
1656  | 0  |         char* start = variantsBuff.data();  | 
1657  | 0  |         T_CString_toLowerCase(start);  | 
1658  | 0  |         char* end;  | 
1659  | 0  |         while ((end = uprv_strchr(start, SEP_CHAR)) != nullptr &&  | 
1660  | 0  |                U_SUCCESS(status)) { | 
1661  | 0  |             *end = NULL_CHAR;  // null terminate inside variantsBuff  | 
1662  | 0  |             variants.addElementX(start, status);  | 
1663  | 0  |             start = end + 1;  | 
1664  | 0  |         }  | 
1665  | 0  |         variants.addElementX(start, status);  | 
1666  | 0  |     }  | 
1667  | 0  |     if (U_FAILURE(status)) { return false; } | 
1668  |  |  | 
1669  |  |     // Sort the variants  | 
1670  | 0  |     variants.sort([](UElement e1, UElement e2) -> int32_t { | 
1671  | 0  |         return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer);  | 
1672  | 0  |     }, status);  | 
1673  |  |  | 
1674  |  |     // A changed count to assert when loop too many times.  | 
1675  | 0  |     int changed = 0;  | 
1676  |  |     // A UVector to to hold CharString allocated by the replace* method  | 
1677  |  |     // and freed when out of scope from his function.  | 
1678  | 0  |     UVector stringsToBeFreed([](void *obj){ delete ((CharString*) obj); }, | 
1679  | 0  |                              nullptr, 10, status);  | 
1680  | 0  |     while (U_SUCCESS(status)) { | 
1681  |  |         // Something wrong with the data cause looping here more than 10 times  | 
1682  |  |         // already.  | 
1683  | 0  |         U_ASSERT(changed < 5);  | 
1684  |  |         // From observation of key in data/misc/metadata.txt  | 
1685  |  |         // we know currently we only need to search in the following combination  | 
1686  |  |         // of fields for type in languageAlias:  | 
1687  |  |         // * lang_region_variant  | 
1688  |  |         // * lang_region  | 
1689  |  |         // * lang_variant  | 
1690  |  |         // * lang  | 
1691  |  |         // * und_variant  | 
1692  |  |         // This assumption is ensured by the U_ASSERT in readLanguageAlias  | 
1693  |  |         //  | 
1694  |  |         //                      lang  REGION variant  | 
1695  | 0  |         if (    replaceLanguage(true, true,  true,  stringsToBeFreed, status) ||  | 
1696  | 0  |                 replaceLanguage(true, true,  false, stringsToBeFreed, status) ||  | 
1697  | 0  |                 replaceLanguage(true, false, true,  stringsToBeFreed, status) ||  | 
1698  | 0  |                 replaceLanguage(true, false, false, stringsToBeFreed, status) ||  | 
1699  | 0  |                 replaceLanguage(false,false, true,  stringsToBeFreed, status) ||  | 
1700  | 0  |                 replaceTerritory(stringsToBeFreed, status) ||  | 
1701  | 0  |                 replaceScript(status) ||  | 
1702  | 0  |                 replaceVariant(status)) { | 
1703  |  |             // Some values in data is changed, try to match from the beginning  | 
1704  |  |             // again.  | 
1705  | 0  |             changed++;  | 
1706  | 0  |             continue;  | 
1707  | 0  |         }  | 
1708  |  |         // Nothing changed. Break out.  | 
1709  | 0  |         break;  | 
1710  | 0  |     }  // while(1)  | 
1711  |  | 
  | 
1712  | 0  |     if (U_FAILURE(status)) { return false; } | 
1713  |  |     // Nothing changed and we know the order of the variants are not change  | 
1714  |  |     // because we have no variant or only one.  | 
1715  | 0  |     const char* extensionsStr = locale_getKeywordsStart(locale.getName());  | 
1716  | 0  |     if (changed == 0 && variants.size() <= 1 && extensionsStr == nullptr) { | 
1717  | 0  |         return false;  | 
1718  | 0  |     }  | 
1719  | 0  |     outputToString(out, status);  | 
1720  | 0  |     if (U_FAILURE(status)) { | 
1721  | 0  |         return false;  | 
1722  | 0  |     }  | 
1723  | 0  |     if (extensionsStr != nullptr) { | 
1724  | 0  |         changed = 0;  | 
1725  | 0  |         Locale temp(locale);  | 
1726  | 0  |         LocalPointer<icu::StringEnumeration> iter(locale.createKeywords(status));  | 
1727  | 0  |         if (U_SUCCESS(status) && !iter.isNull()) { | 
1728  | 0  |             const char* key;  | 
1729  | 0  |             while ((key = iter->next(nullptr, status)) != nullptr) { | 
1730  | 0  |                 if (uprv_strcmp("sd", key) == 0 || uprv_strcmp("rg", key) == 0 || | 
1731  | 0  |                         uprv_strcmp("t", key) == 0) { | 
1732  | 0  |                     CharString value;  | 
1733  | 0  |                     CharStringByteSink valueSink(&value);  | 
1734  | 0  |                     locale.getKeywordValue(key, valueSink, status);  | 
1735  | 0  |                     if (U_FAILURE(status)) { | 
1736  | 0  |                         status = U_ZERO_ERROR;  | 
1737  | 0  |                         continue;  | 
1738  | 0  |                     }  | 
1739  | 0  |                     CharString replacement;  | 
1740  | 0  |                     if (uprv_strlen(key) == 2) { | 
1741  | 0  |                         if (replaceSubdivision(value.toStringPiece(), replacement, status)) { | 
1742  | 0  |                             changed++;  | 
1743  | 0  |                             temp.setKeywordValue(key, replacement.data(), status);  | 
1744  | 0  |                         }  | 
1745  | 0  |                     } else { | 
1746  | 0  |                         U_ASSERT(uprv_strcmp(key, "t") == 0);  | 
1747  | 0  |                         if (replaceTransformedExtensions(value, replacement, status)) { | 
1748  | 0  |                             changed++;  | 
1749  | 0  |                             temp.setKeywordValue(key, replacement.data(), status);  | 
1750  | 0  |                         }  | 
1751  | 0  |                     }  | 
1752  | 0  |                     if (U_FAILURE(status)) { | 
1753  | 0  |                         return false;  | 
1754  | 0  |                     }  | 
1755  | 0  |                 }  | 
1756  | 0  |             }  | 
1757  | 0  |         }  | 
1758  | 0  |         if (changed != 0) { | 
1759  | 0  |             extensionsStr = locale_getKeywordsStart(temp.getName());  | 
1760  | 0  |         }  | 
1761  | 0  |         out.append(extensionsStr, status);  | 
1762  | 0  |     }  | 
1763  | 0  |     if (U_FAILURE(status)) { | 
1764  | 0  |         return false;  | 
1765  | 0  |     }  | 
1766  |  |     // If the tag is not changed, return.  | 
1767  | 0  |     if (uprv_strcmp(out.data(), locale.getName()) == 0) { | 
1768  | 0  |         out.clear();  | 
1769  | 0  |         return false;  | 
1770  | 0  |     }  | 
1771  | 0  |     return true;  | 
1772  | 0  | }  | 
1773  |  |  | 
1774  |  | // Return true if the locale is changed during canonicalization.  | 
1775  |  | // The replaced value then will be put into out.  | 
1776  |  | bool  | 
1777  |  | canonicalizeLocale(const Locale& locale, CharString& out, UErrorCode& status)  | 
1778  | 0  | { | 
1779  | 0  |     AliasReplacer replacer(status);  | 
1780  | 0  |     return replacer.replace(locale, out, status);  | 
1781  | 0  | }  | 
1782  |  |  | 
1783  |  | // Function to optimize for known cases without so we can skip the loading  | 
1784  |  | // of resources in the startup time until we really need it.  | 
1785  |  | bool  | 
1786  |  | isKnownCanonicalizedLocale(const char* locale, UErrorCode& status)  | 
1787  | 0  | { | 
1788  | 0  |     if (    uprv_strcmp(locale, "c") == 0 ||  | 
1789  | 0  |             uprv_strcmp(locale, "en") == 0 ||  | 
1790  | 0  |             uprv_strcmp(locale, "en_US") == 0) { | 
1791  | 0  |         return true;  | 
1792  | 0  |     }  | 
1793  |  |  | 
1794  |  |     // common well-known Canonicalized.  | 
1795  | 0  |     umtx_initOnce(gKnownCanonicalizedInitOnce,  | 
1796  | 0  |                   &loadKnownCanonicalized, status);  | 
1797  | 0  |     if (U_FAILURE(status)) { | 
1798  | 0  |         return false;  | 
1799  | 0  |     }  | 
1800  | 0  |     U_ASSERT(gKnownCanonicalized != nullptr);  | 
1801  | 0  |     return uhash_geti(gKnownCanonicalized, locale) != 0;  | 
1802  | 0  | }  | 
1803  |  |  | 
1804  |  | }  // namespace  | 
1805  |  |  | 
1806  |  | // Function for testing.  | 
1807  |  | U_CAPI const char* const*  | 
1808  |  | ulocimp_getKnownCanonicalizedLocaleForTest(int32_t* length)  | 
1809  | 0  | { | 
1810  | 0  |     *length = UPRV_LENGTHOF(KNOWN_CANONICALIZED);  | 
1811  | 0  |     return KNOWN_CANONICALIZED;  | 
1812  | 0  | }  | 
1813  |  |  | 
1814  |  | // Function for testing.  | 
1815  |  | U_CAPI bool  | 
1816  |  | ulocimp_isCanonicalizedLocaleForTest(const char* localeName)  | 
1817  | 0  | { | 
1818  | 0  |     Locale l(localeName);  | 
1819  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
1820  | 0  |     CharString temp;  | 
1821  | 0  |     return !canonicalizeLocale(l, temp, status) && U_SUCCESS(status);  | 
1822  | 0  | }  | 
1823  |  |  | 
1824  |  | /*This function initializes a Locale from a C locale ID*/  | 
1825  |  | Locale& Locale::init(const char* localeID, UBool canonicalize)  | 
1826  | 0  | { | 
1827  | 0  |     fIsBogus = FALSE;  | 
1828  |  |     /* Free our current storage */  | 
1829  | 0  |     if ((baseName != fullName) && (baseName != fullNameBuffer)) { | 
1830  | 0  |         uprv_free(baseName);  | 
1831  | 0  |     }  | 
1832  | 0  |     baseName = NULL;  | 
1833  | 0  |     if(fullName != fullNameBuffer) { | 
1834  | 0  |         uprv_free(fullName);  | 
1835  | 0  |         fullName = fullNameBuffer;  | 
1836  | 0  |     }  | 
1837  |  |  | 
1838  |  |     // not a loop:  | 
1839  |  |     // just an easy way to have a common error-exit  | 
1840  |  |     // without goto and without another function  | 
1841  | 0  |     do { | 
1842  | 0  |         char *separator;  | 
1843  | 0  |         char *field[5] = {0}; | 
1844  | 0  |         int32_t fieldLen[5] = {0}; | 
1845  | 0  |         int32_t fieldIdx;  | 
1846  | 0  |         int32_t variantField;  | 
1847  | 0  |         int32_t length;  | 
1848  | 0  |         UErrorCode err;  | 
1849  |  | 
  | 
1850  | 0  |         if(localeID == NULL) { | 
1851  |  |             // not an error, just set the default locale  | 
1852  | 0  |             return *this = getDefault();  | 
1853  | 0  |         }  | 
1854  |  |  | 
1855  |  |         /* preset all fields to empty */  | 
1856  | 0  |         language[0] = script[0] = country[0] = 0;  | 
1857  |  |  | 
1858  |  |         // "canonicalize" the locale ID to ICU/Java format  | 
1859  | 0  |         err = U_ZERO_ERROR;  | 
1860  | 0  |         length = canonicalize ?  | 
1861  | 0  |             uloc_canonicalize(localeID, fullName, sizeof(fullNameBuffer), &err) :  | 
1862  | 0  |             uloc_getName(localeID, fullName, sizeof(fullNameBuffer), &err);  | 
1863  |  | 
  | 
1864  | 0  |         if(err == U_BUFFER_OVERFLOW_ERROR || length >= (int32_t)sizeof(fullNameBuffer)) { | 
1865  | 0  |             U_ASSERT(baseName == nullptr);  | 
1866  |  |             /*Go to heap for the fullName if necessary*/  | 
1867  | 0  |             fullName = (char *)uprv_malloc(sizeof(char)*(length + 1));  | 
1868  | 0  |             if(fullName == 0) { | 
1869  | 0  |                 fullName = fullNameBuffer;  | 
1870  | 0  |                 break; // error: out of memory  | 
1871  | 0  |             }  | 
1872  | 0  |             err = U_ZERO_ERROR;  | 
1873  | 0  |             length = canonicalize ?  | 
1874  | 0  |                 uloc_canonicalize(localeID, fullName, length+1, &err) :  | 
1875  | 0  |                 uloc_getName(localeID, fullName, length+1, &err);  | 
1876  | 0  |         }  | 
1877  | 0  |         if(U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) { | 
1878  |  |             /* should never occur */  | 
1879  | 0  |             break;  | 
1880  | 0  |         }  | 
1881  |  |  | 
1882  | 0  |         variantBegin = length;  | 
1883  |  |  | 
1884  |  |         /* after uloc_getName/canonicalize() we know that only '_' are separators */  | 
1885  |  |         /* But _ could also appeared in timezone such as "en@timezone=America/Los_Angeles" */  | 
1886  | 0  |         separator = field[0] = fullName;  | 
1887  | 0  |         fieldIdx = 1;  | 
1888  | 0  |         char* at = uprv_strchr(fullName, '@');  | 
1889  | 0  |         while ((separator = uprv_strchr(field[fieldIdx-1], SEP_CHAR)) != 0 &&  | 
1890  | 0  |                fieldIdx < UPRV_LENGTHOF(field)-1 &&  | 
1891  | 0  |                (at == nullptr || separator < at)) { | 
1892  | 0  |             field[fieldIdx] = separator + 1;  | 
1893  | 0  |             fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]);  | 
1894  | 0  |             fieldIdx++;  | 
1895  | 0  |         }  | 
1896  |  |         // variant may contain @foo or .foo POSIX cruft; remove it  | 
1897  | 0  |         separator = uprv_strchr(field[fieldIdx-1], '@');  | 
1898  | 0  |         char* sep2 = uprv_strchr(field[fieldIdx-1], '.');  | 
1899  | 0  |         if (separator!=NULL || sep2!=NULL) { | 
1900  | 0  |             if (separator==NULL || (sep2!=NULL && separator > sep2)) { | 
1901  | 0  |                 separator = sep2;  | 
1902  | 0  |             }  | 
1903  | 0  |             fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]);  | 
1904  | 0  |         } else { | 
1905  | 0  |             fieldLen[fieldIdx-1] = length - (int32_t)(field[fieldIdx-1] - fullName);  | 
1906  | 0  |         }  | 
1907  |  | 
  | 
1908  | 0  |         if (fieldLen[0] >= (int32_t)(sizeof(language)))  | 
1909  | 0  |         { | 
1910  | 0  |             break; // error: the language field is too long  | 
1911  | 0  |         }  | 
1912  |  |  | 
1913  | 0  |         variantField = 1; /* Usually the 2nd one, except when a script or country is also used. */  | 
1914  | 0  |         if (fieldLen[0] > 0) { | 
1915  |  |             /* We have a language */  | 
1916  | 0  |             uprv_memcpy(language, fullName, fieldLen[0]);  | 
1917  | 0  |             language[fieldLen[0]] = 0;  | 
1918  | 0  |         }  | 
1919  | 0  |         if (fieldLen[1] == 4 && uprv_isASCIILetter(field[1][0]) &&  | 
1920  | 0  |                 uprv_isASCIILetter(field[1][1]) && uprv_isASCIILetter(field[1][2]) &&  | 
1921  | 0  |                 uprv_isASCIILetter(field[1][3])) { | 
1922  |  |             /* We have at least a script */  | 
1923  | 0  |             uprv_memcpy(script, field[1], fieldLen[1]);  | 
1924  | 0  |             script[fieldLen[1]] = 0;  | 
1925  | 0  |             variantField++;  | 
1926  | 0  |         }  | 
1927  |  | 
  | 
1928  | 0  |         if (fieldLen[variantField] == 2 || fieldLen[variantField] == 3) { | 
1929  |  |             /* We have a country */  | 
1930  | 0  |             uprv_memcpy(country, field[variantField], fieldLen[variantField]);  | 
1931  | 0  |             country[fieldLen[variantField]] = 0;  | 
1932  | 0  |             variantField++;  | 
1933  | 0  |         } else if (fieldLen[variantField] == 0) { | 
1934  | 0  |             variantField++; /* script or country empty but variant in next field (i.e. en__POSIX) */  | 
1935  | 0  |         }  | 
1936  |  | 
  | 
1937  | 0  |         if (fieldLen[variantField] > 0) { | 
1938  |  |             /* We have a variant */  | 
1939  | 0  |             variantBegin = (int32_t)(field[variantField] - fullName);  | 
1940  | 0  |         }  | 
1941  |  | 
  | 
1942  | 0  |         err = U_ZERO_ERROR;  | 
1943  | 0  |         initBaseName(err);  | 
1944  | 0  |         if (U_FAILURE(err)) { | 
1945  | 0  |             break;  | 
1946  | 0  |         }  | 
1947  |  |  | 
1948  | 0  |         if (canonicalize) { | 
1949  | 0  |             if (!isKnownCanonicalizedLocale(fullName, err)) { | 
1950  | 0  |                 CharString replaced;  | 
1951  |  |                 // Not sure it is already canonicalized  | 
1952  | 0  |                 if (canonicalizeLocale(*this, replaced, err)) { | 
1953  | 0  |                     U_ASSERT(U_SUCCESS(err));  | 
1954  |  |                     // If need replacement, call init again.  | 
1955  | 0  |                     init(replaced.data(), false);  | 
1956  | 0  |                 }  | 
1957  | 0  |                 if (U_FAILURE(err)) { | 
1958  | 0  |                     break;  | 
1959  | 0  |                 }  | 
1960  | 0  |             }  | 
1961  | 0  |         }   // if (canonicalize) { | 
1962  |  |  | 
1963  |  |         // successful end of init()  | 
1964  | 0  |         return *this;  | 
1965  | 0  |     } while(0); /*loop doesn't iterate*/  | 
1966  |  |  | 
1967  |  |     // when an error occurs, then set this object to "bogus" (there is no UErrorCode here)  | 
1968  | 0  |     setToBogus();  | 
1969  |  | 
  | 
1970  | 0  |     return *this;  | 
1971  | 0  | }  | 
1972  |  |  | 
1973  |  | /*  | 
1974  |  |  * Set up the base name.  | 
1975  |  |  * If there are no key words, it's exactly the full name.  | 
1976  |  |  * If key words exist, it's the full name truncated at the '@' character.  | 
1977  |  |  * Need to set up both at init() and after setting a keyword.  | 
1978  |  |  */  | 
1979  |  | void  | 
1980  | 0  | Locale::initBaseName(UErrorCode &status) { | 
1981  | 0  |     if (U_FAILURE(status)) { | 
1982  | 0  |         return;  | 
1983  | 0  |     }  | 
1984  | 0  |     U_ASSERT(baseName==NULL || baseName==fullName);  | 
1985  | 0  |     const char *atPtr = uprv_strchr(fullName, '@');  | 
1986  | 0  |     const char *eqPtr = uprv_strchr(fullName, '=');  | 
1987  | 0  |     if (atPtr && eqPtr && atPtr < eqPtr) { | 
1988  |  |         // Key words exist.  | 
1989  | 0  |         int32_t baseNameLength = (int32_t)(atPtr - fullName);  | 
1990  | 0  |         baseName = (char *)uprv_malloc(baseNameLength + 1);  | 
1991  | 0  |         if (baseName == NULL) { | 
1992  | 0  |             status = U_MEMORY_ALLOCATION_ERROR;  | 
1993  | 0  |             return;  | 
1994  | 0  |         }  | 
1995  | 0  |         uprv_strncpy(baseName, fullName, baseNameLength);  | 
1996  | 0  |         baseName[baseNameLength] = 0;  | 
1997  |  |  | 
1998  |  |         // The original computation of variantBegin leaves it equal to the length  | 
1999  |  |         // of fullName if there is no variant.  It should instead be  | 
2000  |  |         // the length of the baseName.  | 
2001  | 0  |         if (variantBegin > baseNameLength) { | 
2002  | 0  |             variantBegin = baseNameLength;  | 
2003  | 0  |         }  | 
2004  | 0  |     } else { | 
2005  | 0  |         baseName = fullName;  | 
2006  | 0  |     }  | 
2007  | 0  | }  | 
2008  |  |  | 
2009  |  |  | 
2010  |  | int32_t  | 
2011  |  | Locale::hashCode() const  | 
2012  | 0  | { | 
2013  | 0  |     return ustr_hashCharsN(fullName, static_cast<int32_t>(uprv_strlen(fullName)));  | 
2014  | 0  | }  | 
2015  |  |  | 
2016  |  | void  | 
2017  | 0  | Locale::setToBogus() { | 
2018  |  |     /* Free our current storage */  | 
2019  | 0  |     if((baseName != fullName) && (baseName != fullNameBuffer)) { | 
2020  | 0  |         uprv_free(baseName);  | 
2021  | 0  |     }  | 
2022  | 0  |     baseName = NULL;  | 
2023  | 0  |     if(fullName != fullNameBuffer) { | 
2024  | 0  |         uprv_free(fullName);  | 
2025  | 0  |         fullName = fullNameBuffer;  | 
2026  | 0  |     }  | 
2027  | 0  |     *fullNameBuffer = 0;  | 
2028  | 0  |     *language = 0;  | 
2029  | 0  |     *script = 0;  | 
2030  | 0  |     *country = 0;  | 
2031  | 0  |     fIsBogus = TRUE;  | 
2032  | 0  |     variantBegin = 0;  | 
2033  | 0  | }  | 
2034  |  |  | 
2035  |  | const Locale& U_EXPORT2  | 
2036  |  | Locale::getDefault()  | 
2037  | 0  | { | 
2038  | 0  |     { | 
2039  | 0  |         Mutex lock(&gDefaultLocaleMutex);  | 
2040  | 0  |         if (gDefaultLocale != NULL) { | 
2041  | 0  |             return *gDefaultLocale;  | 
2042  | 0  |         }  | 
2043  | 0  |     }  | 
2044  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
2045  | 0  |     return *locale_set_default_internal(NULL, status);  | 
2046  | 0  | }  | 
2047  |  |  | 
2048  |  |  | 
2049  |  |  | 
2050  |  | void U_EXPORT2  | 
2051  |  | Locale::setDefault( const   Locale&     newLocale,  | 
2052  |  |                             UErrorCode&  status)  | 
2053  | 0  | { | 
2054  | 0  |     if (U_FAILURE(status)) { | 
2055  | 0  |         return;  | 
2056  | 0  |     }  | 
2057  |  |  | 
2058  |  |     /* Set the default from the full name string of the supplied locale.  | 
2059  |  |      * This is a convenient way to access the default locale caching mechanisms.  | 
2060  |  |      */  | 
2061  | 0  |     const char *localeID = newLocale.getName();  | 
2062  | 0  |     locale_set_default_internal(localeID, status);  | 
2063  | 0  | }  | 
2064  |  |  | 
2065  |  | void  | 
2066  | 0  | Locale::addLikelySubtags(UErrorCode& status) { | 
2067  | 0  |     if (U_FAILURE(status)) { | 
2068  | 0  |         return;  | 
2069  | 0  |     }  | 
2070  |  |  | 
2071  | 0  |     CharString maximizedLocaleID;  | 
2072  | 0  |     { | 
2073  | 0  |         CharStringByteSink sink(&maximizedLocaleID);  | 
2074  | 0  |         ulocimp_addLikelySubtags(fullName, sink, &status);  | 
2075  | 0  |     }  | 
2076  |  | 
  | 
2077  | 0  |     if (U_FAILURE(status)) { | 
2078  | 0  |         return;  | 
2079  | 0  |     }  | 
2080  |  |  | 
2081  | 0  |     init(maximizedLocaleID.data(), /*canonicalize=*/FALSE);  | 
2082  | 0  |     if (isBogus()) { | 
2083  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2084  | 0  |     }  | 
2085  | 0  | }  | 
2086  |  |  | 
2087  |  | void  | 
2088  | 0  | Locale::minimizeSubtags(UErrorCode& status) { | 
2089  | 0  |     if (U_FAILURE(status)) { | 
2090  | 0  |         return;  | 
2091  | 0  |     }  | 
2092  |  |  | 
2093  | 0  |     CharString minimizedLocaleID;  | 
2094  | 0  |     { | 
2095  | 0  |         CharStringByteSink sink(&minimizedLocaleID);  | 
2096  | 0  |         ulocimp_minimizeSubtags(fullName, sink, &status);  | 
2097  | 0  |     }  | 
2098  |  | 
  | 
2099  | 0  |     if (U_FAILURE(status)) { | 
2100  | 0  |         return;  | 
2101  | 0  |     }  | 
2102  |  |  | 
2103  | 0  |     init(minimizedLocaleID.data(), /*canonicalize=*/FALSE);  | 
2104  | 0  |     if (isBogus()) { | 
2105  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2106  | 0  |     }  | 
2107  | 0  | }  | 
2108  |  |  | 
2109  |  | void  | 
2110  | 0  | Locale::canonicalize(UErrorCode& status) { | 
2111  | 0  |     if (U_FAILURE(status)) { | 
2112  | 0  |         return;  | 
2113  | 0  |     }  | 
2114  | 0  |     if (isBogus()) { | 
2115  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2116  | 0  |         return;  | 
2117  | 0  |     }  | 
2118  | 0  |     CharString uncanonicalized(fullName, status);  | 
2119  | 0  |     if (U_FAILURE(status)) { | 
2120  | 0  |         return;  | 
2121  | 0  |     }  | 
2122  | 0  |     init(uncanonicalized.data(), /*canonicalize=*/TRUE);  | 
2123  | 0  |     if (isBogus()) { | 
2124  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2125  | 0  |     }  | 
2126  | 0  | }  | 
2127  |  |  | 
2128  |  | Locale U_EXPORT2  | 
2129  |  | Locale::forLanguageTag(StringPiece tag, UErrorCode& status)  | 
2130  | 0  | { | 
2131  | 0  |     Locale result(Locale::eBOGUS);  | 
2132  |  | 
  | 
2133  | 0  |     if (U_FAILURE(status)) { | 
2134  | 0  |         return result;  | 
2135  | 0  |     }  | 
2136  |  |  | 
2137  |  |     // If a BCP 47 language tag is passed as the language parameter to the  | 
2138  |  |     // normal Locale constructor, it will actually fall back to invoking  | 
2139  |  |     // uloc_forLanguageTag() to parse it if it somehow is able to detect that  | 
2140  |  |     // the string actually is BCP 47. This works well for things like strings  | 
2141  |  |     // using BCP 47 extensions, but it does not at all work for things like  | 
2142  |  |     // legacy language tags (marked as “Type: grandfathered” in BCP 47,  | 
2143  |  |     // e.g., "en-GB-oed") which are possible to also  | 
2144  |  |     // interpret as ICU locale IDs and because of that won't trigger the BCP 47  | 
2145  |  |     // parsing. Therefore the code here explicitly calls uloc_forLanguageTag()  | 
2146  |  |     // and then Locale::init(), instead of just calling the normal constructor.  | 
2147  |  |  | 
2148  | 0  |     CharString localeID;  | 
2149  | 0  |     int32_t parsedLength;  | 
2150  | 0  |     { | 
2151  | 0  |         CharStringByteSink sink(&localeID);  | 
2152  | 0  |         ulocimp_forLanguageTag(  | 
2153  | 0  |                 tag.data(),  | 
2154  | 0  |                 tag.length(),  | 
2155  | 0  |                 sink,  | 
2156  | 0  |                 &parsedLength,  | 
2157  | 0  |                 &status);  | 
2158  | 0  |     }  | 
2159  |  | 
  | 
2160  | 0  |     if (U_FAILURE(status)) { | 
2161  | 0  |         return result;  | 
2162  | 0  |     }  | 
2163  |  |  | 
2164  | 0  |     if (parsedLength != tag.size()) { | 
2165  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2166  | 0  |         return result;  | 
2167  | 0  |     }  | 
2168  |  |  | 
2169  | 0  |     result.init(localeID.data(), /*canonicalize=*/FALSE);  | 
2170  | 0  |     if (result.isBogus()) { | 
2171  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2172  | 0  |     }  | 
2173  | 0  |     return result;  | 
2174  | 0  | }  | 
2175  |  |  | 
2176  |  | void  | 
2177  |  | Locale::toLanguageTag(ByteSink& sink, UErrorCode& status) const  | 
2178  | 0  | { | 
2179  | 0  |     if (U_FAILURE(status)) { | 
2180  | 0  |         return;  | 
2181  | 0  |     }  | 
2182  |  |  | 
2183  | 0  |     if (fIsBogus) { | 
2184  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2185  | 0  |         return;  | 
2186  | 0  |     }  | 
2187  |  |  | 
2188  | 0  |     ulocimp_toLanguageTag(fullName, sink, /*strict=*/FALSE, &status);  | 
2189  | 0  | }  | 
2190  |  |  | 
2191  |  | Locale U_EXPORT2  | 
2192  |  | Locale::createFromName (const char *name)  | 
2193  | 0  | { | 
2194  | 0  |     if (name) { | 
2195  | 0  |         Locale l(""); | 
2196  | 0  |         l.init(name, FALSE);  | 
2197  | 0  |         return l;  | 
2198  | 0  |     }  | 
2199  | 0  |     else { | 
2200  | 0  |         return getDefault();  | 
2201  | 0  |     }  | 
2202  | 0  | }  | 
2203  |  |  | 
2204  |  | Locale U_EXPORT2  | 
2205  | 0  | Locale::createCanonical(const char* name) { | 
2206  | 0  |     Locale loc(""); | 
2207  | 0  |     loc.init(name, TRUE);  | 
2208  | 0  |     return loc;  | 
2209  | 0  | }  | 
2210  |  |  | 
2211  |  | const char *  | 
2212  |  | Locale::getISO3Language() const  | 
2213  | 0  | { | 
2214  | 0  |     return uloc_getISO3Language(fullName);  | 
2215  | 0  | }  | 
2216  |  |  | 
2217  |  |  | 
2218  |  | const char *  | 
2219  |  | Locale::getISO3Country() const  | 
2220  | 0  | { | 
2221  | 0  |     return uloc_getISO3Country(fullName);  | 
2222  | 0  | }  | 
2223  |  |  | 
2224  |  | /**  | 
2225  |  |  * Return the LCID value as specified in the "LocaleID" resource for this  | 
2226  |  |  * locale.  The LocaleID must be expressed as a hexadecimal number, from  | 
2227  |  |  * one to four digits.  If the LocaleID resource is not present, or is  | 
2228  |  |  * in an incorrect format, 0 is returned.  The LocaleID is for use in  | 
2229  |  |  * Windows (it is an LCID), but is available on all platforms.  | 
2230  |  |  */  | 
2231  |  | uint32_t  | 
2232  |  | Locale::getLCID() const  | 
2233  | 0  | { | 
2234  | 0  |     return uloc_getLCID(fullName);  | 
2235  | 0  | }  | 
2236  |  |  | 
2237  |  | const char* const* U_EXPORT2 Locale::getISOCountries()  | 
2238  | 0  | { | 
2239  | 0  |     return uloc_getISOCountries();  | 
2240  | 0  | }  | 
2241  |  |  | 
2242  |  | const char* const* U_EXPORT2 Locale::getISOLanguages()  | 
2243  | 0  | { | 
2244  | 0  |     return uloc_getISOLanguages();  | 
2245  | 0  | }  | 
2246  |  |  | 
2247  |  | // Set the locale's data based on a posix id.  | 
2248  |  | void Locale::setFromPOSIXID(const char *posixID)  | 
2249  | 0  | { | 
2250  | 0  |     init(posixID, TRUE);  | 
2251  | 0  | }  | 
2252  |  |  | 
2253  |  | const Locale & U_EXPORT2  | 
2254  |  | Locale::getRoot(void)  | 
2255  | 0  | { | 
2256  | 0  |     return getLocale(eROOT);  | 
2257  | 0  | }  | 
2258  |  |  | 
2259  |  | const Locale & U_EXPORT2  | 
2260  |  | Locale::getEnglish(void)  | 
2261  | 0  | { | 
2262  | 0  |     return getLocale(eENGLISH);  | 
2263  | 0  | }  | 
2264  |  |  | 
2265  |  | const Locale & U_EXPORT2  | 
2266  |  | Locale::getFrench(void)  | 
2267  | 0  | { | 
2268  | 0  |     return getLocale(eFRENCH);  | 
2269  | 0  | }  | 
2270  |  |  | 
2271  |  | const Locale & U_EXPORT2  | 
2272  |  | Locale::getGerman(void)  | 
2273  | 0  | { | 
2274  | 0  |     return getLocale(eGERMAN);  | 
2275  | 0  | }  | 
2276  |  |  | 
2277  |  | const Locale & U_EXPORT2  | 
2278  |  | Locale::getItalian(void)  | 
2279  | 0  | { | 
2280  | 0  |     return getLocale(eITALIAN);  | 
2281  | 0  | }  | 
2282  |  |  | 
2283  |  | const Locale & U_EXPORT2  | 
2284  |  | Locale::getJapanese(void)  | 
2285  | 0  | { | 
2286  | 0  |     return getLocale(eJAPANESE);  | 
2287  | 0  | }  | 
2288  |  |  | 
2289  |  | const Locale & U_EXPORT2  | 
2290  |  | Locale::getKorean(void)  | 
2291  | 0  | { | 
2292  | 0  |     return getLocale(eKOREAN);  | 
2293  | 0  | }  | 
2294  |  |  | 
2295  |  | const Locale & U_EXPORT2  | 
2296  |  | Locale::getChinese(void)  | 
2297  | 0  | { | 
2298  | 0  |     return getLocale(eCHINESE);  | 
2299  | 0  | }  | 
2300  |  |  | 
2301  |  | const Locale & U_EXPORT2  | 
2302  |  | Locale::getSimplifiedChinese(void)  | 
2303  | 0  | { | 
2304  | 0  |     return getLocale(eCHINA);  | 
2305  | 0  | }  | 
2306  |  |  | 
2307  |  | const Locale & U_EXPORT2  | 
2308  |  | Locale::getTraditionalChinese(void)  | 
2309  | 0  | { | 
2310  | 0  |     return getLocale(eTAIWAN);  | 
2311  | 0  | }  | 
2312  |  |  | 
2313  |  |  | 
2314  |  | const Locale & U_EXPORT2  | 
2315  |  | Locale::getFrance(void)  | 
2316  | 0  | { | 
2317  | 0  |     return getLocale(eFRANCE);  | 
2318  | 0  | }  | 
2319  |  |  | 
2320  |  | const Locale & U_EXPORT2  | 
2321  |  | Locale::getGermany(void)  | 
2322  | 0  | { | 
2323  | 0  |     return getLocale(eGERMANY);  | 
2324  | 0  | }  | 
2325  |  |  | 
2326  |  | const Locale & U_EXPORT2  | 
2327  |  | Locale::getItaly(void)  | 
2328  | 0  | { | 
2329  | 0  |     return getLocale(eITALY);  | 
2330  | 0  | }  | 
2331  |  |  | 
2332  |  | const Locale & U_EXPORT2  | 
2333  |  | Locale::getJapan(void)  | 
2334  | 0  | { | 
2335  | 0  |     return getLocale(eJAPAN);  | 
2336  | 0  | }  | 
2337  |  |  | 
2338  |  | const Locale & U_EXPORT2  | 
2339  |  | Locale::getKorea(void)  | 
2340  | 0  | { | 
2341  | 0  |     return getLocale(eKOREA);  | 
2342  | 0  | }  | 
2343  |  |  | 
2344  |  | const Locale & U_EXPORT2  | 
2345  |  | Locale::getChina(void)  | 
2346  | 0  | { | 
2347  | 0  |     return getLocale(eCHINA);  | 
2348  | 0  | }  | 
2349  |  |  | 
2350  |  | const Locale & U_EXPORT2  | 
2351  |  | Locale::getPRC(void)  | 
2352  | 0  | { | 
2353  | 0  |     return getLocale(eCHINA);  | 
2354  | 0  | }  | 
2355  |  |  | 
2356  |  | const Locale & U_EXPORT2  | 
2357  |  | Locale::getTaiwan(void)  | 
2358  | 0  | { | 
2359  | 0  |     return getLocale(eTAIWAN);  | 
2360  | 0  | }  | 
2361  |  |  | 
2362  |  | const Locale & U_EXPORT2  | 
2363  |  | Locale::getUK(void)  | 
2364  | 0  | { | 
2365  | 0  |     return getLocale(eUK);  | 
2366  | 0  | }  | 
2367  |  |  | 
2368  |  | const Locale & U_EXPORT2  | 
2369  |  | Locale::getUS(void)  | 
2370  | 0  | { | 
2371  | 0  |     return getLocale(eUS);  | 
2372  | 0  | }  | 
2373  |  |  | 
2374  |  | const Locale & U_EXPORT2  | 
2375  |  | Locale::getCanada(void)  | 
2376  | 0  | { | 
2377  | 0  |     return getLocale(eCANADA);  | 
2378  | 0  | }  | 
2379  |  |  | 
2380  |  | const Locale & U_EXPORT2  | 
2381  |  | Locale::getCanadaFrench(void)  | 
2382  | 0  | { | 
2383  | 0  |     return getLocale(eCANADA_FRENCH);  | 
2384  | 0  | }  | 
2385  |  |  | 
2386  |  | const Locale &  | 
2387  |  | Locale::getLocale(int locid)  | 
2388  | 0  | { | 
2389  | 0  |     Locale *localeCache = getLocaleCache();  | 
2390  | 0  |     U_ASSERT((locid < eMAX_LOCALES)&&(locid>=0));  | 
2391  | 0  |     if (localeCache == NULL) { | 
2392  |  |         // Failure allocating the locale cache.  | 
2393  |  |         //   The best we can do is return a NULL reference.  | 
2394  | 0  |         locid = 0;  | 
2395  | 0  |     }  | 
2396  | 0  |     return localeCache[locid]; /*operating on NULL*/  | 
2397  | 0  | }  | 
2398  |  |  | 
2399  |  | /*  | 
2400  |  | This function is defined this way in order to get around static  | 
2401  |  | initialization and static destruction.  | 
2402  |  |  */  | 
2403  |  | Locale *  | 
2404  |  | Locale::getLocaleCache(void)  | 
2405  | 0  | { | 
2406  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
2407  | 0  |     umtx_initOnce(gLocaleCacheInitOnce, locale_init, status);  | 
2408  | 0  |     return gLocaleCache;  | 
2409  | 0  | }  | 
2410  |  |  | 
2411  |  | class KeywordEnumeration : public StringEnumeration { | 
2412  |  | private:  | 
2413  |  |     char *keywords;  | 
2414  |  |     char *current;  | 
2415  |  |     int32_t length;  | 
2416  |  |     UnicodeString currUSKey;  | 
2417  |  |     static const char fgClassID;/* Warning this is used beyond the typical RTTI usage. */  | 
2418  |  |  | 
2419  |  | public:  | 
2420  | 0  |     static UClassID U_EXPORT2 getStaticClassID(void) { return (UClassID)&fgClassID; } | 
2421  | 0  |     virtual UClassID getDynamicClassID(void) const { return getStaticClassID(); } | 
2422  |  | public:  | 
2423  |  |     KeywordEnumeration(const char *keys, int32_t keywordLen, int32_t currentIndex, UErrorCode &status)  | 
2424  | 0  |         : keywords((char *)&fgClassID), current((char *)&fgClassID), length(0) { | 
2425  | 0  |         if(U_SUCCESS(status) && keywordLen != 0) { | 
2426  | 0  |             if(keys == NULL || keywordLen < 0) { | 
2427  | 0  |                 status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2428  | 0  |             } else { | 
2429  | 0  |                 keywords = (char *)uprv_malloc(keywordLen+1);  | 
2430  | 0  |                 if (keywords == NULL) { | 
2431  | 0  |                     status = U_MEMORY_ALLOCATION_ERROR;  | 
2432  | 0  |                 }  | 
2433  | 0  |                 else { | 
2434  | 0  |                     uprv_memcpy(keywords, keys, keywordLen);  | 
2435  | 0  |                     keywords[keywordLen] = 0;  | 
2436  | 0  |                     current = keywords + currentIndex;  | 
2437  | 0  |                     length = keywordLen;  | 
2438  | 0  |                 }  | 
2439  | 0  |             }  | 
2440  | 0  |         }  | 
2441  | 0  |     }  | 
2442  |  |  | 
2443  |  |     virtual ~KeywordEnumeration();  | 
2444  |  |  | 
2445  |  |     virtual StringEnumeration * clone() const  | 
2446  | 0  |     { | 
2447  | 0  |         UErrorCode status = U_ZERO_ERROR;  | 
2448  | 0  |         return new KeywordEnumeration(keywords, length, (int32_t)(current - keywords), status);  | 
2449  | 0  |     }  | 
2450  |  |  | 
2451  | 0  |     virtual int32_t count(UErrorCode &/*status*/) const { | 
2452  | 0  |         char *kw = keywords;  | 
2453  | 0  |         int32_t result = 0;  | 
2454  | 0  |         while(*kw) { | 
2455  | 0  |             result++;  | 
2456  | 0  |             kw += uprv_strlen(kw)+1;  | 
2457  | 0  |         }  | 
2458  | 0  |         return result;  | 
2459  | 0  |     }  | 
2460  |  |  | 
2461  | 0  |     virtual const char* next(int32_t* resultLength, UErrorCode& status) { | 
2462  | 0  |         const char* result;  | 
2463  | 0  |         int32_t len;  | 
2464  | 0  |         if(U_SUCCESS(status) && *current != 0) { | 
2465  | 0  |             result = current;  | 
2466  | 0  |             len = (int32_t)uprv_strlen(current);  | 
2467  | 0  |             current += len+1;  | 
2468  | 0  |             if(resultLength != NULL) { | 
2469  | 0  |                 *resultLength = len;  | 
2470  | 0  |             }  | 
2471  | 0  |         } else { | 
2472  | 0  |             if(resultLength != NULL) { | 
2473  | 0  |                 *resultLength = 0;  | 
2474  | 0  |             }  | 
2475  | 0  |             result = NULL;  | 
2476  | 0  |         }  | 
2477  | 0  |         return result;  | 
2478  | 0  |     }  | 
2479  |  |  | 
2480  | 0  |     virtual const UnicodeString* snext(UErrorCode& status) { | 
2481  | 0  |         int32_t resultLength = 0;  | 
2482  | 0  |         const char *s = next(&resultLength, status);  | 
2483  | 0  |         return setChars(s, resultLength, status);  | 
2484  | 0  |     }  | 
2485  |  |  | 
2486  | 0  |     virtual void reset(UErrorCode& /*status*/) { | 
2487  | 0  |         current = keywords;  | 
2488  | 0  |     }  | 
2489  |  | };  | 
2490  |  |  | 
2491  |  | const char KeywordEnumeration::fgClassID = '\0';  | 
2492  |  |  | 
2493  | 0  | KeywordEnumeration::~KeywordEnumeration() { | 
2494  | 0  |     uprv_free(keywords);  | 
2495  | 0  | }  | 
2496  |  |  | 
2497  |  | // A wrapper around KeywordEnumeration that calls uloc_toUnicodeLocaleKey() in  | 
2498  |  | // the next() method for each keyword before returning it.  | 
2499  |  | class UnicodeKeywordEnumeration : public KeywordEnumeration { | 
2500  |  | public:  | 
2501  |  |     using KeywordEnumeration::KeywordEnumeration;  | 
2502  |  |     virtual ~UnicodeKeywordEnumeration();  | 
2503  |  |  | 
2504  | 0  |     virtual const char* next(int32_t* resultLength, UErrorCode& status) { | 
2505  | 0  |         const char* legacy_key = KeywordEnumeration::next(nullptr, status);  | 
2506  | 0  |         while (U_SUCCESS(status) && legacy_key != nullptr) { | 
2507  | 0  |             const char* key = uloc_toUnicodeLocaleKey(legacy_key);  | 
2508  | 0  |             if (key != nullptr) { | 
2509  | 0  |                 if (resultLength != nullptr) { | 
2510  | 0  |                     *resultLength = static_cast<int32_t>(uprv_strlen(key));  | 
2511  | 0  |                 }  | 
2512  | 0  |                 return key;  | 
2513  | 0  |             }  | 
2514  |  |             // Not a Unicode keyword, could be a t, x or other, continue to look at the next one.  | 
2515  | 0  |             legacy_key = KeywordEnumeration::next(nullptr, status);  | 
2516  | 0  |         }  | 
2517  | 0  |         if (resultLength != nullptr) *resultLength = 0;  | 
2518  | 0  |         return nullptr;  | 
2519  | 0  |     }  | 
2520  |  | };  | 
2521  |  |  | 
2522  |  | // Out-of-line virtual destructor to serve as the "key function".  | 
2523  |  | UnicodeKeywordEnumeration::~UnicodeKeywordEnumeration() = default;  | 
2524  |  |  | 
2525  |  | StringEnumeration *  | 
2526  |  | Locale::createKeywords(UErrorCode &status) const  | 
2527  | 0  | { | 
2528  | 0  |     StringEnumeration *result = NULL;  | 
2529  |  | 
  | 
2530  | 0  |     if (U_FAILURE(status)) { | 
2531  | 0  |         return result;  | 
2532  | 0  |     }  | 
2533  |  |  | 
2534  | 0  |     const char* variantStart = uprv_strchr(fullName, '@');  | 
2535  | 0  |     const char* assignment = uprv_strchr(fullName, '=');  | 
2536  | 0  |     if(variantStart) { | 
2537  | 0  |         if(assignment > variantStart) { | 
2538  | 0  |             CharString keywords;  | 
2539  | 0  |             CharStringByteSink sink(&keywords);  | 
2540  | 0  |             ulocimp_getKeywords(variantStart+1, '@', sink, FALSE, &status);  | 
2541  | 0  |             if (U_SUCCESS(status) && !keywords.isEmpty()) { | 
2542  | 0  |                 result = new KeywordEnumeration(keywords.data(), keywords.length(), 0, status);  | 
2543  | 0  |                 if (!result) { | 
2544  | 0  |                     status = U_MEMORY_ALLOCATION_ERROR;  | 
2545  | 0  |                 }  | 
2546  | 0  |             }  | 
2547  | 0  |         } else { | 
2548  | 0  |             status = U_INVALID_FORMAT_ERROR;  | 
2549  | 0  |         }  | 
2550  | 0  |     }  | 
2551  | 0  |     return result;  | 
2552  | 0  | }  | 
2553  |  |  | 
2554  |  | StringEnumeration *  | 
2555  |  | Locale::createUnicodeKeywords(UErrorCode &status) const  | 
2556  | 0  | { | 
2557  | 0  |     StringEnumeration *result = NULL;  | 
2558  |  | 
  | 
2559  | 0  |     if (U_FAILURE(status)) { | 
2560  | 0  |         return result;  | 
2561  | 0  |     }  | 
2562  |  |  | 
2563  | 0  |     const char* variantStart = uprv_strchr(fullName, '@');  | 
2564  | 0  |     const char* assignment = uprv_strchr(fullName, '=');  | 
2565  | 0  |     if(variantStart) { | 
2566  | 0  |         if(assignment > variantStart) { | 
2567  | 0  |             CharString keywords;  | 
2568  | 0  |             CharStringByteSink sink(&keywords);  | 
2569  | 0  |             ulocimp_getKeywords(variantStart+1, '@', sink, FALSE, &status);  | 
2570  | 0  |             if (U_SUCCESS(status) && !keywords.isEmpty()) { | 
2571  | 0  |                 result = new UnicodeKeywordEnumeration(keywords.data(), keywords.length(), 0, status);  | 
2572  | 0  |                 if (!result) { | 
2573  | 0  |                     status = U_MEMORY_ALLOCATION_ERROR;  | 
2574  | 0  |                 }  | 
2575  | 0  |             }  | 
2576  | 0  |         } else { | 
2577  | 0  |             status = U_INVALID_FORMAT_ERROR;  | 
2578  | 0  |         }  | 
2579  | 0  |     }  | 
2580  | 0  |     return result;  | 
2581  | 0  | }  | 
2582  |  |  | 
2583  |  | int32_t  | 
2584  |  | Locale::getKeywordValue(const char* keywordName, char *buffer, int32_t bufLen, UErrorCode &status) const  | 
2585  | 0  | { | 
2586  | 0  |     return uloc_getKeywordValue(fullName, keywordName, buffer, bufLen, &status);  | 
2587  | 0  | }  | 
2588  |  |  | 
2589  |  | void  | 
2590  | 0  | Locale::getKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& status) const { | 
2591  | 0  |     if (U_FAILURE(status)) { | 
2592  | 0  |         return;  | 
2593  | 0  |     }  | 
2594  |  |  | 
2595  | 0  |     if (fIsBogus) { | 
2596  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2597  | 0  |         return;  | 
2598  | 0  |     }  | 
2599  |  |  | 
2600  |  |     // TODO: Remove the need for a const char* to a NUL terminated buffer.  | 
2601  | 0  |     const CharString keywordName_nul(keywordName, status);  | 
2602  | 0  |     if (U_FAILURE(status)) { | 
2603  | 0  |         return;  | 
2604  | 0  |     }  | 
2605  |  |  | 
2606  | 0  |     ulocimp_getKeywordValue(fullName, keywordName_nul.data(), sink, &status);  | 
2607  | 0  | }  | 
2608  |  |  | 
2609  |  | void  | 
2610  |  | Locale::getUnicodeKeywordValue(StringPiece keywordName,  | 
2611  |  |                                ByteSink& sink,  | 
2612  | 0  |                                UErrorCode& status) const { | 
2613  |  |     // TODO: Remove the need for a const char* to a NUL terminated buffer.  | 
2614  | 0  |     const CharString keywordName_nul(keywordName, status);  | 
2615  | 0  |     if (U_FAILURE(status)) { | 
2616  | 0  |         return;  | 
2617  | 0  |     }  | 
2618  |  |  | 
2619  | 0  |     const char* legacy_key = uloc_toLegacyKey(keywordName_nul.data());  | 
2620  |  | 
  | 
2621  | 0  |     if (legacy_key == nullptr) { | 
2622  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2623  | 0  |         return;  | 
2624  | 0  |     }  | 
2625  |  |  | 
2626  | 0  |     CharString legacy_value;  | 
2627  | 0  |     { | 
2628  | 0  |         CharStringByteSink sink(&legacy_value);  | 
2629  | 0  |         getKeywordValue(legacy_key, sink, status);  | 
2630  | 0  |     }  | 
2631  |  | 
  | 
2632  | 0  |     if (U_FAILURE(status)) { | 
2633  | 0  |         return;  | 
2634  | 0  |     }  | 
2635  |  |  | 
2636  | 0  |     const char* unicode_value = uloc_toUnicodeLocaleType(  | 
2637  | 0  |             keywordName_nul.data(), legacy_value.data());  | 
2638  |  | 
  | 
2639  | 0  |     if (unicode_value == nullptr) { | 
2640  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2641  | 0  |         return;  | 
2642  | 0  |     }  | 
2643  |  |  | 
2644  | 0  |     sink.Append(unicode_value, static_cast<int32_t>(uprv_strlen(unicode_value)));  | 
2645  | 0  | }  | 
2646  |  |  | 
2647  |  | void  | 
2648  |  | Locale::setKeywordValue(const char* keywordName, const char* keywordValue, UErrorCode &status)  | 
2649  | 0  | { | 
2650  | 0  |     if (U_FAILURE(status)) { | 
2651  | 0  |         return;  | 
2652  | 0  |     }  | 
2653  | 0  |     if (status == U_STRING_NOT_TERMINATED_WARNING) { | 
2654  | 0  |         status = U_ZERO_ERROR;  | 
2655  | 0  |     }  | 
2656  | 0  |     int32_t bufferLength = uprv_max((int32_t)(uprv_strlen(fullName) + 1), ULOC_FULLNAME_CAPACITY);  | 
2657  | 0  |     int32_t newLength = uloc_setKeywordValue(keywordName, keywordValue, fullName,  | 
2658  | 0  |                                              bufferLength, &status) + 1;  | 
2659  | 0  |     U_ASSERT(status != U_STRING_NOT_TERMINATED_WARNING);  | 
2660  |  |     /* Handle the case the current buffer is not enough to hold the new id */  | 
2661  | 0  |     if (status == U_BUFFER_OVERFLOW_ERROR) { | 
2662  | 0  |         U_ASSERT(newLength > bufferLength);  | 
2663  | 0  |         char* newFullName = (char *)uprv_malloc(newLength);  | 
2664  | 0  |         if (newFullName == nullptr) { | 
2665  | 0  |             status = U_MEMORY_ALLOCATION_ERROR;  | 
2666  | 0  |             return;  | 
2667  | 0  |         }  | 
2668  | 0  |         uprv_strcpy(newFullName, fullName);  | 
2669  | 0  |         if (fullName != fullNameBuffer) { | 
2670  |  |             // if full Name is already on the heap, need to free it.  | 
2671  | 0  |             uprv_free(fullName);  | 
2672  | 0  |             if (baseName == fullName) { | 
2673  | 0  |                 baseName = newFullName; // baseName should not point to freed memory.  | 
2674  | 0  |             }  | 
2675  | 0  |         }  | 
2676  | 0  |         fullName = newFullName;  | 
2677  | 0  |         status = U_ZERO_ERROR;  | 
2678  | 0  |         uloc_setKeywordValue(keywordName, keywordValue, fullName, newLength, &status);  | 
2679  | 0  |         U_ASSERT(status != U_STRING_NOT_TERMINATED_WARNING);  | 
2680  | 0  |     } else { | 
2681  | 0  |         U_ASSERT(newLength <= bufferLength);  | 
2682  | 0  |     }  | 
2683  | 0  |     if (U_SUCCESS(status) && baseName == fullName) { | 
2684  |  |         // May have added the first keyword, meaning that the fullName is no longer also the baseName.  | 
2685  | 0  |         initBaseName(status);  | 
2686  | 0  |     }  | 
2687  | 0  | }  | 
2688  |  |  | 
2689  |  | void  | 
2690  |  | Locale::setKeywordValue(StringPiece keywordName,  | 
2691  |  |                         StringPiece keywordValue,  | 
2692  | 0  |                         UErrorCode& status) { | 
2693  |  |     // TODO: Remove the need for a const char* to a NUL terminated buffer.  | 
2694  | 0  |     const CharString keywordName_nul(keywordName, status);  | 
2695  | 0  |     const CharString keywordValue_nul(keywordValue, status);  | 
2696  | 0  |     setKeywordValue(keywordName_nul.data(), keywordValue_nul.data(), status);  | 
2697  | 0  | }  | 
2698  |  |  | 
2699  |  | void  | 
2700  |  | Locale::setUnicodeKeywordValue(StringPiece keywordName,  | 
2701  |  |                                StringPiece keywordValue,  | 
2702  | 0  |                                UErrorCode& status) { | 
2703  |  |     // TODO: Remove the need for a const char* to a NUL terminated buffer.  | 
2704  | 0  |     const CharString keywordName_nul(keywordName, status);  | 
2705  | 0  |     const CharString keywordValue_nul(keywordValue, status);  | 
2706  |  | 
  | 
2707  | 0  |     if (U_FAILURE(status)) { | 
2708  | 0  |         return;  | 
2709  | 0  |     }  | 
2710  |  |  | 
2711  | 0  |     const char* legacy_key = uloc_toLegacyKey(keywordName_nul.data());  | 
2712  |  | 
  | 
2713  | 0  |     if (legacy_key == nullptr) { | 
2714  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2715  | 0  |         return;  | 
2716  | 0  |     }  | 
2717  |  |  | 
2718  | 0  |     const char* legacy_value = nullptr;  | 
2719  |  | 
  | 
2720  | 0  |     if (!keywordValue_nul.isEmpty()) { | 
2721  | 0  |         legacy_value =  | 
2722  | 0  |             uloc_toLegacyType(keywordName_nul.data(), keywordValue_nul.data());  | 
2723  |  | 
  | 
2724  | 0  |         if (legacy_value == nullptr) { | 
2725  | 0  |             status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2726  | 0  |             return;  | 
2727  | 0  |         }  | 
2728  | 0  |     }  | 
2729  |  |  | 
2730  | 0  |     setKeywordValue(legacy_key, legacy_value, status);  | 
2731  | 0  | }  | 
2732  |  |  | 
2733  |  | const char *  | 
2734  | 0  | Locale::getBaseName() const { | 
2735  | 0  |     return baseName;  | 
2736  | 0  | }  | 
2737  |  |  | 
2738  | 0  | Locale::Iterator::~Iterator() = default;  | 
2739  |  |  | 
2740  |  | //eof  | 
2741  |  | U_NAMESPACE_END  |