]> granicus.if.org Git - icu/commitdiff
ICU-9587 Updated and improved Region API - moving to draft ICU 50
authorJohn Emmons <emmo@us.ibm.com>
Sat, 29 Sep 2012 01:44:13 +0000 (01:44 +0000)
committerJohn Emmons <emmo@us.ibm.com>
Sat, 29 Sep 2012 01:44:13 +0000 (01:44 +0000)
X-SVN-Rev: 32473

icu4j/main/classes/core/src/com/ibm/icu/util/Region.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/RegionTest.java

index ba2c330d58789eabb14800079f63bf3266dcf6fd..db2e615d0f4d77f5e6b5cd255ab76c4965b56a63 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2011, International Business Machines Corporation             *
+ * Copyright (C) 2011-2012, International Business Machines Corporation        *
  * All Rights Reserved.                                                        *
  *******************************************************************************
  */
@@ -8,10 +8,8 @@ package com.ibm.icu.util;
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.BitSet;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -54,8 +52,7 @@ import com.ibm.icu.impl.ICUResourceBundle;
  *  ( WORLD, CONTINENT, or SUBCONTINENT ) will always be the containing region instead.
  *  
  * @author       John Emmons
- * @internal ICU 4.8 technology preview
- * @deprecated This API might change or be removed in a future release. 
+ * @draft ICU 50
  */
 
 public class Region implements Comparable<Region> {
@@ -64,55 +61,47 @@ public class Region implements Comparable<Region> {
      * RegionType is an enumeration defining the different types of regions.  Current possible
      * values are WORLD, CONTINENT, SUBCONTINENT, TERRITORY, GROUPING, DEPRECATED, and UNKNOWN.
      * 
-     * @internal ICU 4.8 technology preview
-     * @deprecated This API might change or be removed in a future release.
+     * @draft ICU 50
      */
 
     public enum RegionType {
         /**
          * Type representing the unknown region.
-         * @internal ICU 4.8 technology preview
-         * @deprecated This API might change or be removed in a future release.
+         * @draft ICU 50
          */
         UNKNOWN,
 
         /**
          * Type representing a territory.
-         * @internal ICU 4.8 technology preview
-         * @deprecated This API might change or be removed in a future release.
+         * @draft ICU 50
          */
         TERRITORY,
 
         /**
          * Type representing the whole world.
-         * @internal ICU 4.8 technology preview
-         * @deprecated This API might change or be removed in a future release.
+         * @draft ICU 50
          */
         WORLD,
         /**
          * Type representing a continent.
-         * @internal ICU 4.8 technology preview
-         * @deprecated This API might change or be removed in a future release.
+         * @draft ICU 50
          */
         CONTINENT,
         /**
          * Type representing a sub-continent.
-         * @internal ICU 4.8 technology preview
-         * @deprecated This API might change or be removed in a future release.
+         * @draft ICU 50
          */
         SUBCONTINENT,
         /**
          * Type representing a grouping of territories that is not to be used in
          * the normal WORLD/CONTINENT/SUBCONTINENT/TERRITORY containment tree.
-         * @internal ICU 4.8 technology preview
-         * @deprecated This API might change or be removed in a future release.
+         * @draft ICU 50
          */
         GROUPING,
         /**
          * Type representing a region whose code has been deprecated, usually
          * due to a country splitting into multiple territories or changing its name.
-         * @internal ICU 4.8 technology preview
-         * @deprecated This API might change or be removed in a future release.
+         * @draft ICU 50
          */
         DEPRECATED,
     }
@@ -120,28 +109,28 @@ public class Region implements Comparable<Region> {
     /**
      * A constant used for unknown numeric region code.
      * @see #getNumericCode()
-     * @internal ICU 4.8 technology preview
-     * @deprecated This API might change or be removed in a future release.
+     * @draft ICU 50
      */
     public static final int UNDEFINED_NUMERIC_CODE = -1;
 
     private String id;
     private int code;
     private RegionType type;
+    private Region containingRegion = null;
+    private Set<Region> containedRegions = new TreeSet<Region>();
+    private List<Region> preferredValues = null;
     
-    private static boolean hasData = false;
-    private static boolean hasContainmentData = false;
+    private static boolean regionDataIsLoaded = false;
     
-    private static Map<String,Integer> regionIndexMap = null;   // Map from ID to position in the table
-    private static Map<Integer,Integer> numericIndexMap = null; // Map from numeric code to position in the table
-    private static Map<String,String> territoryAliasMap = null; // Aliases
-    private static Map<String,Integer> numericCodeMap = null;   // Map of all possible IDs to numeric codes
-    private static Region[] regions = null;
-    private static BitSet[] subRegionData = null;
-    private static Integer[] containingRegionData = null;
+    private static Map<String,Region> regionIDMap = null;       // Map from ID the regions
+    private static Map<Integer,Region> numericCodeMap = null;   // Map from numeric code to the regions
+    private static Map<String,Region> regionAliases = null;     // Aliases
+
+    private static ArrayList<Region> regions = null;            // This is the main data structure where the Regions are stored.
     private static ArrayList<Set<Region>> availableRegions = null;
     
     private static final String UNKNOWN_REGION_ID = "ZZ";
+    private static final String OUTLYING_OCEANIA_REGION_ID = "QO";
     private static final String WORLD_ID = "001";
    
     /*
@@ -152,46 +141,37 @@ public class Region implements Comparable<Region> {
     /*
      * Initializes the region data from the ICU resource bundles.  The region data
      * contains the basic relationships such as which regions are known, what the numeric
-     * codes are, and any known aliases.  It does not contain the territory containment data.
-     * Territory containment data only gets loaded if someone calls an API that is actually
-     * going to use that data.
+     * codes are, any known aliases, and the territory containment data.
      * 
      * If the region data has already loaded, then this method simply returns without doing
      * anything meaningful.
      * 
      */
-    private static synchronized void initRegionData() {
+    private static synchronized void loadRegionData() {
         
-        if ( hasData ) {
+        if ( regionDataIsLoaded ) {
             return;
         }
         
-        territoryAliasMap = new HashMap<String,String>();
-        numericCodeMap = new HashMap<String,Integer>();
-        regionIndexMap = new HashMap<String,Integer>();
-        numericIndexMap = new HashMap<Integer,Integer>();
+        regionAliases = new HashMap<String,Region>();
+        regionIDMap = new HashMap<String,Region>();
+        numericCodeMap = new HashMap<Integer,Region>();
+        
         availableRegions = new ArrayList<Set<Region>>(RegionType.values().length);
         
-        for (int i = 0 ; i < RegionType.values().length ; i++) {
-            availableRegions.add(null);
-        }
+        
         UResourceBundle regionCodes = null;
         UResourceBundle territoryAlias = null;
         UResourceBundle codeMappings = null;
         UResourceBundle worldContainment = null;
         UResourceBundle territoryContainment = null;
         UResourceBundle groupingContainment = null;
-        UResourceBundle rb = UResourceBundle.getBundleInstance(
-                                    ICUResourceBundle.ICU_BASE_NAME,
-                                    "metadata",
-                                    ICUResourceBundle.ICU_DATA_CLASS_LOADER);
+        UResourceBundle rb = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,"metadata",ICUResourceBundle.ICU_DATA_CLASS_LOADER);
         regionCodes = rb.get("regionCodes");
         territoryAlias = rb.get("territoryAlias");
 
-        UResourceBundle rb2 = UResourceBundle.getBundleInstance(
-                    ICUResourceBundle.ICU_BASE_NAME,
-                    "supplementalData",
-                    ICUResourceBundle.ICU_DATA_CLASS_LOADER);
+        UResourceBundle rb2 = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,"supplementalData", ICUResourceBundle.ICU_DATA_CLASS_LOADER);
         codeMappings = rb2.get("codeMappings");
 
         // Right now only fetch as much territory containment as we need in order to determine
@@ -206,125 +186,136 @@ public class Region implements Comparable<Region> {
         String[] groupingArr = groupingContainment.getStringArray();
         List<String> groupings = Arrays.asList(groupingArr);
 
+        // First process the region codes and create the master array of regions.
+        int regionCodeSize = regionCodes.getSize();
+        regions = new ArrayList<Region>(regionCodeSize);
+        for ( int i = 0 ; i < regionCodeSize ; i++ ) {
+            Region r = new Region();
+            String id = regionCodes.getString(i);
+            r.id = id;
+            r.type = RegionType.TERRITORY; // Only temporary - figure out the real type later once the aliases are known.
+            regionIDMap.put(id, r);
+            if ( id.matches("[0-9]{3}")) {
+                r.code = Integer.valueOf(id).intValue();
+                numericCodeMap.put(r.code, r);
+                r.type = RegionType.SUBCONTINENT;
+            } else {
+                r.code = UNDEFINED_NUMERIC_CODE;
+            }
+            regions.add(r);
+        }
         
-        // First put alias mappings for iso3 and numeric code mappings
+        // Process the code mappings - This will allow us to assign numeric codes to most of the territories.
         for ( int i = 0 ; i < codeMappings.getSize(); i++ ) {
             UResourceBundle mapping = codeMappings.get(i);
             if ( mapping.getType() == UResourceBundle.ARRAY ) {
-                String [] codeStrings = mapping.getStringArray();
-                if ( !territoryAliasMap.containsKey(codeStrings[1])) {
-                    territoryAliasMap.put(codeStrings[1],codeStrings[0]); // Put alias from the numeric to the iso2 code
-                }
-                territoryAliasMap.put(codeStrings[2],codeStrings[0]); // Put alias from the iso3 to the iso2 code.
-                numericCodeMap.put(codeStrings[0], Integer.valueOf(codeStrings[1])); // Create the mapping from the iso2 code to its numeric value
+                String [] codeMappingStrings = mapping.getStringArray();
+                String codeMappingID = codeMappingStrings[0];
+                Integer codeMappingNumber = Integer.valueOf(codeMappingStrings[1]);
+                String codeMapping3Letter = codeMappingStrings[2];
+                
+                if ( regionIDMap.containsKey(codeMappingID)) {
+                    Region r = regionIDMap.get(codeMappingID);
+                    r.code = codeMappingNumber.intValue();
+                    numericCodeMap.put(r.code, r);
+                    regionAliases.put(codeMapping3Letter, r);
+                }                    
             }
         }
 
+        // Process the territory aliases
         for ( int i = 0 ; i < territoryAlias.getSize(); i++ ) {
             UResourceBundle res = territoryAlias.get(i);
-            String key = res.getKey();
-            String value = res.getString();
-            if ( !territoryAliasMap.containsKey(key)) {
-                territoryAliasMap.put(key, value);
+            String aliasFrom = res.getKey();
+            String aliasTo = res.getString();
+            
+            if ( regionIDMap.containsKey(aliasFrom) ) {  // This is a deprecated region
+                Region r = regionIDMap.get(aliasFrom);
+                r.type = RegionType.DEPRECATED;
+                List<String> aliasToRegionStrings = Arrays.asList(aliasTo.split(" "));
+                r.preferredValues = new ArrayList<Region>();
+                for ( String s : aliasToRegionStrings ) {
+                    if (regionIDMap.containsKey(s)) {
+                        r.preferredValues.add(regionIDMap.get(s));
+                    }
+                }
+            } else if ( regionIDMap.containsKey(aliasTo) ) { // This is just an alias from some string to a region
+                regionAliases.put(aliasFrom, regionIDMap.get(aliasTo));
             }
         }
 
-        
-        regions = new Region[regionCodes.getSize()];
-        for ( int i = 0 ; i < regions.length ; i++ ) {
-            regions[i] = new Region();
-            String id = regionCodes.getString(i);
-            regions[i].id = id;
-            regionIndexMap.put(id, Integer.valueOf(i));
+        // Now fill in the special cases for WORLD, UNKNOWN, CONTINENTS, and GROUPINGS
+        Region r;
+        if ( regionIDMap.containsKey(WORLD_ID)) {
+            r = regionIDMap.get(WORLD_ID);
+            r.type = RegionType.WORLD;
+        }
 
-            if ( id.matches("[0-9]{3}")) {
-                regions[i].code = Integer.valueOf(id).intValue();
-                numericIndexMap.put(regions[i].code, Integer.valueOf(i));
-            } else if (numericCodeMap.containsKey(id)) {
-                regions[i].code = numericCodeMap.get(id).intValue();
-                if ( !numericIndexMap.containsKey(regions[i].code)) {
-                    numericIndexMap.put(regions[i].code, Integer.valueOf(i));
-                }
-            } else {
-                regions[i].code = UNDEFINED_NUMERIC_CODE;
+        if ( regionIDMap.containsKey(UNKNOWN_REGION_ID)) {
+            r = regionIDMap.get(UNKNOWN_REGION_ID);
+            r.type = RegionType.UNKNOWN;
+        }
+        
+        for ( String continent : continents ) {
+            if (regionIDMap.containsKey(continent)) {
+                r = regionIDMap.get(continent);
+                r.type = RegionType.CONTINENT;
             }
-
-            if ( territoryAliasMap.containsKey(id)){
-                regions[i].type = RegionType.DEPRECATED;
-            } else if ( id.equals(WORLD_ID) ) {
-                regions[i].type = RegionType.WORLD;
-            } else if ( id.equals(UNKNOWN_REGION_ID) ) {
-                regions[i].type = RegionType.UNKNOWN;
-            } else if ( continents.contains(id) ) {
-                regions[i].type = RegionType.CONTINENT;
-            } else if ( groupings.contains(id) ) {
-                regions[i].type = RegionType.GROUPING;
-            } else if ( id.matches("[0-9]{3}|QO") ) {
-                regions[i].type = RegionType.SUBCONTINENT;
-            } else {
-                regions[i].type = RegionType.TERRITORY;
-            }                
         }
         
-        hasData = true;
-    }
-
-    /*
-     * Initializes the containment data from the ICU resource bundles.  The containment data
-     * defines the relationships between different regions, such as which regions are contained
-     * within other regions.
-     * 
-     * Territory containment data only gets loaded if someone calls an API that is actually
-     * going to use that data.  Since you have to have the basic region data as well, this
-     * method will attempt to load the basic region data if it hasn't been loaded already.
-     * 
-     * If the containment data has already loaded, then this method simply returns without doing
-     * anything meaningful.
-     * 
-     */
-
-    private static synchronized void initContainmentData() {
-        if ( hasContainmentData ) {
-            return;
+        for ( String grouping : groupings ) {
+            if (regionIDMap.containsKey(grouping)) {
+                r = regionIDMap.get(grouping);
+                r.type = RegionType.GROUPING;
+            }
         }
         
-        initRegionData();
-        subRegionData = new BitSet[regions.length];
-        containingRegionData = new Integer[regions.length];
-        for ( int i = 0 ; i < regions.length ; i++ ) {
-            subRegionData[i] = new BitSet(regions.length);
-            containingRegionData[i] = null;
+        // Special case: The region code "QO" (Outlying Oceania) is a subcontinent code added by CLDR
+        // even though it looks like a territory code.  Need to handle it here.
+        
+        if ( regionIDMap.containsKey(OUTLYING_OCEANIA_REGION_ID)) {
+            r = regionIDMap.get(OUTLYING_OCEANIA_REGION_ID);
+            r.type = RegionType.SUBCONTINENT;
         }
-        UResourceBundle territoryContainment = null;
-
-        UResourceBundle rb = UResourceBundle.getBundleInstance(
-                    ICUResourceBundle.ICU_BASE_NAME,
-                    "supplementalData",
-                    ICUResourceBundle.ICU_DATA_CLASS_LOADER);
-        territoryContainment = rb.get("territoryContainment");
 
-        
-        // Get territory containment info from the supplemental data.
+        // Load territory containment info from the supplemental data.
         for ( int i = 0 ; i < territoryContainment.getSize(); i++ ) {
             UResourceBundle mapping = territoryContainment.get(i);
             String parent = mapping.getKey();
-            Integer parentRegionIndex = regionIndexMap.get(parent);
+            Region parentRegion = regionIDMap.get(parent);
+
             for ( int j = 0 ; j < mapping.getSize(); j++ ) {
                 String child = mapping.getString(j);
-                Integer childRegionIndex = regionIndexMap.get(child);
-                if ( parentRegionIndex != null && childRegionIndex != null ) {                    
-                    subRegionData[parentRegionIndex.intValue()].set(childRegionIndex.intValue()); // Set the containment bit for this pair
+                Region childRegion = regionIDMap.get(child);
+                if ( parentRegion != null && childRegion != null ) {                    
+                    
+                    // Add the child region to the set of regions contained by the parent
+                    parentRegion.containedRegions.add(childRegion);
+
+                    // Set the parent region to be the containing region of the child.
                     // Regions of type GROUPING can't be set as the parent, since another region
                     // such as a SUBCONTINENT, CONTINENT, or WORLD must always be the parent.
-                    if ( !regions[parentRegionIndex].isOfType(RegionType.GROUPING)) {
-                        containingRegionData[childRegionIndex] = parentRegionIndex; 
+                    if ( parentRegion.getType() != RegionType.GROUPING) {
+                        childRegion.containingRegion = parentRegion;
                     }
                 }
             }
+        }     
+
+        // Create the availableRegions lists
+        
+        for (int i = 0 ; i < RegionType.values().length ; i++) {
+            availableRegions.add(new TreeSet<Region>());
         }
-        hasContainmentData = true;
-    }
-    
+
+        for ( Region ar : regions ) {
+            Set<Region> currentSet = availableRegions.get(ar.type.ordinal());
+            currentSet.add(ar);
+            availableRegions.set(ar.type.ordinal(),currentSet);
+        }
+        
+        regionDataIsLoaded = true;
+    }    
     
     /** Returns a Region using the given region ID.  The region ID can be either a 2-letter ISO code,
      * 3-letter ISO code,  UNM.49 numeric code, or other valid Unicode Region Code as defined by the CLDR.
@@ -332,20 +323,32 @@ public class Region implements Comparable<Region> {
      * @return The corresponding region.
      * @throws NullPointerException if the supplied id is null.
      * @throws IllegalArgumentException if the supplied ID cannot be canonicalized to a Region ID that is known by ICU.
-     * @internal ICU 4.8 technology preview
-     * @deprecated This API might change or be removed in a future release.
+     * @draft ICU 50
      */
     
-    public static Region get(String id) {
+    public static Region getInstance(String id) {
+
         if ( id == null ) {
             throw new NullPointerException();
         }
-        String canonicalID = canonicalize(id);
-        if (canonicalID.equals(UNKNOWN_REGION_ID) && !id.equals(UNKNOWN_REGION_ID)) {
-            throw new IllegalArgumentException("Unknown region id: " + id);
+
+        loadRegionData();
+
+        Region r = regionIDMap.get(id);
+        
+        if ( r == null ) {
+            r = regionAliases.get(id);
+        }
+        
+        if ( r == null ) {
+            throw new IllegalArgumentException("Unknown region id: " + id);         
         }
         
-        return regions[regionIndexMap.get(canonicalID)];
+        if ( r.type == RegionType.DEPRECATED && r.preferredValues.size() == 1) {
+           r = r.preferredValues.get(0);
+        }
+       
+        return r;
     }
     
     
@@ -353,78 +356,49 @@ public class Region implements Comparable<Region> {
      * @param code The numeric code of the region to be retrieved.
      * @return The corresponding region.
      * @throws IllegalArgumentException if the supplied numeric code is not recognized.
-     * @internal ICU 4.8 technology preview
-     * @deprecated This API might change or be removed in a future release.
+     * @draft ICU 50
      */
 
-    public static Region get(int code) {
-        Integer index = numericIndexMap.get(Integer.valueOf(code));
-        if ( index != null ) {
-            Region r = regions[index];
-            // Since a deprecated region will have the same numeric code as its new region code
-            // we get by id which will make sure we get the canonicalized one.
-            return Region.get(r.id);
-        } else {
-            throw new IllegalArgumentException("Unknown region code: " + code);
-        }
-    }
+    public static Region getInstance(int code) {
 
-    /** Returns the canonicalized (preferred) form of the Region code.  For territories, it will
-     * convert the string to the 2-letter ISO 3166 code if at all possible, and will convert any
-     * known aliases to their modern counterparts.
-     * 
-     * @param id The string representing the region code to be canonicalized.
-     * @return The canonicalized (preferred) form of the region code.  If the supplied region
-     *         code is not recognized, the unknown region ( code "ZZ" ) is returned.
-     * @internal ICU 4.8 technology preview
-     * @deprecated This API might change or be removed in a future release.
-     */
+        loadRegionData();
 
-    public static String canonicalize(String id) {
-        initRegionData();
-        String result = territoryAliasMap.get(id);
-        if ( result != null && regionIndexMap.containsKey(result)) {
-            return result;
+        Region r = numericCodeMap.get(code);
+        
+        if ( r == null ) { // Just in case there's an alias that's numeric, try to find it.
+            String pad = "";
+            if ( code < 10 ) {
+                pad = "00";
+            } else if ( code < 100 ) {
+                pad = "0";
+            }
+            String id = pad + Integer.toString(code);
+            r = regionAliases.get(id);
+        }
+
+        if ( r == null ) {
+            throw new IllegalArgumentException("Unknown region code: " + code);
         }
         
-        if ( regionIndexMap.containsKey(id)) {
-            return id;
+        if ( r.type == RegionType.DEPRECATED && r.preferredValues.size() == 1) {
+           r = r.preferredValues.get(0);
         }
-        return UNKNOWN_REGION_ID;
+       
+        return r;
     }
-    /** Returns true if the supplied region code is already in its canonical ( preferred ) form.
-     * 
-     * @param id The string representing the region code to be checked.
-     * @return TRUE if the supplied region code is canonical, FALSE otherwise.
-     * @internal ICU 4.8 technology preview
-     * @deprecated This API might change or be removed in a future release.
-     */
-   
-    public static boolean isCanonical(String id) {
-        return ( canonicalize(id).equals(id));
-    }
-    
+
     
     /** Used to retrieve all available regions of a specific type.
      * 
      * @param type The type of regions to be returned ( TERRITORY, MACROREGION, etc. )
      * @return An unmodifiable set of all known regions that match the given type.
-     * @internal ICU 4.8 technology preview
-     * @deprecated This API might change or be removed in a future release.
+     * @draft ICU 50
      */
 
     public static Set<Region> getAvailable(RegionType type) {
-        initRegionData();
-        if ( availableRegions.get(type.ordinal()) == null) {
-            Set<Region> result = new TreeSet<Region>();
-            for ( Region r : regions ) {
-                if ( r.type == type ) {
-                    result.add(r);
-                }
-            }
-            availableRegions.set(type.ordinal(), Collections.unmodifiableSet(result));
-        }
-        return availableRegions.get(type.ordinal());
+        
+        loadRegionData();
+        return Collections.unmodifiableSet(availableRegions.get(type.ordinal()));
     }
 
     
@@ -433,19 +407,12 @@ public class Region implements Comparable<Region> {
      * @return The region that geographically contains this region.  Returns NULL if this region is
      *  code "001" (World) or "ZZ" (Unknown region).  For example, calling this method with region "IT" (Italy)
      *  returns the region "039" (Southern Europe).    
-     * @internal ICU 4.8 technology preview
-     * @deprecated This API might change or be removed in a future release.
+     * @draft ICU 50
      */
 
     public Region getContainingRegion() {
-        initContainmentData();
-        Integer index = regionIndexMap.get(id);
-        assert(index!=null);
-        if ( containingRegionData[index] == null ) {
-            return null;
-        } else {
-            return regions[containingRegionData[index]];
-        }
+        loadRegionData();        
+        return containingRegion;
     }
 
     /** Used to determine the sub-regions that are contained within this region.
@@ -458,56 +425,77 @@ public class Region implements Comparable<Region> {
      * the various sub regions of Europe - "039" (Southern Europe) - "151" (Eastern Europe) 
      * - "154" (Northern Europe) and "155" (Western Europe).
      *
-     * @internal ICU 4.8 technology preview
-     * @deprecated This API might change or be removed in a future release.
+     * @draft ICU 50
      */
 
-    public Set<Region> getSubRegions() {
-        initContainmentData();
-        
-        Set<Region> result = new TreeSet<Region>();
-        Integer index = regionIndexMap.get(id);
-        BitSet contains = subRegionData[index];
-        for( int i = contains.nextSetBit(0); i>=0; i=contains.nextSetBit(i+1)) {
-            result.add(regions[i]);
-        }
-        return Collections.unmodifiableSet(result);
+    public Set<Region> getContainedRegions() {
+        loadRegionData();
+        return Collections.unmodifiableSet(containedRegions);
     }
     
-    /** Used to determine all the territories that are contained within this region.
+    /** Used to determine all the regions that are contained within this region and that match the given type
      * 
-     * @return An unmodifiable set containing all the territories that are children of this
-     *  region anywhere in the region hierarchy.  If this region is already a territory,
-     *  the empty set is returned, since territories by definition do not contain other regions.
-     *  For example, calling this method with region "150" (Europe) returns a set containing all
-     *  the territories in Europe ( "FR" (France) - "IT" (Italy) - "DE" (Germany) etc. )
-     *
-     * @internal ICU 4.8 technology preview
-     * @deprecated This API might change or be removed in a future release.
+     * @return An unmodifiable set containing all the regions that are children of this region
+     * anywhere in the region hierarchy and match the given type.  This API may return an empty set
+     * if this region doesn't have any sub-regions that match the given type.
+     * For example, calling this method with region "150" (Europe) and type "TERRITORY" returns a set
+     *  containing all the territories in Europe ( "FR" (France) - "IT" (Italy) - "DE" (Germany) etc. )
+     * @draft ICU 50
      */
     
-    public Set<Region> getContainedTerritories() {
-        initContainmentData();
+    public Set<Region> getContainedRegions(RegionType type) {
+        
+        loadRegionData();
+
         Set<Region> result = new TreeSet<Region>();
-        Set<Region> subRegions = getSubRegions();
-        Iterator<Region> it = subRegions.iterator();
-        while ( it.hasNext() ) {
-            Region r = it.next();
-            if ( r.isOfType(RegionType.TERRITORY) ) {
+        Set<Region> cr = getContainedRegions();
+        
+       for ( Region r : cr ) {
+            if ( r.getType() == type ) {
                 result.add(r);
-            } else if ( r.isOfType(RegionType.CONTINENT) || r.isOfType(RegionType.SUBCONTINENT)) {
-                result.addAll(r.getContainedTerritories()); // Recursion!!!
+            } else {
+                result.addAll(r.getContainedRegions(type));
             }
         }
         return Collections.unmodifiableSet(result);
     }
 
+    /** 
+     * @return For deprecated regions, return an unmodifiable list of the regions that are the preferred replacement regions for this region.  
+     * Returns null for a non-deprecated region.  For example, calling this method with region "SU" (Soviet Union) would 
+     * return a list of the regions containing "RU" (Russia), "AM" (Armenia), "AZ" (Azerbaijan), etc... 
+     *    
+     * @draft ICU 50
+     */
+    public List<Region> getPreferredValues() {
+        return Collections.unmodifiableList(preferredValues);
+    }
+    
+    /**
+     * @return Returns true if this region contains the supplied other region anywhere in the region hierarchy.
+     */
+    public boolean contains(Region other) {
+
+        loadRegionData();
+        
+        if (containedRegions.contains(other)) {
+            return true;
+        } else {
+            for (Region cr : containedRegions) {
+                if (cr.contains(other)) {
+                    return true;
+                }
+            }
+        }
+        
+        return false;
+    }
+    
     /** Returns the string representation of this region
      * 
-     * @return The string representation of this region, which is its canonical ID.
+     * @return The string representation of this region, which is its ID.
      *
-     * @internal ICU 4.8 technology preview
-     * @deprecated This API might change or be removed in a future release.
+     * @draft ICU 50
      */
  
     public String toString() {
@@ -520,8 +508,7 @@ public class Region implements Comparable<Region> {
      * given region does not have a numeric code assigned to it.  This is a very rare case and
      * only occurs for a few very small territories.
      *
-     * @internal ICU 4.8 technology preview
-     * @deprecated This API might change or be removed in a future release.
+     * @draft ICU 50
      */
    
     public int getNumericCode() {
@@ -532,30 +519,16 @@ public class Region implements Comparable<Region> {
      * 
      * @return This region's type classification, such as MACROREGION or TERRITORY.
      *
-     * @internal ICU 4.8 technology preview
-     * @deprecated This API might change or be removed in a future release.
+     * @draft ICU 50
      */
   
     public RegionType getType() {
         return type;
     }
     
-    /** Checks to see if this region is of a specific type.
-     * 
-     * @return Returns TRUE if this region matches the supplied type.
-     *
-     * @internal ICU 4.8 technology preview
-     * @deprecated This API might change or be removed in a future release.
-     */
-  
-    public boolean isOfType(RegionType type) {
-        return this.type.equals(type);
-    }
-
     /**
      * {@inheritDoc}
-     * @internal ICU 4.8 technology preview
-     * @deprecated This API might change or be removed in a future release.
+     * @draft ICU 50
      */
     public int compareTo(Region other) {
         return id.compareTo(other.id);
index c2f8a4e6061733589a8023553d031cb755b2c63b..17d82e550c53add20da35801b777ecd1c8133b76 100644 (file)
@@ -1,6 +1,6 @@
 /*
  **********************************************************************
- * Copyright (c) 2011, International Business Machines
+ * Copyright (c) 2011-2012, International Business Machines
  * Corporation and others.  All Rights Reserved.
  **********************************************************************
  * Author: John Emmons
@@ -349,7 +349,7 @@ public class RegionTest extends TestFmwk {
         
         for (String [] rd : knownRegions ) {
             try {
-               Region r = Region.get(rd[0]);
+               Region r = Region.getInstance(rd[0]);
                int n = r.getNumericCode();
                int e = Integer.valueOf(rd[1]).intValue();
                if ( n != e ) {
@@ -361,57 +361,58 @@ public class RegionTest extends TestFmwk {
                        errln("Parent for " + r.toString() + " should have been NULL.  Got: " + c.toString());
                    }
                } else {
-                   Region p = Region.get(rd[2]);                   
+                   Region p = Region.getInstance(rd[2]);                   
                    if ( !p.equals(c)) {
                        errln("Expected parent of region " + r.toString() + " was " + p.toString() + ". Got: " + ( c == null ? "NULL" : c.toString()) );
                    }
                }
-               if (!r.isOfType(Region.RegionType.valueOf(rd[3]))) {
+               if (r.getType() != Region.RegionType.valueOf(rd[3])) {
                    errln("Expected region " + r.toString() + " to be of type " + rd[3] + ". Got:" + r.getType().toString());
                }
                int nc = Integer.valueOf(rd[1]).intValue();
                if ( nc > 0 ) {
-                   Region ncRegion = Region.get(nc);
+                   Region ncRegion = Region.getInstance(nc);
                    if ( !ncRegion.equals(r) && nc != 891 ) { // 891 is special case - CS and YU both deprecated codes for region 891
                        errln("Creating region " + r.toString() + " by its numeric code returned a different region. Got: " + ncRegion.toString());
                    }
                }
             } catch (IllegalArgumentException ex ) {
-               errln("Known region " + rd[0] + "was not recognized.");
+               errln("Known region " + rd[0] + " was not recognized.");
             }
         }
     }
     
     public void TestBadArguments() {
         try {
-            Region.get(null);
+            Region.getInstance(null);
             errln("Calling Region.get(null) should have thrown a NullPointerException, but didn't.");
         } catch ( NullPointerException ex ) {
             // Do nothing - we're supposed to get here.
         }
         try {
-            Region.get("BOGUS");
+            Region.getInstance("BOGUS");
             errln("Calling Region.get(BOGUS) should have thrown a IllegalArgumentException, but didn't.");
         } catch ( IllegalArgumentException ex ) {
             // Do nothing - we're supposed to get here.
         }
         try {
-            Region.get(-123);
+            Region.getInstance(-123);
             errln("Calling Region.get(-123) should have thrown a IllegalArgumentException, but didn't.");
         } catch ( IllegalArgumentException ex ) {
             // Do nothing - we're supposed to get here.
         }
     }
-    public void TestAvailableRegions() {
+
+    public void TestAvailableTerritories() {
         // Test to make sure that the set of territories contained in World and the set of all available
         // territories are one and the same.
         Set<Region> availableTerritories = Region.getAvailable(RegionType.TERRITORY);
-        Region world = Region.get("001");
-        Set<Region> containedInWorld = world.getContainedTerritories();
+        Region world = Region.getInstance("001");
+        Set<Region> containedInWorld = world.getContainedRegions(RegionType.TERRITORY);
         if ( !availableTerritories.equals(containedInWorld) ) {
             errln("Available territories and all territories contained in world should be the same set." +
-                 "Available = " + availableTerritories.toString() + 
-                 " : Contained in World = " + containedInWorld.toString());
+                  "Available = " + availableTerritories.toString() + 
+                  " : Contained in World = " + containedInWorld.toString());
         }
     }
 }