/src/icu/source/i18n/region.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) 2014-2016, International Business Machines Corporation and  | 
6  |  | * others. All Rights Reserved.  | 
7  |  | *******************************************************************************  | 
8  |  | *  | 
9  |  | *  | 
10  |  | * File REGION.CPP  | 
11  |  | *  | 
12  |  | * Modification History:*  | 
13  |  | *   Date        Name        Description  | 
14  |  | * 01/15/13      Emmons      Original Port from ICU4J  | 
15  |  | ********************************************************************************  | 
16  |  | */  | 
17  |  |  | 
18  |  | /**  | 
19  |  |  * \file  | 
20  |  |  * \brief C++ API: Region classes (territory containment)  | 
21  |  |  */  | 
22  |  |  | 
23  |  | #include "unicode/region.h"  | 
24  |  | #include "unicode/utypes.h"  | 
25  |  | #include "unicode/uobject.h"  | 
26  |  | #include "unicode/unistr.h"  | 
27  |  | #include "unicode/ures.h"  | 
28  |  | #include "ucln_in.h"  | 
29  |  | #include "cstring.h"  | 
30  |  | #include "mutex.h"  | 
31  |  | #include "uhash.h"  | 
32  |  | #include "umutex.h"  | 
33  |  | #include "uresimp.h"  | 
34  |  | #include "region_impl.h"  | 
35  |  | #include "util.h"  | 
36  |  |  | 
37  |  | #if !UCONFIG_NO_FORMATTING  | 
38  |  |  | 
39  |  |  | 
40  |  | U_CDECL_BEGIN  | 
41  |  |  | 
42  |  | static void U_CALLCONV  | 
43  | 0  | deleteRegion(void *obj) { | 
44  | 0  |     delete (icu::Region *)obj;  | 
45  | 0  | }  | 
46  |  |  | 
47  |  | /**  | 
48  |  |  * Cleanup callback func  | 
49  |  |  */  | 
50  |  | static UBool U_CALLCONV region_cleanup(void)  | 
51  | 0  | { | 
52  | 0  |     icu::Region::cleanupRegionData();  | 
53  |  | 
  | 
54  | 0  |     return TRUE;  | 
55  | 0  | }  | 
56  |  |  | 
57  |  | U_CDECL_END  | 
58  |  |  | 
59  |  | U_NAMESPACE_BEGIN  | 
60  |  |  | 
61  |  | static UInitOnce gRegionDataInitOnce = U_INITONCE_INITIALIZER;  | 
62  |  | static UVector* availableRegions[URGN_LIMIT];  | 
63  |  |  | 
64  |  | static UHashtable *regionAliases = NULL;  | 
65  |  | static UHashtable *regionIDMap = NULL;  | 
66  |  | static UHashtable *numericCodeMap = NULL;  | 
67  |  | static UVector *allRegions = NULL;  | 
68  |  |  | 
69  |  | static const UChar UNKNOWN_REGION_ID [] = { 0x5A, 0x5A, 0 };  /* "ZZ" */ | 
70  |  | static const UChar OUTLYING_OCEANIA_REGION_ID [] = { 0x51, 0x4F, 0 };  /* "QO" */ | 
71  |  | static const UChar WORLD_ID [] = { 0x30, 0x30, 0x31, 0 };  /* "001" */ | 
72  |  | static const UChar RANGE_MARKER = 0x7E; /* '~' */  | 
73  |  |  | 
74  |  | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RegionNameEnumeration)  | 
75  |  |  | 
76  |  | /*  | 
77  |  |  * Initializes the region data from the ICU resource bundles.  The region data  | 
78  |  |  * contains the basic relationships such as which regions are known, what the numeric  | 
79  |  |  * codes are, any known aliases, and the territory containment data.  | 
80  |  |  *  | 
81  |  |  * If the region data has already loaded, then this method simply returns without doing  | 
82  |  |  * anything meaningful.  | 
83  |  |  */  | 
84  | 0  | void U_CALLCONV Region::loadRegionData(UErrorCode &status) { | 
85  |  |  | 
86  |  |     // Construct service objs first  | 
87  | 0  |     LocalUHashtablePointer newRegionIDMap(uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status));  | 
88  | 0  |     LocalUHashtablePointer newNumericCodeMap(uhash_open(uhash_hashLong,uhash_compareLong,NULL,&status));  | 
89  | 0  |     LocalUHashtablePointer newRegionAliases(uhash_open(uhash_hashUnicodeString,uhash_compareUnicodeString,NULL,&status));  | 
90  |  | 
  | 
91  | 0  |     LocalPointer<UVector> continents(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status);  | 
92  | 0  |     LocalPointer<UVector> groupings(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status);  | 
93  | 0  |     allRegions = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status);  | 
94  |  | 
  | 
95  | 0  |     LocalUResourceBundlePointer metadata(ures_openDirect(NULL,"metadata",&status));  | 
96  | 0  |     LocalUResourceBundlePointer metadataAlias(ures_getByKey(metadata.getAlias(),"alias",NULL,&status));  | 
97  | 0  |     LocalUResourceBundlePointer territoryAlias(ures_getByKey(metadataAlias.getAlias(),"territory",NULL,&status));  | 
98  |  | 
  | 
99  | 0  |     LocalUResourceBundlePointer supplementalData(ures_openDirect(NULL,"supplementalData",&status));  | 
100  | 0  |     LocalUResourceBundlePointer codeMappings(ures_getByKey(supplementalData.getAlias(),"codeMappings",NULL,&status));  | 
101  |  | 
  | 
102  | 0  |     LocalUResourceBundlePointer idValidity(ures_getByKey(supplementalData.getAlias(),"idValidity",NULL,&status));  | 
103  | 0  |     LocalUResourceBundlePointer regionList(ures_getByKey(idValidity.getAlias(),"region",NULL,&status));  | 
104  | 0  |     LocalUResourceBundlePointer regionRegular(ures_getByKey(regionList.getAlias(),"regular",NULL,&status));  | 
105  | 0  |     LocalUResourceBundlePointer regionMacro(ures_getByKey(regionList.getAlias(),"macroregion",NULL,&status));  | 
106  | 0  |     LocalUResourceBundlePointer regionUnknown(ures_getByKey(regionList.getAlias(),"unknown",NULL,&status));  | 
107  |  | 
  | 
108  | 0  |     LocalUResourceBundlePointer territoryContainment(ures_getByKey(supplementalData.getAlias(),"territoryContainment",NULL,&status));  | 
109  | 0  |     LocalUResourceBundlePointer worldContainment(ures_getByKey(territoryContainment.getAlias(),"001",NULL,&status));  | 
110  | 0  |     LocalUResourceBundlePointer groupingContainment(ures_getByKey(territoryContainment.getAlias(),"grouping",NULL,&status));  | 
111  |  | 
  | 
112  | 0  |     if (U_FAILURE(status)) { | 
113  | 0  |         return;  | 
114  | 0  |     }  | 
115  |  |  | 
116  |  |     // now, initialize  | 
117  | 0  |     uhash_setValueDeleter(newRegionIDMap.getAlias(), deleteRegion);  // regionIDMap owns objs  | 
118  | 0  |     uhash_setKeyDeleter(newRegionAliases.getAlias(), uprv_deleteUObject); // regionAliases owns the string keys  | 
119  |  |  | 
120  |  | 
  | 
121  | 0  |     while ( ures_hasNext(regionRegular.getAlias()) ) { | 
122  | 0  |         UnicodeString regionName = ures_getNextUnicodeString(regionRegular.getAlias(),NULL,&status);  | 
123  | 0  |         int32_t rangeMarkerLocation = regionName.indexOf(RANGE_MARKER);  | 
124  | 0  |         UChar buf[6];  | 
125  | 0  |         regionName.extract(buf,6,status);  | 
126  | 0  |         if ( rangeMarkerLocation > 0 ) { | 
127  | 0  |             UChar endRange = regionName.charAt(rangeMarkerLocation+1);  | 
128  | 0  |             buf[rangeMarkerLocation] = 0;  | 
129  | 0  |             while ( buf[rangeMarkerLocation-1] <= endRange ) { | 
130  | 0  |                 LocalPointer<UnicodeString> newRegion(new UnicodeString(buf), status);  | 
131  | 0  |                 allRegions->addElementX(newRegion.orphan(),status);  | 
132  | 0  |                 buf[rangeMarkerLocation-1]++;  | 
133  | 0  |             }  | 
134  | 0  |         } else { | 
135  | 0  |             LocalPointer<UnicodeString> newRegion(new UnicodeString(regionName), status);  | 
136  | 0  |             allRegions->addElementX(newRegion.orphan(),status);  | 
137  | 0  |         }  | 
138  | 0  |     }  | 
139  |  | 
  | 
140  | 0  |     while ( ures_hasNext(regionMacro.getAlias()) ) { | 
141  | 0  |         UnicodeString regionName = ures_getNextUnicodeString(regionMacro.getAlias(),NULL,&status);  | 
142  | 0  |         int32_t rangeMarkerLocation = regionName.indexOf(RANGE_MARKER);  | 
143  | 0  |         UChar buf[6];  | 
144  | 0  |         regionName.extract(buf,6,status);  | 
145  | 0  |         if ( rangeMarkerLocation > 0 ) { | 
146  | 0  |             UChar endRange = regionName.charAt(rangeMarkerLocation+1);  | 
147  | 0  |             buf[rangeMarkerLocation] = 0;  | 
148  | 0  |             while ( buf[rangeMarkerLocation-1] <= endRange ) { | 
149  | 0  |                 LocalPointer<UnicodeString> newRegion(new UnicodeString(buf), status);  | 
150  | 0  |                 allRegions->addElementX(newRegion.orphan(),status);  | 
151  | 0  |                 buf[rangeMarkerLocation-1]++;  | 
152  | 0  |             }  | 
153  | 0  |         } else { | 
154  | 0  |             LocalPointer<UnicodeString> newRegion(new UnicodeString(regionName), status);  | 
155  | 0  |             allRegions->addElementX(newRegion.orphan(),status);  | 
156  | 0  |         }  | 
157  | 0  |     }  | 
158  |  | 
  | 
159  | 0  |     while ( ures_hasNext(regionUnknown.getAlias()) ) { | 
160  | 0  |         LocalPointer<UnicodeString> regionName (new UnicodeString(ures_getNextUnicodeString(regionUnknown.getAlias(),NULL,&status),status));  | 
161  | 0  |         allRegions->addElementX(regionName.orphan(),status);  | 
162  | 0  |     }  | 
163  |  | 
  | 
164  | 0  |     while ( ures_hasNext(worldContainment.getAlias()) ) { | 
165  | 0  |         UnicodeString *continentName = new UnicodeString(ures_getNextUnicodeString(worldContainment.getAlias(),NULL,&status));  | 
166  | 0  |         continents->addElementX(continentName,status);  | 
167  | 0  |     }  | 
168  |  | 
  | 
169  | 0  |     for ( int32_t i = 0 ; i < allRegions->size() ; i++ ) { | 
170  | 0  |         LocalPointer<Region> r(new Region(), status);  | 
171  | 0  |         if ( U_FAILURE(status) ) { | 
172  | 0  |            return;  | 
173  | 0  |         }  | 
174  | 0  |         UnicodeString *regionName = (UnicodeString *)allRegions->elementAt(i);  | 
175  | 0  |         r->idStr = *regionName;  | 
176  |  | 
  | 
177  | 0  |         r->idStr.extract(0,r->idStr.length(),r->id,sizeof(r->id),US_INV);  | 
178  | 0  |         r->fType = URGN_TERRITORY; // Only temporary - figure out the real type later once the aliases are known.  | 
179  |  | 
  | 
180  | 0  |         int32_t pos = 0;  | 
181  | 0  |         int32_t result = ICU_Utility::parseAsciiInteger(r->idStr, pos);  | 
182  | 0  |         if (pos > 0) { | 
183  | 0  |             r->code = result; // Convert string to number  | 
184  | 0  |             uhash_iput(newNumericCodeMap.getAlias(),r->code,(void *)(r.getAlias()),&status);  | 
185  | 0  |             r->fType = URGN_SUBCONTINENT;  | 
186  | 0  |         } else { | 
187  | 0  |             r->code = -1;  | 
188  | 0  |         }  | 
189  | 0  |         void* idStrAlias = (void*)&(r->idStr); // about to orphan 'r'. Save this off.  | 
190  | 0  |         uhash_put(newRegionIDMap.getAlias(),idStrAlias,(void *)(r.orphan()),&status); // regionIDMap takes ownership  | 
191  | 0  |     }  | 
192  |  |  | 
193  | 0  |     UResourceBundle *groupingBundle = nullptr;  | 
194  | 0  |     while ( ures_hasNext(groupingContainment.getAlias()) ) { | 
195  | 0  |         groupingBundle = ures_getNextResource(groupingContainment.getAlias(), groupingBundle, &status);  | 
196  | 0  |         if (U_FAILURE(status)) { | 
197  | 0  |             break;  | 
198  | 0  |         }  | 
199  | 0  |         UnicodeString *groupingName = new UnicodeString(ures_getKey(groupingBundle), -1, US_INV);  | 
200  | 0  |         groupings->addElementX(groupingName,status);  | 
201  | 0  |         Region *grouping = (Region *) uhash_get(newRegionIDMap.getAlias(),groupingName);  | 
202  | 0  |         if (grouping != NULL) { | 
203  | 0  |             for (int32_t i = 0; i < ures_getSize(groupingBundle); i++) { | 
204  | 0  |                 UnicodeString child = ures_getUnicodeStringByIndex(groupingBundle, i, &status);  | 
205  | 0  |                 if (U_SUCCESS(status)) { | 
206  | 0  |                     if (grouping->containedRegions == NULL) { | 
207  | 0  |                         grouping->containedRegions = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status);  | 
208  | 0  |                     }  | 
209  | 0  |                     grouping->containedRegions->addElementX(new UnicodeString(child), status);  | 
210  | 0  |                 }  | 
211  | 0  |             }  | 
212  | 0  |         }  | 
213  | 0  |     }  | 
214  | 0  |     ures_close(groupingBundle);  | 
215  |  |       | 
216  |  |     // Process the territory aliases  | 
217  | 0  |     while ( ures_hasNext(territoryAlias.getAlias()) ) { | 
218  | 0  |         LocalUResourceBundlePointer res(ures_getNextResource(territoryAlias.getAlias(),NULL,&status));  | 
219  | 0  |         const char *aliasFrom = ures_getKey(res.getAlias());  | 
220  | 0  |         LocalPointer<UnicodeString> aliasFromStr(new UnicodeString(aliasFrom, -1, US_INV), status);  | 
221  | 0  |         UnicodeString aliasTo = ures_getUnicodeStringByKey(res.getAlias(),"replacement",&status);  | 
222  | 0  |         res.adoptInstead(NULL);  | 
223  |  | 
  | 
224  | 0  |         const Region *aliasToRegion = (Region *) uhash_get(newRegionIDMap.getAlias(),&aliasTo);  | 
225  | 0  |         Region *aliasFromRegion = (Region *)uhash_get(newRegionIDMap.getAlias(),aliasFromStr.getAlias());  | 
226  |  | 
  | 
227  | 0  |         if ( aliasToRegion != NULL && aliasFromRegion == NULL ) { // This is just an alias from some string to a region | 
228  | 0  |             uhash_put(newRegionAliases.getAlias(),(void *)aliasFromStr.orphan(), (void *)aliasToRegion,&status);  | 
229  | 0  |         } else { | 
230  | 0  |             if ( aliasFromRegion == NULL ) { // Deprecated region code not in the primary codes list - so need to create a deprecated region for it. | 
231  | 0  |                 LocalPointer<Region> newRgn(new Region, status);   | 
232  | 0  |                 if ( U_SUCCESS(status) ) { | 
233  | 0  |                     aliasFromRegion = newRgn.orphan();  | 
234  | 0  |                 } else { | 
235  | 0  |                     return; // error out  | 
236  | 0  |                 }  | 
237  | 0  |                 aliasFromRegion->idStr.setTo(*aliasFromStr);  | 
238  | 0  |                 aliasFromRegion->idStr.extract(0,aliasFromRegion->idStr.length(),aliasFromRegion->id,sizeof(aliasFromRegion->id),US_INV);  | 
239  | 0  |                 uhash_put(newRegionIDMap.getAlias(),(void *)&(aliasFromRegion->idStr),(void *)aliasFromRegion,&status);  | 
240  | 0  |                 int32_t pos = 0;  | 
241  | 0  |                 int32_t result = ICU_Utility::parseAsciiInteger(aliasFromRegion->idStr, pos);  | 
242  | 0  |                 if ( pos > 0 ) { | 
243  | 0  |                     aliasFromRegion->code = result; // Convert string to number  | 
244  | 0  |                     uhash_iput(newNumericCodeMap.getAlias(),aliasFromRegion->code,(void *)aliasFromRegion,&status);  | 
245  | 0  |                 } else { | 
246  | 0  |                     aliasFromRegion->code = -1;  | 
247  | 0  |                 }  | 
248  | 0  |                 aliasFromRegion->fType = URGN_DEPRECATED;  | 
249  | 0  |             } else { | 
250  | 0  |                 aliasFromRegion->fType = URGN_DEPRECATED;  | 
251  | 0  |             }  | 
252  |  |  | 
253  | 0  |             { | 
254  | 0  |                 LocalPointer<UVector> newPreferredValues(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status);  | 
255  | 0  |                 aliasFromRegion->preferredValues = newPreferredValues.orphan();  | 
256  | 0  |             }  | 
257  | 0  |             if( U_FAILURE(status)) { | 
258  | 0  |                 return;  | 
259  | 0  |             }  | 
260  | 0  |             UnicodeString currentRegion;  | 
261  |  |             //currentRegion.remove();   TODO: was already 0 length?  | 
262  | 0  |             for (int32_t i = 0 ; i < aliasTo.length() ; i++ ) { | 
263  | 0  |                 if ( aliasTo.charAt(i) != 0x0020 ) { | 
264  | 0  |                     currentRegion.append(aliasTo.charAt(i));  | 
265  | 0  |                 }  | 
266  | 0  |                 if ( aliasTo.charAt(i) == 0x0020 || i+1 == aliasTo.length() ) { | 
267  | 0  |                     Region *target = (Region *)uhash_get(newRegionIDMap.getAlias(),(void *)¤tRegion);  | 
268  | 0  |                     if (target) { | 
269  | 0  |                         LocalPointer<UnicodeString> preferredValue(new UnicodeString(target->idStr), status);  | 
270  | 0  |                         aliasFromRegion->preferredValues->addElementX((void *)preferredValue.orphan(),status);  // may add null if err  | 
271  | 0  |                     }  | 
272  | 0  |                     currentRegion.remove();  | 
273  | 0  |                 }  | 
274  | 0  |             }  | 
275  | 0  |         }  | 
276  | 0  |     }  | 
277  |  |  | 
278  |  |     // Process the code mappings - This will allow us to assign numeric codes to most of the territories.  | 
279  | 0  |     while ( ures_hasNext(codeMappings.getAlias()) ) { | 
280  | 0  |         UResourceBundle *mapping = ures_getNextResource(codeMappings.getAlias(),NULL,&status);  | 
281  | 0  |         if ( ures_getType(mapping) == URES_ARRAY && ures_getSize(mapping) == 3) { | 
282  | 0  |             UnicodeString codeMappingID = ures_getUnicodeStringByIndex(mapping,0,&status);  | 
283  | 0  |             UnicodeString codeMappingNumber = ures_getUnicodeStringByIndex(mapping,1,&status);  | 
284  | 0  |             UnicodeString codeMapping3Letter = ures_getUnicodeStringByIndex(mapping,2,&status);  | 
285  |  | 
  | 
286  | 0  |             Region *r = (Region *)uhash_get(newRegionIDMap.getAlias(),(void *)&codeMappingID);  | 
287  | 0  |             if ( r ) { | 
288  | 0  |                 int32_t pos = 0;  | 
289  | 0  |                 int32_t result = ICU_Utility::parseAsciiInteger(codeMappingNumber, pos);  | 
290  | 0  |                 if ( pos > 0 ) { | 
291  | 0  |                     r->code = result; // Convert string to number  | 
292  | 0  |                     uhash_iput(newNumericCodeMap.getAlias(),r->code,(void *)r,&status);  | 
293  | 0  |                 }  | 
294  | 0  |                 LocalPointer<UnicodeString> code3(new UnicodeString(codeMapping3Letter), status);  | 
295  | 0  |                 uhash_put(newRegionAliases.getAlias(),(void *)code3.orphan(), (void *)r,&status);  | 
296  | 0  |             }  | 
297  | 0  |         }  | 
298  | 0  |         ures_close(mapping);  | 
299  | 0  |     }  | 
300  |  |  | 
301  |  |     // Now fill in the special cases for WORLD, UNKNOWN, CONTINENTS, and GROUPINGS  | 
302  | 0  |     Region *r;  | 
303  | 0  |     UnicodeString WORLD_ID_STRING(WORLD_ID);  | 
304  | 0  |     r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&WORLD_ID_STRING);  | 
305  | 0  |     if ( r ) { | 
306  | 0  |         r->fType = URGN_WORLD;  | 
307  | 0  |     }  | 
308  |  | 
  | 
309  | 0  |     UnicodeString UNKNOWN_REGION_ID_STRING(UNKNOWN_REGION_ID);  | 
310  | 0  |     r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&UNKNOWN_REGION_ID_STRING);  | 
311  | 0  |     if ( r ) { | 
312  | 0  |         r->fType = URGN_UNKNOWN;  | 
313  | 0  |     }  | 
314  |  | 
  | 
315  | 0  |     for ( int32_t i = 0 ; i < continents->size() ; i++ ) { | 
316  | 0  |         r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)continents->elementAt(i));  | 
317  | 0  |         if ( r ) { | 
318  | 0  |             r->fType = URGN_CONTINENT;  | 
319  | 0  |         }  | 
320  | 0  |     }  | 
321  |  | 
  | 
322  | 0  |     for ( int32_t i = 0 ; i < groupings->size() ; i++ ) { | 
323  | 0  |         r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)groupings->elementAt(i));  | 
324  | 0  |         if ( r ) { | 
325  | 0  |             r->fType = URGN_GROUPING;  | 
326  | 0  |         }  | 
327  | 0  |     }  | 
328  |  |  | 
329  |  |     // Special case: The region code "QO" (Outlying Oceania) is a subcontinent code added by CLDR  | 
330  |  |     // even though it looks like a territory code.  Need to handle it here.  | 
331  |  | 
  | 
332  | 0  |     UnicodeString OUTLYING_OCEANIA_REGION_ID_STRING(OUTLYING_OCEANIA_REGION_ID);  | 
333  | 0  |     r = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&OUTLYING_OCEANIA_REGION_ID_STRING);  | 
334  | 0  |     if ( r ) { | 
335  | 0  |         r->fType = URGN_SUBCONTINENT;  | 
336  | 0  |     }  | 
337  |  |  | 
338  |  |     // Load territory containment info from the supplemental data.  | 
339  | 0  |     while ( ures_hasNext(territoryContainment.getAlias()) ) { | 
340  | 0  |         LocalUResourceBundlePointer mapping(ures_getNextResource(territoryContainment.getAlias(),NULL,&status));  | 
341  | 0  |         if( U_FAILURE(status) ) { | 
342  | 0  |             return;  // error out  | 
343  | 0  |         }  | 
344  | 0  |         const char *parent = ures_getKey(mapping.getAlias());  | 
345  | 0  |         if (uprv_strcmp(parent, "containedGroupings") == 0 || uprv_strcmp(parent, "deprecated") == 0) { | 
346  | 0  |             continue; // handle new pseudo-parent types added in ICU data per cldrbug 7808; for now just skip.  | 
347  |  |             // #11232 is to do something useful with these.  | 
348  | 0  |         }  | 
349  | 0  |         UnicodeString parentStr = UnicodeString(parent, -1 , US_INV);  | 
350  | 0  |         Region *parentRegion = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&parentStr);  | 
351  |  | 
  | 
352  | 0  |         for ( int j = 0 ; j < ures_getSize(mapping.getAlias()); j++ ) { | 
353  | 0  |             UnicodeString child = ures_getUnicodeStringByIndex(mapping.getAlias(),j,&status);  | 
354  | 0  |             Region *childRegion = (Region *) uhash_get(newRegionIDMap.getAlias(),(void *)&child);  | 
355  | 0  |             if ( parentRegion != NULL && childRegion != NULL ) { | 
356  |  |  | 
357  |  |                 // Add the child region to the set of regions contained by the parent  | 
358  | 0  |                 if (parentRegion->containedRegions == NULL) { | 
359  | 0  |                     parentRegion->containedRegions = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status);  | 
360  | 0  |                 }  | 
361  |  | 
  | 
362  | 0  |                 LocalPointer<UnicodeString> childStr(new UnicodeString(), status);  | 
363  | 0  |                 if( U_FAILURE(status) ) { | 
364  | 0  |                     return;  // error out  | 
365  | 0  |                 }  | 
366  | 0  |                 childStr->fastCopyFrom(childRegion->idStr);  | 
367  | 0  |                 parentRegion->containedRegions->addElementX((void *)childStr.orphan(),status);  | 
368  |  |  | 
369  |  |                 // Set the parent region to be the containing region of the child.  | 
370  |  |                 // Regions of type GROUPING can't be set as the parent, since another region  | 
371  |  |                 // such as a SUBCONTINENT, CONTINENT, or WORLD must always be the parent.  | 
372  | 0  |                 if ( parentRegion->fType != URGN_GROUPING) { | 
373  | 0  |                     childRegion->containingRegion = parentRegion;  | 
374  | 0  |                 }  | 
375  | 0  |             }  | 
376  | 0  |         }  | 
377  | 0  |     }  | 
378  |  |  | 
379  |  |     // Create the availableRegions lists  | 
380  | 0  |     int32_t pos = UHASH_FIRST;  | 
381  | 0  |     while ( const UHashElement* element = uhash_nextElement(newRegionIDMap.getAlias(),&pos)) { | 
382  | 0  |         Region *ar = (Region *)element->value.pointer;  | 
383  | 0  |         if ( availableRegions[ar->fType] == NULL ) { | 
384  | 0  |             LocalPointer<UVector> newAr(new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status), status);  | 
385  | 0  |             availableRegions[ar->fType] = newAr.orphan();  | 
386  | 0  |         }  | 
387  | 0  |         LocalPointer<UnicodeString> arString(new UnicodeString(ar->idStr), status);  | 
388  | 0  |         if( U_FAILURE(status) ) { | 
389  | 0  |             return;  // error out  | 
390  | 0  |         }  | 
391  | 0  |         availableRegions[ar->fType]->addElementX((void *)arString.orphan(),status);  | 
392  | 0  |     }  | 
393  |  |       | 
394  | 0  |     ucln_i18n_registerCleanup(UCLN_I18N_REGION, region_cleanup);  | 
395  |  |     // copy hashtables  | 
396  | 0  |     numericCodeMap = newNumericCodeMap.orphan();  | 
397  | 0  |     regionIDMap = newRegionIDMap.orphan();  | 
398  | 0  |     regionAliases = newRegionAliases.orphan();  | 
399  | 0  | }  | 
400  |  |  | 
401  | 0  | void Region::cleanupRegionData() { | 
402  | 0  |     for (int32_t i = 0 ; i < URGN_LIMIT ; i++ ) { | 
403  | 0  |         if ( availableRegions[i] ) { | 
404  | 0  |             delete availableRegions[i];  | 
405  | 0  |         }  | 
406  | 0  |     }  | 
407  |  | 
  | 
408  | 0  |     if (regionAliases) { | 
409  | 0  |         uhash_close(regionAliases);  | 
410  | 0  |     }  | 
411  |  | 
  | 
412  | 0  |     if (numericCodeMap) { | 
413  | 0  |         uhash_close(numericCodeMap);  | 
414  | 0  |     }  | 
415  |  | 
  | 
416  | 0  |     if (regionIDMap) { | 
417  | 0  |         uhash_close(regionIDMap);  | 
418  | 0  |     }  | 
419  | 0  |     if (allRegions) { | 
420  | 0  |         allRegions->removeAllElements(); // Don't need the temporary list anymore.  | 
421  | 0  |         delete allRegions;  | 
422  | 0  |         allRegions = NULL;  | 
423  | 0  |     }  | 
424  |  | 
  | 
425  | 0  |     regionAliases = numericCodeMap = regionIDMap = NULL;  | 
426  |  | 
  | 
427  | 0  |     gRegionDataInitOnce.reset();  | 
428  | 0  | }  | 
429  |  |  | 
430  |  | Region::Region ()  | 
431  | 0  |         : code(-1),  | 
432  | 0  |           fType(URGN_UNKNOWN),  | 
433  |  |           containingRegion(NULL),  | 
434  |  |           containedRegions(NULL),  | 
435  | 0  |           preferredValues(NULL) { | 
436  | 0  |     id[0] = 0;  | 
437  | 0  | }  | 
438  |  |  | 
439  | 0  | Region::~Region () { | 
440  | 0  |         if (containedRegions) { | 
441  | 0  |             delete containedRegions;  | 
442  | 0  |         }  | 
443  | 0  |         if (preferredValues) { | 
444  | 0  |             delete preferredValues;  | 
445  | 0  |         }  | 
446  | 0  | }  | 
447  |  |  | 
448  |  | /**  | 
449  |  |  * Returns true if the two regions are equal.  | 
450  |  |  * Per PMC, just use pointer compare, since we have at most one instance of each Region.  | 
451  |  |  */  | 
452  |  | bool  | 
453  | 0  | Region::operator==(const Region &that) const { | 
454  | 0  |     return (idStr == that.idStr);  | 
455  | 0  | }  | 
456  |  |  | 
457  |  | /**  | 
458  |  |  * Returns true if the two regions are NOT equal; that is, if operator ==() returns false.  | 
459  |  |  * Per PMC, just use pointer compare, since we have at most one instance of each Region.  | 
460  |  |  */  | 
461  |  | bool  | 
462  | 0  | Region::operator!=(const Region &that) const { | 
463  | 0  |         return (idStr != that.idStr);  | 
464  | 0  | }  | 
465  |  |  | 
466  |  | /**  | 
467  |  |  * Returns a pointer to a Region using the given region code.  The region code can be either 2-letter ISO code,  | 
468  |  |  * 3-letter ISO code,  UNM.49 numeric code, or other valid Unicode Region Code as defined by the LDML specification.  | 
469  |  |  * The identifier will be canonicalized internally using the supplemental metadata as defined in the CLDR.  | 
470  |  |  * If the region code is NULL or not recognized, the appropriate error code will be set ( U_ILLEGAL_ARGUMENT_ERROR )  | 
471  |  |  */  | 
472  |  | const Region* U_EXPORT2  | 
473  | 0  | Region::getInstance(const char *region_code, UErrorCode &status) { | 
474  |  | 
  | 
475  | 0  |     umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status);  | 
476  | 0  |     if (U_FAILURE(status)) { | 
477  | 0  |         return NULL;  | 
478  | 0  |     }  | 
479  |  |  | 
480  | 0  |     if ( !region_code ) { | 
481  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
482  | 0  |         return NULL;  | 
483  | 0  |     }  | 
484  |  |  | 
485  | 0  |     UnicodeString regionCodeString = UnicodeString(region_code, -1, US_INV);  | 
486  | 0  |     Region *r = (Region *)uhash_get(regionIDMap,(void *)®ionCodeString);  | 
487  |  | 
  | 
488  | 0  |     if ( !r ) { | 
489  | 0  |         r = (Region *)uhash_get(regionAliases,(void *)®ionCodeString);  | 
490  | 0  |     }  | 
491  |  | 
  | 
492  | 0  |     if ( !r ) { // Unknown region code | 
493  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
494  | 0  |         return NULL;  | 
495  | 0  |     }  | 
496  |  |  | 
497  | 0  |     if ( r->fType == URGN_DEPRECATED && r->preferredValues->size() == 1) { | 
498  | 0  |         StringEnumeration *pv = r->getPreferredValues(status);  | 
499  | 0  |         pv->reset(status);  | 
500  | 0  |         const UnicodeString *ustr = pv->snext(status);  | 
501  | 0  |         r = (Region *)uhash_get(regionIDMap,(void *)ustr);  | 
502  | 0  |         delete pv;  | 
503  | 0  |     }  | 
504  |  | 
  | 
505  | 0  |     return r;  | 
506  |  | 
  | 
507  | 0  | }  | 
508  |  |  | 
509  |  | /**  | 
510  |  |  * Returns a pointer to a Region using the given numeric region code. If the numeric region code is not recognized,  | 
511  |  |  * the appropriate error code will be set ( U_ILLEGAL_ARGUMENT_ERROR ).  | 
512  |  |  */  | 
513  |  | const Region* U_EXPORT2  | 
514  | 0  | Region::getInstance (int32_t code, UErrorCode &status) { | 
515  |  | 
  | 
516  | 0  |     umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status);  | 
517  | 0  |     if (U_FAILURE(status)) { | 
518  | 0  |         return NULL;  | 
519  | 0  |     }  | 
520  |  |  | 
521  | 0  |     Region *r = (Region *)uhash_iget(numericCodeMap,code);  | 
522  |  | 
  | 
523  | 0  |     if ( !r ) { // Just in case there's an alias that's numeric, try to find it. | 
524  | 0  |         UnicodeString id;  | 
525  | 0  |         ICU_Utility::appendNumber(id, code, 10, 1);  | 
526  | 0  |         r = (Region *)uhash_get(regionAliases,&id);  | 
527  | 0  |     }  | 
528  |  | 
  | 
529  | 0  |     if( U_FAILURE(status) ) { | 
530  | 0  |         return NULL;  | 
531  | 0  |     }  | 
532  |  |  | 
533  | 0  |     if ( !r ) { | 
534  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
535  | 0  |         return NULL;  | 
536  | 0  |     }  | 
537  |  |  | 
538  | 0  |     if ( r->fType == URGN_DEPRECATED && r->preferredValues->size() == 1) { | 
539  | 0  |         StringEnumeration *pv = r->getPreferredValues(status);  | 
540  | 0  |         pv->reset(status);  | 
541  | 0  |         const UnicodeString *ustr = pv->snext(status);  | 
542  | 0  |         r = (Region *)uhash_get(regionIDMap,(void *)ustr);  | 
543  | 0  |         delete pv;  | 
544  | 0  |     }  | 
545  |  | 
  | 
546  | 0  |     return r;  | 
547  | 0  | }  | 
548  |  |  | 
549  |  |  | 
550  |  | /**  | 
551  |  |  * Returns an enumeration over the IDs of all known regions that match the given type.  | 
552  |  |  */  | 
553  |  | StringEnumeration* U_EXPORT2  | 
554  | 0  | Region::getAvailable(URegionType type, UErrorCode &status) { | 
555  | 0  |     umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); // returns immediately if U_FAILURE(status)  | 
556  | 0  |     if (U_FAILURE(status)) { | 
557  | 0  |         return NULL;  | 
558  | 0  |     }  | 
559  | 0  |     return new RegionNameEnumeration(availableRegions[type],status);  | 
560  | 0  | }  | 
561  |  |  | 
562  |  | /**  | 
563  |  |  * Returns a pointer to the region that contains this region.  Returns NULL if this region is code "001" (World)  | 
564  |  |  * or "ZZ" (Unknown region). For example, calling this method with region "IT" (Italy) returns the  | 
565  |  |  * region "039" (Southern Europe).  | 
566  |  |  */  | 
567  |  | const Region*  | 
568  | 0  | Region::getContainingRegion() const { | 
569  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
570  | 0  |     umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status);  | 
571  | 0  |     return containingRegion;  | 
572  | 0  | }  | 
573  |  |  | 
574  |  | /**  | 
575  |  |  * Return a pointer to the region that geographically contains this region and matches the given type,  | 
576  |  |  * moving multiple steps up the containment chain if necessary.  Returns NULL if no containing region can be found  | 
577  |  |  * that matches the given type. Note: The URegionTypes = "URGN_GROUPING", "URGN_DEPRECATED", or "URGN_UNKNOWN"  | 
578  |  |  * are not appropriate for use in this API. NULL will be returned in this case. For example, calling this method  | 
579  |  |  * with region "IT" (Italy) for type "URGN_CONTINENT" returns the region "150" ( Europe ).  | 
580  |  |  */  | 
581  |  | const Region*  | 
582  | 0  | Region::getContainingRegion(URegionType type) const { | 
583  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
584  | 0  |     umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status);  | 
585  | 0  |     if ( containingRegion == NULL ) { | 
586  | 0  |         return NULL;  | 
587  | 0  |     }  | 
588  |  |  | 
589  | 0  |     return ( containingRegion->fType == type)? containingRegion: containingRegion->getContainingRegion(type);  | 
590  | 0  | }  | 
591  |  |  | 
592  |  | /**  | 
593  |  |  * Return an enumeration over the IDs of all the regions that are immediate children of this region in the  | 
594  |  |  * region hierarchy. These returned regions could be either macro regions, territories, or a mixture of the two,  | 
595  |  |  * depending on the containment data as defined in CLDR.  This API may return NULL if this region doesn't have  | 
596  |  |  * any sub-regions. For example, calling this method with region "150" (Europe) returns an enumeration containing  | 
597  |  |  * the various sub regions of Europe - "039" (Southern Europe) - "151" (Eastern Europe) - "154" (Northern Europe)  | 
598  |  |  * and "155" (Western Europe).  | 
599  |  |  */  | 
600  |  | StringEnumeration*  | 
601  | 0  | Region::getContainedRegions(UErrorCode &status) const { | 
602  | 0  |     umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); // returns immediately if U_FAILURE(status)  | 
603  | 0  |     if (U_FAILURE(status)) { | 
604  | 0  |         return NULL;  | 
605  | 0  |     }  | 
606  | 0  |     return new RegionNameEnumeration(containedRegions,status);  | 
607  | 0  | }  | 
608  |  |  | 
609  |  | /**  | 
610  |  |  * Returns an enumeration over the IDs of all the regions that are children of this region anywhere in the region  | 
611  |  |  * hierarchy and match the given type.  This API may return an empty enumeration if this region doesn't have any  | 
612  |  |  * sub-regions that match the given type. For example, calling this method with region "150" (Europe) and type  | 
613  |  |  * "URGN_TERRITORY" returns a set containing all the territories in Europe ( "FR" (France) - "IT" (Italy) - "DE" (Germany) etc. )  | 
614  |  |  */  | 
615  |  | StringEnumeration*  | 
616  | 0  | Region::getContainedRegions( URegionType type, UErrorCode &status ) const { | 
617  | 0  |     umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); // returns immediately if U_FAILURE(status)  | 
618  | 0  |     if (U_FAILURE(status)) { | 
619  | 0  |         return NULL;  | 
620  | 0  |     }  | 
621  |  |  | 
622  | 0  |     UVector *result = new UVector(NULL, uhash_compareChars, status);  | 
623  |  | 
  | 
624  | 0  |     StringEnumeration *cr = getContainedRegions(status);  | 
625  |  | 
  | 
626  | 0  |     for ( int32_t i = 0 ; i < cr->count(status) ; i++ ) { | 
627  | 0  |         const char *regionId = cr->next(NULL,status);  | 
628  | 0  |         const Region *r = Region::getInstance(regionId,status);  | 
629  | 0  |         if ( r->getType() == type) { | 
630  | 0  |             result->addElementX((void *)&r->idStr,status);  | 
631  | 0  |         } else { | 
632  | 0  |             StringEnumeration *children = r->getContainedRegions(type, status);  | 
633  | 0  |             for ( int32_t j = 0 ; j < children->count(status) ; j++ ) { | 
634  | 0  |                 const char *id2 = children->next(NULL,status);  | 
635  | 0  |                 const Region *r2 = Region::getInstance(id2,status);  | 
636  | 0  |                 result->addElementX((void *)&r2->idStr,status);  | 
637  | 0  |             }  | 
638  | 0  |             delete children;  | 
639  | 0  |         }  | 
640  | 0  |     }  | 
641  | 0  |     delete cr;  | 
642  | 0  |     StringEnumeration* resultEnumeration = new RegionNameEnumeration(result,status);  | 
643  | 0  |     delete result;  | 
644  | 0  |     return resultEnumeration;  | 
645  | 0  | }  | 
646  |  |  | 
647  |  | /**  | 
648  |  |  * Returns true if this region contains the supplied other region anywhere in the region hierarchy.  | 
649  |  |  */  | 
650  |  | UBool  | 
651  | 0  | Region::contains(const Region &other) const { | 
652  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
653  | 0  |     umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status);  | 
654  |  | 
  | 
655  | 0  |     if (!containedRegions) { | 
656  | 0  |           return FALSE;  | 
657  | 0  |     }  | 
658  | 0  |     if (containedRegions->contains((void *)&other.idStr)) { | 
659  | 0  |         return TRUE;  | 
660  | 0  |     } else { | 
661  | 0  |         for ( int32_t i = 0 ; i < containedRegions->size() ; i++ ) { | 
662  | 0  |             UnicodeString *crStr = (UnicodeString *)containedRegions->elementAt(i);  | 
663  | 0  |             Region *cr = (Region *) uhash_get(regionIDMap,(void *)crStr);  | 
664  | 0  |             if ( cr && cr->contains(other) ) { | 
665  | 0  |                 return TRUE;  | 
666  | 0  |             }  | 
667  | 0  |         }  | 
668  | 0  |     }  | 
669  |  |  | 
670  | 0  |     return FALSE;  | 
671  | 0  | }  | 
672  |  |  | 
673  |  | /**  | 
674  |  |  * For deprecated regions, return an enumeration over the IDs of the regions that are the preferred replacement  | 
675  |  |  * regions for this region.  Returns NULL for a non-deprecated region.  For example, calling this method with region  | 
676  |  |  * "SU" (Soviet Union) would return a list of the regions containing "RU" (Russia), "AM" (Armenia), "AZ" (Azerbaijan), etc...  | 
677  |  |  */  | 
678  |  | StringEnumeration*  | 
679  | 0  | Region::getPreferredValues(UErrorCode &status) const { | 
680  | 0  |     umtx_initOnce(gRegionDataInitOnce, &loadRegionData, status); // returns immediately if U_FAILURE(status)  | 
681  | 0  |     if (U_FAILURE(status) || fType != URGN_DEPRECATED) { | 
682  | 0  |         return NULL;  | 
683  | 0  |     }  | 
684  | 0  |     return new RegionNameEnumeration(preferredValues,status);  | 
685  | 0  | }  | 
686  |  |  | 
687  |  |  | 
688  |  | /**  | 
689  |  |  * Return this region's canonical region code.  | 
690  |  |  */  | 
691  |  | const char*  | 
692  | 0  | Region::getRegionCode() const { | 
693  | 0  |     return id;  | 
694  | 0  | }  | 
695  |  |  | 
696  |  | int32_t  | 
697  | 0  | Region::getNumericCode() const { | 
698  | 0  |     return code;  | 
699  | 0  | }  | 
700  |  |  | 
701  |  | /**  | 
702  |  |  * Returns the region type of this region.  | 
703  |  |  */  | 
704  |  | URegionType  | 
705  | 0  | Region::getType() const { | 
706  | 0  |     return fType;  | 
707  | 0  | }  | 
708  |  |  | 
709  | 0  | RegionNameEnumeration::RegionNameEnumeration(UVector *fNameList, UErrorCode& status) { | 
710  | 0  |     pos=0;  | 
711  | 0  |     if (fNameList && U_SUCCESS(status)) { | 
712  | 0  |         fRegionNames = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, fNameList->size(),status);  | 
713  | 0  |         for ( int32_t i = 0 ; i < fNameList->size() ; i++ ) { | 
714  | 0  |             UnicodeString* this_region_name = (UnicodeString *)fNameList->elementAt(i);  | 
715  | 0  |             UnicodeString* new_region_name = new UnicodeString(*this_region_name);  | 
716  | 0  |             fRegionNames->addElementX((void *)new_region_name,status);  | 
717  | 0  |         }  | 
718  | 0  |     }  | 
719  | 0  |     else { | 
720  | 0  |         fRegionNames = NULL;  | 
721  | 0  |     }  | 
722  | 0  | }  | 
723  |  |  | 
724  |  | const UnicodeString*  | 
725  | 0  | RegionNameEnumeration::snext(UErrorCode& status) { | 
726  | 0  |   if (U_FAILURE(status) || (fRegionNames==NULL)) { | 
727  | 0  |     return NULL;  | 
728  | 0  |   }  | 
729  | 0  |   const UnicodeString* nextStr = (const UnicodeString *)fRegionNames->elementAt(pos);  | 
730  | 0  |   if (nextStr!=NULL) { | 
731  | 0  |     pos++;  | 
732  | 0  |   }  | 
733  | 0  |   return nextStr;  | 
734  | 0  | }  | 
735  |  |  | 
736  |  | void  | 
737  | 0  | RegionNameEnumeration::reset(UErrorCode& /*status*/) { | 
738  | 0  |     pos=0;  | 
739  | 0  | }  | 
740  |  |  | 
741  |  | int32_t  | 
742  | 0  | RegionNameEnumeration::count(UErrorCode& /*status*/) const { | 
743  | 0  |     return (fRegionNames==NULL) ? 0 : fRegionNames->size();  | 
744  | 0  | }  | 
745  |  |  | 
746  | 0  | RegionNameEnumeration::~RegionNameEnumeration() { | 
747  | 0  |     delete fRegionNames;  | 
748  | 0  | }  | 
749  |  |  | 
750  |  | U_NAMESPACE_END  | 
751  |  |  | 
752  |  | #endif /* #if !UCONFIG_NO_FORMATTING */  | 
753  |  |  | 
754  |  | //eof  |