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