import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
+import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
*/
public class BuildMetadataFromXml {
private static final Logger LOGGER = Logger.getLogger(BuildMetadataFromXml.class.getName());
- private static Boolean liteBuild;
+ private static boolean liteBuild;
+
+ // String constants used to fetch the XML nodes and attributes.
+ private static final String CARRIER_CODE_FORMATTING_RULE = "carrierCodeFormattingRule";
+ private static final String COUNTRY_CODE = "countryCode";
+ private static final String EXAMPLE_NUMBER = "exampleNumber";
+ private static final String FIXED_LINE = "fixedLine";
+ private static final String FORMAT = "format";
+ private static final String GENERAL_DESC = "generalDesc";
+ private static final String INTERNATIONAL_PREFIX = "internationalPrefix";
+ private static final String INTL_FORMAT = "intlFormat";
+ private static final String LEADING_DIGITS = "leadingDigits";
+ private static final String LEADING_ZERO_POSSIBLE = "leadingZeroPossible";
+ private static final String MAIN_COUNTRY_FOR_CODE = "mainCountryForCode";
+ private static final String MOBILE = "mobile";
+ private static final String NATIONAL_NUMBER_PATTERN = "nationalNumberPattern";
+ private static final String NATIONAL_PREFIX = "nationalPrefix";
+ private static final String NATIONAL_PREFIX_FORMATTING_RULE = "nationalPrefixFormattingRule";
+ private static final String NATIONAL_PREFIX_FOR_PARSING = "nationalPrefixForParsing";
+ private static final String NATIONAL_PREFIX_TRANSFORM_RULE = "nationalPrefixTransformRule";
+ private static final String NO_INTERNATIONAL_DIALLING = "noInternationalDialling";
+ private static final String NUMBER_FORMAT = "numberFormat";
+ private static final String PAGER = "pager";
+ private static final String PATTERN = "pattern";
+ private static final String PERSONAL_NUMBER = "personalNumber";
+ private static final String POSSIBLE_NUMBER_PATTERN = "possibleNumberPattern";
+ private static final String PREFERRED_EXTN_PREFIX = "preferredExtnPrefix";
+ private static final String PREFERRED_INTERNATIONAL_PREFIX = "preferredInternationalPrefix";
+ private static final String PREMIUM_RATE = "premiumRate";
+ private static final String SHARED_COST = "sharedCost";
+ private static final String TOLL_FREE = "tollFree";
+ private static final String UAN = "uan";
+ private static final String VOIP = "voip";
+
+ // @VisibleForTesting
+ static void setLiteBuild(boolean b) {
+ liteBuild = b;
+ }
// Build the PhoneMetadataCollection from the input XML file.
public static PhoneMetadataCollection buildPhoneMetadataCollection(String inputXmlFile,
// 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 "getMainCountryForCode" in the metadata
+ // 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) {
return validateRE(regex, false);
}
- private static String validateRE(String regex, boolean removeWhitespace) {
+ // @VisibleForTesting
+ static String validateRE(String regex, boolean removeWhitespace) {
// Removes all the whitespace and newline from the regexp. Not using pattern compile options to
// make it work across programming languages.
if (removeWhitespace) {
return regex;
}
- private static PhoneMetadata loadCountryMetadata(String regionCode, Element element) {
+ /**
+ * Returns the national prefix of the provided country element.
+ */
+ // @VisibleForTesting
+ static String getNationalPrefix(Element element) {
+ return element.hasAttribute(NATIONAL_PREFIX) ? element.getAttribute(NATIONAL_PREFIX) : "";
+ }
+
+ // @VisibleForTesting
+ static PhoneMetadata.Builder loadTerritoryTagMetadata(String regionCode, Element element,
+ String nationalPrefix,
+ String nationalPrefixFormattingRule) {
PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
metadata.setId(regionCode);
- metadata.setCountryCode(Integer.parseInt(element.getAttribute("countryCode")));
- if (element.hasAttribute("leadingDigits")) {
- metadata.setLeadingDigits(validateRE(element.getAttribute("leadingDigits")));
+ metadata.setCountryCode(Integer.parseInt(element.getAttribute(COUNTRY_CODE)));
+ if (element.hasAttribute(LEADING_DIGITS)) {
+ metadata.setLeadingDigits(validateRE(element.getAttribute(LEADING_DIGITS)));
}
- metadata.setInternationalPrefix(validateRE(element.getAttribute("internationalPrefix")));
- if (element.hasAttribute("preferredInternationalPrefix")) {
- String preferredInternationalPrefix = element.getAttribute("preferredInternationalPrefix");
+ metadata.setInternationalPrefix(validateRE(element.getAttribute(INTERNATIONAL_PREFIX)));
+ if (element.hasAttribute(PREFERRED_INTERNATIONAL_PREFIX)) {
+ String preferredInternationalPrefix = element.getAttribute(PREFERRED_INTERNATIONAL_PREFIX);
metadata.setPreferredInternationalPrefix(preferredInternationalPrefix);
}
- if (element.hasAttribute("nationalPrefixForParsing")) {
+ if (element.hasAttribute(NATIONAL_PREFIX_FOR_PARSING)) {
metadata.setNationalPrefixForParsing(
- validateRE(element.getAttribute("nationalPrefixForParsing")));
- if (element.hasAttribute("nationalPrefixTransformRule")) {
+ validateRE(element.getAttribute(NATIONAL_PREFIX_FOR_PARSING)));
+ if (element.hasAttribute(NATIONAL_PREFIX_TRANSFORM_RULE)) {
metadata.setNationalPrefixTransformRule(
- validateRE(element.getAttribute("nationalPrefixTransformRule")));
+ validateRE(element.getAttribute(NATIONAL_PREFIX_TRANSFORM_RULE)));
}
}
- String nationalPrefix = "";
- String nationalPrefixFormattingRule = "";
- if (element.hasAttribute("nationalPrefix")) {
- nationalPrefix = element.getAttribute("nationalPrefix");
+ if (!nationalPrefix.isEmpty()) {
metadata.setNationalPrefix(nationalPrefix);
- nationalPrefixFormattingRule =
- getNationalPrefixFormattingRuleFromElement(element, nationalPrefix);
-
if (!metadata.hasNationalPrefixForParsing()) {
metadata.setNationalPrefixForParsing(nationalPrefix);
}
}
- String carrierCodeFormattingRule = "";
- if (element.hasAttribute("carrierCodeFormattingRule")) {
- carrierCodeFormattingRule = validateRE(
- getDomesticCarrierCodeFormattingRuleFromElement(element, nationalPrefix));
- }
- if (element.hasAttribute("preferredExtnPrefix")) {
- metadata.setPreferredExtnPrefix(element.getAttribute("preferredExtnPrefix"));
+ if (element.hasAttribute(PREFERRED_EXTN_PREFIX)) {
+ metadata.setPreferredExtnPrefix(element.getAttribute(PREFERRED_EXTN_PREFIX));
}
- if (element.hasAttribute("mainCountryForCode")) {
+ if (element.hasAttribute(MAIN_COUNTRY_FOR_CODE)) {
metadata.setMainCountryForCode(true);
}
- if (element.hasAttribute("leadingZeroPossible")) {
+ if (element.hasAttribute(LEADING_ZERO_POSSIBLE)) {
metadata.setLeadingZeroPossible(true);
}
+ return metadata;
+ }
- // Extract availableFormats
- NodeList numberFormatElements = element.getElementsByTagName("numberFormat");
+ /**
+ * Extracts the pattern for international format. If there is no intlFormat, default to using the
+ * national format. If the intlFormat is set to "NA" the intlFormat should be ignored.
+ *
+ * @throws RuntimeException if multiple intlFormats have been encountered.
+ * @return whether an international number format is defined.
+ */
+ // @VisibleForTesting
+ static boolean loadInternationalFormat(PhoneMetadata.Builder metadata,
+ Element numberFormatElement,
+ String nationalFormat) {
+ NumberFormat.Builder intlFormat = NumberFormat.newBuilder();
+ setLeadingDigitsPatterns(numberFormatElement, intlFormat);
+ intlFormat.setPattern(numberFormatElement.getAttribute(PATTERN));
+ NodeList intlFormatPattern = numberFormatElement.getElementsByTagName(INTL_FORMAT);
+ boolean hasExplicitIntlFormatDefined = false;
+
+ if (intlFormatPattern.getLength() > 1) {
+ LOGGER.log(Level.SEVERE,
+ "A maximum of one intlFormat pattern for a numberFormat element should be " +
+ "defined.");
+ throw new RuntimeException("Invalid number of intlFormat patterns for country: " +
+ metadata.getId());
+ } else if (intlFormatPattern.getLength() == 0) {
+ // Default to use the same as the national pattern if none is defined.
+ intlFormat.setFormat(nationalFormat);
+ } else {
+ String intlFormatPatternValue = intlFormatPattern.item(0).getFirstChild().getNodeValue();
+ if (!intlFormatPatternValue.equals("NA")) {
+ intlFormat.setFormat(intlFormatPatternValue);
+ }
+ hasExplicitIntlFormatDefined = true;
+ }
+
+ if (intlFormat.hasFormat()) {
+ metadata.addIntlNumberFormat(intlFormat);
+ }
+ return hasExplicitIntlFormatDefined;
+ }
+
+ /**
+ * Extracts the pattern for the national format.
+ *
+ * @throws RuntimeException if multiple or no formats have been encountered.
+ * @return the national format string.
+ */
+ // @VisibleForTesting
+ static String loadNationalFormat(PhoneMetadata.Builder metadata, Element numberFormatElement,
+ NumberFormat.Builder format) {
+ 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: " +
+ metadata.getId());
+ }
+ String nationalFormat = formatPattern.item(0).getFirstChild().getNodeValue();
+ format.setFormat(nationalFormat);
+ return nationalFormat;
+ }
+
+ /**
+ * Extracts the available formats from the provided DOM element. If it does not contain any
+ * nationalPrefixFormattingRule, the one passed-in is retained.
+ */
+ // @VisibleForTesting
+ static void loadAvailableFormats(PhoneMetadata.Builder metadata, String regionCode,
+ Element element, String nationalPrefix,
+ String nationalPrefixFormattingRule) {
+ String carrierCodeFormattingRule = "";
+ if (element.hasAttribute(CARRIER_CODE_FORMATTING_RULE)) {
+ carrierCodeFormattingRule = validateRE(
+ getDomesticCarrierCodeFormattingRuleFromElement(element, nationalPrefix));
+ }
+ NodeList numberFormatElements = element.getElementsByTagName(NUMBER_FORMAT);
boolean hasExplicitIntlFormatDefined = false;
int numOfFormatElements = numberFormatElements.getLength();
Element numberFormatElement = (Element) numberFormatElements.item(i);
NumberFormat.Builder format = NumberFormat.newBuilder();
- if (numberFormatElement.hasAttribute("nationalPrefixFormattingRule")) {
+ if (numberFormatElement.hasAttribute(NATIONAL_PREFIX_FORMATTING_RULE)) {
format.setNationalPrefixFormattingRule(
getNationalPrefixFormattingRuleFromElement(numberFormatElement, nationalPrefix));
} else {
format.setNationalPrefixFormattingRule(nationalPrefixFormattingRule);
}
- if (numberFormatElement.hasAttribute("carrierCodeFormattingRule")) {
+ if (numberFormatElement.hasAttribute(CARRIER_CODE_FORMATTING_RULE)) {
format.setDomesticCarrierCodeFormattingRule(validateRE(
getDomesticCarrierCodeFormattingRuleFromElement(numberFormatElement,
nationalPrefix)));
} else {
format.setDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule);
}
-
- // Extract the pattern for the national format.
- 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);
- }
- String nationalFormat = formatPattern.item(0).getFirstChild().getNodeValue();
- format.setFormat(nationalFormat);
+ String nationalFormat =
+ loadNationalFormat(metadata, numberFormatElement, format);
metadata.addNumberFormat(format);
- // Extract the pattern for international format. If there is no intlFormat, default to
- // using the national format. If the intlFormat is set to "NA" the intlFormat should be
- // ignored.
- NumberFormat.Builder intlFormat = NumberFormat.newBuilder();
- setLeadingDigitsPatterns(numberFormatElement, intlFormat);
- intlFormat.setPattern(numberFormatElement.getAttribute("pattern"));
- NodeList intlFormatPattern = numberFormatElement.getElementsByTagName("intlFormat");
-
- if (intlFormatPattern.getLength() > 1) {
- LOGGER.log(Level.SEVERE,
- "A maximum of one intlFormat pattern for a numberFormat element should be " +
- "defined.");
- throw new RuntimeException("Invalid number of intlFormat patterns for country: " +
- regionCode);
- } else if (intlFormatPattern.getLength() == 0) {
- // Default to use the same as the national pattern if none is defined.
- intlFormat.setFormat(nationalFormat);
- } else {
- String intlFormatPatternValue =
- intlFormatPattern.item(0).getFirstChild().getNodeValue();
- if (!intlFormatPatternValue.equals("NA")) {
- intlFormat.setFormat(intlFormatPatternValue);
- }
- hasExplicitIntlFormatDefined = true;
- }
-
- if (intlFormat.hasFormat()) {
- metadata.addIntlNumberFormat(intlFormat);
+ if (loadInternationalFormat(metadata, numberFormatElement, nationalFormat)) {
+ hasExplicitIntlFormatDefined = true;
}
}
// Only a small number of regions need to specify the intlFormats in the xml. For the majority
metadata.clearIntlNumberFormat();
}
}
-
- PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
- 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"));
- metadata.setPager(processPhoneNumberDescElement(generalDesc, element, "pager"));
- metadata.setUan(processPhoneNumberDescElement(generalDesc, element, "uan"));
- metadata.setNoInternationalDialling(processPhoneNumberDescElement(generalDesc, element,
- "noInternationalDialling"));
-
- if (metadata.getMobile().getNationalNumberPattern().equals(
- metadata.getFixedLine().getNationalNumberPattern())) {
- metadata.setSameMobileAndFixedLinePattern(true);
- }
- return metadata.build();
}
- private static void setLeadingDigitsPatterns(Element numberFormatElement,
- NumberFormat.Builder format) {
- NodeList leadingDigitsPatternNodes = numberFormatElement.getElementsByTagName("leadingDigits");
+ // @VisibleForTesting
+ static void setLeadingDigitsPatterns(Element numberFormatElement, NumberFormat.Builder format) {
+ NodeList leadingDigitsPatternNodes = numberFormatElement.getElementsByTagName(LEADING_DIGITS);
int numOfLeadingDigitsPatterns = leadingDigitsPatternNodes.getLength();
if (numOfLeadingDigitsPatterns > 0) {
for (int i = 0; i < numOfLeadingDigitsPatterns; i++) {
}
}
- private static String getNationalPrefixFormattingRuleFromElement(Element element,
- String nationalPrefix) {
- String nationalPrefixFormattingRule = element.getAttribute("nationalPrefixFormattingRule");
+ // @VisibleForTesting
+ static String getNationalPrefixFormattingRuleFromElement(Element element,
+ String nationalPrefix) {
+ String nationalPrefixFormattingRule = element.getAttribute(NATIONAL_PREFIX_FORMATTING_RULE);
// Replace $NP with national prefix and $FG with the first group ($1).
nationalPrefixFormattingRule =
nationalPrefixFormattingRule.replaceFirst("\\$NP", nationalPrefix)
return nationalPrefixFormattingRule;
}
- private static String getDomesticCarrierCodeFormattingRuleFromElement(Element element,
- String nationalPrefix) {
- String carrierCodeFormattingRule = element.getAttribute("carrierCodeFormattingRule");
+ // @VisibleForTesting
+ static String getDomesticCarrierCodeFormattingRuleFromElement(Element element,
+ String nationalPrefix) {
+ String carrierCodeFormattingRule = element.getAttribute(CARRIER_CODE_FORMATTING_RULE);
// Replace $FG with the first group ($1) and $NP with the national prefix.
carrierCodeFormattingRule = carrierCodeFormattingRule.replaceFirst("\\$FG", "\\$1")
.replaceFirst("\\$NP", nationalPrefix);
return carrierCodeFormattingRule;
}
+ // @VisibleForTesting
+ static boolean isValidNumberType(String numberType) {
+ return numberType.equals(FIXED_LINE) || numberType.equals(MOBILE) ||
+ numberType.equals(GENERAL_DESC);
+ }
+
/**
* 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
* file with information about that type
* @return complete description of that phone number type
*/
- private static PhoneNumberDesc.Builder processPhoneNumberDescElement(
- PhoneNumberDesc.Builder generalDesc,
- Element countryElement,
- String numberType) {
+ // @VisibleForTesting
+ static PhoneNumberDesc.Builder processPhoneNumberDescElement(PhoneNumberDesc.Builder generalDesc,
+ Element countryElement,
+ String numberType) {
NodeList phoneNumberDescList = countryElement.getElementsByTagName(numberType);
PhoneNumberDesc.Builder numberDesc = PhoneNumberDesc.newBuilder();
- if (phoneNumberDescList.getLength() == 0 &&
- (!numberType.equals("fixedLine") && !numberType.equals("mobile") &&
- !numberType.equals("generalDesc"))) {
+ if (phoneNumberDescList.getLength() == 0 && !isValidNumberType(numberType)) {
numberDesc.setNationalNumberPattern("NA");
numberDesc.setPossibleNumberPattern("NA");
return numberDesc;
numberDesc.mergeFrom(generalDesc.build());
if (phoneNumberDescList.getLength() > 0) {
Element element = (Element) phoneNumberDescList.item(0);
- NodeList possiblePattern = element.getElementsByTagName("possibleNumberPattern");
+ NodeList possiblePattern = element.getElementsByTagName(POSSIBLE_NUMBER_PATTERN);
if (possiblePattern.getLength() > 0) {
numberDesc.setPossibleNumberPattern(
validateRE(possiblePattern.item(0).getFirstChild().getNodeValue(), true));
}
- NodeList validPattern = element.getElementsByTagName("nationalNumberPattern");
+ NodeList validPattern = element.getElementsByTagName(NATIONAL_NUMBER_PATTERN);
if (validPattern.getLength() > 0) {
numberDesc.setNationalNumberPattern(
validateRE(validPattern.item(0).getFirstChild().getNodeValue(), true));
}
if (!liteBuild) {
- NodeList exampleNumber = element.getElementsByTagName("exampleNumber");
+ NodeList exampleNumber = element.getElementsByTagName(EXAMPLE_NUMBER);
if (exampleNumber.getLength() > 0) {
numberDesc.setExampleNumber(exampleNumber.item(0).getFirstChild().getNodeValue());
}
}
return numberDesc;
}
+
+ // @VisibleForTesting
+ static void loadGeneralDesc(PhoneMetadata.Builder metadata, Element element) {
+ PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
+ generalDesc = processPhoneNumberDescElement(generalDesc, element, GENERAL_DESC);
+ metadata.setGeneralDesc(generalDesc);
+
+ metadata.setFixedLine(processPhoneNumberDescElement(generalDesc, element, FIXED_LINE));
+ metadata.setMobile(processPhoneNumberDescElement(generalDesc, element, MOBILE));
+ metadata.setTollFree(processPhoneNumberDescElement(generalDesc, element, TOLL_FREE));
+ metadata.setPremiumRate(processPhoneNumberDescElement(generalDesc, element, PREMIUM_RATE));
+ metadata.setSharedCost(processPhoneNumberDescElement(generalDesc, element, SHARED_COST));
+ metadata.setVoip(processPhoneNumberDescElement(generalDesc, element, VOIP));
+ metadata.setPersonalNumber(processPhoneNumberDescElement(generalDesc, element,
+ PERSONAL_NUMBER));
+ metadata.setPager(processPhoneNumberDescElement(generalDesc, element, PAGER));
+ metadata.setUan(processPhoneNumberDescElement(generalDesc, element, UAN));
+ metadata.setNoInternationalDialling(processPhoneNumberDescElement(generalDesc, element,
+ NO_INTERNATIONAL_DIALLING));
+ metadata.setSameMobileAndFixedLinePattern(
+ metadata.getMobile().getNationalNumberPattern().equals(
+ metadata.getFixedLine().getNationalNumberPattern()));
+ }
+
+ public static PhoneMetadata loadCountryMetadata(String regionCode, Element element) {
+ String nationalPrefix = getNationalPrefix(element);
+ String nationalPrefixFormattingRule =
+ getNationalPrefixFormattingRuleFromElement(element, nationalPrefix);
+ PhoneMetadata.Builder metadata =
+ loadTerritoryTagMetadata(regionCode, element, nationalPrefix, nationalPrefixFormattingRule);
+
+ loadAvailableFormats(metadata, regionCode, element, nationalPrefix.toString(),
+ nationalPrefixFormattingRule.toString());
+ loadGeneralDesc(metadata, element);
+ return metadata.build();
+ }
}
--- /dev/null
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+package com.google.i18n.phonenumbers.tools;
+
+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 junit.framework.TestCase;
+
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.regex.PatternSyntaxException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+/**
+ * Unit tests for BuildMetadataFromXml.java
+ *
+ * @author Philippe Liard
+ */
+public class BuildMetadataFromXmlTest extends TestCase {
+
+ // Helper method that outputs a DOM element from a XML string.
+ private static Element parseXmlString(String xmlString)
+ throws ParserConfigurationException, SAXException, IOException {
+ DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
+ InputSource inputSource = new InputSource();
+ inputSource.setCharacterStream(new StringReader(xmlString));
+ return documentBuilder.parse(inputSource).getDocumentElement();
+ }
+
+ // Tests validateRE().
+ public void testValidateRERemovesWhiteSpaces() {
+ String input = " hello world ";
+ // Should remove all the white spaces contained in the provided string.
+ assertEquals("helloworld", BuildMetadataFromXml.validateRE(input, true));
+ // Make sure it only happens when the last parameter is set to true.
+ assertEquals(" hello world ", BuildMetadataFromXml.validateRE(input, false));
+ }
+
+ public void testValidateREThrowsException() {
+ String invalidPattern = "[";
+ // Should throw an exception when an invalid pattern is provided independently of the last
+ // parameter (remove white spaces).
+ try {
+ BuildMetadataFromXml.validateRE(invalidPattern, false);
+ fail();
+ } catch (PatternSyntaxException e) {
+ // Test passed.
+ }
+ try {
+ BuildMetadataFromXml.validateRE(invalidPattern, true);
+ fail();
+ } catch (PatternSyntaxException e) {
+ // Test passed.
+ }
+ }
+
+ public void testValidateRE() {
+ String validPattern = "[a-zA-Z]d{1,9}";
+ // The provided pattern should be left unchanged.
+ assertEquals(validPattern, BuildMetadataFromXml.validateRE(validPattern, false));
+ }
+
+ // Tests getNationalPrefix().
+ public void testGetNationalPrefix()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput = "<territory nationalPrefix='00'/>";
+ Element territoryElement = parseXmlString(xmlInput);
+ assertEquals("00", BuildMetadataFromXml.getNationalPrefix(territoryElement));
+ }
+
+ // Tests loadTerritoryTagMetadata().
+ public void testLoadTerritoryTagMetadata()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput =
+ "<territory countryCode='33' leadingDigits='2' internationalPrefix='00'" +
+ " preferredInternationalPrefix='0011' nationalPrefixForParsing='0'" +
+ " nationalPrefixTransformRule='9$1'" + // nationalPrefix manually injected.
+ " preferredExtnPrefix=' x' mainCountryForCode='true'" +
+ " leadingZeroPossible='true'>" +
+ "</territory>";
+ Element territoryElement = parseXmlString(xmlInput);
+ PhoneMetadata.Builder phoneMetadata =
+ BuildMetadataFromXml.loadTerritoryTagMetadata("33", territoryElement, "0", "");
+ assertEquals(33, phoneMetadata.getCountryCode());
+ assertEquals("2", phoneMetadata.getLeadingDigits());
+ assertEquals("00", phoneMetadata.getInternationalPrefix());
+ assertEquals("0011", phoneMetadata.getPreferredInternationalPrefix());
+ assertEquals("0", phoneMetadata.getNationalPrefixForParsing());
+ assertEquals("9$1", phoneMetadata.getNationalPrefixTransformRule());
+ assertEquals("0", phoneMetadata.getNationalPrefix());
+ assertEquals(" x", phoneMetadata.getPreferredExtnPrefix());
+ assertTrue(phoneMetadata.getMainCountryForCode());
+ assertTrue(phoneMetadata.getLeadingZeroPossible());
+ }
+
+ public void testLoadTerritoryTagMetadataSetsBooleanFieldsToFalseByDefault()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput = "<territory countryCode='33'/>";
+ Element territoryElement = parseXmlString(xmlInput);
+ PhoneMetadata.Builder phoneMetadata =
+ BuildMetadataFromXml.loadTerritoryTagMetadata("33", territoryElement, "", "");
+ assertFalse(phoneMetadata.getMainCountryForCode());
+ assertFalse(phoneMetadata.getLeadingZeroPossible());
+ }
+
+ public void testLoadTerritoryTagMetadataSetsNationalPrefixForParsingByDefault()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput = "<territory countryCode='33'/>";
+ Element territoryElement = parseXmlString(xmlInput);
+ PhoneMetadata.Builder phoneMetadata =
+ BuildMetadataFromXml.loadTerritoryTagMetadata("33", territoryElement, "00", "");
+ // When unspecified, nationalPrefixForParsing defaults to nationalPrefix.
+ assertEquals("00", phoneMetadata.getNationalPrefix());
+ assertEquals(phoneMetadata.getNationalPrefix(), phoneMetadata.getNationalPrefixForParsing());
+ }
+
+ public void testLoadTerritoryTagMetadataWithRequiredAttributesOnly()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput = "<territory countryCode='33' internationalPrefix='00'/>";
+ Element territoryElement = parseXmlString(xmlInput);
+ // Should not throw any exception.
+ PhoneMetadata.Builder phoneMetadata =
+ BuildMetadataFromXml.loadTerritoryTagMetadata("33", territoryElement, "", "");
+ }
+
+ // Tests loadInternationalFormat().
+ public void testLoadInternationalFormat()
+ throws ParserConfigurationException, SAXException, IOException {
+ String intlFormat = "$1 $2";
+ String xmlInput = "<numberFormat><intlFormat>" + intlFormat + "</intlFormat></numberFormat>";
+ Element numberFormatElement = parseXmlString(xmlInput);
+ PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
+ String nationalFormat = "";
+
+ assertTrue(BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement,
+ nationalFormat));
+ assertEquals(intlFormat, metadata.getIntlNumberFormat(0).getFormat());
+ }
+
+ public void testLoadInternationalFormatWithBothNationalAndIntlFormatsDefined()
+ throws ParserConfigurationException, SAXException, IOException {
+ String intlFormat = "$1 $2";
+ String xmlInput = "<numberFormat><intlFormat>" + intlFormat + "</intlFormat></numberFormat>";
+ Element numberFormatElement = parseXmlString(xmlInput);
+ PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
+ String nationalFormat = "$1";
+
+ assertTrue(BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement,
+ nationalFormat));
+ assertEquals(intlFormat, metadata.getIntlNumberFormat(0).getFormat());
+ }
+
+ public void testLoadInternationalFormatExpectsOnlyOnePattern()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput = "<numberFormat><intlFormat/><intlFormat/></numberFormat>";
+ Element numberFormatElement = parseXmlString(xmlInput);
+ PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
+
+ // Should throw an exception as multiple intlFormats are provided.
+ try {
+ BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement, "");
+ fail();
+ } catch (RuntimeException e) {
+ // Test passed.
+ }
+ }
+
+ public void testLoadInternationalFormatUsesNationalFormatByDefault()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput = "<numberFormat></numberFormat>";
+ Element numberFormatElement = parseXmlString(xmlInput);
+ PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
+ String nationalFormat = "$1 $2 $3";
+
+ assertFalse(BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement,
+ nationalFormat));
+ assertEquals(nationalFormat, metadata.getIntlNumberFormat(0).getFormat());
+ }
+
+ // Tests loadNationalFormat().
+ public void testLoadNationalFormat()
+ throws ParserConfigurationException, SAXException, IOException {
+ String nationalFormat = "$1 $2";
+ String xmlInput = String.format("<numberFormat><format>%s</format></numberFormat>",
+ nationalFormat);
+ Element numberFormatElement = parseXmlString(xmlInput);
+ PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
+ NumberFormat.Builder numberFormat = NumberFormat.newBuilder();
+
+ assertEquals(nationalFormat,
+ BuildMetadataFromXml.loadNationalFormat(metadata, numberFormatElement,
+ numberFormat));
+ }
+
+ public void testLoadNationalFormatRequiresFormat()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput = "<numberFormat></numberFormat>";
+ Element numberFormatElement = parseXmlString(xmlInput);
+ PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
+ NumberFormat.Builder numberFormat = NumberFormat.newBuilder();
+
+ try {
+ BuildMetadataFromXml.loadNationalFormat(metadata, numberFormatElement, numberFormat);
+ fail();
+ } catch (RuntimeException e) {
+ // Test passed.
+ }
+ }
+
+ public void testLoadNationalFormatExpectsExactlyOneFormat()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput = "<numberFormat><format/><format/></numberFormat>";
+ Element numberFormatElement = parseXmlString(xmlInput);
+ PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
+ NumberFormat.Builder numberFormat = NumberFormat.newBuilder();
+
+ try {
+ BuildMetadataFromXml.loadNationalFormat(metadata, numberFormatElement, numberFormat);
+ fail();
+ } catch (RuntimeException e) {
+ // Test passed.
+ }
+ }
+
+ // Tests loadAvailableFormats().
+ public void testLoadAvailableFormats()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput =
+ "<territory >" +
+ " <availableFormats>" +
+ " <numberFormat nationalPrefixFormattingRule='($FG)'" +
+ " carrierCodeFormattingRule='$NP $CC ($FG)'>" +
+ " <format>$1 $2 $3</format>" +
+ " </numberFormat>" +
+ " </availableFormats>" +
+ "</territory>";
+ Element element = parseXmlString(xmlInput);
+ PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
+ BuildMetadataFromXml.loadAvailableFormats(metadata, "AE", element, "0", "");
+ assertEquals("($1)", metadata.getNumberFormat(0).getNationalPrefixFormattingRule());
+ assertEquals("0 $CC ($1)", metadata.getNumberFormat(0).getDomesticCarrierCodeFormattingRule());
+ assertEquals("$1 $2 $3", metadata.getNumberFormat(0).getFormat());
+ }
+
+ public void testLoadAvailableFormatsPropagatesCarrierCodeFormattingRule()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput =
+ "<territory carrierCodeFormattingRule='$NP $CC ($FG)'>" +
+ " <availableFormats>" +
+ " <numberFormat nationalPrefixFormattingRule='($FG)'>" +
+ " <format>$1 $2 $3</format>" +
+ " </numberFormat>" +
+ " </availableFormats>" +
+ "</territory>";
+ Element element = parseXmlString(xmlInput);
+ PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
+ BuildMetadataFromXml.loadAvailableFormats(metadata, "AE", element, "0", "");
+ assertEquals("($1)", metadata.getNumberFormat(0).getNationalPrefixFormattingRule());
+ assertEquals("0 $CC ($1)", metadata.getNumberFormat(0).getDomesticCarrierCodeFormattingRule());
+ assertEquals("$1 $2 $3", metadata.getNumberFormat(0).getFormat());
+ }
+
+ public void testLoadAvailableFormatsSetsProvidedNationalPrefixFormattingRule()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput =
+ "<territory>" +
+ " <availableFormats>" +
+ " <numberFormat><format>$1 $2 $3</format></numberFormat>" +
+ " </availableFormats>" +
+ "</territory>";
+ Element element = parseXmlString(xmlInput);
+ PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
+ BuildMetadataFromXml.loadAvailableFormats(metadata, "AE", element, "", "($1)");
+ assertEquals("($1)", metadata.getNumberFormat(0).getNationalPrefixFormattingRule());
+ }
+
+ public void testLoadAvailableFormatsClearsIntlFormat()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput =
+ "<territory>" +
+ " <availableFormats>" +
+ " <numberFormat><format>$1 $2 $3</format></numberFormat>" +
+ " </availableFormats>" +
+ "</territory>";
+ Element element = parseXmlString(xmlInput);
+ PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
+ BuildMetadataFromXml.loadAvailableFormats(metadata, "AE", element, "0", "($1)");
+ assertEquals(0, metadata.getIntlNumberFormatCount());
+ }
+
+ public void testLoadAvailableFormatsHandlesMultipleNumberFormats()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput =
+ "<territory>" +
+ " <availableFormats>" +
+ " <numberFormat><format>$1 $2 $3</format></numberFormat>" +
+ " <numberFormat><format>$1-$2</format></numberFormat>" +
+ " </availableFormats>" +
+ "</territory>";
+ Element element = parseXmlString(xmlInput);
+ PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
+ BuildMetadataFromXml.loadAvailableFormats(metadata, "AE", element, "0", "($1)");
+ assertEquals("$1 $2 $3", metadata.getNumberFormat(0).getFormat());
+ assertEquals("$1-$2", metadata.getNumberFormat(1).getFormat());
+ }
+
+ public void testLoadInternationalFormatDoesNotSetIntlFormatWhenNA()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput = "<numberFormat><intlFormat>NA</intlFormat></numberFormat>";
+ Element numberFormatElement = parseXmlString(xmlInput);
+ PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
+ String nationalFormat = "$1 $2";
+
+ BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement, nationalFormat);
+ assertEquals(0, metadata.getIntlNumberFormatCount());
+ }
+
+ // Tests setLeadingDigitsPatterns().
+ public void testSetLeadingDigitsPatterns()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput =
+ "<numberFormat>" +
+ "<leadingDigits>1</leadingDigits><leadingDigits>2</leadingDigits>" +
+ "</numberFormat>";
+ Element numberFormatElement = parseXmlString(xmlInput);
+ NumberFormat.Builder numberFormat = NumberFormat.newBuilder();
+ BuildMetadataFromXml.setLeadingDigitsPatterns(numberFormatElement, numberFormat);
+
+ assertEquals("1", numberFormat.getLeadingDigitsPattern(0));
+ assertEquals("2", numberFormat.getLeadingDigitsPattern(1));
+ }
+
+ // Tests getNationalPrefixFormattingRuleFromElement().
+ public void testGetNationalPrefixFormattingRuleFromElement()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput = "<territory nationalPrefixFormattingRule='$NP$FG'/>";
+ Element element = parseXmlString(xmlInput);
+ assertEquals("0$1",
+ BuildMetadataFromXml.getNationalPrefixFormattingRuleFromElement(element, "0"));
+ }
+
+ // Tests getDomesticCarrierCodeFormattingRuleFromElement().
+ public void testGetDomesticCarrierCodeFormattingRuleFromElement()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput = "<territory carrierCodeFormattingRule='$NP$CC $FG'/>";
+ Element element = parseXmlString(xmlInput);
+ assertEquals("0$CC $1",
+ BuildMetadataFromXml.getDomesticCarrierCodeFormattingRuleFromElement(element,
+ "0"));
+ }
+
+ // Tests isValidNumberType().
+ public void testIsValidNumberTypeWithInvalidInput() {
+ assertFalse(BuildMetadataFromXml.isValidNumberType("invalidType"));
+ }
+
+ // Tests processPhoneNumberDescElement().
+ public void testProcessPhoneNumberDescElementWithInvalidInput()
+ throws ParserConfigurationException, SAXException, IOException {
+ PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
+ Element territoryElement = parseXmlString("<territory/>");
+ PhoneNumberDesc.Builder phoneNumberDesc;
+
+ phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
+ generalDesc, territoryElement, "invalidType");
+ assertEquals("NA", phoneNumberDesc.getPossibleNumberPattern());
+ assertEquals("NA", phoneNumberDesc.getNationalNumberPattern());
+ }
+
+ public void testProcessPhoneNumberDescElementMergesWithGeneralDesc()
+ throws ParserConfigurationException, SAXException, IOException {
+ PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
+ generalDesc.setPossibleNumberPattern("\\d{6}");
+ Element territoryElement = parseXmlString("<territory><fixedLine/></territory>");
+ PhoneNumberDesc.Builder phoneNumberDesc;
+
+ phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
+ generalDesc, territoryElement, "fixedLine");
+ assertEquals("\\d{6}", phoneNumberDesc.getPossibleNumberPattern());
+ }
+
+ public void testProcessPhoneNumberDescElementOverridesGeneralDesc()
+ throws ParserConfigurationException, SAXException, IOException {
+ PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
+ generalDesc.setPossibleNumberPattern("\\d{8}");
+ String xmlInput =
+ "<territory><fixedLine>" +
+ " <possibleNumberPattern>\\d{6}</possibleNumberPattern>" +
+ "</fixedLine></territory>";
+ Element territoryElement = parseXmlString(xmlInput);
+ PhoneNumberDesc.Builder phoneNumberDesc;
+
+ phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
+ generalDesc, territoryElement, "fixedLine");
+ assertEquals("\\d{6}", phoneNumberDesc.getPossibleNumberPattern());
+ }
+
+ public void testProcessPhoneNumberDescElementHandlesLiteBuild()
+ throws ParserConfigurationException, SAXException, IOException {
+ try {
+ BuildMetadataFromXml.setLiteBuild(true);
+ PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
+ String xmlInput =
+ "<territory><fixedLine>" +
+ " <exampleNumber>01 01 01 01</exampleNumber>" +
+ "</fixedLine></territory>";
+ Element territoryElement = parseXmlString(xmlInput);
+ PhoneNumberDesc.Builder phoneNumberDesc;
+
+ phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
+ generalDesc, territoryElement, "fixedLine");
+ assertEquals("", phoneNumberDesc.getExampleNumber());
+ } finally {
+ // Restore the lite build parameter to its default value (false) to avoid potential
+ // side-effects in other tests.
+ BuildMetadataFromXml.setLiteBuild(false);
+ }
+ }
+
+ public void testProcessPhoneNumberDescOutputsExampleNumberByDefault()
+ throws ParserConfigurationException, SAXException, IOException {
+ PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
+ String xmlInput =
+ "<territory><fixedLine>" +
+ " <exampleNumber>01 01 01 01</exampleNumber>" +
+ "</fixedLine></territory>";
+ Element territoryElement = parseXmlString(xmlInput);
+ PhoneNumberDesc.Builder phoneNumberDesc;
+
+ phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
+ generalDesc, territoryElement, "fixedLine");
+ assertEquals("01 01 01 01", phoneNumberDesc.getExampleNumber());
+ }
+
+ public void testProcessPhoneNumberDescRemovesWhiteSpacesInPatterns()
+ throws ParserConfigurationException, SAXException, IOException {
+ PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
+ String xmlInput =
+ "<territory><fixedLine>" +
+ " <possibleNumberPattern>\t \\d { 6 } </possibleNumberPattern>" +
+ "</fixedLine></territory>";
+ Element countryElement = parseXmlString(xmlInput);
+ PhoneNumberDesc.Builder phoneNumberDesc;
+
+ phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
+ generalDesc, countryElement, "fixedLine");
+ assertEquals("\\d{6}", phoneNumberDesc.getPossibleNumberPattern());
+ }
+
+ // Tests loadGeneralDesc().
+ public void testLoadGeneralDescSetsSameMobileAndFixedLinePattern()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput =
+ "<territory countryCode=\"33\">" +
+ " <fixedLine><nationalNumberPattern>\\d{6}</nationalNumberPattern></fixedLine>" +
+ " <mobile><nationalNumberPattern>\\d{6}</nationalNumberPattern></mobile>" +
+ "</territory>";
+ Element territoryElement = parseXmlString(xmlInput);
+ PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
+ // Should set sameMobileAndFixedPattern to true.
+ BuildMetadataFromXml.loadGeneralDesc(metadata, territoryElement);
+ assertTrue(metadata.getSameMobileAndFixedLinePattern());
+ }
+
+ public void testLoadGeneralDescSetsAllDescriptions()
+ throws ParserConfigurationException, SAXException, IOException {
+ String xmlInput =
+ "<territory countryCode=\"33\">" +
+ " <fixedLine><nationalNumberPattern>\\d{1}</nationalNumberPattern></fixedLine>" +
+ " <mobile><nationalNumberPattern>\\d{2}</nationalNumberPattern></mobile>" +
+ " <pager><nationalNumberPattern>\\d{3}</nationalNumberPattern></pager>" +
+ " <tollFree><nationalNumberPattern>\\d{4}</nationalNumberPattern></tollFree>" +
+ " <premiumRate><nationalNumberPattern>\\d{5}</nationalNumberPattern></premiumRate>" +
+ " <sharedCost><nationalNumberPattern>\\d{6}</nationalNumberPattern></sharedCost>" +
+ " <personalNumber><nationalNumberPattern>\\d{7}</nationalNumberPattern></personalNumber>" +
+ " <voip><nationalNumberPattern>\\d{8}</nationalNumberPattern></voip>" +
+ " <uan><nationalNumberPattern>\\d{9}</nationalNumberPattern></uan>" +
+ " <shortCode><nationalNumberPattern>\\d{10}</nationalNumberPattern></shortCode>" +
+ "</territory>";
+ Element territoryElement = parseXmlString(xmlInput);
+ PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
+ BuildMetadataFromXml.loadGeneralDesc(metadata, territoryElement);
+ assertEquals("\\d{1}", metadata.getFixedLine().getNationalNumberPattern());
+ assertEquals("\\d{2}", metadata.getMobile().getNationalNumberPattern());
+ assertEquals("\\d{3}", metadata.getPager().getNationalNumberPattern());
+ assertEquals("\\d{4}", metadata.getTollFree().getNationalNumberPattern());
+ assertEquals("\\d{5}", metadata.getPremiumRate().getNationalNumberPattern());
+ assertEquals("\\d{6}", metadata.getSharedCost().getNationalNumberPattern());
+ assertEquals("\\d{7}", metadata.getPersonalNumber().getNationalNumberPattern());
+ assertEquals("\\d{8}", metadata.getVoip().getNationalNumberPattern());
+ assertEquals("\\d{9}", metadata.getUan().getNationalNumberPattern());
+ }
+}