diff --git a/cpp/src/phonenumbers/default_logger.cc b/cpp/src/phonenumbers/default_logger.cc index 766e215a8..538c1b30d 100644 --- a/cpp/src/phonenumbers/default_logger.cc +++ b/cpp/src/phonenumbers/default_logger.cc @@ -14,9 +14,6 @@ // Author: Philippe Liard -// This file should not be compiled when using Google base/. -#ifndef USE_GOOGLE_BASE - #include #include "phonenumbers/default_logger.h" @@ -50,5 +47,3 @@ void StdoutLogger::WriteLevel() { } // namespace phonenumbers } // namespace i18n - -#endif // USE_GOOGLE_BASE diff --git a/cpp/src/phonenumbers/default_logger.h b/cpp/src/phonenumbers/default_logger.h index 28212ff7f..53ec9cd7a 100644 --- a/cpp/src/phonenumbers/default_logger.h +++ b/cpp/src/phonenumbers/default_logger.h @@ -17,28 +17,6 @@ #ifndef I18N_PHONENUMBERS_DEFAULT_LOGGER_H_ #define I18N_PHONENUMBERS_DEFAULT_LOGGER_H_ -#ifdef USE_GOOGLE_BASE - -namespace i18n { -namespace phonenumbers { - -// If Google base/ is used, LOG() and VLOG() from base/logging.h are used -// therefore the default logger implementation (StdoutLogger) instantiated in -// phonenumberutil will actually never be used. Thus provide a dummy -// implementation of this logger. -class StdoutLogger : public Logger { - public: - virtual ~StdoutLogger() {} - - virtual void WriteLevel() {} - virtual void WriteMessage(const string& /* msg */) {} -}; - -} // namespace phonenumbers -} // namespace i18n - -#else - #include #include "phonenumbers/logger.h" @@ -125,5 +103,4 @@ class StdoutLogger : public Logger { } // namespace phonenumbers } // namespace i18n -#endif // USE_GOOGLE_BASE #endif // I18N_PHONENUMBERS_DEFAULT_LOGGER_H_ diff --git a/tools/java/common/src/com/google/i18n/phonenumbers/tools/BuildMetadataFromXml.java b/tools/java/common/src/com/google/i18n/phonenumbers/tools/BuildMetadataFromXml.java index 3b19d736f..798668926 100644 --- a/tools/java/common/src/com/google/i18n/phonenumbers/tools/BuildMetadataFromXml.java +++ b/tools/java/common/src/com/google/i18n/phonenumbers/tools/BuildMetadataFromXml.java @@ -32,6 +32,7 @@ import java.util.Map; 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; @@ -44,7 +45,44 @@ import javax.xml.parsers.DocumentBuilderFactory; */ 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, @@ -70,7 +108,7 @@ public class BuildMetadataFromXml { // 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> buildCountryCodeToRegionCodeMap( PhoneMetadataCollection metadataCollection) { @@ -99,7 +137,8 @@ public class BuildMetadataFromXml { 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) { @@ -111,55 +150,133 @@ public class BuildMetadataFromXml { 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(); @@ -168,63 +285,25 @@ public class BuildMetadataFromXml { 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 @@ -235,33 +314,11 @@ public class BuildMetadataFromXml { 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++) { @@ -271,9 +328,10 @@ public class BuildMetadataFromXml { } } - 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) @@ -281,15 +339,22 @@ public class BuildMetadataFromXml { 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 @@ -306,15 +371,13 @@ public class BuildMetadataFromXml { * 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; @@ -322,20 +385,20 @@ public class BuildMetadataFromXml { 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()); } @@ -343,4 +406,40 @@ public class BuildMetadataFromXml { } 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(); + } } diff --git a/tools/java/common/test/com/google/i18n/phonenumbers/tools/BuildMetadataFromXmlTest.java b/tools/java/common/test/com/google/i18n/phonenumbers/tools/BuildMetadataFromXmlTest.java new file mode 100644 index 000000000..42fe782be --- /dev/null +++ b/tools/java/common/test/com/google/i18n/phonenumbers/tools/BuildMetadataFromXmlTest.java @@ -0,0 +1,504 @@ +// 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 = ""; + Element territoryElement = parseXmlString(xmlInput); + assertEquals("00", BuildMetadataFromXml.getNationalPrefix(territoryElement)); + } + + // Tests loadTerritoryTagMetadata(). + public void testLoadTerritoryTagMetadata() + throws ParserConfigurationException, SAXException, IOException { + String xmlInput = + "" + + ""; + 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 = ""; + 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 = ""; + 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 = ""; + 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 = "" + intlFormat + ""; + 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 = "" + intlFormat + ""; + 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 = ""; + 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 = ""; + 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("%s", + 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 = ""; + 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 = ""; + 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 = + "" + + " " + + " " + + " $1 $2 $3" + + " " + + " " + + ""; + 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 = + "" + + " " + + " " + + " $1 $2 $3" + + " " + + " " + + ""; + 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 = + "" + + " " + + " $1 $2 $3" + + " " + + ""; + 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 = + "" + + " " + + " $1 $2 $3" + + " " + + ""; + 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 = + "" + + " " + + " $1 $2 $3" + + " $1-$2" + + " " + + ""; + 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 = "NA"; + 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 = + "" + + "12" + + ""; + 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 = ""; + Element element = parseXmlString(xmlInput); + assertEquals("0$1", + BuildMetadataFromXml.getNationalPrefixFormattingRuleFromElement(element, "0")); + } + + // Tests getDomesticCarrierCodeFormattingRuleFromElement(). + public void testGetDomesticCarrierCodeFormattingRuleFromElement() + throws ParserConfigurationException, SAXException, IOException { + String xmlInput = ""; + 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(""); + 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(""); + 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 = + "" + + " \\d{6}" + + ""; + 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 = + "" + + " 01 01 01 01" + + ""; + 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 = + "" + + " 01 01 01 01" + + ""; + 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 = + "" + + " \t \\d { 6 } " + + ""; + 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 = + "" + + " \\d{6}" + + " \\d{6}" + + ""; + 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 = + "" + + " \\d{1}" + + " \\d{2}" + + " \\d{3}" + + " \\d{4}" + + " \\d{5}" + + " \\d{6}" + + " \\d{7}" + + " \\d{8}" + + " \\d{9}" + + " \\d{10}" + + ""; + 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()); + } +} diff --git a/tools/java/cpp-build/target/cpp-build-1.0-SNAPSHOT-jar-with-dependencies.jar b/tools/java/cpp-build/target/cpp-build-1.0-SNAPSHOT-jar-with-dependencies.jar index f9db26e59..f042be740 100644 Binary files a/tools/java/cpp-build/target/cpp-build-1.0-SNAPSHOT-jar-with-dependencies.jar and b/tools/java/cpp-build/target/cpp-build-1.0-SNAPSHOT-jar-with-dependencies.jar differ diff --git a/tools/java/java-build/target/java-build-1.0-SNAPSHOT-jar-with-dependencies.jar b/tools/java/java-build/target/java-build-1.0-SNAPSHOT-jar-with-dependencies.jar index 426827c81..b66b4a354 100644 Binary files a/tools/java/java-build/target/java-build-1.0-SNAPSHOT-jar-with-dependencies.jar and b/tools/java/java-build/target/java-build-1.0-SNAPSHOT-jar-with-dependencies.jar differ