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