Commits

Anonymous committed 614090d

Code refactoring. Patch submitted by tronikos.

Comments (0)

Files changed (9)

java/release_notes.txt

+August 17th, 2010
+* Code improvement:
+  - Refactored BuildMetadataProtoFromXml.java into BuildMetadataFromXml.java and
+    BuildMetadataProtoFromXml.java
+  - Added BuildMetadataProtoFromXml.java and JSArrayBuilder.java
+  - Refactored CountryCodeToRegionCodeMap and CountryCodeToRegionCodeMapForTesting into separate
+    files.
+
 August 16th, 2010
 * Bug fixes
   - AsYouTypeFormatter for formatting Chinese geographical numbers entered in national format.

java/resources/com/google/i18n/phonenumbers/BuildMetadataFromXml.java

+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.i18n.phonenumbers;
+
+import com.google.i18n.phonenumbers.Phonemetadata.NumberFormat;
+import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata;
+import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection;
+import com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+/**
+ * Library to build phone number metadata from the XML format.
+ *
+ * @author Shaopeng Jia
+ */
+public class BuildMetadataFromXml {
+  private static final Logger LOGGER = Logger.getLogger(BuildMetadataFromXml.class.getName());
+  private static Boolean liteBuild;
+
+  // Build the PhoneMetadataCollection from the input XML file.
+  public static PhoneMetadataCollection buildPhoneMetadataCollection(String inputXmlFile,
+      boolean liteBuild) throws Exception {
+    BuildMetadataFromXml.liteBuild = liteBuild;
+    DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+    DocumentBuilder builder = builderFactory.newDocumentBuilder();
+    File xmlFile = new File(inputXmlFile);
+    Document document = builder.parse(xmlFile);
+    document.getDocumentElement().normalize();
+    Element rootElement = document.getDocumentElement();
+    NodeList territory = rootElement.getElementsByTagName("territory");
+    PhoneMetadataCollection metadataCollection = new PhoneMetadataCollection();
+    int numOfTerritories = territory.getLength();
+    for (int i = 0; i < numOfTerritories; i++) {
+      Element territoryElement = (Element) territory.item(i);
+      String regionCode = territoryElement.getAttribute("id");
+      PhoneMetadata metadata = loadCountryMetadata(regionCode, territoryElement);
+      metadataCollection.addMetadata(metadata);
+    }
+    return metadataCollection;
+  }
+
+  // Build a mapping from a country calling code to the region codes which denote the country/region
+  // represented by that country code. In the case of multiple countries sharing a calling code,
+  // such as the NANPA countries, the one indicated with "isMainCountryForCode" in the metadata
+  // should be first.
+  public static Map<Integer, List<String>> buildCountryCodeToRegionCodeMap(
+      PhoneMetadataCollection metadataCollection) {
+    Map<Integer, List<String>> countryCodeToRegionCodeMap =
+        new TreeMap<Integer, List<String>>();
+    for (PhoneMetadata metadata : metadataCollection.getMetadataList()) {
+      String regionCode = metadata.getId();
+      int countryCode = metadata.getCountryCode();
+      if (countryCodeToRegionCodeMap.containsKey(countryCode)) {
+        if (metadata.getMainCountryForCode()) {
+          countryCodeToRegionCodeMap.get(countryCode).add(0, regionCode);
+        } else {
+          countryCodeToRegionCodeMap.get(countryCode).add(regionCode);
+        }
+      } else {
+        // For most countries, there will be only one region code for the country calling code.
+        List<String> listWithRegionCode = new ArrayList<String>(1);
+        listWithRegionCode.add(regionCode);
+        countryCodeToRegionCodeMap.put(countryCode, listWithRegionCode);
+      }
+    }
+    return countryCodeToRegionCodeMap;
+  }
+
+  private static String validateRE(String regex) {
+    Pattern.compile(regex);
+    // return regex itself if it is of correct regex syntax
+    // i.e. compile did not fail with a PatternSyntaxException.
+    return regex;
+  }
+
+  private static PhoneMetadata loadCountryMetadata(String regionCode, Element element) {
+    PhoneMetadata metadata = new PhoneMetadata();
+    metadata.setId(regionCode);
+    metadata.setCountryCode(Integer.parseInt(element.getAttribute("countryCode")));
+    if (element.hasAttribute("leadingDigits")) {
+      metadata.setLeadingDigits(validateRE(element.getAttribute("leadingDigits")));
+    }
+    metadata.setInternationalPrefix(validateRE(element.getAttribute("internationalPrefix")));
+    if (element.hasAttribute("preferredInternationalPrefix")) {
+      String preferredInternationalPrefix = element.getAttribute("preferredInternationalPrefix");
+      metadata.setPreferredInternationalPrefix(preferredInternationalPrefix);
+    }
+    String nationalPrefix = "";
+    String nationalPrefixFormattingRule = "";
+    String carrierCodeFormattingRule = "";
+    if (element.hasAttribute("nationalPrefix")) {
+      nationalPrefix = element.getAttribute("nationalPrefix");
+      metadata.setNationalPrefix(nationalPrefix);
+      nationalPrefixFormattingRule =
+          validateRE(getNationalPrefixFormattingRuleFromElement(element, nationalPrefix));
+
+      if (element.hasAttribute("nationalPrefixForParsing")) {
+        metadata.setNationalPrefixForParsing(
+            validateRE(element.getAttribute("nationalPrefixForParsing")));
+        if (element.hasAttribute("nationalPrefixTransformRule")) {
+          metadata.setNationalPrefixTransformRule(
+              validateRE(element.getAttribute("nationalPrefixTransformRule")));
+        }
+      } else {
+        metadata.setNationalPrefixForParsing(nationalPrefix);
+      }
+    }
+    if (element.hasAttribute("preferredExtnPrefix")) {
+      metadata.setPreferredExtnPrefix(element.getAttribute("preferredExtnPrefix"));
+    }
+    if (element.hasAttribute("mainCountryForCode")) {
+      metadata.setMainCountryForCode(true);
+    }
+
+    // Extract availableFormats
+    NodeList numberFormatElements = element.getElementsByTagName("numberFormat");
+    int numOfFormatElements = numberFormatElements.getLength();
+    if (numOfFormatElements > 0) {
+      for (int i = 0; i < numOfFormatElements; i++) {
+        Element numberFormatElement = (Element) numberFormatElements.item(i);
+        NumberFormat format = new NumberFormat();
+        if (numberFormatElement.hasAttribute("nationalPrefixFormattingRule")) {
+          format.setNationalPrefixFormattingRule(validateRE(
+              getNationalPrefixFormattingRuleFromElement(numberFormatElement, nationalPrefix)));
+        } else {
+          format.setNationalPrefixFormattingRule(nationalPrefixFormattingRule);
+        }
+        if (numberFormatElement.hasAttribute("carrierCodeFormattingRule")) {
+          format.setDomesticCarrierCodeFormattingRule(validateRE(
+              getDomesticCarrierCodeFormattingRuleFromElement(numberFormatElement,
+                                                              nationalPrefix)));
+        } else {
+          format.setDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule);
+        }
+        setLeadingDigitsPatterns(numberFormatElement, format);
+        format.setPattern(validateRE(numberFormatElement.getAttribute("pattern")));
+        NodeList formatPattern = numberFormatElement.getElementsByTagName("format");
+        if (formatPattern.getLength() != 1) {
+          LOGGER.log(Level.SEVERE,
+                     "Only one format pattern for a numberFormat element should be defined.");
+          throw new RuntimeException("Invalid number of format patterns for country: " +
+                                     regionCode);
+        }
+        format.setFormat(validateRE(formatPattern.item(0).getFirstChild().getNodeValue()));
+        metadata.addNumberFormat(format);
+      }
+    }
+
+    NodeList intlNumberFormatElements = element.getElementsByTagName("intlNumberFormat");
+    int numOfIntlFormatElements = intlNumberFormatElements.getLength();
+    if (numOfIntlFormatElements > 0) {
+      for (int i = 0; i < numOfIntlFormatElements; i++) {
+        Element numberFormatElement = (Element) intlNumberFormatElements.item(i);
+        NumberFormat format = new NumberFormat();
+        setLeadingDigitsPatterns(numberFormatElement, format);
+        format.setPattern(validateRE(numberFormatElement.getAttribute("pattern")));
+        NodeList formatPattern = numberFormatElement.getElementsByTagName("format");
+        if (formatPattern.getLength() != 1) {
+          LOGGER.log(Level.SEVERE,
+                     "Only one format pattern for a numberFormat element should be defined.");
+          throw new RuntimeException("Invalid number of format patterns for country: " +
+                                     regionCode);
+        }
+        format.setFormat(validateRE(formatPattern.item(0).getFirstChild().getNodeValue()));
+        if (numberFormatElement.hasAttribute("carrierCodeFormattingRule")) {
+          format.setDomesticCarrierCodeFormattingRule(validateRE(
+              getDomesticCarrierCodeFormattingRuleFromElement(numberFormatElement,
+                                                              nationalPrefix)));
+        } else {
+          format.setDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule);
+        }
+        metadata.addIntlNumberFormat(format);
+      }
+    }
+
+    PhoneNumberDesc generalDesc = new PhoneNumberDesc();
+    generalDesc = processPhoneNumberDescElement(generalDesc, element, "generalDesc");
+    metadata.setGeneralDesc(generalDesc);
+    metadata.setFixedLine(processPhoneNumberDescElement(generalDesc, element, "fixedLine"));
+    metadata.setMobile(processPhoneNumberDescElement(generalDesc, element, "mobile"));
+    metadata.setTollFree(processPhoneNumberDescElement(generalDesc, element, "tollFree"));
+    metadata.setPremiumRate(processPhoneNumberDescElement(generalDesc, element, "premiumRate"));
+    metadata.setSharedCost(processPhoneNumberDescElement(generalDesc, element, "sharedCost"));
+    metadata.setVoip(processPhoneNumberDescElement(generalDesc, element, "voip"));
+    metadata.setPersonalNumber(processPhoneNumberDescElement(generalDesc, element,
+                                                             "personalNumber"));
+
+    if (metadata.getMobile().getNationalNumberPattern().equals(
+        metadata.getFixedLine().getNationalNumberPattern())) {
+      metadata.setSameMobileAndFixedLinePattern(true);
+    }
+    return metadata;
+  }
+
+  private static void setLeadingDigitsPatterns(Element numberFormatElement, NumberFormat format) {
+    NodeList leadingDigitsPatternNodes = numberFormatElement.getElementsByTagName("leadingDigits");
+    int numOfLeadingDigitsPatterns = leadingDigitsPatternNodes.getLength();
+    if (numOfLeadingDigitsPatterns > 0) {
+      for (int i = 0; i < numOfLeadingDigitsPatterns; i++) {
+        format.addLeadingDigitsPattern(
+            validateRE((leadingDigitsPatternNodes.item(i)).getFirstChild().getNodeValue()));
+      }
+    }
+  }
+
+  private static String getNationalPrefixFormattingRuleFromElement(Element element,
+                                                                   String nationalPrefix) {
+    String nationalPrefixFormattingRule = element.getAttribute("nationalPrefixFormattingRule");
+    // Replace $NP with national prefix and $FG with the first group ($1).
+    nationalPrefixFormattingRule =
+        nationalPrefixFormattingRule.replaceFirst("\\$NP", nationalPrefix)
+            .replaceFirst("\\$FG", "\\$1");
+    return nationalPrefixFormattingRule;
+  }
+
+  private static String getDomesticCarrierCodeFormattingRuleFromElement(Element element,
+                                                                        String nationalPrefix) {
+    String carrierCodeFormattingRule = element.getAttribute("carrierCodeFormattingRule");
+    // Replace $FG with the first group ($1) and $NP with the national prefix.
+    carrierCodeFormattingRule = carrierCodeFormattingRule.replaceFirst("\\$FG", "\\$1")
+        .replaceFirst("\\$NP", nationalPrefix);
+    return carrierCodeFormattingRule;
+  }
+
+  /**
+   * Processes a phone number description element from the XML file and returns it as a
+   * PhoneNumberDesc. If the description element is a fixed line or mobile number, the general
+   * description will be used to fill in the whole element if necessary, or any components that are
+   * missing. For all other types, the general description will only be used to fill in missing
+   * components if the type has a partial definition. For example, if no "tollFree" element exists,
+   * we assume there are no toll free numbers for that locale, and return a phone number description
+   * with "NA" for both the national and possible number patterns.
+   *
+   * @param generalDesc  a generic phone number description that will be used to fill in missing
+   *                     parts of the description
+   * @param countryElement  the XML element representing all the country information
+   * @param numberType  the name of the number type, corresponding to the appropriate tag in the XML
+   *                    file with information about that type
+   * @return  complete description of that phone number type
+   */
+  private static PhoneNumberDesc processPhoneNumberDescElement(PhoneNumberDesc generalDesc,
+                                                               Element countryElement,
+                                                               String numberType) {
+    NodeList phoneNumberDescList = countryElement.getElementsByTagName(numberType);
+    PhoneNumberDesc numberDesc = new PhoneNumberDesc();
+    if (phoneNumberDescList.getLength() == 0 &&
+        (!numberType.equals("fixedLine") && !numberType.equals("mobile") &&
+         !numberType.equals("generalDesc"))) {
+      numberDesc.setNationalNumberPattern("NA");
+      numberDesc.setPossibleNumberPattern("NA");
+      return numberDesc;
+    }
+    numberDesc.mergeFrom(generalDesc);
+    if (phoneNumberDescList.getLength() > 0) {
+      Element element = (Element) phoneNumberDescList.item(0);
+      NodeList possiblePattern = element.getElementsByTagName("possibleNumberPattern");
+      if (possiblePattern.getLength() > 0) {
+        numberDesc.setPossibleNumberPattern(
+            validateRE(possiblePattern.item(0).getFirstChild().getNodeValue()));
+      }
+
+      NodeList validPattern = element.getElementsByTagName("nationalNumberPattern");
+      if (validPattern.getLength() > 0) {
+        numberDesc.setNationalNumberPattern(
+            validateRE(validPattern.item(0).getFirstChild().getNodeValue()));
+      }
+
+      if (!liteBuild) {
+        NodeList exampleNumber = element.getElementsByTagName("exampleNumber");
+        if (exampleNumber.getLength() > 0) {
+          numberDesc.setExampleNumber(exampleNumber.item(0).getFirstChild().getNodeValue());
+        }
+      }
+    }
+    return numberDesc;
+  }
+}

java/resources/com/google/i18n/phonenumbers/BuildMetadataJsonFromXml.java

+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.i18n.phonenumbers;
+
+import com.google.i18n.phonenumbers.Phonemetadata.NumberFormat;
+import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata;
+import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection;
+import com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Formatter;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Tool to convert phone number metadata from the XML format to JSON format.
+ *
+ * @author Nikolaos Trogkanis
+ */
+public class BuildMetadataJsonFromXml {
+  private static final String NAMESPACE = "i18n.phonenumbers.metadata";
+
+  private static final String HELP_MESSAGE =
+      "Usage:\n" +
+      "BuildMetadataJsonFromXml <inputFile> <outputFile> [<liteBuild>]\n" +
+      "\n" +
+      "where:\n" +
+      "  inputFile    The input file containing phone number metadata in XML format.\n" +
+      "  outputFile   The output file to contain phone number metadata in JSON format.\n" +
+      "  liteBuild    Whether to generate the lite-version of the metadata (default:\n" +
+      "               false). When set to true certain metadata will be omitted.\n" +
+      "               At this moment, example numbers information is omitted.\n" +
+      "\n" +
+      "Example command line invocation:\n" +
+      "BuildMetadataJsonFromXml PhoneNumberMetadata.xml metadatalite.js true\n";
+
+  static final String COPYRIGHT_NOTICE =
+      "/*\n" +
+      " * @license\n" +
+      " * Copyright (C) 2010 Google Inc.\n" +
+      " *\n" +
+      " * Licensed under the Apache License, Version 2.0 (the \"License\");\n" +
+      " * you may not use this file except in compliance with the License.\n" +
+      " * You may obtain a copy of the License at\n" +
+      " *\n" +
+      " * http://www.apache.org/licenses/LICENSE-2.0\n" +
+      " *\n" +
+      " * Unless required by applicable law or agreed to in writing, software\n" +
+      " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" +
+      " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
+      " * See the License for the specific language governing permissions and\n" +
+      " * limitations under the License.\n" +
+      " */\n\n";
+
+  private static final String FILE_OVERVIEW =
+      "/**\n" +
+      " * @fileoverview Generated metadata for file\n" +
+      " * %s\n" +
+      " * @author Nikolaos Trogkanis\n" +
+      " */\n\n";
+
+  private static final String COUNTRY_CODE_TO_REGION_CODE_MAP_COMMENT =
+      "/**\n" +
+      " * A mapping from a country code to the region codes which denote the\n" +
+      " * country/region represented by that country code. In the case of multiple\n" +
+      " * countries sharing a calling code, such as the NANPA countries, the one\n" +
+      " * indicated with \"isMainCountryForCode\" in the metadata should be first.\n" +
+      " * @type {Object.<number, Array.<string>>}\n" +
+      " */\n";
+
+  private static final String COUNTRY_TO_METADATA_COMMENT =
+      "/**\n" +
+      " * A mapping from a region code to the PhoneMetadata for that region.\n" +
+      " * @type {Object.<string, Array>}\n" +
+      " */\n";
+
+  public static void main(String[] args) throws Exception {
+    if (args.length != 2 && args.length != 3) {
+      System.err.println(HELP_MESSAGE);
+      System.exit(1);
+    }
+    String inputFile = args[0];
+    String outputFile = args[1];
+    boolean liteBuild = args.length > 2 && args[2].equals("true");
+
+    PhoneMetadataCollection metadataCollection =
+        BuildMetadataFromXml.buildPhoneMetadataCollection(inputFile, liteBuild);
+    Map<Integer, List<String>> countryCodeToRegionCodeMap =
+        BuildMetadataFromXml.buildCountryCodeToRegionCodeMap(metadataCollection);
+
+    BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile));
+
+    writer.write(COPYRIGHT_NOTICE);
+    Formatter formatter = new Formatter(writer);
+    formatter.format(FILE_OVERVIEW, inputFile);
+
+    writer.write("goog.provide('" + NAMESPACE + "');\n\n");
+
+    writer.write(COUNTRY_CODE_TO_REGION_CODE_MAP_COMMENT);
+    writer.write(NAMESPACE + ".countryCodeToRegionCodeMap = ");
+    writeCountryCodeCodeToRegionCodeMap(countryCodeToRegionCodeMap, writer);
+    writer.write(";\n\n");
+
+    writer.write(COUNTRY_TO_METADATA_COMMENT);
+    writer.write(NAMESPACE + ".countryToMetadata = ");
+    writeCountryToMetadataMap(metadataCollection, writer);
+    writer.write(";\n");
+
+    writer.flush();
+    writer.close();
+  }
+
+  // Writes a PhoneMetadataCollection in JSON format.
+  private static void writeCountryToMetadataMap(PhoneMetadataCollection metadataCollection,
+                                                BufferedWriter writer) throws IOException {
+    writer.write("{\n");
+    boolean isFirstTimeInLoop = true;
+    for (PhoneMetadata metadata : metadataCollection.getMetadataList()) {
+      if (isFirstTimeInLoop) {
+        isFirstTimeInLoop = false;
+      } else {
+        writer.write(",");
+      }
+      String regionCode = metadata.getId();
+      JSArrayBuilder jsArrayBuilder = new JSArrayBuilder();
+      toJsArray(metadata, jsArrayBuilder);
+      writer.write("\"");
+      writer.write(regionCode);
+      writer.write("\":");
+      writer.write(jsArrayBuilder.toString());
+    }
+    writer.write("}");
+  }
+
+  // Writes a Map<Integer, List<String>> in JSON format.
+  private static void writeCountryCodeCodeToRegionCodeMap(
+      Map<Integer, List<String>> countryCodeToRegionCodeMap,
+      BufferedWriter writer) throws IOException {
+    writer.write("{\n");
+    boolean isFirstTimeInLoop = true;
+    for (Map.Entry<Integer, List<String>> entry : countryCodeToRegionCodeMap.entrySet()) {
+      if (isFirstTimeInLoop) {
+        isFirstTimeInLoop = false;
+      } else {
+        writer.write(",");
+      }
+      writer.write(Integer.toString(entry.getKey()));
+      writer.write(":");
+      JSArrayBuilder jsArrayBuilder = new JSArrayBuilder();
+      jsArrayBuilder.beginArray();
+      jsArrayBuilder.appendIterator(entry.getValue().iterator());
+      jsArrayBuilder.endArray();
+      writer.write(jsArrayBuilder.toString());
+    }
+    writer.write("}");
+  }
+
+  // Converts NumberFormat to JSArray.
+  private static void toJsArray(NumberFormat format, JSArrayBuilder jsArrayBuilder) {
+    jsArrayBuilder.beginArray();
+
+    // missing 0
+    jsArrayBuilder.append(null);
+    // required string pattern = 1;
+    jsArrayBuilder.append(format.getPattern());
+    // required string format = 2;
+    jsArrayBuilder.append(format.getFormat());
+    // repeated string leading_digits_pattern = 3;
+    int leadingDigitsPatternSize = format.getLeadingDigitsPatternCount();
+    if (leadingDigitsPatternSize > 0) {
+      jsArrayBuilder.beginArray();
+      for (int i = 0; i < leadingDigitsPatternSize; i++) {
+        jsArrayBuilder.append(format.getLeadingDigitsPattern(i));
+      }
+      jsArrayBuilder.endArray();
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // optional string national_prefix_formatting_rule = 4;
+    if (format.hasNationalPrefixFormattingRule()) {
+      jsArrayBuilder.append(format.getNationalPrefixFormattingRule());
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // optional string domestic_carrier_code_formatting_rule = 5;
+    if (format.hasDomesticCarrierCodeFormattingRule()) {
+      jsArrayBuilder.append(format.getDomesticCarrierCodeFormattingRule());
+    } else {
+      jsArrayBuilder.append(null);
+    }
+
+    jsArrayBuilder.endArray();
+  }
+
+  // Converts PhoneNumberDesc to JSArray.
+  private static void toJsArray(PhoneNumberDesc desc, JSArrayBuilder jsArrayBuilder) {
+    jsArrayBuilder.beginArray();
+
+    // missing 0
+    jsArrayBuilder.append(null);
+    // missing 1
+    jsArrayBuilder.append(null);
+    // optional string national_number_pattern = 2;
+    if (desc.hasNationalNumberPattern()) {
+      jsArrayBuilder.append(desc.getNationalNumberPattern());
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // optional string possible_number_pattern = 3;
+    if (desc.hasPossibleNumberPattern()) {
+      jsArrayBuilder.append(desc.getPossibleNumberPattern());
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // missing 4
+    jsArrayBuilder.append(null);
+    // missing 5
+    jsArrayBuilder.append(null);
+    // optional string example_number = 6;
+    if (desc.hasExampleNumber()) {
+      jsArrayBuilder.append(desc.getExampleNumber());
+    } else {
+      jsArrayBuilder.append(null);
+    }
+
+    jsArrayBuilder.endArray();
+  }
+
+  // Converts PhoneMetadata to JSArray.
+  private static void toJsArray(PhoneMetadata metadata, JSArrayBuilder jsArrayBuilder) {
+    jsArrayBuilder.beginArray();
+
+    // missing 0
+    jsArrayBuilder.append(null);
+    // required PhoneNumberDesc general_desc = 1;
+    if (metadata.hasGeneralDesc()) {
+      toJsArray(metadata.getGeneralDesc(), jsArrayBuilder);
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // required PhoneNumberDesc fixed_line = 2;
+    if (metadata.hasFixedLine()) {
+      toJsArray(metadata.getFixedLine(), jsArrayBuilder);
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // required PhoneNumberDesc mobile = 3;
+    if (metadata.hasMobile()) {
+      toJsArray(metadata.getMobile(), jsArrayBuilder);
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // required PhoneNumberDesc toll_free = 4;
+    if (metadata.hasTollFree()) {
+      toJsArray(metadata.getTollFree(), jsArrayBuilder);
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // required PhoneNumberDesc premium_rate = 5;
+    if (metadata.hasPremiumRate()) {
+      toJsArray(metadata.getPremiumRate(), jsArrayBuilder);
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // required PhoneNumberDesc shared_cost = 6;
+    if (metadata.hasSharedCost()) {
+      toJsArray(metadata.getSharedCost(), jsArrayBuilder);
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // required PhoneNumberDesc personal_number = 7;
+    if (metadata.hasPersonalNumber()) {
+      toJsArray(metadata.getPersonalNumber(), jsArrayBuilder);
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // required PhoneNumberDesc voip = 8;
+    if (metadata.hasVoip()) {
+      toJsArray(metadata.getVoip(), jsArrayBuilder);
+    } else {
+      jsArrayBuilder.append(null);
+    }
+
+    // required string id = 9;
+    jsArrayBuilder.append(metadata.getId());
+    // required int32 country_code = 10;
+    jsArrayBuilder.append(metadata.getCountryCode());
+    // required string international_prefix = 11;
+    jsArrayBuilder.append(metadata.getInternationalPrefix());
+
+    // optional string national_prefix = 12;
+    if (metadata.hasNationalPrefix()) {
+      jsArrayBuilder.append(metadata.getNationalPrefix());
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // optional string preferred_extn_prefix = 13;
+    if (metadata.hasPreferredExtnPrefix()) {
+      jsArrayBuilder.append(metadata.getPreferredExtnPrefix());
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // missing 14
+    jsArrayBuilder.append(null);
+    // optional string national_prefix_for_parsing = 15;
+    if (metadata.hasNationalPrefixForParsing()) {
+      jsArrayBuilder.append(metadata.getNationalPrefixForParsing());
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // optional string national_prefix_transform_rule = 16;
+    if (metadata.hasNationalPrefixTransformRule()) {
+      jsArrayBuilder.append(metadata.getNationalPrefixTransformRule());
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // optional string preferred_international_prefix = 17;
+    if (metadata.hasPreferredInternationalPrefix()) {
+      jsArrayBuilder.append(metadata.getPreferredInternationalPrefix());
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // optional bool same_mobile_and_fixed_line_pattern = 18 [default=false];
+    if (metadata.getSameMobileAndFixedLinePattern()) {
+      jsArrayBuilder.append(1);
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // repeated NumberFormat number_format = 19;
+    int numberFormatSize = metadata.getNumberFormatCount();
+    if (numberFormatSize > 0) {
+      jsArrayBuilder.beginArray();
+      for (int i = 0; i < numberFormatSize; i++) {
+        toJsArray(metadata.getNumberFormat(i), jsArrayBuilder);
+      }
+      jsArrayBuilder.endArray();
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // repeated NumberFormat intl_number_format = 20;
+    int intlNumberFormatSize = metadata.getIntlNumberFormatCount();
+    if (intlNumberFormatSize > 0) {
+      jsArrayBuilder.beginArray();
+      for (int i = 0; i < intlNumberFormatSize; i++) {
+        toJsArray(metadata.getIntlNumberFormat(i), jsArrayBuilder);
+      }
+      jsArrayBuilder.endArray();
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // missing 21
+    jsArrayBuilder.append(null);
+    // optional bool main_country_for_code = 22 [default=false];
+    if (metadata.getMainCountryForCode()) {
+      jsArrayBuilder.append(1);
+    } else {
+      jsArrayBuilder.append(null);
+    }
+    // optional string leading_digits = 23;
+    if (metadata.hasLeadingDigits()) {
+      jsArrayBuilder.append(metadata.getLeadingDigits());
+    } else {
+      jsArrayBuilder.append(null);
+    }
+
+    jsArrayBuilder.endArray();
+  }
+}

java/resources/com/google/i18n/phonenumbers/BuildMetadataProtoFromXml.java

 
 package com.google.i18n.phonenumbers;
 
-import com.google.i18n.phonenumbers.Phonemetadata.NumberFormat;
 import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata;
 import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection;
-import com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
 
-import java.io.File;
+import java.io.BufferedWriter;
 import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
 import java.io.ObjectOutputStream;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Pattern;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
+import java.util.Formatter;
+import java.util.List;
+import java.util.Map;
 
 /**
- * Tool to convert phone number metadata from the XML format to protocol buffer format. It is
- * wrapped in the genrule of the BUILD file and run as a preprocessing step when building the
- * phone number library. Example command line invocation:
- *
- * ./BuildMetadataProtoFromXml PhoneNumberMetadata.xml PhoneNumberMetadataProto true
- *
- * When liteBuild flag is set to true, the outputFile generated omits certain metadata which is not
- * needed for clients using liteBuild. At this moment, example numbers information is omitted.
+ * Tool to convert phone number metadata from the XML format to protocol buffer format.
  *
  * @author Shaopeng Jia
  */
 public class BuildMetadataProtoFromXml {
-  private BuildMetadataProtoFromXml() {
-  }
-  private static final Logger LOGGER = Logger.getLogger(BuildMetadataProtoFromXml.class.getName());
-  private static Boolean liteBuild;
+  private static final String PACKAGE_NAME = PhoneNumberUtil.class.getPackage().getName();
+
+  private static final String HELP_MESSAGE =
+      "Usage:\n" +
+      "BuildMetadataProtoFromXml <inputFile> <outputDir> <forTesting> [<liteBuild>]\n" +
+      "\n" +
+      "where:\n" +
+      "  inputFile    The input file containing phone number metadata in XML format.\n" +
+      "  outputDir    The output directory to store phone number metadata in proto\n" +
+      "               format (one file per region) and the country code to region code\n" +
+      "               mapping file.\n" +
+      "  forTesting   Flag whether to generate metadata for testing purposes or not.\n" +
+      "  liteBuild    Whether to generate the lite-version of the metadata (default:\n" +
+      "               false). When set to true certain metadata will be omitted.\n" +
+      "               At this moment, example numbers information is omitted.\n" +
+      "\n" +
+      "Metadata will be stored in:\n" +
+      "  <outputDir>" + PhoneNumberUtil.META_DATA_FILE_PREFIX + "_*\n" +
+      "Mapping file will be stored in:\n" +
+      "  <outputDir>/" + PACKAGE_NAME.replaceAll("\\.", "/") + "/" +
+          PhoneNumberUtil.COUNTRY_CODE_TO_REGION_CODE_MAP_CLASS_NAME + ".java\n" +
+      "\n" +
+      "Example command line invocation:\n" +
+      "BuildMetadataProtoFromXml PhoneNumberMetadata.xml src false false\n";
 
   public static void main(String[] args) throws Exception {
+    if (args.length != 3 && args.length != 4) {
+      System.err.println(HELP_MESSAGE);
+      System.exit(1);
+    }
     String inputFile = args[0];
-    String filePrefix = args[1];
-    liteBuild = args.length > 2 && Boolean.getBoolean(args[2]);
-    File xmlFile = new File(inputFile);
+    String outputDir = args[1];
+    boolean forTesting = args[2].equals("true");
+    boolean liteBuild = args.length > 3 && args[3].equals("true");
+    
+    String filePrefix;
+    if (forTesting) {
+      filePrefix = outputDir + PhoneNumberUtilTest.TEST_META_DATA_FILE_PREFIX;
+    } else {
+      filePrefix = outputDir + PhoneNumberUtil.META_DATA_FILE_PREFIX;
+    }
 
-    DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
-    DocumentBuilder builder = builderFactory.newDocumentBuilder();
-    Document document = builder.parse(xmlFile);
-    document.getDocumentElement().normalize();
-    Element rootElement = document.getDocumentElement();
-    NodeList territory = rootElement.getElementsByTagName("territory");
-    PhoneMetadataCollection metadataCollection = new PhoneMetadataCollection();
-    int numOfTerritories = territory.getLength();
-    for (int i = 0; i < numOfTerritories; i++) {
-      Element territoryElement = (Element) territory.item(i);
-      String regionCode = territoryElement.getAttribute("id");
-      PhoneMetadata metadata = loadCountryMetadata(regionCode, territoryElement);
-      metadataCollection.addMetadata(metadata);
+    PhoneMetadataCollection metadataCollection =
+        BuildMetadataFromXml.buildPhoneMetadataCollection(inputFile, liteBuild);
+
+    for (PhoneMetadata metadata : metadataCollection.getMetadataList()) {
+      String regionCode = metadata.getId();
+      PhoneMetadataCollection outMetadataCollection = new PhoneMetadataCollection();
+      outMetadataCollection.addMetadata(metadata);
       FileOutputStream outputForRegion = new FileOutputStream(filePrefix + "_" + regionCode);
       ObjectOutputStream out = new ObjectOutputStream(outputForRegion);
-      metadataCollection.writeExternal(out);
+      outMetadataCollection.writeExternal(out);
       out.close();
-      metadataCollection.clear();
     }
+
+    Map<Integer, List<String>> countryCodeToRegionCodeMap =
+        BuildMetadataFromXml.buildCountryCodeToRegionCodeMap(metadataCollection);
+
+    writeCountryCallingCodeMappingToJavaFile(countryCodeToRegionCodeMap, outputDir, forTesting);
   }
 
-  private static String validateRE(String regex) {
-    Pattern.compile(regex);
-    // return regex itself if it is of correct regex syntax
-    return regex;
-  }
+  static final String COPYRIGHT_NOTICE =
+      "/*\n" +
+      " * Copyright (C) 2010 Google Inc.\n" +
+      " *\n" +
+      " * Licensed under the Apache License, Version 2.0 (the \"License\");\n" +
+      " * you may not use this file except in compliance with the License.\n" +
+      " * You may obtain a copy of the License at\n" +
+      " *\n" +
+      " * http://www.apache.org/licenses/LICENSE-2.0\n" +
+      " *\n" +
+      " * Unless required by applicable law or agreed to in writing, software\n" +
+      " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" +
+      " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
+      " * See the License for the specific language governing permissions and\n" +
+      " * limitations under the License.\n" +
+      " */\n\n";
+  private static final String MAPPING_IMPORTS =
+      "import java.util.ArrayList;\n" +
+      "import java.util.HashMap;\n" +
+      "import java.util.List;\n" +
+      "import java.util.Map;\n";
+  private static final String MAPPING_COMMENT =
+      "  // A mapping from a country code to the region codes which denote the\n" +
+      "  // country/region represented by that country code. In the case of multiple\n" +
+      "  // countries sharing a calling code, such as the NANPA countries, the one\n" +
+      "  // indicated with \"isMainCountryForCode\" in the metadata should be first.\n";
+  private static final double MAPPING_LOAD_FACTOR = 0.75;
+  private static final String MAPPING_COMMENT_2 =
+      "    // The capacity is set to %d as there are %d different country codes,\n" +
+      "    // and this offers a load factor of roughly " + MAPPING_LOAD_FACTOR + ".\n";
 
-  private static PhoneMetadata loadCountryMetadata(String regionCode, Element element) {
-    PhoneMetadata metadata = new PhoneMetadata();
-    metadata.setId(regionCode);
-    metadata.setCountryCode(Integer.parseInt(element.getAttribute("countryCode")));
-    if (element.hasAttribute("leadingDigits")) {
-      metadata.setLeadingDigits(validateRE(element.getAttribute("leadingDigits")));
+  private static void writeCountryCallingCodeMappingToJavaFile(
+      Map<Integer, List<String>> countryCodeToRegionCodeMap,
+      String outputDir, boolean forTesting) throws IOException {
+    String mappingClassName;
+    if (forTesting) {
+      mappingClassName = PhoneNumberUtilTest.TEST_COUNTRY_CODE_TO_REGION_CODE_MAP_CLASS_NAME;
+    } else {
+      mappingClassName = PhoneNumberUtil.COUNTRY_CODE_TO_REGION_CODE_MAP_CLASS_NAME;
     }
-    metadata.setInternationalPrefix(validateRE(element.getAttribute("internationalPrefix")));
-    if (element.hasAttribute("preferredInternationalPrefix")) {
-      String preferredInternationalPrefix = element.getAttribute("preferredInternationalPrefix");
-      metadata.setPreferredInternationalPrefix(preferredInternationalPrefix);
+    String mappingFile =
+        outputDir + "/" + PACKAGE_NAME.replaceAll("\\.", "/") + "/" + mappingClassName + ".java";
+    int capacity = (int) (countryCodeToRegionCodeMap.size() / MAPPING_LOAD_FACTOR);
+
+    BufferedWriter writer = new BufferedWriter(new FileWriter(mappingFile));
+
+    writer.write(COPYRIGHT_NOTICE);
+    if (PACKAGE_NAME.length() > 0) {
+      writer.write("package " + PACKAGE_NAME + ";\n\n");
     }
-    String nationalPrefix = "";
-    String nationalPrefixFormattingRule = "";
-    String carrierCodeFormattingRule = "";
-    if (element.hasAttribute("nationalPrefix")) {
-      nationalPrefix = element.getAttribute("nationalPrefix");
-      metadata.setNationalPrefix(nationalPrefix);
-       nationalPrefixFormattingRule =
-          validateRE(getNationalPrefixFormattingRuleFromElement(element, nationalPrefix));
+    writer.write(MAPPING_IMPORTS);
+    writer.write("\n");
+    writer.write("public class " + mappingClassName + " {\n");
+    writer.write(MAPPING_COMMENT);
+    writer.write("  static Map<Integer, List<String>> getCountryCodeToRegionCodeMap() {\n");
+    Formatter formatter = new Formatter(writer);
+    formatter.format(MAPPING_COMMENT_2, capacity, countryCodeToRegionCodeMap.size());
+    writer.write("    Map<Integer, List<String>> countryCodeToRegionCodeMap =\n");
+    writer.write("        new HashMap<Integer, List<String>>(" + capacity + ");\n");
+    writer.write("\n");
+    writer.write("    ArrayList<String> listWithRegionCode;\n");
+    writer.write("\n");
 
-      if (element.hasAttribute("nationalPrefixForParsing")) {
-        metadata.setNationalPrefixForParsing(
-            validateRE(element.getAttribute("nationalPrefixForParsing")));
-        if (element.hasAttribute("nationalPrefixTransformRule")) {
-          metadata.setNationalPrefixTransformRule(
-              element.getAttribute("nationalPrefixTransformRule"));
-        }
-      } else {
-        metadata.setNationalPrefixForParsing(nationalPrefix);
+    for (Map.Entry<Integer, List<String>> entry : countryCodeToRegionCodeMap.entrySet()) {
+      int countryCallingCode = entry.getKey();
+      List<String> regionCodes = entry.getValue();
+      writer.write("    listWithRegionCode = new ArrayList<String>(" + regionCodes.size() + ");\n");
+      for (String regionCode : regionCodes) {
+        writer.write("    listWithRegionCode.add(\"" + regionCode + "\");\n");
       }
-    }
-    if (element.hasAttribute("preferredExtnPrefix")) {
-      metadata.setPreferredExtnPrefix(element.getAttribute("preferredExtnPrefix"));
+      writer.write("    countryCodeToRegionCodeMap.put(" + countryCallingCode +
+                   ", listWithRegionCode);\n");
+      writer.write("\n");
     }
 
-    if (element.hasAttribute("mainCountryForCode")) {
-      metadata.setMainCountryForCode(true);
-    }
+    writer.write("    return countryCodeToRegionCodeMap;\n");
+    writer.write("  }\n");
+    writer.write("}\n");
 
-    // Extract availableFormats
-    NodeList numberFormatElements = element.getElementsByTagName("numberFormat");
-    int numOfFormatElements = numberFormatElements.getLength();
-    if (numOfFormatElements > 0) {
-      for (int i = 0; i < numOfFormatElements; i++) {
-        Element numberFormatElement = (Element) numberFormatElements.item(i);
-        NumberFormat format = new NumberFormat();
-        if (numberFormatElement.hasAttribute("nationalPrefixFormattingRule")) {
-          format.setNationalPrefixFormattingRule(validateRE(
-              getNationalPrefixFormattingRuleFromElement(numberFormatElement, nationalPrefix)));
-        } else {
-          format.setNationalPrefixFormattingRule(nationalPrefixFormattingRule);
-        }
-        if (numberFormatElement.hasAttribute("carrierCodeFormattingRule")) {
-          format.setDomesticCarrierCodeFormattingRule(validateRE(
-              getDomesticCarrierCodeFormattingRuleFromElement(numberFormatElement,
-                                                              nationalPrefix)));
-        } else {
-          format.setDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule);
-        }
-        setLeadingDigitsPatterns(numberFormatElement, format);
-        format.setPattern(validateRE(numberFormatElement.getAttribute("pattern")));
-        NodeList formatPattern = numberFormatElement.getElementsByTagName("format");
-        if (formatPattern.getLength() != 1) {
-          LOGGER.log(Level.SEVERE,
-                     "Only one format pattern for a numberFormat element should be defined.");
-          throw new RuntimeException("Invalid number of format patterns for country: " +
-                                     regionCode);
-        }
-        format.setFormat(validateRE(formatPattern.item(0).getFirstChild().getNodeValue()));
-        metadata.addNumberFormat(format);
-      }
-    }
-
-    NodeList intlNumberFormatElements = element.getElementsByTagName("intlNumberFormat");
-    int numOfIntlFormatElements = intlNumberFormatElements.getLength();
-    if (numOfIntlFormatElements > 0) {
-      for (int i = 0; i < numOfIntlFormatElements; i++) {
-        Element numberFormatElement = (Element) intlNumberFormatElements.item(i);
-        NumberFormat format = new NumberFormat();
-      setLeadingDigitsPatterns(numberFormatElement, format);
-        format.setPattern(validateRE(numberFormatElement.getAttribute("pattern")));
-        NodeList formatPattern = numberFormatElement.getElementsByTagName("format");
-        if (formatPattern.getLength() != 1) {
-          LOGGER.log(Level.SEVERE,
-                     "Only one format pattern for a numberFormat element should be defined.");
-          throw new RuntimeException("Invalid number of format patterns for country: " +
-                                     regionCode);
-        }
-        format.setFormat(validateRE(formatPattern.item(0).getFirstChild().getNodeValue()));
-        if (numberFormatElement.hasAttribute("carrierCodeFormattingRule")) {
-          format.setDomesticCarrierCodeFormattingRule(validateRE(
-              getDomesticCarrierCodeFormattingRuleFromElement(numberFormatElement,
-                                                              nationalPrefix)));
-        } else {
-          format.setDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule);
-        }
-        metadata.addIntlNumberFormat(format);
-      }
-    }
-
-    PhoneNumberDesc generalDesc =
-        processPhoneNumberDescElement(new PhoneNumberDesc(), element, "generalDesc");
-    metadata.setGeneralDesc(generalDesc);
-    metadata.setFixedLine(processPhoneNumberDescElement(generalDesc, element, "fixedLine"));
-    metadata.setMobile(processPhoneNumberDescElement(generalDesc, element, "mobile"));
-    metadata.setTollFree(processPhoneNumberDescElement(generalDesc, element, "tollFree"));
-    metadata.setPremiumRate(processPhoneNumberDescElement(generalDesc, element, "premiumRate"));
-    metadata.setSharedCost(processPhoneNumberDescElement(generalDesc, element, "sharedCost"));
-    metadata.setVoip(processPhoneNumberDescElement(generalDesc, element, "voip"));
-    metadata.setPersonalNumber(processPhoneNumberDescElement(generalDesc, element,
-                                                             "personalNumber"));
-
-    if (metadata.getMobile().getNationalNumberPattern().equals(
-        metadata.getFixedLine().getNationalNumberPattern())) {
-      metadata.setSameMobileAndFixedLinePattern(true);
-    }
-    return metadata;
-  }
-
-  private static void setLeadingDigitsPatterns(Element numberFormatElement, NumberFormat format) {
-    NodeList leadingDigitsPatternNodes = numberFormatElement.getElementsByTagName("leadingDigits");
-    int numOfLeadingDigitsPatterns = leadingDigitsPatternNodes.getLength();
-    if (numOfLeadingDigitsPatterns > 0) {
-      for (int i = 0; i < numOfLeadingDigitsPatterns; i++) {
-        format.addLeadingDigitsPattern(
-            validateRE((leadingDigitsPatternNodes.item(i)).getFirstChild().getNodeValue()));
-      }
-    }
-  }
-
-  private static String getNationalPrefixFormattingRuleFromElement(Element element,
-                                                                   String nationalPrefix) {
-    String nationalPrefixFormattingRule = element.getAttribute("nationalPrefixFormattingRule");
-    // Replace $NP with national prefix and $FG with the first group ($1).
-    nationalPrefixFormattingRule =
-        nationalPrefixFormattingRule.replaceFirst("\\$NP", nationalPrefix)
-            .replaceFirst("\\$FG", "\\$1");
-    return nationalPrefixFormattingRule;
-  }
-
-  private static String getDomesticCarrierCodeFormattingRuleFromElement(Element element,
-                                                                        String nationalPrefix) {
-    String carrierCodeFormattingRule = element.getAttribute("carrierCodeFormattingRule");
-    // Replace $FG with the first group ($1) and $NP with the national prefix.
-    carrierCodeFormattingRule = carrierCodeFormattingRule.replaceFirst("\\$FG", "\\$1")
-        .replaceFirst("\\$NP", nationalPrefix);
-    return carrierCodeFormattingRule;
-  }
-
-  /**
-   * Processes a phone number description element from the XML file and returns it as a
-   * PhoneNumberDesc. If the description element is a fixed line or mobile number, the general
-   * description will be used to fill in the whole element if necessary, or any components that are
-   * missing. For all other types, the general description will only be used to fill in missing
-   * components if the type has a partial definition. For example, if no "tollFree" element exists,
-   * we assume there are no toll free numbers for that locale, and return a phone number description
-   * with "NA" for both the national and possible number patterns.
-   *
-   * @param generalDesc  a generic phone number description that will be used to fill in missing
-   *                     parts of the description
-   * @param countryElement  the XML element representing all the country information
-   * @param numberType  the name of the number type, corresponding to the appropriate tag in the XML
-   *                    file with information about that type
-   * @return  complete description of that phone number type
-   */
-  private static PhoneNumberDesc processPhoneNumberDescElement(PhoneNumberDesc generalDesc,
-                                                               Element countryElement,
-                                                               String numberType) {
-    NodeList phoneNumberDescList = countryElement.getElementsByTagName(numberType);
-    PhoneNumberDesc numberDesc = new PhoneNumberDesc();
-    if (phoneNumberDescList.getLength() == 0 &&
-        (!numberType.equals("fixedLine") && !numberType.equals("mobile") &&
-         !numberType.equals("generalDesc"))) {
-      numberDesc.setNationalNumberPattern("NA");
-      numberDesc.setPossibleNumberPattern("NA");
-      return numberDesc;
-    }
-    numberDesc.mergeFrom(generalDesc);
-    if (phoneNumberDescList.getLength() > 0) {
-      Element element = (Element) phoneNumberDescList.item(0);
-      NodeList possiblePattern = element.getElementsByTagName("possibleNumberPattern");
-      if (possiblePattern.getLength() > 0) {
-        numberDesc.setPossibleNumberPattern(
-            validateRE(possiblePattern.item(0).getFirstChild().getNodeValue()));
-      }
-
-      NodeList validPattern = element.getElementsByTagName("nationalNumberPattern");
-      if (validPattern.getLength() > 0) {
-        numberDesc.setNationalNumberPattern(
-            validateRE(validPattern.item(0).getFirstChild().getNodeValue()));
-      }
-
-      if (!liteBuild) {
-        NodeList exampleNumber = element.getElementsByTagName("exampleNumber");
-        if (exampleNumber.getLength() > 0) {
-          numberDesc.setExampleNumber(exampleNumber.item(0).getFirstChild().getNodeValue());
-        }
-      }
-    }
-    return numberDesc;
+    writer.flush();
+    writer.close();
   }
 }

java/resources/com/google/i18n/phonenumbers/JSArrayBuilder.java

+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.i18n.phonenumbers;
+
+import java.util.Iterator;
+
+/**
+ * A sequence of elements representing a JavaScript Array. The principal operation on a
+ * JSArrayBuilder is the append method that appends an element to the array. To facilitate nesting
+ * beginArray and endArray are also supported. Example of a JSArray: ["a", ["b', "c"]].
+ *
+ * @author Nikolaos Trogkanis
+ */
+public class JSArrayBuilder implements CharSequence {
+  // Internal representation.
+  private StringBuilder data = new StringBuilder();
+  // Flag that keeps track whether the element being added to the array is the first element.
+  private boolean isFirstElement = true;
+
+  /**
+   * Begin a new element.
+   */
+  private void beginElement() {
+    if (!isFirstElement) {
+      data.append(',');
+    }
+    isFirstElement = false;
+  }
+
+  /**
+   * Begin a new array.
+   */
+  public JSArrayBuilder beginArray() {
+    beginElement();
+    data.append('[');
+    isFirstElement = true;
+    return this;
+  }
+
+  /**
+   * End an array.
+   */
+  public JSArrayBuilder endArray() {
+    trimTrailingCommas();
+    data.append("]\n");
+    isFirstElement = false;
+    return this;
+  }
+
+  /**
+   * Add a number to the array.
+   */
+  public JSArrayBuilder append(int number) {
+    return append(Integer.toString(number), false);
+  }
+
+  /**
+   * Add a string to the array.
+   */
+  public JSArrayBuilder append(String string) {
+    return append(string, true);
+  }
+
+  /**
+   * Add a collection of strings to the array.
+   */
+  public final JSArrayBuilder appendIterator(Iterator<String> iterator) {
+    while (iterator.hasNext()) {
+      append(iterator.next());
+    }
+    return this;
+  }
+
+  // Adds a string to the array with an option to escape the string or not.
+  private JSArrayBuilder append(String string, boolean escapeString) {
+    beginElement();
+    if (string != null) {
+      if (escapeString) {
+        escape(string, data);
+      } else {
+        data.append(string);
+      }
+    }
+    return this;
+  }
+
+  // Returns a string representing the data in this JSArray.
+  @Override public String toString() {
+    return data.toString();
+  }
+
+  // Double quotes a string and replaces "\" with "\\".
+  private static void escape(String str, StringBuilder out) {
+    out.append('"');
+    out.append(str.replaceAll("\\\\", "\\\\\\\\"));
+    out.append('"');
+  }
+
+  // Trims trailing commas.
+  private void trimTrailingCommas() {
+    int i = data.length();
+    while (i > 0 && data.charAt(i - 1) == ',') {
+      i--;
+    }
+    if (i < data.length()) {
+      data.delete(i, data.length());
+    }
+  }
+
+  @Override public char charAt(int index) {
+    return data.charAt(index);
+  }
+
+  @Override public int length() {
+    return data.length();
+  }
+
+  @Override public CharSequence subSequence(int start, int end) {
+    return data.subSequence(start, end);
+  }
+}

java/src/com/google/i18n/phonenumbers/CountryCodeToRegionCodeMap.java

+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.i18n.phonenumbers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class CountryCodeToRegionCodeMap {
+  // A mapping from a country code to the region codes which denote the
+  // country/region represented by that country code. In the case of multiple
+  // countries sharing a calling code, such as the NANPA countries, the one
+  // indicated with "isMainCountryForCode" in the metadata should be first.
+  static Map<Integer, List<String>> getCountryCodeToRegionCodeMap() {
+    // The capacity is set to 272 as there are 204 different country codes,
+    // and this offers a load factor of roughly 0.75.
+    Map<Integer, List<String>> countryCodeToRegionCodeMap =
+        new HashMap<Integer, List<String>>(272);
+
+    ArrayList<String> listWithRegionCode;
+
+    listWithRegionCode = new ArrayList<String>(24);
+    listWithRegionCode.add("US");
+    listWithRegionCode.add("AG");
+    listWithRegionCode.add("AI");
+    listWithRegionCode.add("AS");
+    listWithRegionCode.add("BB");
+    listWithRegionCode.add("BM");
+    listWithRegionCode.add("BS");
+    listWithRegionCode.add("CA");
+    listWithRegionCode.add("DM");
+    listWithRegionCode.add("DO");
+    listWithRegionCode.add("GD");
+    listWithRegionCode.add("GU");
+    listWithRegionCode.add("JM");
+    listWithRegionCode.add("KN");
+    listWithRegionCode.add("KY");
+    listWithRegionCode.add("LC");
+    listWithRegionCode.add("MP");
+    listWithRegionCode.add("MS");
+    listWithRegionCode.add("PR");
+    listWithRegionCode.add("TC");
+    listWithRegionCode.add("TT");
+    listWithRegionCode.add("VC");
+    listWithRegionCode.add("VG");
+    listWithRegionCode.add("VI");
+    countryCodeToRegionCodeMap.put(1, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(2);
+    listWithRegionCode.add("RU");
+    listWithRegionCode.add("KZ");
+    countryCodeToRegionCodeMap.put(7, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("EG");
+    countryCodeToRegionCodeMap.put(20, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("ZA");
+    countryCodeToRegionCodeMap.put(27, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("GR");
+    countryCodeToRegionCodeMap.put(30, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("NL");
+    countryCodeToRegionCodeMap.put(31, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("BE");
+    countryCodeToRegionCodeMap.put(32, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("FR");
+    countryCodeToRegionCodeMap.put(33, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("ES");
+    countryCodeToRegionCodeMap.put(34, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("HU");
+    countryCodeToRegionCodeMap.put(36, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("IT");
+    countryCodeToRegionCodeMap.put(39, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("RO");
+    countryCodeToRegionCodeMap.put(40, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("CH");
+    countryCodeToRegionCodeMap.put(41, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("AT");
+    countryCodeToRegionCodeMap.put(43, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(4);
+    listWithRegionCode.add("GB");
+    listWithRegionCode.add("GG");
+    listWithRegionCode.add("IM");
+    listWithRegionCode.add("JE");
+    countryCodeToRegionCodeMap.put(44, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("DK");
+    countryCodeToRegionCodeMap.put(45, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("SE");
+    countryCodeToRegionCodeMap.put(46, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("NO");
+    countryCodeToRegionCodeMap.put(47, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("PL");
+    countryCodeToRegionCodeMap.put(48, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("DE");
+    countryCodeToRegionCodeMap.put(49, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("PE");
+    countryCodeToRegionCodeMap.put(51, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("MX");
+    countryCodeToRegionCodeMap.put(52, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("CU");
+    countryCodeToRegionCodeMap.put(53, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("AR");
+    countryCodeToRegionCodeMap.put(54, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("BR");
+    countryCodeToRegionCodeMap.put(55, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("CL");
+    countryCodeToRegionCodeMap.put(56, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("CO");
+    countryCodeToRegionCodeMap.put(57, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("VE");
+    countryCodeToRegionCodeMap.put(58, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("MY");
+    countryCodeToRegionCodeMap.put(60, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("AU");
+    countryCodeToRegionCodeMap.put(61, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("ID");
+    countryCodeToRegionCodeMap.put(62, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("PH");
+    countryCodeToRegionCodeMap.put(63, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("NZ");
+    countryCodeToRegionCodeMap.put(64, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("SG");
+    countryCodeToRegionCodeMap.put(65, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("TH");
+    countryCodeToRegionCodeMap.put(66, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("JP");
+    countryCodeToRegionCodeMap.put(81, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("KR");
+    countryCodeToRegionCodeMap.put(82, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("VN");
+    countryCodeToRegionCodeMap.put(84, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("CN");
+    countryCodeToRegionCodeMap.put(86, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("TR");
+    countryCodeToRegionCodeMap.put(90, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("IN");
+    countryCodeToRegionCodeMap.put(91, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("PK");
+    countryCodeToRegionCodeMap.put(92, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("AF");
+    countryCodeToRegionCodeMap.put(93, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("LK");
+    countryCodeToRegionCodeMap.put(94, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("MM");
+    countryCodeToRegionCodeMap.put(95, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("IR");
+    countryCodeToRegionCodeMap.put(98, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("MA");
+    countryCodeToRegionCodeMap.put(212, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("DZ");
+    countryCodeToRegionCodeMap.put(213, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("TN");
+    countryCodeToRegionCodeMap.put(216, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("LY");
+    countryCodeToRegionCodeMap.put(218, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("GM");
+    countryCodeToRegionCodeMap.put(220, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("SN");
+    countryCodeToRegionCodeMap.put(221, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("MR");
+    countryCodeToRegionCodeMap.put(222, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("ML");
+    countryCodeToRegionCodeMap.put(223, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("GN");
+    countryCodeToRegionCodeMap.put(224, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("CI");
+    countryCodeToRegionCodeMap.put(225, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("BF");
+    countryCodeToRegionCodeMap.put(226, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("NE");
+    countryCodeToRegionCodeMap.put(227, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("TG");
+    countryCodeToRegionCodeMap.put(228, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("BJ");
+    countryCodeToRegionCodeMap.put(229, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("MU");
+    countryCodeToRegionCodeMap.put(230, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("LR");
+    countryCodeToRegionCodeMap.put(231, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("SL");
+    countryCodeToRegionCodeMap.put(232, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("GH");
+    countryCodeToRegionCodeMap.put(233, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("NG");
+    countryCodeToRegionCodeMap.put(234, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("TD");
+    countryCodeToRegionCodeMap.put(235, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("CF");
+    countryCodeToRegionCodeMap.put(236, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("CM");
+    countryCodeToRegionCodeMap.put(237, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("CV");
+    countryCodeToRegionCodeMap.put(238, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("ST");
+    countryCodeToRegionCodeMap.put(239, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("GQ");
+    countryCodeToRegionCodeMap.put(240, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("GA");
+    countryCodeToRegionCodeMap.put(241, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("CG");
+    countryCodeToRegionCodeMap.put(242, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("CD");
+    countryCodeToRegionCodeMap.put(243, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("AO");
+    countryCodeToRegionCodeMap.put(244, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("GW");
+    countryCodeToRegionCodeMap.put(245, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("IO");
+    countryCodeToRegionCodeMap.put(246, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("SC");
+    countryCodeToRegionCodeMap.put(248, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("SD");
+    countryCodeToRegionCodeMap.put(249, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("RW");
+    countryCodeToRegionCodeMap.put(250, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("ET");
+    countryCodeToRegionCodeMap.put(251, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("SO");
+    countryCodeToRegionCodeMap.put(252, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("DJ");
+    countryCodeToRegionCodeMap.put(253, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("KE");
+    countryCodeToRegionCodeMap.put(254, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("TZ");
+    countryCodeToRegionCodeMap.put(255, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("UG");
+    countryCodeToRegionCodeMap.put(256, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("BI");
+    countryCodeToRegionCodeMap.put(257, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("MZ");
+    countryCodeToRegionCodeMap.put(258, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("ZM");
+    countryCodeToRegionCodeMap.put(260, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("MG");
+    countryCodeToRegionCodeMap.put(261, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(3);
+    listWithRegionCode.add("RE");
+    listWithRegionCode.add("TF");
+    listWithRegionCode.add("YT");
+    countryCodeToRegionCodeMap.put(262, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("ZW");
+    countryCodeToRegionCodeMap.put(263, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("NA");
+    countryCodeToRegionCodeMap.put(264, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("MW");
+    countryCodeToRegionCodeMap.put(265, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("LS");
+    countryCodeToRegionCodeMap.put(266, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("BW");
+    countryCodeToRegionCodeMap.put(267, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("SZ");
+    countryCodeToRegionCodeMap.put(268, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("KM");
+    countryCodeToRegionCodeMap.put(269, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("SH");
+    countryCodeToRegionCodeMap.put(290, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("ER");
+    countryCodeToRegionCodeMap.put(291, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("AW");
+    countryCodeToRegionCodeMap.put(297, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("FO");
+    countryCodeToRegionCodeMap.put(298, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("GL");
+    countryCodeToRegionCodeMap.put(299, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("GI");
+    countryCodeToRegionCodeMap.put(350, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("PT");
+    countryCodeToRegionCodeMap.put(351, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("LU");
+    countryCodeToRegionCodeMap.put(352, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("IE");
+    countryCodeToRegionCodeMap.put(353, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);
+    listWithRegionCode.add("IS");
+    countryCodeToRegionCodeMap.put(354, listWithRegionCode);
+
+    listWithRegionCode = new ArrayList<String>(1);