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