intl/icu/source/i18n/region.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

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

mercurial