17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
3f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert * Copyright (C) 2011-2015, International Business Machines Corporation        *
47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * All Rights Reserved.                                                        *
57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.util;
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.ArrayList;
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Arrays;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Collections;
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.HashMap;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.List;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Set;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.TreeSet;
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.ICUResourceBundle;
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <code>Region</code> is the class representing a Unicode Region Code, also known as a
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Unicode Region Subtag, which is defined based upon the BCP 47 standard. We often think of
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "regions" as "countries" when defining the characteristics of a locale.  Region codes There are different
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * types of region codes that are important to distinguish.
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p>
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  Macroregion - A code for a "macro geographical (continental) region, geographical sub-region, or
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  selected economic and other grouping" as defined in
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  UN M.49 (http://unstats.un.org/unsd/methods/m49/m49regin.htm).
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  These are typically 3-digit codes, but contain some 2-letter codes, such as the LDML code QO
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  added for Outlying Oceania.  Not all UNM.49 codes are defined in LDML, but most of them are.
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  Macroregions are represented in ICU by one of three region types: WORLD ( region code 001 ),
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  CONTINENTS ( regions contained directly by WORLD ), and SUBCONTINENTS ( things contained directly
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  by a continent ).
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  <p>
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  TERRITORY - A Region that is not a Macroregion. These are typically codes for countries, but also
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  include areas that are not separate countries, such as the code "AQ" for Antarctica or the code
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  "HK" for Hong Kong (SAR China). Overseas dependencies of countries may or may not have separate
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  codes. The codes are typically 2-letter codes aligned with the ISO 3166 standard, but BCP47 allows
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  for the use of 3-digit codes in the future.
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  <p>
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  UNKNOWN - The code ZZ is defined by Unicode LDML for use to indicate that the Region is unknown,
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  or that the value supplied as a region was invalid.
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  <p>
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  DEPRECATED - Region codes that have been defined in the past but are no longer in modern usage,
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  usually due to a country splitting into multiple territories or changing its name.
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  <p>
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  GROUPING - A widely understood grouping of territories that has a well defined membership such
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  that a region code has been assigned for it.  Some of these are UNM.49 codes that do't fall into
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  the world/continent/sub-continent hierarchy, while others are just well known groupings that have
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  their own region code. Region "EU" (European Union) is one such region code that is a grouping.
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  Groupings will never be returned by the getContainingRegion() API, since a different type of region
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *  ( WORLD, CONTINENT, or SUBCONTINENT ) will always be the containing region instead.
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @author       John Emmons
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 50
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class Region implements Comparable<Region> {
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * RegionType is an enumeration defining the different types of regions.  Current possible
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * values are WORLD, CONTINENT, SUBCONTINENT, TERRITORY, GROUPING, DEPRECATED, and UNKNOWN.
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public enum RegionType {
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Type representing the unknown region.
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 50
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        UNKNOWN,
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Type representing a territory.
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 50
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        TERRITORY,
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Type representing the whole world.
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 50
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        WORLD,
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Type representing a continent.
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 50
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        CONTINENT,
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Type representing a sub-continent.
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 50
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        SUBCONTINENT,
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Type representing a grouping of territories that is not to be used in
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * the normal WORLD/CONTINENT/SUBCONTINENT/TERRITORY containment tree.
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 50
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        GROUPING,
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Type representing a region whose code has been deprecated, usually
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * due to a country splitting into multiple territories or changing its name.
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 50
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DEPRECATED,
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String id;
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private int code;
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private RegionType type;
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Region containingRegion = null;
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Set<Region> containedRegions = new TreeSet<Region>();
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private List<Region> preferredValues = null;
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static boolean regionDataIsLoaded = false;
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static Map<String,Region> regionIDMap = null;       // Map from ID the regions
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static Map<Integer,Region> numericCodeMap = null;   // Map from numeric code to the regions
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static Map<String,Region> regionAliases = null;     // Aliases
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static ArrayList<Region> regions = null;            // This is the main data structure where the Regions are stored.
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static ArrayList<Set<Region>> availableRegions = null;
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final String UNKNOWN_REGION_ID = "ZZ";
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final String OUTLYING_OCEANIA_REGION_ID = "QO";
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final String WORLD_ID = "001";
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Private default constructor.  Use factory methods only.
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Region () {}
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Initializes the region data from the ICU resource bundles.  The region data
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * contains the basic relationships such as which regions are known, what the numeric
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * codes are, any known aliases, and the territory containment data.
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * If the region data has already loaded, then this method simply returns without doing
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * anything meaningful.
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static synchronized void loadRegionData() {
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( regionDataIsLoaded ) {
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return;
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        regionAliases = new HashMap<String,Region>();
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        regionIDMap = new HashMap<String,Region>();
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        numericCodeMap = new HashMap<Integer,Region>();
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        availableRegions = new ArrayList<Set<Region>>(RegionType.values().length);
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
156f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        UResourceBundle metadataAlias = null;
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        UResourceBundle territoryAlias = null;
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        UResourceBundle codeMappings = null;
159bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        UResourceBundle idValidity = null;
160bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        UResourceBundle regionList = null;
161bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        UResourceBundle regionRegular = null;
162bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        UResourceBundle regionMacro = null;
163bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        UResourceBundle regionUnknown = null;
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        UResourceBundle worldContainment = null;
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        UResourceBundle territoryContainment = null;
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        UResourceBundle groupingContainment = null;
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
168f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        UResourceBundle metadata = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,"metadata",ICUResourceBundle.ICU_DATA_CLASS_LOADER);
169f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        metadataAlias = metadata.get("alias");
170f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        territoryAlias = metadataAlias.get("territory");
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
172f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        UResourceBundle supplementalData = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,"supplementalData", ICUResourceBundle.ICU_DATA_CLASS_LOADER);
173f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        codeMappings = supplementalData.get("codeMappings");
174bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        idValidity = supplementalData.get("idValidity");
175bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        regionList = idValidity.get("region");
176bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        regionRegular = regionList.get("regular");
177bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        regionMacro = regionList.get("macroregion");
178bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        regionUnknown = regionList.get("unknown");
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
180f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        territoryContainment = supplementalData.get("territoryContainment");
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        worldContainment = territoryContainment.get("001");
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        groupingContainment = territoryContainment.get("grouping");
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String[] continentsArr = worldContainment.getStringArray();
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        List<String> continents = Arrays.asList(continentsArr);
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String[] groupingArr = groupingContainment.getStringArray();
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        List<String> groupings = Arrays.asList(groupingArr);
188bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        List<String> regionCodes = new ArrayList<String>();
189bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
190bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        List<String> allRegions = new ArrayList<String>();
191bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        allRegions.addAll(Arrays.asList(regionRegular.getStringArray()));
192bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        allRegions.addAll(Arrays.asList(regionMacro.getStringArray()));
193bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        allRegions.add(regionUnknown.getString());
194bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
195bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        for ( String r : allRegions ) {
196bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            int rangeMarkerLocation = r.indexOf("~");
197bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if ( rangeMarkerLocation > 0 ) {
198bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                StringBuilder regionName = new StringBuilder(r);
199bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                char endRange = regionName.charAt(rangeMarkerLocation+1);
200bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                regionName.setLength(rangeMarkerLocation);
201bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                char lastChar = regionName.charAt(rangeMarkerLocation-1);
202bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                while ( lastChar <= endRange ) {
203bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    String newRegion = regionName.toString();
204bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    regionCodes.add(newRegion);
205bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    lastChar++;
206bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    regionName.setCharAt(rangeMarkerLocation-1,lastChar);
207bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
208bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            } else {
209bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                regionCodes.add(r);
210bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
211bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
212bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
213bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        regions = new ArrayList<Region>(regionCodes.size());
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // First process the region codes and create the master array of regions.
216bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        for ( String id : regionCodes) {
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Region r = new Region();
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r.id = id;
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r.type = RegionType.TERRITORY; // Only temporary - figure out the real type later once the aliases are known.
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            regionIDMap.put(id, r);
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( id.matches("[0-9]{3}")) {
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                r.code = Integer.valueOf(id).intValue();
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                numericCodeMap.put(r.code, r);
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                r.type = RegionType.SUBCONTINENT;
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                r.code = -1;
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            regions.add(r);
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Process the territory aliases
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for ( int i = 0 ; i < territoryAlias.getSize(); i++ ) {
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            UResourceBundle res = territoryAlias.get(i);
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String aliasFrom = res.getKey();
236f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            String aliasTo = res.get("replacement").getString();
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( regionIDMap.containsKey(aliasTo) && !regionIDMap.containsKey(aliasFrom) ) { // This is just an alias from some string to a region
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                regionAliases.put(aliasFrom, regionIDMap.get(aliasTo));
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Region r;
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ( regionIDMap.containsKey(aliasFrom) ) {  // This is a deprecated region
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    r = regionIDMap.get(aliasFrom);
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else { // Deprecated region code not in the master codes list - so need to create a deprecated region for it.
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    r = new Region();
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    r.id = aliasFrom;
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    regionIDMap.put(aliasFrom, r);
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if ( aliasFrom.matches("[0-9]{3}")) {
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        r.code = Integer.valueOf(aliasFrom).intValue();
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        numericCodeMap.put(r.code, r);
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        r.code = -1;
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    regions.add(r);
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                r.type = RegionType.DEPRECATED;
2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                List<String> aliasToRegionStrings = Arrays.asList(aliasTo.split(" "));
2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                r.preferredValues = new ArrayList<Region>();
2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for ( String s : aliasToRegionStrings ) {
2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (regionIDMap.containsKey(s)) {
2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        r.preferredValues.add(regionIDMap.get(s));
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Process the code mappings - This will allow us to assign numeric codes to most of the territories.
2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for ( int i = 0 ; i < codeMappings.getSize(); i++ ) {
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            UResourceBundle mapping = codeMappings.get(i);
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( mapping.getType() == UResourceBundle.ARRAY ) {
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String [] codeMappingStrings = mapping.getStringArray();
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String codeMappingID = codeMappingStrings[0];
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Integer codeMappingNumber = Integer.valueOf(codeMappingStrings[1]);
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String codeMapping3Letter = codeMappingStrings[2];
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ( regionIDMap.containsKey(codeMappingID)) {
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    Region r = regionIDMap.get(codeMappingID);
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    r.code = codeMappingNumber.intValue();
2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    numericCodeMap.put(r.code, r);
2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    regionAliases.put(codeMapping3Letter, r);
2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Now fill in the special cases for WORLD, UNKNOWN, CONTINENTS, and GROUPINGS
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Region r;
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( regionIDMap.containsKey(WORLD_ID)) {
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r = regionIDMap.get(WORLD_ID);
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r.type = RegionType.WORLD;
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( regionIDMap.containsKey(UNKNOWN_REGION_ID)) {
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r = regionIDMap.get(UNKNOWN_REGION_ID);
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r.type = RegionType.UNKNOWN;
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for ( String continent : continents ) {
2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (regionIDMap.containsKey(continent)) {
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                r = regionIDMap.get(continent);
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                r.type = RegionType.CONTINENT;
3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for ( String grouping : groupings ) {
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (regionIDMap.containsKey(grouping)) {
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                r = regionIDMap.get(grouping);
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                r.type = RegionType.GROUPING;
3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Special case: The region code "QO" (Outlying Oceania) is a subcontinent code added by CLDR
3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // even though it looks like a territory code.  Need to handle it here.
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( regionIDMap.containsKey(OUTLYING_OCEANIA_REGION_ID)) {
3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r = regionIDMap.get(OUTLYING_OCEANIA_REGION_ID);
3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r.type = RegionType.SUBCONTINENT;
3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Load territory containment info from the supplemental data.
3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for ( int i = 0 ; i < territoryContainment.getSize(); i++ ) {
3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            UResourceBundle mapping = territoryContainment.get(i);
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String parent = mapping.getKey();
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (parent.equals("containedGroupings") || parent.equals("deprecated")) {
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                continue; // handle new pseudo-parent types added in ICU data per cldrbug 7808; for now just skip.
3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // #11232 is to do something useful with these.
3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Region parentRegion = regionIDMap.get(parent);
3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for ( int j = 0 ; j < mapping.getSize(); j++ ) {
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String child = mapping.getString(j);
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Region childRegion = regionIDMap.get(child);
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ( parentRegion != null && childRegion != null ) {
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Add the child region to the set of regions contained by the parent
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    parentRegion.containedRegions.add(childRegion);
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Set the parent region to be the containing region of the child.
3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // Regions of type GROUPING can't be set as the parent, since another region
3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    // such as a SUBCONTINENT, CONTINENT, or WORLD must always be the parent.
3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if ( parentRegion.getType() != RegionType.GROUPING) {
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        childRegion.containingRegion = parentRegion;
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // Create the availableRegions lists
3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (int i = 0 ; i < RegionType.values().length ; i++) {
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            availableRegions.add(new TreeSet<Region>());
3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for ( Region ar : regions ) {
3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Set<Region> currentSet = availableRegions.get(ar.type.ordinal());
3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            currentSet.add(ar);
3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            availableRegions.set(ar.type.ordinal(),currentSet);
3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        regionDataIsLoaded = true;
3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /** Returns a Region using the given region ID.  The region ID can be either a 2-letter ISO code,
3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * 3-letter ISO code,  UNM.49 numeric code, or other valid Unicode Region Code as defined by the CLDR.
3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param id The id of the region to be retrieved.
3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The corresponding region.
3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws NullPointerException if the supplied id is null.
3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws IllegalArgumentException if the supplied ID cannot be canonicalized to a Region ID that is known by ICU.
3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static Region getInstance(String id) {
3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( id == null ) {
3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new NullPointerException();
3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        loadRegionData();
3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Region r = regionIDMap.get(id);
3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( r == null ) {
3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r = regionAliases.get(id);
3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( r == null ) {
3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("Unknown region id: " + id);
3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( r.type == RegionType.DEPRECATED && r.preferredValues.size() == 1) {
3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r = r.preferredValues.get(0);
3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return r;
3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /** Returns a Region using the given numeric code as defined by UNM.49
3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param code The numeric code of the region to be retrieved.
3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The corresponding region.
3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws IllegalArgumentException if the supplied numeric code is not recognized.
4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static Region getInstance(int code) {
4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        loadRegionData();
4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Region r = numericCodeMap.get(code);
4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( r == null ) { // Just in case there's an alias that's numeric, try to find it.
4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String pad = "";
4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( code < 10 ) {
4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                pad = "00";
4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else if ( code < 100 ) {
4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                pad = "0";
4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String id = pad + Integer.toString(code);
4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r = regionAliases.get(id);
4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( r == null ) {
4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("Unknown region code: " + code);
4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( r.type == RegionType.DEPRECATED && r.preferredValues.size() == 1) {
4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            r = r.preferredValues.get(0);
4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return r;
4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /** Used to retrieve all available regions of a specific type.
4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param type The type of regions to be returned ( TERRITORY, MACROREGION, etc. )
4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return An unmodifiable set of all known regions that match the given type.
4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static Set<Region> getAvailable(RegionType type) {
4407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        loadRegionData();
4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return Collections.unmodifiableSet(availableRegions.get(type.ordinal()));
4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /** Used to determine the macroregion that geographically contains this region.
4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The region that geographically contains this region.  Returns NULL if this region is
4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  code "001" (World) or "ZZ" (Unknown region).  For example, calling this method with region "IT" (Italy)
4507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  returns the region "039" (Southern Europe).
4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
4527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Region getContainingRegion() {
4557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        loadRegionData();
4567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return containingRegion;
4577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /** Used to determine the macroregion that geographically contains this region and that matches the given type.
4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The region that geographically contains this region and matches the given type.  May return NULL if
4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  no containing region can be found that matches the given type.  For example, calling this method with region "IT" (Italy)
4637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  and type CONTINENT returns the region "150" (Europe).
4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
4657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Region getContainingRegion(RegionType type) {
4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        loadRegionData();
4697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( containingRegion == null ) {
4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
4717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( containingRegion.type.equals(type)) {
4737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return containingRegion;
4747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
4757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return containingRegion.getContainingRegion(type);
4767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /** Used to determine the sub-regions that are contained within this region.
4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return An unmodifiable set containing all the regions that are immediate children
4827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * of this region in the region hierarchy.  These returned regions could be either macro
4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * regions, territories, or a mixture of the two, depending on the containment data as defined
4847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * in CLDR.  This API may return an empty set if this region doesn't have any sub-regions.
4857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For example, calling this method with region "150" (Europe) returns a set containing
4867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the various sub regions of Europe - "039" (Southern Europe) - "151" (Eastern Europe)
4877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * - "154" (Northern Europe) and "155" (Western Europe).
4887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
4907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Set<Region> getContainedRegions() {
4937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        loadRegionData();
4947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return Collections.unmodifiableSet(containedRegions);
4957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /** Used to determine all the regions that are contained within this region and that match the given type
4987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
4997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return An unmodifiable set containing all the regions that are children of this region
5007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * anywhere in the region hierarchy and match the given type.  This API may return an empty set
5017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * if this region doesn't have any sub-regions that match the given type.
5027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For example, calling this method with region "150" (Europe) and type "TERRITORY" returns a set
5037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *  containing all the territories in Europe ( "FR" (France) - "IT" (Italy) - "DE" (Germany) etc. )
5047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
5057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Set<Region> getContainedRegions(RegionType type) {
5087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        loadRegionData();
5107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Set<Region> result = new TreeSet<Region>();
5127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Set<Region> cr = getContainedRegions();
5137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for ( Region r : cr ) {
5157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( r.getType() == type ) {
5167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.add(r);
5177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
5187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.addAll(r.getContainedRegions(type));
5197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return Collections.unmodifiableSet(result);
5227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return For deprecated regions, return an unmodifiable list of the regions that are the preferred replacement regions for this region.
5267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns null for a non-deprecated region.  For example, calling this method with region "SU" (Soviet Union) would
5277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * return a list of the regions containing "RU" (Russia), "AM" (Armenia), "AZ" (Azerbaijan), etc...
5287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
5307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public List<Region> getPreferredValues() {
5327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        loadRegionData();
5347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( type == RegionType.DEPRECATED) {
5367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return Collections.unmodifiableList(preferredValues);
5377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
5387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
5397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return Returns true if this region contains the supplied other region anywhere in the region hierarchy.
5447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
5467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean contains(Region other) {
5487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        loadRegionData();
5507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (containedRegions.contains(other)) {
5527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return true;
5537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
5547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Region cr : containedRegions) {
5557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (cr.contains(other)) {
5567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    return true;
5577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
5587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return false;
5627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /** Returns the string representation of this region
5657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The string representation of this region, which is its ID.
5677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
5697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String toString() {
5727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return id;
5737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Returns the numeric code for this region
5777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return The numeric code for this region. Returns a negative value if the given region does not have a numeric
5797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *         code assigned to it. This is a very rare case and only occurs for a few very small territories.
5807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
5827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int getNumericCode() {
5857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return code;
5867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /** Returns this region's type.
5897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return This region's type classification, such as MACROREGION or TERRITORY.
5917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
5937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public RegionType getType() {
5967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return type;
5977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
6007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * {@inheritDoc}
6017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 50
6027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int compareTo(Region other) {
6047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return id.compareTo(other.id);
6057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
607