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