]> granicus.if.org Git - icu/commitdiff
ICU-8451 Initial port - Region APIs from Java to C++
authorJohn Emmons <emmo@us.ibm.com>
Mon, 14 Jan 2013 18:37:56 +0000 (18:37 +0000)
committerJohn Emmons <emmo@us.ibm.com>
Mon, 14 Jan 2013 18:37:56 +0000 (18:37 +0000)
X-SVN-Rev: 33048

.gitattributes
icu4c/source/i18n/i18n.vcxproj
icu4c/source/i18n/i18n.vcxproj.filters
icu4c/source/i18n/region.cpp [new file with mode: 0644]
icu4c/source/i18n/region_impl.h [new file with mode: 0644]
icu4c/source/i18n/unicode/region.h [new file with mode: 0644]
icu4c/source/test/intltest/intltest.vcxproj
icu4c/source/test/intltest/intltest.vcxproj.filters
icu4c/source/test/intltest/itformat.cpp
icu4c/source/test/intltest/regiontst.cpp [new file with mode: 0644]
icu4c/source/test/intltest/regiontst.h [new file with mode: 0644]

index 7bc4fd0ac8d3bc88c7982a37b06a86db37801185..4d92330b33eb112f539e5e53b779cd76ec20f7f4 100644 (file)
@@ -80,6 +80,9 @@ icu4c/source/extra/uconv/uconv.vcxproj -text
 icu4c/source/extra/uconv/uconv.vcxproj.filters -text
 icu4c/source/i18n/i18n.vcxproj -text
 icu4c/source/i18n/i18n.vcxproj.filters -text
+icu4c/source/i18n/region.cpp -text
+icu4c/source/i18n/region_impl.h -text
+icu4c/source/i18n/unicode/region.h -text
 icu4c/source/io/io.vcxproj -text
 icu4c/source/io/io.vcxproj.filters -text
 icu4c/source/layout/layout.vcxproj -text
@@ -141,6 +144,8 @@ icu4c/source/test/cintltst/cintltst.vcxproj -text
 icu4c/source/test/cintltst/cintltst.vcxproj.filters -text
 icu4c/source/test/intltest/intltest.vcxproj -text
 icu4c/source/test/intltest/intltest.vcxproj.filters -text
+icu4c/source/test/intltest/regiontst.cpp -text
+icu4c/source/test/intltest/regiontst.h -text
 icu4c/source/test/iotest/iotest.vcxproj -text
 icu4c/source/test/iotest/iotest.vcxproj.filters -text
 icu4c/source/test/letest/cletest.vcxproj -text
index bfde1503f4de805e2467038b921317150d45211b..b1f49722ac324fcecead2ce99b085d5deaa5b667 100644 (file)
     <ClCompile Include="bocsu.cpp" />\r
     <ClCompile Include="coleitr.cpp" />\r
     <ClCompile Include="coll.cpp" />\r
+    <ClCompile Include="region.cpp" />\r
     <ClCompile Include="search.cpp" />\r
     <ClCompile Include="sortkey.cpp" />\r
     <ClCompile Include="stsearch.cpp" />\r
     </CustomBuild>\r
     <ClInclude Include="dcfmtimp.h" />\r
     <ClInclude Include="numsys_impl.h" />\r
+    <ClInclude Include="region_impl.h" />\r
     <ClInclude Include="selfmtimpl.h" />\r
     <ClInclude Include="tzgnames.h" />\r
     <ClInclude Include="tznames_impl.h" />\r
       <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">copy "%(FullPath)" ..\..\include\unicode</Command>\r
       <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>\r
     </CustomBuild>\r
+    <ClInclude Include="unicode\region.h" />\r
     <ClInclude Include="usrchimp.h" />\r
     <ClInclude Include="astro.h" />\r
     <CustomBuild Include="unicode\basictz.h">\r
index 4a5f64eeddf3fba7af5cf4cad266bc4ea01ec560..080b9d49797b5e780e336f3b36269e0462f4f5a2 100644 (file)
       <Filter>formatting</Filter>\r
     </ClCompile>\r
     <ClCompile Include="bocsu.cpp" />\r
+    <ClCompile Include="region.cpp">\r
+      <Filter>formatting</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="bocsu.h">\r
     <ClInclude Include="selfmtimpl.h">\r
       <Filter>formatting</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="region_impl.h">\r
+      <Filter>formatting</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="unicode\region.h">\r
+      <Filter>formatting</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ResourceCompile Include="i18n.rc">\r
diff --git a/icu4c/source/i18n/region.cpp b/icu4c/source/i18n/region.cpp
new file mode 100644 (file)
index 0000000..9e6762d
--- /dev/null
@@ -0,0 +1,617 @@
+/*\r
+*******************************************************************************\r
+* Copyright (C) 2013, International Business Machines Corporation and\r
+* others. All Rights Reserved.\r
+*******************************************************************************\r
+*\r
+*\r
+* File REGION.CPP\r
+*\r
+* Modification History:*\r
+*   Date        Name        Description\r
+* 01/15/13      Emmons      Original Port from ICU4J\r
+********************************************************************************\r
+*/\r
+\r
+/**\r
+ * \file \r
+ * \brief C++ API: Region classes (territory containment)\r
+ */\r
+\r
+#include "unicode/region.h"\r
+#include "unicode/utypes.h"\r
+#include "unicode/uobject.h"\r
+#include "unicode/unistr.h"\r
+#include "unicode/ures.h"\r
+#include "unicode/decimfmt.h"\r
+#include "cstring.h"\r
+#include "uhash.h"\r
+#include "uresimp.h"\r
+#include "region_impl.h"\r
+\r
+#if !UCONFIG_NO_FORMATTING\r
+\r
+U_NAMESPACE_BEGIN\r
+\r
+static UBool regionDataIsLoaded = false;\r
+static UVector* regions = NULL;\r
+static UVector* availableRegions[URGN_LIMIT];\r
+\r
+static UHashtable *regionAliases;\r
+static UHashtable *regionIDMap;\r
+static UHashtable *numericCodeMap;\r
+\r
+static UnicodeString UNKNOWN_REGION_ID = UNICODE_STRING_SIMPLE("ZZ");\r
+static UnicodeString OUTLYING_OCEANIA_REGION_ID = UNICODE_STRING_SIMPLE("QO");\r
+static UnicodeString WORLD_ID = UNICODE_STRING_SIMPLE("001");\r
+\r
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Region)\r
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RegionNameEnumeration)\r
+\r
+void addRegion( Region *r ) {\r
+\r
+    UErrorCode status = U_ZERO_ERROR;\r
+    if ( regions == NULL ) {\r
+        regions = new UVector(NULL, NULL, status);\r
+    }\r
+    regions->addElement(r,status);\r
+}\r
+\r
+void addAvailableRegion( const Region *r , URegionType type) {\r
+\r
+    UErrorCode status = U_ZERO_ERROR;\r
+    if ( availableRegions[type] == NULL ) {\r
+        availableRegions[type] = new UVector(NULL, uhash_compareChars, status);\r
+    }\r
+\r
+    availableRegions[type]->addElement((void *)r->getRegionCode(),status);\r
+}\r
+/*\r
+ * Initializes the region data from the ICU resource bundles.  The region data\r
+ * contains the basic relationships such as which regions are known, what the numeric\r
+ * codes are, any known aliases, and the territory containment data.\r
+ * \r
+ * If the region data has already loaded, then this method simply returns without doing\r
+ * anything meaningful.\r
+ */\r
+void Region::loadRegionData() {\r
+\r
+    if (regionDataIsLoaded) {\r
+        return;\r
+    }\r
+    \r
+    UErrorCode status = U_ZERO_ERROR;\r
+\r
+    UResourceBundle* regionCodes = NULL;\r
+    UResourceBundle* territoryAlias = NULL;\r
+    UResourceBundle* codeMappings = NULL;\r
+    UResourceBundle* worldContainment = NULL;\r
+    UResourceBundle* territoryContainment = NULL;\r
+    UResourceBundle* groupingContainment = NULL;\r
+\r
+    DecimalFormat *df = new DecimalFormat(status);\r
+    df->setParseIntegerOnly(TRUE);\r
+\r
+    regionAliases = uhash_open(uhash_hashUnicodeString,uhash_compareUnicodeString,NULL,&status);\r
+    regionIDMap = uhash_open(uhash_hashUnicodeString,uhash_compareUnicodeString,NULL,&status);\r
+    numericCodeMap = uhash_open(uhash_hashLong,uhash_compareLong,NULL,&status);\r
+\r
+    UResourceBundle *rb = ures_openDirect(NULL,"metadata",&status);\r
+    regionCodes = ures_getByKey(rb,"regionCodes",NULL,&status);\r
+    territoryAlias = ures_getByKey(rb,"territoryAlias",NULL,&status);\r
+    \r
+    UResourceBundle *rb2 = ures_openDirect(NULL,"supplementalData",&status);\r
+    codeMappings = ures_getByKey(rb2,"codeMappings",NULL,&status);\r
+\r
+    territoryContainment = ures_getByKey(rb2,"territoryContainment",NULL,&status);\r
+    worldContainment = ures_getByKey(territoryContainment,"001",NULL,&status);\r
+    groupingContainment = ures_getByKey(territoryContainment,"grouping",NULL,&status);\r
+\r
+    UVector *continents = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status);\r
+\r
+    while ( ures_hasNext(worldContainment) ) {\r
+        UnicodeString *continentName = new UnicodeString(ures_getNextUnicodeString(worldContainment,NULL,&status));\r
+        continents->addElement(continentName,status);\r
+    }\r
+\r
+    UVector *groupings = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status);\r
+    while ( ures_hasNext(groupingContainment) ) {\r
+        UnicodeString *groupingName = new UnicodeString(ures_getNextUnicodeString(groupingContainment,NULL,&status));\r
+        groupings->addElement(groupingName,status);\r
+    }\r
+\r
+    while ( ures_hasNext(regionCodes) ) {\r
+        UnicodeString regionID = ures_getNextUnicodeString(regionCodes,NULL,&status);\r
+        Region *r = new Region();\r
+        r->idStr = regionID;\r
+        r->idStr.extract(0,r->idStr.length(),r->id,sizeof(r->id),US_INV);\r
+        r->type = URGN_TERRITORY; // Only temporary - figure out the real type later once the aliases are known.\r
+\r
+        uhash_put(regionIDMap,(void *)&(r->idStr),(void *)r,&status);\r
+        Formattable result;\r
+        UErrorCode ps = U_ZERO_ERROR;\r
+        df->parse(r->idStr,result,ps);\r
+        if ( U_SUCCESS(ps) ) {\r
+            r->code = result.getLong(); // Convert string to number\r
+            uhash_iput(numericCodeMap,r->code,(void *)r,&status);\r
+            r->type = URGN_SUBCONTINENT;\r
+        } else {\r
+            r->code = Region::UNDEFINED_NUMERIC_CODE;\r
+        }\r
+        addRegion(r);\r
+    }\r
+\r
+\r
+    // Process the territory aliases\r
+    int32_t territoryAliasSize = ures_getSize(territoryAlias);\r
+    while ( ures_hasNext(territoryAlias) ) {\r
+        UResourceBundle *res = ures_getNextResource(territoryAlias,NULL,&status);\r
+        const char *aliasFrom = ures_getKey(res);\r
+        UnicodeString* aliasFromStr = new UnicodeString(aliasFrom);\r
+        UnicodeString aliasTo = ures_getUnicodeString(res,&status);\r
+\r
+        Region *aliasToRegion = (Region *) uhash_get(regionIDMap,&aliasTo);\r
+        Region *aliasFromRegion = (Region *)uhash_get(regionIDMap,aliasFromStr);\r
+\r
+        if ( aliasToRegion != NULL && aliasFromRegion == NULL ) { // This is just an alias from some string to a region\r
+            uhash_put(regionAliases,(void *)aliasFromStr, (void *)aliasToRegion,&status);\r
+        } else {\r
+           if ( aliasFromRegion == NULL ) { // Deprecated region code not in the master codes list - so need to create a deprecated region for it.\r
+                aliasFromRegion = new Region();\r
+                aliasFromRegion->idStr.setTo(*aliasFromStr);\r
+                aliasFromRegion->idStr.extract(0,aliasFromRegion->idStr.length(),aliasFromRegion->id,sizeof(aliasFromRegion->id),US_INV);\r
+                uhash_put(regionIDMap,(void *)&(aliasFromRegion->idStr),(void *)aliasFromRegion,&status);\r
+                Formattable result;\r
+                UErrorCode ps = U_ZERO_ERROR;\r
+                df->parse(aliasFromRegion->idStr,result,ps);\r
+                if ( U_SUCCESS(ps) ) {\r
+                    aliasFromRegion->code = result.getLong(); // Convert string to number\r
+                    uhash_iput(numericCodeMap,aliasFromRegion->code,(void *)aliasFromRegion,&status);\r
+                } else {\r
+                    aliasFromRegion->code = Region::UNDEFINED_NUMERIC_CODE;\r
+                }\r
+                aliasFromRegion->type = URGN_DEPRECATED;\r
+                addRegion(aliasFromRegion);\r
+            } else {\r
+                aliasFromRegion->type = URGN_DEPRECATED;\r
+            }\r
+            delete aliasFromStr;\r
+\r
+            aliasFromRegion->preferredValues = new UVector(NULL, uhash_compareChars, status);\r
+            UnicodeString currentRegion;\r
+            currentRegion.remove();\r
+            for (int32_t i = 0 ; i < aliasTo.length() ; i++ ) {\r
+                if ( aliasTo.charAt(i) != 0x0020 ) {\r
+                    currentRegion.append(aliasTo.charAt(i));\r
+                }\r
+                if ( aliasTo.charAt(i) == 0x0020 || i+1 == aliasTo.length() ) {\r
+                    Region *target = (Region *)uhash_get(regionIDMap,(void *)&currentRegion);\r
+                    if (target) {\r
+                        aliasFromRegion->preferredValues->addElement((void *)target->id,status);\r
+                    }\r
+                    currentRegion.remove();\r
+                }\r
+            }\r
+        }\r
+    }\r
+\r
+    // Process the code mappings - This will allow us to assign numeric codes to most of the territories.\r
+    while ( ures_hasNext(codeMappings) ) {\r
+        UResourceBundle *mapping = ures_getNextResource(codeMappings,NULL,&status);\r
+        if ( ures_getType(mapping) == URES_ARRAY && ures_getSize(mapping) == 3) {\r
+            UnicodeString codeMappingID = ures_getUnicodeStringByIndex(mapping,0,&status);\r
+            UnicodeString codeMappingNumber = ures_getUnicodeStringByIndex(mapping,1,&status);\r
+            UnicodeString codeMapping3Letter = ures_getUnicodeStringByIndex(mapping,2,&status);\r
+\r
+            Region *r = (Region *)uhash_get(regionIDMap,(void *)&codeMappingID);\r
+            if ( r ) {\r
+                Formattable result;\r
+                UErrorCode ps = U_ZERO_ERROR;\r
+                df->parse(codeMappingNumber,result,ps);\r
+                if ( U_SUCCESS(ps) ) {\r
+                    r->code = result.getLong(); // Convert string to number\r
+                    uhash_iput(numericCodeMap,r->code,(void *)r,&status);\r
+                }\r
+                UnicodeString *code3 = new UnicodeString(codeMapping3Letter);\r
+                uhash_put(regionAliases,(void *)code3, (void *)r,&status);\r
+            }                    \r
+        }\r
+    }\r
+\r
+    // Now fill in the special cases for WORLD, UNKNOWN, CONTINENTS, and GROUPINGS\r
+    Region *r;\r
+    r = (Region *) uhash_get(regionIDMap,(void *)&WORLD_ID);\r
+    if ( r ) {\r
+        r->type = URGN_WORLD;\r
+    }\r
+\r
+    r = (Region *) uhash_get(regionIDMap,(void *)&UNKNOWN_REGION_ID);\r
+    if ( r ) {\r
+        r->type = URGN_UNKNOWN;\r
+    }\r
+\r
+    for ( int32_t i = 0 ; i < continents->size() ; i++ ) {\r
+        r = (Region *) uhash_get(regionIDMap,(void *)continents->elementAt(i));\r
+        if ( r ) {\r
+            r->type = URGN_CONTINENT;\r
+        }\r
+    }\r
+    delete continents;\r
+\r
+    for ( int32_t i = 0 ; i < groupings->size() ; i++ ) {\r
+        r = (Region *) uhash_get(regionIDMap,(void *)groupings->elementAt(i));\r
+        if ( r ) {\r
+            r->type = URGN_GROUPING;\r
+        }\r
+    }\r
+    delete groupings;\r
+\r
+    // Special case: The region code "QO" (Outlying Oceania) is a subcontinent code added by CLDR\r
+    // even though it looks like a territory code.  Need to handle it here.\r
+\r
+    r = (Region *) uhash_get(regionIDMap,(void *)&OUTLYING_OCEANIA_REGION_ID);\r
+    if ( r ) {\r
+        r->type = URGN_SUBCONTINENT;\r
+    }\r
+\r
+    // Load territory containment info from the supplemental data.\r
+    while ( ures_hasNext(territoryContainment) ) {\r
+        UResourceBundle *mapping = ures_getNextResource(territoryContainment,NULL,&status);\r
+        const char *parent = ures_getKey(mapping);\r
+        UnicodeString parentStr = UnicodeString(parent);\r
+        Region *parentRegion = (Region *) uhash_get(regionIDMap,(void *)&parentStr);\r
+\r
+        for ( int j = 0 ; j < ures_getSize(mapping); j++ ) {\r
+            UnicodeString child = ures_getUnicodeStringByIndex(mapping,j,&status);\r
+            Region *childRegion = (Region *) uhash_get(regionIDMap,(void *)&child);\r
+            if ( parentRegion != NULL && childRegion != NULL ) {                    \r
+\r
+                // Add the child region to the set of regions contained by the parent\r
+                if (parentRegion->containedRegions == NULL) {\r
+                    parentRegion->containedRegions = new UVector(NULL, uhash_compareChars, status);\r
+                }\r
+                parentRegion->containedRegions->addElement((void *)childRegion->id,status);\r
+\r
+                // Set the parent region to be the containing region of the child.\r
+                // Regions of type GROUPING can't be set as the parent, since another region\r
+                // such as a SUBCONTINENT, CONTINENT, or WORLD must always be the parent.\r
+                if ( parentRegion->type != URGN_GROUPING) {\r
+                    childRegion->containingRegion = parentRegion;\r
+                }\r
+            }\r
+        }\r
+    }     \r
+\r
+    // Create the availableRegions lists\r
+\r
+    for ( int32_t i = 0 ; i < regions->size() ; i++ ) {\r
+        Region *ar = (Region *)regions->elementAt(i);\r
+        addAvailableRegion(ar,ar->type);\r
+    }\r
+\r
+    regionDataIsLoaded = true;\r
+\r
+    ures_close(territoryContainment);\r
+    ures_close(worldContainment);\r
+    ures_close(groupingContainment);\r
+\r
+    ures_close(codeMappings);\r
+    ures_close(rb2);\r
+    ures_close(territoryAlias);\r
+    ures_close(regionCodes);\r
+    ures_close(rb);\r
+\r
+    delete df;\r
+}\r
+\r
+\r
+\r
+/*\r
+ * Default constructor.  Use factory methods only.\r
+ * @internal\r
+ */\r
+\r
+Region::Region () {\r
+        idStr.remove();\r
+        code = UNDEFINED_NUMERIC_CODE;\r
+        type = URGN_UNKNOWN;\r
+        containingRegion = NULL;\r
+        containedRegions = NULL;\r
+        preferredValues = NULL;\r
+}\r
+\r
+\r
+/**\r
+ * Returns true if the two regions are equal.\r
+ */\r
+UBool U_EXPORT2\r
+Region::operator==(const Region &that) const {\r
+    return (idStr == that.idStr);\r
+}\r
+\r
+/**\r
+ * Returns true if the two regions are NOT equal; that is, if operator ==() returns false.\r
+ */\r
+UBool U_EXPORT2\r
+Region::operator!=(const Region &that) const {\r
+        return (idStr != that.idStr);\r
+}\r
\r
+/**\r
+ * Returns a pointer to a Region using the given region code.  The region code can be either 2-letter ISO code,\r
+ * 3-letter ISO code,  UNM.49 numeric code, or other valid Unicode Region Code as defined by the LDML specification.\r
+ * The identifier will be canonicalized internally using the supplemental metadata as defined in the CLDR.\r
+ * If the region code is NULL or not recognized, the appropriate error code will be set ( U_ILLEGAL_ARGUMENT_ERROR )\r
+ */\r
+const Region* U_EXPORT2\r
+Region::getInstance(const char *region_code, UErrorCode &status) {\r
+\r
+    if ( !region_code ) {\r
+        status = U_ILLEGAL_ARGUMENT_ERROR;\r
+        return NULL;\r
+    }\r
+\r
+    loadRegionData();\r
+\r
+    UnicodeString regionCodeString = UnicodeString(region_code);\r
+    Region *r = (Region *)uhash_get(regionIDMap,(void *)&regionCodeString);\r
+\r
+    if ( !r ) {\r
+        r = (Region *)uhash_get(regionAliases,(void *)&regionCodeString);\r
+    }\r
+\r
+    if ( !r ) { // Unknown region code\r
+        status = U_ILLEGAL_ARGUMENT_ERROR;\r
+        return NULL;\r
+    }\r
+\r
+    if ( r->type == URGN_DEPRECATED && r->preferredValues->size() == 1) {\r
+        StringEnumeration *pv = r->getPreferredValues();\r
+        pv->reset(status);\r
+        const UnicodeString *ustr = pv->snext(status);\r
+        r = (Region *)uhash_get(regionIDMap,(void *)ustr);\r
+    }\r
+\r
+    return r;\r
+\r
+}\r
+\r
+/**\r
+ * Returns a pointer to a Region using the given numeric region code. If the numeric region code is not recognized,\r
+ * the appropriate error code will be set ( U_ILLEGAL_ARGUMENT_ERROR ).\r
+ */\r
+const Region * U_EXPORT2\r
+Region::getInstance (int32_t code, UErrorCode &status) {\r
+\r
+    loadRegionData();\r
+\r
+    Region *r = (Region *)uhash_iget(numericCodeMap,code);\r
+\r
+    if ( !r ) { // Just in case there's an alias that's numeric, try to find it.\r
+        UErrorCode fs = U_ZERO_ERROR;\r
+        UnicodeString pat = UNICODE_STRING_SIMPLE("00#");\r
+        DecimalFormat *df = new DecimalFormat(pat,fs);\r
+        \r
+        UnicodeString id;\r
+        id.remove();\r
+        df->format(code,id);\r
+        delete df;\r
+        r = (Region *)uhash_get(regionAliases,&id);\r
+    }\r
+\r
+    if ( !r ) {\r
+        status = U_ILLEGAL_ARGUMENT_ERROR;\r
+        return NULL;\r
+    }\r
+\r
+    if ( r->type == URGN_DEPRECATED && r->preferredValues->size() == 1) {\r
+        StringEnumeration *pv = r->getPreferredValues();\r
+        pv->reset(status);\r
+        const UnicodeString *ustr = pv->snext(status);\r
+        r = (Region *)uhash_get(regionIDMap,(void *)ustr);\r
+    }\r
+\r
+    return r;\r
+}\r
+\r
+\r
+/**\r
+ * Returns an enumeration over the IDs of all known regions that match the given type.\r
+ */\r
+StringEnumeration * U_EXPORT2\r
+Region::getAvailable(URegionType type) {\r
+    \r
+    loadRegionData();\r
+    UErrorCode status = U_ZERO_ERROR;\r
+    return new RegionNameEnumeration(availableRegions[type],status);\r
+\r
+    return NULL; \r
+}\r
+   \r
+/**\r
+ * Returns a pointer to the region that contains this region.  Returns NULL if this region is code "001" (World)\r
+ * or "ZZ" (Unknown region). For example, calling this method with region "IT" (Italy) returns the\r
+ * region "039" (Southern Europe).\r
+ */\r
+const Region * U_EXPORT2\r
+Region::getContainingRegion() const {\r
+    loadRegionData();\r
+    return containingRegion;\r
+}\r
+\r
+/**\r
+ * Return a pointer to the region that geographically contains this region and matches the given type,\r
+ * moving multiple steps up the containment chain if necessary.  Returns NULL if no containing region can be found\r
+ * that matches the given type. Note: The URegionTypes = "URGN_GROUPING", "URGN_DEPRECATED", or "URGN_UNKNOWN"\r
+ * are not appropriate for use in this API. NULL will be returned in this case. For example, calling this method\r
+ * with region "IT" (Italy) for type "URGN_CONTINENT" returns the region "150" ( Europe ).\r
+ */\r
+const Region* U_EXPORT2\r
+Region::getContainingRegion(URegionType type) const {\r
+    loadRegionData();\r
+    if ( containingRegion == NULL ) {\r
+        return NULL;\r
+    }\r
+\r
+    if ( containingRegion->type == type ) {\r
+        return containingRegion;\r
+    } else {\r
+        return containingRegion->getContainingRegion(type);\r
+    }\r
+}\r
+\r
+/**\r
+ * Return an enumeration over the IDs of all the regions that are immediate children of this region in the\r
+ * region hierarchy. These returned regions could be either macro regions, territories, or a mixture of the two,\r
+ * depending on the containment data as defined in CLDR.  This API may return NULL if this region doesn't have\r
+ * any sub-regions. For example, calling this method with region "150" (Europe) returns an enumeration containing\r
+ * the various sub regions of Europe - "039" (Southern Europe) - "151" (Eastern Europe) - "154" (Northern Europe)\r
+ * and "155" (Western Europe).\r
+ */\r
+StringEnumeration * U_EXPORT2\r
+Region::getContainedRegions() const {\r
+    loadRegionData();\r
+    UErrorCode status = U_ZERO_ERROR;\r
+    return new RegionNameEnumeration(containedRegions,status);\r
+}\r
+\r
+/**\r
+ * Returns an enumeration over the IDs of all the regions that are children of this region anywhere in the region\r
+ * hierarchy and match the given type.  This API may return an empty enumeration if this region doesn't have any\r
+ * sub-regions that match the given type. For example, calling this method with region "150" (Europe) and type\r
+ * "URGN_TERRITORY" returns a set containing all the territories in Europe ( "FR" (France) - "IT" (Italy) - "DE" (Germany) etc. )\r
+ */\r
+StringEnumeration * U_EXPORT2\r
+Region::getContainedRegions( URegionType type ) const {\r
+    loadRegionData();\r
+\r
+    UErrorCode status = U_ZERO_ERROR;\r
+    UVector *result = new UVector(uprv_deleteUObject, uhash_compareChars, status);\r
\r
+    StringEnumeration *cr = getContainedRegions();\r
+\r
+    for ( int32_t i = 0 ; i < cr->count(status) ; i++ ) {\r
+        const char *id = cr->next(NULL,status);\r
+        const Region *r = Region::getInstance(id,status);\r
+        if ( r->getType() == type ) {\r
+            result->addElement((void *)r->id,status);\r
+        } else {\r
+            StringEnumeration *children = r->getContainedRegions(type);\r
+            for ( int32_t j = 0 ; j < children->count(status) ; j++ ) {\r
+                const char *id2 = children->next(NULL,status);\r
+                const Region *r2 = Region::getInstance(id2,status);\r
+                result->addElement((void *)r2->id,status);\r
+            }\r
+        }\r
+    }\r
+    return new RegionNameEnumeration(result,status);\r
+}\r
\r
+/**\r
+ * Returns true if this region contains the supplied other region anywhere in the region hierarchy.\r
+ */\r
+UBool U_EXPORT2\r
+Region::contains(const Region &other) const {\r
+    loadRegionData();\r
+\r
+    if (!containedRegions) {\r
+          return FALSE;\r
+    }\r
+    if (containedRegions->contains((void *)other.id)) {\r
+        return TRUE;\r
+    } else {\r
+        for ( int32_t i = 0 ; i < containedRegions->size() ; i++ ) {\r
+            UErrorCode status = U_ZERO_ERROR;\r
+            const Region *cr = Region::getInstance((const char *)containedRegions->elementAt(i),status);\r
+            if ( cr && cr->contains(other) ) {\r
+                return TRUE;\r
+            }\r
+        }\r
+    }\r
+\r
+    return FALSE;\r
+}\r
+\r
+/**\r
+ * For deprecated regions, return an enumeration over the IDs of the regions that are the preferred replacement\r
+ * regions for this region.  Returns NULL for a non-deprecated region.  For example, calling this method with region\r
+ * "SU" (Soviet Union) would return a list of the regions containing "RU" (Russia), "AM" (Armenia), "AZ" (Azerbaijan), etc...\r
+ */\r
+StringEnumeration * U_EXPORT2\r
+Region::getPreferredValues() const {\r
+    loadRegionData();\r
+    UErrorCode status = U_ZERO_ERROR;\r
+    if ( type == URGN_DEPRECATED ) {\r
+        return new RegionNameEnumeration(preferredValues,status);\r
+    } else {\r
+        return NULL;\r
+    }\r
+}\r
\r
+\r
+/**\r
+ * Return this region's canonical region code.\r
+ */\r
+const char * U_EXPORT2\r
+Region::getRegionCode() const {\r
+    return id;\r
+}\r
+\r
+/**\r
+ * Return this region's numeric code. Returns UNDEFINED_NUMERIC_CODE (-1) if the given region does not have a numeric code assigned to it.\r
+ */\r
+int32_t U_EXPORT2\r
+Region::getNumericCode() const {\r
+    return code;\r
+}\r
+\r
+/**\r
+ * Returns the region type of this region.\r
+ */\r
+URegionType U_EXPORT2\r
+Region::getType() const {\r
+    return type;\r
+}\r
+\r
+RegionNameEnumeration::RegionNameEnumeration(UVector *fNameList, UErrorCode& /*status*/) {\r
+    pos=0;\r
+    fRegionNames = fNameList;\r
+}\r
+\r
+const char *\r
+RegionNameEnumeration::next(int32_t *resultLength, UErrorCode& status) {\r
+    if (U_SUCCESS(status) && pos < fRegionNames->size()) {\r
+        if (resultLength != NULL) {\r
+            *resultLength = uprv_strlen((const char *)fRegionNames->elementAt(pos));\r
+        }\r
+        return (const char *)fRegionNames->elementAt(pos++);\r
+    }\r
+    return NULL;\r
+}\r
+\r
+const UnicodeString*\r
+RegionNameEnumeration::snext(UErrorCode& status) { \r
+    int32_t resultLength=0;\r
+    const char *s=next(&resultLength, status);\r
+    return setChars(s, resultLength, status);\r
+}\r
+\r
+void\r
+RegionNameEnumeration::reset(UErrorCode& /*status*/) {\r
+    pos=0;\r
+}\r
+\r
+int32_t\r
+RegionNameEnumeration::count(UErrorCode& /*status*/) const {\r
+    return (fRegionNames==NULL) ? 0 : fRegionNames->size();\r
+}\r
+\r
+RegionNameEnumeration::~RegionNameEnumeration() {\r
+    delete fRegionNames;\r
+}\r
+\r
+U_NAMESPACE_END\r
+\r
+#endif /* #if !UCONFIG_NO_FORMATTING */\r
+\r
+//eof\r
diff --git a/icu4c/source/i18n/region_impl.h b/icu4c/source/i18n/region_impl.h
new file mode 100644 (file)
index 0000000..a7e5c46
--- /dev/null
@@ -0,0 +1,44 @@
+/*\r
+*******************************************************************************\r
+* Copyright (C) 2013, International Business Machines Corporation and         *\r
+* others. All Rights Reserved.                                                *\r
+*******************************************************************************\r
+*\r
+* File REGION_IMPL.H\r
+*\r
+*******************************************************************************\r
+*/\r
+\r
+#ifndef __REGION_IMPL_H__\r
+#define __REGION_IMPL_H__\r
+\r
+#include "unicode/utypes.h"\r
+\r
+#if !UCONFIG_NO_FORMATTING\r
+    \r
+#include "uvector.h"\r
+#include "unicode/strenum.h"\r
+\r
+U_NAMESPACE_BEGIN\r
+\r
+\r
+class RegionNameEnumeration : public StringEnumeration {\r
+public:\r
+    RegionNameEnumeration(UVector *fNameList, UErrorCode& status);\r
+    virtual ~RegionNameEnumeration();\r
+    static UClassID U_EXPORT2 getStaticClassID(void);\r
+    virtual UClassID getDynamicClassID(void) const;\r
+    virtual const char *next(int32_t *resultLength, UErrorCode& status);\r
+    virtual const UnicodeString* snext(UErrorCode& status);\r
+    virtual void reset(UErrorCode& status);\r
+    virtual int32_t count(UErrorCode& status) const;\r
+private:\r
+    int32_t pos;\r
+    UVector *fRegionNames;\r
+};\r
+\r
+U_NAMESPACE_END\r
+\r
+#endif\r
+\r
+#endif\r
diff --git a/icu4c/source/i18n/unicode/region.h b/icu4c/source/i18n/unicode/region.h
new file mode 100644 (file)
index 0000000..9b89c76
--- /dev/null
@@ -0,0 +1,290 @@
+/*\r
+ *******************************************************************************\r
+ * Copyright (C) 2013, International Business Machines Corporation             *\r
+ * All Rights Reserved.                                                        *\r
+ *******************************************************************************\r
+ */\r
+\r
+#ifndef REGION_H\r
+#define REGION_H\r
+\r
+/**\r
+ * \file \r
+ * \brief C++ API: Region classes (territory containment)\r
+ */\r
+\r
+#include "unicode/utypes.h"\r
+\r
+typedef enum URegionType {\r
+    /**\r
+     * Type representing the unknown region.\r
+     * @draft ICU 51 \r
+     */\r
+    URGN_UNKNOWN,\r
+\r
+    /**\r
+     * Type representing a territory.\r
+     * @draft ICU 51 \r
+     */\r
+    URGN_TERRITORY,\r
+\r
+    /**\r
+     * Type representing the whole world.\r
+     * @draft ICU 51 \r
+     */\r
+    URGN_WORLD,\r
+\r
+    /**\r
+     * Type representing a continent.\r
+     * @draft ICU 51 \r
+     */\r
+    URGN_CONTINENT,\r
+\r
+    /**\r
+     * Type representing a sub-continent.\r
+     * @draft ICU 51 \r
+     */\r
+    URGN_SUBCONTINENT,\r
+\r
+    /**\r
+     * Type representing a grouping of territories that is not to be used in\r
+     * the normal WORLD/CONTINENT/SUBCONTINENT/TERRITORY containment tree.\r
+     * @draft ICU 51 \r
+     */\r
+    URGN_GROUPING,\r
+\r
+    /**\r
+     * Type representing a region whose code has been deprecated, usually\r
+     * due to a country splitting into multiple territories or changing its name.\r
+     * @draft ICU 51 \r
+     */\r
+    URGN_DEPRECATED,\r
+\r
+    /**\r
+     * Maximum value for this unumeration.\r
+     * @draft ICU 51 \r
+     */\r
+    URGN_LIMIT\r
+} URegionType;\r
+\r
+\r
+#if !UCONFIG_NO_FORMATTING\r
+\r
+#include "unicode/uobject.h"\r
+#include "unicode/uniset.h"\r
+#include "unicode/unistr.h"\r
+#include "unicode/strenum.h"\r
+\r
+U_NAMESPACE_BEGIN\r
+\r
+/**\r
+ * <code>Region</code> is the class representing a Unicode Region Code, also known as a \r
+ * Unicode Region Subtag, which is defined based upon the BCP 47 standard. We often think of\r
+ * "regions" as "countries" when defining the characteristics of a locale.  Region codes There are different\r
+ * types of region codes that are important to distinguish.\r
+ * <p>\r
+ *  Macroregion - A code for a "macro geographical (continental) region, geographical sub-region, or \r
+ *  selected economic and other grouping" as defined in \r
+ *  UN M.49 (http://unstats.un.org/unsd/methods/m49/m49regin.htm). \r
+ *  These are typically 3-digit codes, but contain some 2-letter codes, such as the LDML code QO \r
+ *  added for Outlying Oceania.  Not all UNM.49 codes are defined in LDML, but most of them are.\r
+ *  Macroregions are represented in ICU by one of three region types: WORLD ( region code 001 ),\r
+ *  CONTINENTS ( regions contained directly by WORLD ), and SUBCONTINENTS ( things contained directly\r
+ *  by a continent ).\r
+ *  <p>\r
+ *  TERRITORY - A Region that is not a Macroregion. These are typically codes for countries, but also\r
+ *  include areas that are not separate countries, such as the code "AQ" for Antarctica or the code \r
+ *  "HK" for Hong Kong (SAR China). Overseas dependencies of countries may or may not have separate \r
+ *  codes. The codes are typically 2-letter codes aligned with the ISO 3166 standard, but BCP47 allows\r
+ *  for the use of 3-digit codes in the future.\r
+ *  <p>\r
+ *  UNKNOWN - The code ZZ is defined by Unicode LDML for use to indicate that the Region is unknown,\r
+ *  or that the value supplied as a region was invalid.\r
+ *  <p>\r
+ *  DEPRECATED - Region codes that have been defined in the past but are no longer in modern usage,\r
+ *  usually due to a country splitting into multiple territories or changing its name.\r
+ *  <p>\r
+ *  GROUPING - A widely understood grouping of territories that has a well defined membership such\r
+ *  that a region code has been assigned for it.  Some of these are UNM.49 codes that do't fall into \r
+ *  the world/continent/sub-continent hierarchy, while others are just well known groupings that have\r
+ *  their own region code. Region "EU" (European Union) is one such region code that is a grouping.\r
+ *  Groupings will never be returned by the getContainingRegion() API, since a different type of region\r
+ *  ( WORLD, CONTINENT, or SUBCONTINENT ) will always be the containing region instead.\r
+ *  \r
+ * @author       John Emmons\r
+ * @draft ICU 51\r
+ */\r
+\r
+class U_I18N_API Region : public UObject {\r
+\r
+public:\r
+\r
+    /**\r
+     * URegionType is an enumeration defining the different types of regions.  Current possible\r
+     * values are URGN_WORLD, URGN_CONTINENT, URGN_SUBCONTINENT, URGN_TERRITORY, URGN_GROUPING,\r
+     * URGN_DEPRECATED, and URGN_UNKNOWN.\r
+     * \r
+     * @draft ICU 51 \r
+     */\r
+    static const int32_t UNDEFINED_NUMERIC_CODE = -1;\r
+\r
+    /**\r
+     * Default Constructor.\r
+     *\r
+     * @draft ICU 51\r
+     */\r
+    Region();\r
+\r
+\r
+    /**\r
+     * Returns true if the two regions are equal.\r
+     * @draft ICU 51\r
+     */\r
+    UBool operator==(const Region &that) const;\r
+\r
+    /**\r
+     * Returns true if the two regions are NOT equal; that is, if operator ==() returns false.\r
+     * @draft ICU 51\r
+     */\r
+    UBool operator!=(const Region &that) const;\r
+\r
+\r
\r
+    /**\r
+     * Returns a pointer to a Region using the given region code.  The region code can be either 2-letter ISO code,\r
+     * 3-letter ISO code,  UNM.49 numeric code, or other valid Unicode Region Code as defined by the LDML specification.\r
+     * The identifier will be canonicalized internally using the supplemental metadata as defined in the CLDR.\r
+     * If the region code is NULL or not recognized, the appropriate error code will be set ( U_ILLEGAL_ARGUMENT_ERROR )\r
+     * @draft ICU 51 \r
+     */\r
+    static const Region *getInstance(const char *region_code, UErrorCode &status);\r
+\r
+    /**\r
+     * Returns a pointer to a Region using the given numeric region code. If the numeric region code is not recognized,\r
+     * the appropriate error code will be set ( U_ILLEGAL_ARGUMENT_ERROR ).\r
+     * @draft ICU 51 \r
+     */\r
+    static const Region *getInstance (int32_t code, UErrorCode &status);\r
+\r
+    /**\r
+     * Returns an enumeration over the IDs of all known regions that match the given type.\r
+     * @draft ICU 51 \r
+     */\r
+    static StringEnumeration *getAvailable(URegionType type);\r
+   \r
+    /**\r
+     * Returns a pointer to the region that contains this region.  Returns NULL if this region is code "001" (World)\r
+     * or "ZZ" (Unknown region). For example, calling this method with region "IT" (Italy) returns the\r
+     * region "039" (Southern Europe).\r
+     * @draft ICU 51 \r
+     */\r
+    const Region *getContainingRegion() const;\r
+\r
+    /**\r
+     * Return a pointer to the region that geographically contains this region and matches the given type,\r
+     * moving multiple steps up the containment chain if necessary.  Returns NULL if no containing region can be found\r
+     * that matches the given type. Note: The URegionTypes = "URGN_GROUPING", "URGN_DEPRECATED", or "URGN_UNKNOWN"\r
+     * are not appropriate for use in this API. NULL will be returned in this case. For example, calling this method\r
+     * with region "IT" (Italy) for type "URGN_CONTINENT" returns the region "150" ( Europe ).\r
+     * @draft ICU 51 \r
+     */\r
+    const Region* getContainingRegion(URegionType type) const;\r
+\r
+    /**\r
+     * Return an enumeration over the IDs of all the regions that are immediate children of this region in the\r
+     * region hierarchy. These returned regions could be either macro regions, territories, or a mixture of the two,\r
+     * depending on the containment data as defined in CLDR.  This API may return NULL if this region doesn't have\r
+     * any sub-regions. For example, calling this method with region "150" (Europe) returns an enumeration containing\r
+     * the various sub regions of Europe - "039" (Southern Europe) - "151" (Eastern Europe) - "154" (Northern Europe)\r
+     * and "155" (Western Europe).\r
+     * @draft ICU 51 \r
+     */\r
+    StringEnumeration *getContainedRegions() const;\r
+\r
+    /**\r
+     * Returns an enumeration over the IDs of all the regions that are children of this region anywhere in the region\r
+     * hierarchy and match the given type.  This API may return an empty enumeration if this region doesn't have any\r
+     * sub-regions that match the given type. For example, calling this method with region "150" (Europe) and type\r
+     * "URGN_TERRITORY" returns a set containing all the territories in Europe ( "FR" (France) - "IT" (Italy) - "DE" (Germany) etc. )\r
+     * @draft ICU 51 \r
+     */\r
+    StringEnumeration *getContainedRegions( URegionType type ) const;\r
\r
+    /**\r
+     * Returns true if this region contains the supplied other region anywhere in the region hierarchy.\r
+     * @draft ICU 51 \r
+     */\r
+    UBool contains(const Region &other) const;\r
+\r
+    /**\r
+     * For deprecated regions, return an enumeration over the IDs of the regions that are the preferred replacement\r
+     * regions for this region.  Returns null for a non-deprecated region.  For example, calling this method with region\r
+     * "SU" (Soviet Union) would return a list of the regions containing "RU" (Russia), "AM" (Armenia), "AZ" (Azerbaijan), etc...\r
+     * @draft ICU 51 \r
+     */\r
+    StringEnumeration *getPreferredValues() const;\r
\r
+\r
+    /**\r
+     * Return this region's canonical region code.\r
+     * @draft ICU 51 \r
+     */\r
+    const char *getRegionCode() const;\r
+\r
+    /**\r
+     * Return this region's numeric code. Returns UNDEFINED_NUMERIC_CODE (-1) if the given region does not have a numeric code assigned to it.\r
+     * @draft ICU 51 \r
+     */\r
+    int32_t getNumericCode() const;\r
+\r
+    /**\r
+     * Returns the region type of this region.\r
+     * @draft ICU 51 \r
+     */\r
+    URegionType getType() const;\r
\r
+    /**\r
+     * ICU "poor man's RTTI", returns a UClassID for this class.\r
+     *\r
+     * @stable ICU 4.2\r
+     *\r
+    */\r
+    static UClassID U_EXPORT2 getStaticClassID(void);\r
+\r
+    /**\r
+     * ICU "poor man's RTTI", returns a UClassID for the actual class.\r
+     *\r
+     * @stable ICU 4.2\r
+     */\r
+    virtual UClassID getDynamicClassID() const;\r
+\r
+private:\r
+    char id[4];\r
+    UnicodeString idStr;\r
+    int32_t code;\r
+    URegionType type;\r
+    Region *containingRegion;\r
+    UVector *containedRegions;\r
+    UVector *preferredValues;\r
+\r
+    /*\r
+     * Initializes the region data from the ICU resource bundles.  The region data\r
+     * contains the basic relationships such as which regions are known, what the numeric\r
+     * codes are, any known aliases, and the territory containment data.\r
+     * \r
+     * If the region data has already loaded, then this method simply returns without doing\r
+     * anything meaningful.\r
+     * @internal\r
+     */\r
+\r
+    static void loadRegionData();\r
+\r
+};\r
+\r
+U_NAMESPACE_END\r
+\r
+#endif /* #if !UCONFIG_NO_FORMATTING */\r
+\r
+#endif // REGION_H\r
+\r
+//eof\r
index 2820333e97b4e16f0eb7f409d9b14f345a9f2ef3..3bb847068053e253bb9016651821b57412f92c6d 100644 (file)
   <ItemGroup>\r
     <ClCompile Include="bytestrietest.cpp" />\r
     <ClCompile Include="colldata.cpp" />\r
+    <ClCompile Include="regiontst.cpp" />\r
     <ClCompile Include="ucharstrietest.cpp" />\r
     <ClCompile Include="itrbbi.cpp" />\r
     <ClCompile Include="rbbiapts.cpp" />\r
     <ClInclude Include="lcukocol.h" />\r
     <ClInclude Include="mnkytst.h" />\r
     <ClInclude Include="regcoll.h" />\r
+    <ClInclude Include="regiontst.h" />\r
     <ClInclude Include="srchtest.h" />\r
     <ClInclude Include="ssearch.h" />\r
     <ClInclude Include="svccoll.h" />\r
index fdd84ad0a0e4d58b851af350a9c4c9b2ca61c524..aa9c7e79b9f61ca68bcf0b2dd7f26a80c1279124 100644 (file)
     <ClCompile Include="colldata.cpp">\r
       <Filter>collation</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="regiontst.cpp">\r
+      <Filter>formatting</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="itrbbi.h">\r
     <ClInclude Include="colldata.h">\r
       <Filter>collation</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="regiontst.h">\r
+      <Filter>formatting</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
index d5c61107ec891eea845fe5b70ab7424bcbbbf636..aa919d34aea1564220b1d3ac9fb1eecd004935e1 100644 (file)
@@ -1,6 +1,6 @@
 /********************************************************************
  * COPYRIGHT:
- * Copyright (c) 1997-2012, International Business Machines
+ * Copyright (c) 1997-2013, International Business Machines
  * Corporation and others. All Rights Reserved.
  ********************************************************************/
 
@@ -57,6 +57,7 @@
 #include "locnmtst.h"       // LocaleDisplayNamesTest
 #include "dcfmtest.h"       // DecimalFormatTest
 #include "listformattertest.h"  // ListFormatterTest
+#include "regiontst.h"      // RegionTest
 
 extern IntlTest *createCompactDecimalFormatTest();
 extern IntlTest *createGenderInfoTest();
@@ -155,6 +156,7 @@ void IntlTestFormat::runIndexedTest( int32_t index, UBool exec, const char* &nam
             callTest(*test, par);
           }
           break;
+        TESTCLASS(45,RegionTest);
         default: name = ""; break; //needed to end loop
     }
     if (exec) {
diff --git a/icu4c/source/test/intltest/regiontst.cpp b/icu4c/source/test/intltest/regiontst.cpp
new file mode 100644 (file)
index 0000000..08d130a
--- /dev/null
@@ -0,0 +1,689 @@
+/***********************************************************************\r
+ * COPYRIGHT: \r
+ * Copyright (c) 2013, International Business Machines Corporation\r
+ * and others. All Rights Reserved.\r
+ ***********************************************************************/\r
\r
+/***********************************************************************\r
+ * This testcase ported from ICU4J ( RegionTest.java ) to ICU4C        *\r
+ * Try to keep them in sync if at all possible...!                     *\r
+ ***********************************************************************/\r
+\r
+#include "unicode/utypes.h"\r
+#include "cstring.h"\r
+\r
+#if !UCONFIG_NO_FORMATTING\r
+\r
+#include "unicode/region.h"\r
+#include "regiontst.h"\r
+\r
+typedef struct KnownRegion {\r
+  const char *code;\r
+  int32_t numeric;\r
+  const char *parent;\r
+  URegionType type;\r
+  const char *containingContinent;\r
+} KnownRegion;\r
+\r
+#define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0]))\r
+\r
+static KnownRegion knownRegions[] = {\r
+    // Code, Num, Parent, Type,             Containing Continent\r
+    { "TP" , 626, "035", URGN_TERRITORY, "142" },\r
+    { "001", 1,  NULL ,  URGN_WORLD,        NULL },\r
+    { "002", 2,  "001",  URGN_CONTINENT,    NULL },\r
+    { "003", 3,  NULL,   URGN_GROUPING,     NULL },\r
+    { "005", 5,  "019",  URGN_SUBCONTINENT, "019" },\r
+    { "009", 9,  "001",  URGN_CONTINENT,    NULL},\r
+    { "011", 11, "002",  URGN_SUBCONTINENT, "002" },\r
+    { "013", 13, "019",  URGN_SUBCONTINENT, "019" },\r
+    { "014", 14, "002",  URGN_SUBCONTINENT, "002" },\r
+    { "015", 15, "002",  URGN_SUBCONTINENT, "002" },\r
+    { "017", 17, "002",  URGN_SUBCONTINENT, "002" },\r
+    { "018", 18, "002",  URGN_SUBCONTINENT, "002" },\r
+    { "019", 19, "001",  URGN_CONTINENT, NULL },\r
+    { "021", 21, "019",  URGN_SUBCONTINENT, "019" },\r
+    { "029", 29, "019",  URGN_SUBCONTINENT, "019" },\r
+    { "030", 30, "142",  URGN_SUBCONTINENT, "142" },\r
+    { "034", 34, "142",  URGN_SUBCONTINENT, "142" },\r
+    { "035", 35, "142",  URGN_SUBCONTINENT, "142" },\r
+    { "039", 39, "150",  URGN_SUBCONTINENT, "150"},\r
+    { "053", 53, "009",  URGN_SUBCONTINENT, "009" },\r
+    { "054", 54, "009",  URGN_SUBCONTINENT, "009" },\r
+    { "057", 57, "009",  URGN_SUBCONTINENT, "009" },\r
+    { "061", 61, "009",  URGN_SUBCONTINENT, "009" },\r
+    { "142", 142, "001", URGN_CONTINENT, NULL },\r
+    { "143", 143, "142", URGN_SUBCONTINENT, "142" },\r
+    { "145", 145, "142", URGN_SUBCONTINENT, "142" },\r
+    { "150", 150, "001", URGN_CONTINENT, NULL },\r
+    { "151", 151, "150", URGN_SUBCONTINENT, "150" },\r
+    { "154", 154, "150", URGN_SUBCONTINENT, "150" },\r
+    { "155", 155, "150", URGN_SUBCONTINENT, "150" },\r
+    { "419", 419, NULL,  URGN_GROUPING , NULL},\r
+    { "AC" ,  -1, "QO" , URGN_TERRITORY, "009" },\r
+    { "AD" ,  20, "039", URGN_TERRITORY, "150" },\r
+    { "AE" , 784, "145", URGN_TERRITORY, "142" },\r
+    { "AF" ,   4, "034", URGN_TERRITORY, "142" },\r
+    { "AG" ,  28, "029", URGN_TERRITORY, "019" },\r
+    { "AI" , 660, "029", URGN_TERRITORY, "019" },\r
+    { "AL" ,   8, "039", URGN_TERRITORY, "150" },\r
+    { "AM" ,  51, "145", URGN_TERRITORY, "142" },\r
+    { "AN" , 530, NULL,  URGN_DEPRECATED, NULL },\r
+    { "AO" ,  24, "017", URGN_TERRITORY, "002" },\r
+    { "AQ" ,  10, "QO" , URGN_TERRITORY, "009" },\r
+    { "AR" ,  32, "005", URGN_TERRITORY, "019" },\r
+    { "AS" ,  16, "061", URGN_TERRITORY, "009" },\r
+    { "AT" ,  40, "155", URGN_TERRITORY, "150" },\r
+    { "AU" ,  36, "053", URGN_TERRITORY, "009" },\r
+    { "AW" , 533, "029", URGN_TERRITORY, "019" },\r
+    { "AX" , 248, "154", URGN_TERRITORY, "150" },\r
+    { "AZ" ,  31, "145", URGN_TERRITORY, "142" },\r
+    { "BA" ,  70, "039", URGN_TERRITORY, "150" },\r
+    { "BB" ,  52, "029", URGN_TERRITORY, "019" },\r
+    { "BD" ,  50, "034", URGN_TERRITORY, "142" },\r
+    { "BE" ,  56, "155", URGN_TERRITORY, "150" },\r
+    { "BF" , 854, "011", URGN_TERRITORY, "002" },\r
+    { "BG" , 100, "151", URGN_TERRITORY, "150" },\r
+    { "BH" ,  48, "145", URGN_TERRITORY, "142" },\r
+    { "BI" , 108, "014", URGN_TERRITORY, "002" },\r
+    { "BJ" , 204, "011", URGN_TERRITORY, "002" },\r
+    { "BL" , 652, "029", URGN_TERRITORY, "019" },\r
+    { "BM" ,  60, "021", URGN_TERRITORY, "019" },\r
+    { "BN" ,  96, "035", URGN_TERRITORY, "142" },\r
+    { "BO" ,  68, "005", URGN_TERRITORY, "019" },\r
+    { "BQ" , 535, "029", URGN_TERRITORY, "019" },\r
+    { "BR" ,  76, "005", URGN_TERRITORY, "019" },\r
+    { "BS" ,  44, "029", URGN_TERRITORY, "019" },\r
+    { "BT" ,  64, "034", URGN_TERRITORY, "142" },\r
+    { "BU" , 104, "035", URGN_TERRITORY, "142" },\r
+    { "BV" ,  74, "QO" , URGN_TERRITORY, "009" },\r
+    { "BW" ,  72, "018", URGN_TERRITORY, "002" },\r
+    { "BY" , 112, "151", URGN_TERRITORY, "150" },\r
+    { "BZ" ,  84, "013", URGN_TERRITORY, "019" },\r
+    { "CA" , 124, "021", URGN_TERRITORY, "019" },\r
+    { "CC" , 166, "QO" , URGN_TERRITORY, "009" },\r
+    { "CD" , 180, "017", URGN_TERRITORY, "002" },\r
+    { "CF" , 140, "017", URGN_TERRITORY, "002" },\r
+    { "CG" , 178, "017", URGN_TERRITORY, "002" },\r
+    { "CH" , 756, "155", URGN_TERRITORY, "150" },\r
+    { "CI" , 384, "011", URGN_TERRITORY, "002" },\r
+    { "CK" , 184, "061", URGN_TERRITORY, "009" },\r
+    { "CL" , 152, "005", URGN_TERRITORY, "019" },\r
+    { "CM" , 120, "017", URGN_TERRITORY, "002" },\r
+    { "CN" , 156, "030", URGN_TERRITORY, "142" },\r
+    { "CO" , 170, "005", URGN_TERRITORY, "019" },\r
+    { "CP" , -1 , "QO" , URGN_TERRITORY, "009" },\r
+    { "CR" , 188, "013", URGN_TERRITORY, "019" },\r
+    { "CU" , 192, "029", URGN_TERRITORY, "019" },\r
+    { "CV" , 132, "011", URGN_TERRITORY, "002" },\r
+    { "CW" , 531, "029", URGN_TERRITORY, "019" },\r
+    { "CX" , 162, "QO" , URGN_TERRITORY, "009" },\r
+    { "CY" , 196, "145", URGN_TERRITORY, "142" },\r
+    { "CZ" , 203, "151", URGN_TERRITORY, "150" },\r
+    { "DD" , 276, "155", URGN_TERRITORY, "150" },\r
+    { "DE" , 276, "155", URGN_TERRITORY, "150" },\r
+    { "DG" , -1 , "QO" , URGN_TERRITORY, "009" },\r
+    { "DJ" , 262, "014", URGN_TERRITORY, "002" },\r
+    { "DK" , 208, "154", URGN_TERRITORY, "150" },\r
+    { "DM" , 212, "029", URGN_TERRITORY, "019" },\r
+    { "DO" , 214, "029", URGN_TERRITORY, "019" },\r
+    { "DZ" ,  12, "015", URGN_TERRITORY, "002" },\r
+    { "EA" ,  -1, "015", URGN_TERRITORY, "002" },\r
+    { "EC" , 218, "005", URGN_TERRITORY, "019" },\r
+    { "EE" , 233, "154", URGN_TERRITORY, "150" },\r
+    { "EG" , 818, "015", URGN_TERRITORY, "002" },\r
+    { "EH" , 732, "015", URGN_TERRITORY, "002" },\r
+    { "ER" , 232, "014", URGN_TERRITORY, "002" },\r
+    { "ES" , 724, "039", URGN_TERRITORY, "150" },\r
+    { "ET" , 231, "014", URGN_TERRITORY, "002" },\r
+    { "EU" , 967, NULL,  URGN_GROUPING, NULL },\r
+    { "FI" , 246, "154", URGN_TERRITORY, "150" },\r
+    { "FJ" , 242, "054", URGN_TERRITORY, "009" },\r
+    { "FK" , 238, "005", URGN_TERRITORY, "019" },\r
+    { "FM" , 583, "057", URGN_TERRITORY, "009" },\r
+    { "FO" , 234, "154", URGN_TERRITORY, "150" },\r
+    { "FR" , 250, "155", URGN_TERRITORY, "150" },\r
+    { "FX" , 250, "155", URGN_TERRITORY, "150" },\r
+    { "GA" , 266, "017", URGN_TERRITORY, "002" },\r
+    { "GB" , 826, "154", URGN_TERRITORY, "150" },\r
+    { "GD" , 308, "029", URGN_TERRITORY, "019" },\r
+    { "GE" , 268, "145", URGN_TERRITORY, "142" },\r
+    { "GF" , 254, "005", URGN_TERRITORY, "019" },\r
+    { "GG" , 831, "154", URGN_TERRITORY, "150" },\r
+    { "GH" , 288, "011", URGN_TERRITORY, "002" },\r
+    { "GI" , 292, "039", URGN_TERRITORY, "150" },\r
+    { "GL" , 304, "021", URGN_TERRITORY, "019" },\r
+    { "GM" , 270, "011", URGN_TERRITORY, "002" },\r
+    { "GN" , 324, "011", URGN_TERRITORY, "002" },\r
+    { "GP" , 312, "029", URGN_TERRITORY, "019" },\r
+    { "GQ" , 226, "017", URGN_TERRITORY, "002" },\r
+    { "GR" , 300, "039", URGN_TERRITORY, "150" },\r
+    { "GS" , 239, "QO" , URGN_TERRITORY, "009" },\r
+    { "GT" , 320, "013", URGN_TERRITORY, "019" },\r
+    { "GU" , 316, "057", URGN_TERRITORY, "009" },\r
+    { "GW" , 624, "011", URGN_TERRITORY, "002" },\r
+    { "GY" , 328, "005", URGN_TERRITORY, "019" },\r
+    { "HK" , 344, "030", URGN_TERRITORY, "142" },\r
+    { "HM" , 334, "QO" , URGN_TERRITORY, "009" },\r
+    { "HN" , 340, "013", URGN_TERRITORY, "019" },\r
+    { "HR" , 191, "039", URGN_TERRITORY, "150" },\r
+    { "HT" , 332, "029", URGN_TERRITORY, "019" },\r
+    { "HU" , 348, "151", URGN_TERRITORY, "150" },\r
+    { "IC" ,  -1, "015", URGN_TERRITORY, "002" },\r
+    { "ID" , 360, "035", URGN_TERRITORY, "142" },\r
+    { "IE" , 372, "154", URGN_TERRITORY, "150" },\r
+    { "IL" , 376, "145", URGN_TERRITORY, "142" },\r
+    { "IM" , 833, "154", URGN_TERRITORY, "150" },\r
+    { "IN" , 356, "034", URGN_TERRITORY, "142" },\r
+    { "IO" ,  86, "QO" , URGN_TERRITORY, "009" },\r
+    { "IQ" , 368, "145", URGN_TERRITORY, "142" },\r
+    { "IR" , 364, "034", URGN_TERRITORY, "142" },\r
+    { "IS" , 352, "154", URGN_TERRITORY, "150" },\r
+    { "IT" , 380, "039", URGN_TERRITORY, "150" },\r
+    { "JE" , 832, "154", URGN_TERRITORY, "150" },\r
+    { "JM" , 388, "029", URGN_TERRITORY, "019" },\r
+    { "JO" , 400, "145", URGN_TERRITORY, "142" },\r
+    { "JP" , 392, "030", URGN_TERRITORY, "142" },\r
+    { "KE" , 404, "014", URGN_TERRITORY, "002" },\r
+    { "KG" , 417, "143", URGN_TERRITORY, "142" },\r
+    { "KH" , 116, "035", URGN_TERRITORY, "142" },\r
+    { "KI" , 296, "057", URGN_TERRITORY, "009" },\r
+    { "KM" , 174, "014", URGN_TERRITORY, "002" },\r
+    { "KN" , 659, "029", URGN_TERRITORY, "019" },\r
+    { "KP" , 408, "030", URGN_TERRITORY, "142" },\r
+    { "KR" , 410, "030", URGN_TERRITORY, "142" },\r
+    { "KW" , 414, "145", URGN_TERRITORY, "142" },\r
+    { "KY" , 136, "029", URGN_TERRITORY, "019" },\r
+    { "KZ" , 398, "143", URGN_TERRITORY, "142" },\r
+    { "LA" , 418, "035", URGN_TERRITORY, "142" },\r
+    { "LB" , 422, "145", URGN_TERRITORY, "142" },\r
+    { "LC" , 662, "029", URGN_TERRITORY, "019" },\r
+    { "LI" , 438, "155", URGN_TERRITORY, "150" },\r
+    { "LK" , 144, "034", URGN_TERRITORY, "142" },\r
+    { "LR" , 430, "011", URGN_TERRITORY, "002" },\r
+    { "LS" , 426, "018", URGN_TERRITORY, "002" },\r
+    { "LT" , 440, "154", URGN_TERRITORY, "150" },\r
+    { "LU" , 442, "155", URGN_TERRITORY, "150" },\r
+    { "LV" , 428, "154", URGN_TERRITORY, "150" },\r
+    { "LY" , 434, "015", URGN_TERRITORY, "002" },\r
+    { "MA" , 504, "015", URGN_TERRITORY, "002" },\r
+    { "MC" , 492, "155", URGN_TERRITORY, "150" },\r
+    { "MD" , 498, "151", URGN_TERRITORY, "150" },\r
+    { "ME" , 499, "039", URGN_TERRITORY, "150" },\r
+    { "MF" , 663, "029", URGN_TERRITORY, "019" },\r
+    { "MG" , 450, "014", URGN_TERRITORY, "002" },\r
+    { "MH" , 584, "057", URGN_TERRITORY, "009" },\r
+    { "MK" , 807, "039", URGN_TERRITORY, "150" },\r
+    { "ML" , 466, "011", URGN_TERRITORY, "002" },\r
+    { "MM" , 104, "035", URGN_TERRITORY, "142" },\r
+    { "MN" , 496, "030", URGN_TERRITORY, "142" },\r
+    { "MO" , 446, "030", URGN_TERRITORY, "142" },\r
+    { "MP" , 580, "057", URGN_TERRITORY, "009" },\r
+    { "MQ" , 474, "029", URGN_TERRITORY, "019" },\r
+    { "MR" , 478, "011", URGN_TERRITORY, "002" },\r
+    { "MS" , 500, "029", URGN_TERRITORY, "019" },\r
+    { "MT" , 470, "039", URGN_TERRITORY, "150" },\r
+    { "MU" , 480, "014", URGN_TERRITORY, "002" },\r
+    { "MV" , 462, "034", URGN_TERRITORY, "142" },\r
+    { "MW" , 454, "014", URGN_TERRITORY, "002" },\r
+    { "MX" , 484, "013", URGN_TERRITORY, "019"},\r
+    { "MY" , 458, "035", URGN_TERRITORY, "142" },\r
+    { "MZ" , 508, "014", URGN_TERRITORY, "002" },\r
+    { "NA" , 516, "018", URGN_TERRITORY, "002" },\r
+    { "NC" , 540, "054", URGN_TERRITORY, "009" },\r
+    { "NE" , 562, "011", URGN_TERRITORY, "002" },\r
+    { "NF" , 574, "053", URGN_TERRITORY, "009" },\r
+    { "NG" , 566, "011", URGN_TERRITORY, "002" },\r
+    { "NI" , 558, "013", URGN_TERRITORY, "019" },\r
+    { "NL" , 528, "155", URGN_TERRITORY, "150" },\r
+    { "NO" , 578, "154", URGN_TERRITORY, "150" },\r
+    { "NP" , 524, "034", URGN_TERRITORY, "142" },\r
+    { "NR" , 520, "057", URGN_TERRITORY, "009" },\r
+    { "NT" , 536, NULL , URGN_DEPRECATED, NULL },\r
+    { "NU" , 570, "061", URGN_TERRITORY, "009" },\r
+    { "NZ" , 554, "053", URGN_TERRITORY, "009" },\r
+    { "OM" , 512, "145", URGN_TERRITORY, "142" },\r
+    { "PA" , 591, "013", URGN_TERRITORY, "019" },\r
+    { "PE" , 604, "005", URGN_TERRITORY, "019" },\r
+    { "PF" , 258, "061", URGN_TERRITORY, "009" },\r
+    { "PG" , 598, "054", URGN_TERRITORY, "009" },\r
+    { "PH" , 608, "035", URGN_TERRITORY, "142" },\r
+    { "PK" , 586, "034", URGN_TERRITORY, "142" },\r
+    { "PL" , 616, "151", URGN_TERRITORY, "150" },\r
+    { "PM" , 666, "021", URGN_TERRITORY, "019" },\r
+    { "PN" , 612, "061", URGN_TERRITORY, "009" },\r
+    { "PR" , 630, "029", URGN_TERRITORY, "019" },\r
+    { "PS" , 275, "145", URGN_TERRITORY, "142" },\r
+    { "PT" , 620, "039", URGN_TERRITORY, "150" },\r
+    { "PW" , 585, "057", URGN_TERRITORY, "009" },\r
+    { "PY" , 600, "005", URGN_TERRITORY, "019" },\r
+    { "QA" , 634, "145", URGN_TERRITORY, "142" },\r
+    { "QO" , 961, "009", URGN_SUBCONTINENT, "009" },\r
+    { "QU" , 967, NULL,  URGN_GROUPING, NULL },\r
+    { "RE" , 638, "014", URGN_TERRITORY, "002" },\r
+    { "RO" , 642, "151", URGN_TERRITORY, "150" },\r
+    { "RS" , 688, "039", URGN_TERRITORY, "150" },\r
+    { "RU" , 643, "151", URGN_TERRITORY, "150" },\r
+    { "RW" , 646, "014", URGN_TERRITORY, "002" },\r
+    { "SA" , 682, "145", URGN_TERRITORY, "142" },\r
+    { "SB" ,  90, "054", URGN_TERRITORY, "009" },\r
+    { "SC" , 690, "014", URGN_TERRITORY, "002" },\r
+    { "SD" , 729, "015", URGN_TERRITORY, "002" },\r
+    { "SE" , 752, "154", URGN_TERRITORY, "150" },\r
+    { "SG" , 702, "035", URGN_TERRITORY, "142" },\r
+    { "SH" , 654, "011", URGN_TERRITORY, "002" },\r
+    { "SI" , 705, "039", URGN_TERRITORY, "150" },\r
+    { "SJ" , 744, "154", URGN_TERRITORY, "150" },\r
+    { "SK" , 703, "151", URGN_TERRITORY, "150" },\r
+    { "SL" , 694, "011", URGN_TERRITORY, "002" },\r
+    { "SM" , 674, "039", URGN_TERRITORY, "150" },\r
+    { "SN" , 686, "011", URGN_TERRITORY, "002" },\r
+    { "SO" , 706, "014", URGN_TERRITORY, "002" },\r
+    { "SR" , 740, "005", URGN_TERRITORY, "019" },\r
+    { "SS" , 728, "015", URGN_TERRITORY, "002" },\r
+    { "ST" , 678, "017", URGN_TERRITORY, "002" },\r
+    { "SU" , 810, NULL , URGN_DEPRECATED , NULL},\r
+    { "SV" , 222, "013", URGN_TERRITORY, "019" },\r
+    { "SX" , 534, "029", URGN_TERRITORY, "019" },\r
+    { "SY" , 760, "145", URGN_TERRITORY, "142" },\r
+    { "SZ" , 748, "018", URGN_TERRITORY, "002" },\r
+    { "TA" ,  -1, "QO",  URGN_TERRITORY, "009" },\r
+    { "TC" , 796, "029", URGN_TERRITORY, "019" },\r
+    { "TD" , 148, "017", URGN_TERRITORY, "002" },\r
+    { "TF" , 260, "QO" , URGN_TERRITORY, "009" },\r
+    { "TG" , 768, "011", URGN_TERRITORY, "002" },\r
+    { "TH" , 764, "035", URGN_TERRITORY, "142" },\r
+    { "TJ" , 762, "143", URGN_TERRITORY, "142" },\r
+    { "TK" , 772, "061", URGN_TERRITORY, "009" },\r
+    { "TL" , 626, "035", URGN_TERRITORY, "142" },\r
+    { "TM" , 795, "143", URGN_TERRITORY, "142" },\r
+    { "TN" , 788, "015", URGN_TERRITORY, "002" },\r
+    { "TO" , 776, "061", URGN_TERRITORY, "009" },\r
+    { "TP" , 626, "035", URGN_TERRITORY, "142" },\r
+    { "TR" , 792, "145", URGN_TERRITORY, "142" },\r
+    { "TT" , 780, "029", URGN_TERRITORY, "019" },\r
+    { "TV" , 798, "061", URGN_TERRITORY, "009" },\r
+    { "TW" , 158, "030", URGN_TERRITORY, "142" },\r
+    { "TZ" , 834, "014", URGN_TERRITORY, "002" },\r
+    { "UA" , 804, "151", URGN_TERRITORY, "150" },\r
+    { "UG" , 800, "014", URGN_TERRITORY, "002" },\r
+    { "UM" , 581, "QO" , URGN_TERRITORY, "009" },\r
+    { "US" , 840, "021", URGN_TERRITORY, "019" },\r
+    { "UY" , 858, "005", URGN_TERRITORY, "019" },\r
+    { "UZ" , 860, "143", URGN_TERRITORY, "142" },\r
+    { "VA" , 336, "039", URGN_TERRITORY, "150" },\r
+    { "VC" , 670, "029", URGN_TERRITORY, "019" },\r
+    { "VE" , 862, "005", URGN_TERRITORY, "019" },\r
+    { "VG" ,  92, "029", URGN_TERRITORY, "019" },\r
+    { "VI" , 850, "029", URGN_TERRITORY, "019" },\r
+    { "VN" , 704, "035", URGN_TERRITORY, "142" },\r
+    { "VU" , 548, "054", URGN_TERRITORY, "009" },\r
+    { "WF" , 876, "061", URGN_TERRITORY, "009" },\r
+    { "WS" , 882, "061", URGN_TERRITORY, "009" },\r
+    { "YD" , 887, "145", URGN_TERRITORY, "142" },\r
+    { "YE" , 887, "145", URGN_TERRITORY, "142" },\r
+    { "YT" , 175, "014", URGN_TERRITORY, "002" },\r
+    { "ZA" , 710, "018", URGN_TERRITORY, "002" },\r
+    { "ZM" , 894, "014", URGN_TERRITORY, "002" },\r
+    { "ZR" , 180, "017", URGN_TERRITORY, "002" },\r
+    { "ZW" , 716, "014", URGN_TERRITORY, "002" },\r
+    { "ZZ" , 999, NULL , URGN_UNKNOWN, NULL }\r
+    };\r
+\r
+// *****************************************************************************\r
+// class RegionTest\r
+// *****************************************************************************\r
+\r
+\r
+RegionTest::RegionTest() {\r
+}\r
+\r
+RegionTest::~RegionTest() {\r
+}\r
+\r
+#define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break;\r
+\r
+void \r
+RegionTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* par )\r
+{\r
+    optionv = (par && *par=='v');\r
+    switch (index) {\r
+        CASE(0, TestKnownRegions)\r
+        CASE(1, TestGetInstanceString)\r
+        CASE(2, TestGetInstanceInt)\r
+        CASE(3, TestGetContainedRegions)\r
+        CASE(4, TestGetContainedRegionsWithType)\r
+        CASE(5, TestGetContainingRegion)\r
+        CASE(6, TestGetContainingRegionWithType)\r
+        CASE(7, TestGetPreferredValues)\r
+        CASE(8, TestContains);\r
+        CASE(9, TestAvailableTerritories)\r
+        default: name = ""; break;\r
+    }\r
+}\r
+\r
+\r
+void RegionTest::TestKnownRegions() {\r
+\r
+    for (int32_t i = 0 ; i < LENGTHOF(knownRegions) ; i++ ) {\r
+        KnownRegion rd = knownRegions[i];\r
+        UErrorCode status = U_ZERO_ERROR;\r
+        const Region *r = Region::getInstance(rd.code,status);\r
+        if ( r ) {\r
+            int32_t n = r->getNumericCode();\r
+            int32_t e = rd.numeric;\r
+            if ( n != e ) {\r
+                errln("Numeric code mismatch for region %s.  Expected:%d Got:%d",r->getRegionCode(),e,n);\r
+            }\r
+\r
+            if (r->getType() != rd.type) {\r
+                errln("Expected region %s to be of type %d. Got: %d",r->getRegionCode(),rd.type,r->getType());\r
+            }\r
+\r
+            int32_t nc = rd.numeric;\r
+            if ( nc > 0 ) {\r
+                const Region *ncRegion = Region::getInstance(nc,status);\r
+                if ( *ncRegion != *r && nc != 891 ) { // 891 is special case - CS and YU both deprecated codes for region 891\r
+                    errln("Creating region %s by its numeric code returned a different region. Got: %s instead.",r->getRegionCode(),ncRegion->getRegionCode());\r
+                }\r
+             }\r
+        } else {\r
+            errln("Known region %s was not recognized.",rd.code);\r
+        }\r
+    }\r
+}\r
+\r
+void RegionTest::TestGetInstanceString() {\r
+    typedef struct TestData {\r
+        const char *inputID;\r
+        const char *expectedID;\r
+        URegionType expectedType;\r
+    } TestData;\r
+\r
+    static TestData testData[] = {\r
+    //  Input ID, Expected ID, Expected Type\r
+        { "DE", "DE", URGN_TERRITORY },  // Normal region\r
+        { "QU", "EU", URGN_GROUPING },   // Alias to a grouping\r
+        { "DD", "DE", URGN_TERRITORY },  // Alias to a deprecated region (East Germany) with single preferred value\r
+        { "276", "DE", URGN_TERRITORY }, // Numeric code for Germany\r
+        { "278", "DE", URGN_TERRITORY }, // Numeric code for East Germany (Deprecated)\r
+        { "SU", "SU", URGN_DEPRECATED }, // Alias to a deprecated region with multiple preferred values\r
+        { "AN", "AN", URGN_DEPRECATED }, // Deprecated region with multiple preferred values\r
+        { "SVK", "SK", URGN_TERRITORY }  // 3-letter code - Slovakia\r
+    };\r
+\r
+\r
+    UErrorCode status = U_ZERO_ERROR;\r
+    const Region *r = Region::getInstance(NULL,status);\r
+    if ( status != U_ILLEGAL_ARGUMENT_ERROR ) {\r
+        errln("Calling Region::getInstance(NULL) should have triggered an U_ILLEGAL_ARGUMENT_ERROR, but didn't.");\r
+    }\r
+\r
+    status = U_ZERO_ERROR;\r
+    r = Region::getInstance("BOGUS",status);\r
+    if ( status != U_ILLEGAL_ARGUMENT_ERROR ) {\r
+        errln("Calling Region::getInstance(\"BOGUS\") should have triggered an U_ILLEGAL_ARGUMENT_ERROR, but didn't.");\r
+    }\r
+\r
+\r
+    for (int32_t i = 0 ; i < LENGTHOF(testData) ; i++ ) {\r
+        TestData data = testData[i];\r
+        status = U_ZERO_ERROR;\r
+        r = Region::getInstance(data.inputID,status);\r
+        const char *id;\r
+        URegionType type;\r
+        if ( r ) {\r
+            id = r->getRegionCode();\r
+            type = r->getType();\r
+        } else {\r
+            id = "NULL";\r
+            type = URGN_UNKNOWN;\r
+        }\r
+        if ( uprv_strcmp(id,data.expectedID)) {\r
+            errln("Unexpected region ID for Region::getInstance(\"%s\"); Expected: %s Got: %s",data.inputID,data.expectedID,id);\r
+        }\r
+        if ( type != data.expectedType) {\r
+            errln("Unexpected region type for Region::getInstance(\"%s\"); Expected: %d Got: %d",data.inputID,data.expectedType,type);\r
+        }\r
+    }\r
+}\r
+\r
+void RegionTest::TestGetInstanceInt() {\r
+    typedef struct TestData {\r
+        int32_t inputID;\r
+        const char *expectedID;\r
+        URegionType expectedType;\r
+    } TestData;\r
+\r
+    static TestData testData[] = {\r
+        //  Input ID, Expected ID, Expected Type\r
+        { 276, "DE",  URGN_TERRITORY }, // Numeric code for Germany\r
+        { 278, "DE",  URGN_TERRITORY }, // Numeric code for East Germany (Deprecated)\r
+        { 419, "419", URGN_GROUPING },  // Latin America\r
+        { 736, "SD",  URGN_TERRITORY }, // Sudan (pre-2011) - changed numeric code after South Sudan split off\r
+        { 729, "SD",  URGN_TERRITORY }, // Sudan (post-2011) - changed numeric code after South Sudan split off\r
+    };\r
+\r
+    UErrorCode status = U_ZERO_ERROR;\r
+    const Region *r = Region::getInstance(-123,status);\r
+    if ( status != U_ILLEGAL_ARGUMENT_ERROR ) {\r
+        errln("Calling Region::getInstance(-123) should have triggered an U_ILLEGAL_ARGUMENT_ERROR, but didn't.");\r
+    }\r
+\r
+    for (int32_t i = 0 ; i < LENGTHOF(testData) ; i++ ) {\r
+        TestData data = testData[i];\r
+        status = U_ZERO_ERROR;\r
+        const Region *r = Region::getInstance(data.inputID,status);\r
+        const char *id;\r
+        URegionType type;\r
+        if ( r ) {\r
+            id = r->getRegionCode();\r
+            type = r->getType();\r
+        } else {\r
+            id = "NULL";\r
+            type = URGN_UNKNOWN;\r
+        }\r
+        if ( uprv_strcmp(data.expectedID,id)) {\r
+            errln("Unexpected region ID for Region.getInstance(%d)); Expected: %s Got: %s",data.inputID,data.expectedID,id);\r
+        }\r
+        if ( data.expectedType != type) {\r
+            errln("Unexpected region type for Region.getInstance(%d)); Expected: %d Got: %d",data.inputID,data.expectedType,type);\r
+        }\r
+    }\r
+}\r
+\r
+void RegionTest::TestGetContainedRegions() {\r
+    for (int32_t i = 0 ; i < LENGTHOF(knownRegions) ; i++ ) {\r
+        KnownRegion rd = knownRegions[i];\r
+        UErrorCode status = U_ZERO_ERROR;\r
+\r
+        const Region *r = Region::getInstance(rd.code,status);\r
+        if (r) {\r
+            if (r->getType() == URGN_GROUPING) {\r
+                continue;\r
+            }\r
+            StringEnumeration *containedRegions = r->getContainedRegions();\r
+            int32_t count = containedRegions->count(status);\r
+            for ( int32_t i = 0 ; i < containedRegions->count(status); i++ ) {\r
+                const char *crID = containedRegions->next(NULL,status);\r
+                const Region *cr = Region::getInstance(crID,status);\r
+                const Region *containingRegion = cr ? cr->getContainingRegion() : NULL;\r
+                if ( !containingRegion || *containingRegion != *r ) {\r
+                    errln("Region: %s contains region %s. Expected containing region of this region to be the original region, but got %s",\r
+                        r->getRegionCode(),cr->getRegionCode(),containingRegion?containingRegion->getRegionCode():"NULL"); \r
+                }\r
+            }\r
+        } else {\r
+            errln("Known region %s was not recognized.",rd.code);\r
+        }\r
+    }\r
+}\r
+\r
+void RegionTest::TestGetContainedRegionsWithType() {\r
+    for (int32_t i = 0 ; i < LENGTHOF(knownRegions) ; i++ ) {\r
+        KnownRegion rd = knownRegions[i];\r
+        UErrorCode status = U_ZERO_ERROR;\r
+\r
+        const Region *r = Region::getInstance(rd.code,status);\r
+        if (r) {\r
+            if (r->getType() != URGN_CONTINENT) {\r
+                continue;\r
+            }\r
+            StringEnumeration *containedRegions = r->getContainedRegions(URGN_TERRITORY);\r
+            for ( int32_t j = 0 ; j < containedRegions->count(status); j++ ) {\r
+                const char *crID = containedRegions->next(NULL,status);\r
+                const Region *cr = Region::getInstance(crID,status);\r
+                const Region *containingRegion = cr ? cr->getContainingRegion(URGN_CONTINENT) : NULL;\r
+                if ( !containingRegion || *containingRegion != *r ) {\r
+                    errln("Continent: %s contains territory %s. Expected containing continent of this region to be the original region, but got %s",\r
+                        r->getRegionCode(),cr->getRegionCode(),containingRegion?containingRegion->getRegionCode():"NULL"); \r
+                }\r
+            }\r
+        } else {\r
+            errln("Known region %s was not recognized.",rd.code);\r
+        }\r
+    }\r
+}\r
+\r
+void RegionTest::TestGetContainingRegion() {        \r
+    for (int32_t i = 0 ; i < LENGTHOF(knownRegions) ; i++ ) {\r
+        KnownRegion rd = knownRegions[i];\r
+        UErrorCode status = U_ZERO_ERROR;\r
+        const Region *r = Region::getInstance(rd.code,status);\r
+        if (r) {\r
+            const Region *c = r->getContainingRegion();\r
+            if (rd.parent == NULL) {                   \r
+                if ( c ) {\r
+                    errln("Containing region for %s should have been NULL.  Got: %s",r->getRegionCode(),c->getRegionCode());\r
+                }\r
+            } else {\r
+                const Region *p = Region::getInstance(rd.parent,status);                   \r
+                if ( !c || *p != *c ) {\r
+                    errln("Expected containing continent of region %s to be %s. Got: %s",\r
+                        r->getRegionCode(),p?p->getRegionCode():"NULL",c?c->getRegionCode():"NULL" );\r
+                }\r
+            }\r
+        } else {\r
+            errln("Known region %s was not recognized.",rd.code);\r
+        }\r
+    }\r
+}\r
+\r
+void RegionTest::TestGetContainingRegionWithType() {        \r
+    for (int32_t i = 0 ; i < LENGTHOF(knownRegions) ; i++ ) {\r
+        KnownRegion rd = knownRegions[i];\r
+        UErrorCode status = U_ZERO_ERROR;\r
+\r
+        const Region *r = Region::getInstance(rd.code,status);\r
+        if (r) {\r
+            const Region *c = r->getContainingRegion(URGN_CONTINENT);\r
+            if (rd.containingContinent == NULL) {\r
+                 if ( c != NULL) {\r
+                     errln("Containing continent for %s should have been NULL.  Got: %s",r->getRegionCode(), c->getRegionCode());\r
+                 }\r
+            } else {\r
+                const Region *p = Region::getInstance(rd.containingContinent,status);                   \r
+                if ( *p != *c ) {\r
+                    errln("Expected containing continent of region %s to be %s. Got: %s",\r
+                        r->getRegionCode(),p?p->getRegionCode():"NULL",c?c->getRegionCode():"NULL" );\r
+                }\r
+            }\r
+        } else {\r
+            errln("Known region %s was not recognized.",rd.code);\r
+        }\r
+    }\r
+}\r
+\r
+void RegionTest::TestGetPreferredValues() {\r
+    static const char *testData[6][17] = {\r
+        //  Input ID, Expected Preferred Values...\r
+        { "AN", "CW", "SX", "BQ", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, // Netherlands Antilles\r
+        { "CS", "RS", "ME", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },     // Serbia & Montenegro\r
+        { "FQ", "AQ", "TF", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },     // French Southern and Antarctic Territories\r
+        { "NT", "IQ", "SA", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },     // Neutral Zone\r
+        { "PC", "FM", "MH", "MP", "PW", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }, // Pacific Islands Trust Territory\r
+        { "SU", "RU", "AM", "AZ", "BY", "EE", "GE", "KZ", "KG", "LV", "LT", "MD", "TJ", "TM", "UA", "UZ" , NULL}, // Soviet Union\r
+    };\r
+\r
+    for ( int32_t i = 0 ; i < 6 ; i++ ) {\r
+        const char **data = testData[i];\r
+        UErrorCode status = U_ZERO_ERROR;\r
+        const Region *r = Region::getInstance(data[0],status);\r
+        if (r) {\r
+            StringEnumeration *preferredValues = r->getPreferredValues();\r
+            for ( int i = 1 ; data[i] ; i++ ) {\r
+                UBool found = FALSE;\r
+                preferredValues->reset(status);\r
+                while ( const char *check = preferredValues->next(NULL,status) ) {\r
+                    if ( !uprv_strcmp(check,data[i]) ) {\r
+                        found = TRUE;\r
+                        break;\r
+                    }\r
+                }\r
+                if ( !found ) {\r
+                    errln("Region::getPreferredValues() for region \"%s\" should have contained \"%s\" but it didn't.",r->getRegionCode(),data[i]);\r
+                }\r
+            }\r
+        } else {\r
+            errln("Known region %s was not recognized.",data[0]);\r
+        }\r
+    }\r
+}\r
+\r
+void RegionTest::TestContains() {        \r
+    for (int32_t i = 0 ; i < LENGTHOF(knownRegions) ; i++ ) {\r
+        KnownRegion rd = knownRegions[i];\r
+        UErrorCode status = U_ZERO_ERROR;\r
+\r
+        const Region *r = Region::getInstance(rd.code,status);\r
+        if (r) {\r
+            const Region *c = r->getContainingRegion();\r
+            while ( c ) {\r
+                if ( !c->contains(*r)) {\r
+                    errln("Region \"%s\" should have contained \"%s\" but it didn't.",c->getRegionCode(),r->getRegionCode());\r
+                }\r
+                c = c->getContainingRegion();\r
+            }\r
+        } else {\r
+            errln("Known region %s was not recognized.",rd.code);\r
+        }\r
+    }\r
+}\r
+\r
+void RegionTest::TestAvailableTerritories() {\r
+        // Test to make sure that the set of territories contained in World and the set of all available\r
+        // territories are one and the same.\r
+        UErrorCode status = U_ZERO_ERROR;\r
+        StringEnumeration *availableTerritories = Region::getAvailable(URGN_TERRITORY);\r
+        const Region *world = Region::getInstance("001",status);\r
+        StringEnumeration *containedInWorld = world->getContainedRegions(URGN_TERRITORY);\r
+        if ( !availableTerritories || !containedInWorld || *availableTerritories != *containedInWorld ) {\r
+            char availableTerritoriesString[1024] = "";\r
+            char containedInWorldString[1024] = "";\r
+            if ( availableTerritories ) {\r
+                for (int32_t i = 0 ; i < availableTerritories->count(status) ; i++ ) {\r
+                    if ( i > 0 ) {\r
+                        uprv_strcat(availableTerritoriesString," ");\r
+                    }\r
+                    uprv_strcat(availableTerritoriesString,availableTerritories->next(NULL,status));\r
+                }\r
+            } else {\r
+                uprv_strcpy(availableTerritoriesString,"NULL");\r
+            }\r
+            if ( containedInWorld ) {\r
+                for (int32_t i = 0 ; i < containedInWorld->count(status) ; i++ ) {\r
+                    if ( i > 0 ) {\r
+                        uprv_strcat(containedInWorldString," ");\r
+                    }\r
+                    uprv_strcat(containedInWorldString,containedInWorld->next(NULL,status));\r
+                }\r
+            } else {\r
+                uprv_strcpy(containedInWorldString,"NULL");\r
+            }\r
+            errln("Available territories and all territories contained in world should be the same set.\nAvailable          = %s\nContained in World = %s",\r
+                availableTerritoriesString,containedInWorldString);\r
+        }\r
+    }\r
+#endif /* #if !UCONFIG_NO_FORMATTING */\r
+\r
+//eof\r
diff --git a/icu4c/source/test/intltest/regiontst.h b/icu4c/source/test/intltest/regiontst.h
new file mode 100644 (file)
index 0000000..a5088ef
--- /dev/null
@@ -0,0 +1,47 @@
+/************************************************************************\r
+ * COPYRIGHT:\r
+ * Copyright (c) 2013, International Business Machines Corporation\r
+ * and others. All Rights Reserved.\r
+ ************************************************************************/\r
+\r
+#ifndef _REGIONTEST_\r
+#define _REGIONTEST_\r
+\r
+#include "unicode/utypes.h"\r
+\r
+#if !UCONFIG_NO_FORMATTING\r
+\r
+#include "unicode/region.h"\r
+#include "intltest.h"\r
+\r
+/**\r
+ * Performs various tests on Region APIs\r
+ **/\r
+class RegionTest: public IntlTest {\r
+    // IntlTest override\r
+    void runIndexedTest( int32_t index, UBool exec, const char* &name, char* par );\r
+\r
+public:\r
+    RegionTest();\r
+    virtual ~RegionTest();\r
+    \r
+    void TestKnownRegions(void);\r
+    void TestGetInstanceString(void);\r
+    void TestGetInstanceInt(void);\r
+    void TestGetContainedRegions(void);\r
+    void TestGetContainedRegionsWithType(void);\r
+    void TestGetContainingRegion(void);\r
+    void TestGetContainingRegionWithType(void);\r
+    void TestGetPreferredValues(void);\r
+    void TestContains(void);\r
+    void TestAvailableTerritories(void);\r
+\r
+private:\r
+\r
+    UBool optionv; // TRUE if @v option is given on command line\r
+};\r
+\r
+#endif /* #if !UCONFIG_NO_FORMATTING */\r
\r
+#endif // _REGIONTEST_\r
+//eof\r