Browse Source

Update library with new functionalities and metadata.

pull/567/head
Shaopeng Jia 16 years ago
committed by Mihaela Rosca
parent
commit
72660ad60c
11 changed files with 1090 additions and 443 deletions
  1. +31
    -0
      java/resources/com/google/i18n/phonenumbers/BuildMetadataProtoFromXml.java
  2. +21
    -2
      java/resources/com/google/i18n/phonenumbers/proto/phonemetadata.proto
  3. +496
    -106
      java/resources/com/google/i18n/phonenumbers/src/PhoneNumberMetaData.xml
  4. +61
    -2
      java/resources/com/google/i18n/phonenumbers/test/PhoneNumberMetaDataForTesting.xml
  5. +56
    -57
      java/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java
  6. BIN
      java/src/com/google/i18n/phonenumbers/PhoneNumberMetadataProto
  7. +170
    -164
      java/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java
  8. +56
    -0
      java/src/com/google/i18n/phonenumbers/Phonemetadata.java
  9. +95
    -106
      java/test/com/google/i18n/phonenumbers/AsYouTypeFormatterTest.java
  10. BIN
      java/test/com/google/i18n/phonenumbers/PhoneNumberMetadataProtoForTesting
  11. +104
    -6
      java/test/com/google/i18n/phonenumbers/PhoneNumberUtilTest.java

+ 31
- 0
java/resources/com/google/i18n/phonenumbers/BuildMetadataProtoFromXml.java View File

@ -98,6 +98,9 @@ public class BuildMetadataProtoFromXml {
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");
@ -105,6 +108,7 @@ public class BuildMetadataProtoFromXml {
}
String nationalPrefix = "";
String nationalPrefixFormattingRule = "";
String carrierCodeFormattingRule = "";
if (element.hasAttribute("nationalPrefix")) {
nationalPrefix = element.getAttribute("nationalPrefix");
metadata.setNationalPrefix(nationalPrefix);
@ -126,6 +130,10 @@ public class BuildMetadataProtoFromXml {
metadata.setPreferredExtnPrefix(element.getAttribute("preferredExtnPrefix"));
}
if (element.hasAttribute("mainCountryForCode")) {
metadata.setMainCountryForCode(true);
}
// Extract availableFormats
NodeList numberFormatElements = element.getElementsByTagName("numberFormat");
int numOfFormatElements = numberFormatElements.getLength();
@ -139,6 +147,13 @@ public class BuildMetadataProtoFromXml {
} else {
format.setNationalPrefixFormattingRule(nationalPrefixFormattingRule);
}
if (numberFormatElement.hasAttribute("carrierCodeFormattingRule")) {
format.setDomesticCarrierCodeFormattingRule(validateRE(
getDomesticCarrierCodeFormattingRuleFromElement(numberFormatElement,
nationalPrefix)));
} else {
format.setDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule);
}
if (numberFormatElement.hasAttribute("leadingDigits")) {
format.setLeadingDigits(validateRE(numberFormatElement.getAttribute("leadingDigits")));
}
@ -159,6 +174,13 @@ public class BuildMetadataProtoFromXml {
}
format.setPattern(validateRE(numberFormatElement.getAttribute("pattern")));
format.setFormat(validateRE(numberFormatElement.getFirstChild().getNodeValue()));
if (numberFormatElement.hasAttribute("carrierCodeFormattingRule")) {
format.setDomesticCarrierCodeFormattingRule(validateRE(
getDomesticCarrierCodeFormattingRuleFromElement(numberFormatElement,
nationalPrefix)));
} else {
format.setDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule);
}
metadata.addIntlNumberFormat(format);
}
}
@ -192,6 +214,15 @@ public class BuildMetadataProtoFromXml {
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


+ 21
- 2
java/resources/com/google/i18n/phonenumbers/proto/phonemetadata.proto View File

@ -64,6 +64,12 @@ message NumberFormat {
// prefix in NATIONAL format. This field does not affect how a number
// is formatted in other formats, such as INTERNATIONAL.
optional string national_prefix_formatting_rule = 4;
// This field specifies how any carrier code ($CC) together with the first
// group ($FG) in the national significant number should be formatted
// when formatWithCarrierCode is called, if carrier codes are used for a
// certain country.
optional string domestic_carrier_code_formatting_rule = 5;
}
message PhoneNumberDesc {
@ -193,8 +199,21 @@ message PhoneMetadata {
// 15 (inserted after the area code of 343) is used.
repeated NumberFormat intl_number_format = 20;
// Deprecated.
optional string national_prefix_formatting_rule = 21;
// This field is set when this country is considered to be the main country
// for a calling code. It may not be set by more than one country with the
// same calling code, and it should not be set by countries with a unique
// calling code. This can be used to indicate that "GB" is the main country
// for the calling code "44" for example, rather than Jersey or the Isle of
// Man.
optional bool main_country_for_code = 22 [default=false];
// This field is populated only for countries or regions that share a country
// calling code. If a number matches this pattern, it could belong to this
// region. This is not intended as a replacement for IsValidForRegion, and
// does not mean the number must come from this region (for example, 800
// numbers are valid for all NANPA countries.) This field should be a regular
// expression of the expected prefix match.
optional string leading_digits = 23;
}
message PhoneMetadataCollection {


+ 496
- 106
java/resources/com/google/i18n/phonenumbers/src/PhoneNumberMetaData.xml
File diff suppressed because it is too large
View File


+ 61
- 2
java/resources/com/google/i18n/phonenumbers/test/PhoneNumberMetaDataForTesting.xml View File

@ -38,7 +38,7 @@
<numberFormat leadingDigits="11" pattern="(\d{2})(\d{4})(\d{4})">$1 $2-$3</numberFormat>
<numberFormat leadingDigits="1[02-9]|[23]" pattern="(\d{4})(\d{2})(\d{4})">$1 $2-$3</numberFormat>
<numberFormat leadingDigits="911" pattern="9(11)(\d{4})(\d{4})">$1 15 $2-$3</numberFormat>
<numberFormat leadingDigits="9(?:1[02-9]|[23])" pattern="9(\d{4})(\d{2})(\d{4})">$1 15 $2-$3</numberFormat>
<numberFormat leadingDigits="9(?:1[02-9]|[23])" pattern="9(\d{4})(\d{2})(\d{4})" carrierCodeFormattingRule="$FG $CC">$1 $2-$3</numberFormat>
<numberFormat leadingDigits="[68]" pattern="(\d{3})(\d{3})(\d{4})">$1-$2-$3</numberFormat>
<intlNumberFormat leadingDigits="11" pattern="(\d{2})(\d{4})(\d{4})">$1 $2-$3</intlNumberFormat>
<intlNumberFormat leadingDigits="1[02-9]|[23]" pattern="(\d{4})(\d{2})(\d{4})">$1 $2-$3</intlNumberFormat>
@ -370,6 +370,41 @@
</premiumRate>
</territory>
<!-- Réunion (French Departments and Territories in the Indian Ocean) -->
<!-- Note this shares the same country code as La Mayotte and French
Southern Territories, and the formatting patterns here are used by all of
them. This is present to test leadingDigits. -->
<territory id="RE" countryCode="262" leadingDigits="262|6(?:9[23]|47)|8"
internationalPrefix="00" nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG">
<availableFormats>
<numberFormat pattern="([268]\d{2})(\d{2})(\d{2})(\d{2})">$1 $2 $3 $4</numberFormat>
</availableFormats>
<generalDesc>
<nationalNumberPattern>[268]\d{8}</nationalNumberPattern>
<possibleNumberPattern>\d{9}</possibleNumberPattern>
</generalDesc>
<fixedLine>
<!-- 0876 numbers are mentioned in the plan, but none in use can be
found. -->
<nationalNumberPattern>262\d{6}</nationalNumberPattern>
<exampleNumber>262161234</exampleNumber>
</fixedLine>
<mobile>
<nationalNumberPattern>6(?:9[23]|47)\d{6}</nationalNumberPattern>
<possibleNumberPattern>\d{9}</possibleNumberPattern>
<exampleNumber>692123456</exampleNumber>
</mobile>
<!-- 08* Numbers in Réunion are the same as those valid in France. -->
<tollFree>
<nationalNumberPattern>80\d{7}</nationalNumberPattern>
<exampleNumber>801234567</exampleNumber>
</tollFree>
<premiumRate>
<nationalNumberPattern>8(?:1[01]|2[0156]|84|9[0-37-9])\d{6}</nationalNumberPattern>
<exampleNumber>810123456</exampleNumber>
</premiumRate>
</territory>
<!-- Singapore -->
<!-- http://www.ida.gov.sg/policies%20and%20regulation/20060508120124.aspx -->
<territory id="SG" countryCode="65" internationalPrefix="0[0-3][0-9]">
@ -405,7 +440,8 @@
<!-- For testing purposes, numbers starting with 24 are not considered US
numbers.-->
<territory id="US" countryCode="1" internationalPrefix="011"
preferredExtnPrefix=" extn. ">
preferredExtnPrefix=" extn. "
mainCountryForCode="true">
<availableFormats>
<numberFormat pattern="(\d{3})(\d{3})(\d{4})">$1 $2 $3</numberFormat>
<numberFormat pattern="(\d{3})(\d{4})">$1 $2</numberFormat>
@ -424,5 +460,28 @@
<possibleNumberPattern>\d{10}</possibleNumberPattern>
</premiumRate>
</territory>
<!-- Mayotte -->
<territory id="YT" countryCode="262" leadingDigits="269|639"
internationalPrefix="00" nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG">
<!-- Formatting as per La Réunion. -->
<generalDesc>
<nationalNumberPattern>[268]\d{8}</nationalNumberPattern>
<possibleNumberPattern>\d{9}</possibleNumberPattern>
</generalDesc>
<fixedLine>
<nationalNumberPattern>2696[0-4]\d{4}</nationalNumberPattern>
<exampleNumber>269601234</exampleNumber>
</fixedLine>
<mobile>
<nationalNumberPattern>639\d{6}</nationalNumberPattern>
<exampleNumber>639123456</exampleNumber>
</mobile>
<!-- Same as in France. -->
<tollFree>
<nationalNumberPattern>80\d{7}</nationalNumberPattern>
<exampleNumber>801234567</exampleNumber>
</tollFree>
</territory>
</territories>
</phoneNumberMetadata>

+ 56
- 57
java/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java View File

@ -49,23 +49,31 @@ public class AsYouTypeFormatter {
private Phonemetadata.PhoneMetadata defaultMetaData;
private PhoneMetadata currentMetaData;
// A pattern that is used to match character classes in regular expressions. An example of a
// character class is [1-4].
private final Pattern CHARACTER_CLASS_PATTERN = Pattern.compile("\\[([^\\[\\]])*\\]");
// Any digit in a regular expression that actually denotes a digit. For example, in the regular
// expression 80[0-2]\d{6,10}, the first 2 digits (8 and 0) are standalone digits, but the rest
// are not.
// Two look-aheads are needed because the number following \\d could be a two-digit number, since
// the phone number can be as long as 15 digits.
private static final Pattern STANDALONE_DIGIT_PATTERN = Pattern.compile("\\d(?=[^,}][^,}])");
// The digits that have not been entered yet will be represented by a \u2008, the punctuation
// space.
private String digitPlaceholder = "\u2008";
private Pattern digitPattern = Pattern.compile(digitPlaceholder);
private int lastMatchPosition = 0;
private boolean rememberPosition = false;
// The position of a digit upon which inputDigitAndRememberPosition is most recently invoked, as
// found in the current output.
private int positionRemembered = 0;
// The position of a digit upon which inputDigitAndRememberPosition is most recently invoked, as
// found in the original sequence of characters the user entered.
private int originalPosition = 0;
private Pattern nationalPrefixForParsing;
private Pattern internationalPrefix;
private StringBuffer prefixBeforeNationalNumber;
private StringBuffer nationalNumber;
// No formatting will be applied when any of the character in the following character class is
// entered by users.
private final Pattern UNSUPPORTED_SYNTAX = Pattern.compile("[- *#;,.()/a-zA-Z]");
private final Pattern CHARACTER_CLASS_PATTERN = Pattern.compile("\\[([^\\[\\]])*\\]");
private final Pattern STANDALONE_DIGIT_PATTERN = Pattern.compile("\\d(?=[^,}][^,}])");
/**
* Constructs a light-weight formatter which does no formatting, but outputs exactly what is
@ -182,25 +190,32 @@ public class AsYouTypeFormatter {
}
}
public String inputDigit(char nextChar) {
return inputDigitWithOptionToRememberPosition(nextChar, false);
}
/**
* Formats a phone number on-the-fly as each digit is entered.
*
* @param nextChar the most recently entered digit of a phone number. Formatting characters are
* allowed, but they are removed from the result. Full width digits and Arabic-indic digits
* are allowed, and will be shown as they are.
* @return the partially formatted phone number.
* Same as inputDigit, but remembers the position where nextChar is inserted, so that it could be
* retrieved later by using getRememberedPosition(). The remembered position will be automatically
* adjusted if additional formatting characters are later inserted/removed in front of nextChar.
*/
public String inputDigit(char nextChar) {
public String inputDigitAndRememberPosition(char nextChar) {
return inputDigitWithOptionToRememberPosition(nextChar, true);
}
private String inputDigitWithOptionToRememberPosition(char nextChar, boolean rememberPosition) {
accruedInput.append(nextChar);
rememberPosition();
if (UNSUPPORTED_SYNTAX.matcher(Character.toString(nextChar)).matches()) {
if (rememberPosition) {
positionRemembered = accruedInput.length();
originalPosition = positionRemembered;
}
// We do formatting on-the-fly only when each character entered is either a plus sign or a
// digit.
if (!PhoneNumberUtil.VALID_START_CHAR_PATTERN.matcher(Character.toString(nextChar)).matches()) {
ableToFormat = false;
}
if (!ableToFormat) {
if (positionRemembered > 0 && currentOutput.length() > 0) {
positionRemembered = originalPosition;
currentOutput.setLength(0);
}
resetPositionOnFailureToFormat();
return accruedInput.toString();
}
@ -222,38 +237,26 @@ public class AsYouTypeFormatter {
return accruedInput.toString();
}
removeNationalPrefixFromNationalNumber();
return attemptToChooseFormattingPattern();
return attemptToChooseFormattingPattern(rememberPosition);
default:
if (nationalNumber.length() > 4) { // The formatting pattern is already chosen.
String temp = inputDigitHelper(nextChar);
String temp = inputDigitHelper(nextChar, rememberPosition);
return ableToFormat
? prefixBeforeNationalNumber + temp
: temp;
} else {
return attemptToChooseFormattingPattern();
return attemptToChooseFormattingPattern(rememberPosition);
}
}
}
private void rememberPosition() {
if (rememberPosition) {
positionRemembered = accruedInput.length();
originalPosition = positionRemembered;
private void resetPositionOnFailureToFormat() {
if (positionRemembered > 0) {
positionRemembered = originalPosition;
currentOutput.setLength(0);
}
}
/**
* Same as inputDigit, but remembers the position where nextChar is inserted, so that it could be
* retrieved later by using getRememberedPosition(). The remembered position will be automatically
* adjusted if additional formatting characters are later inserted/removed in front of nextChar.
*/
public String inputDigitAndRememberPosition(char nextChar) {
rememberPosition = true;
String result = inputDigit(nextChar);
rememberPosition = false;
return result;
}
/**
* Returns the current position in the partially formatted phone number of the character which was
* previously passed in as the parameter of inputDigitAndRememberPosition().
@ -264,12 +267,12 @@ public class AsYouTypeFormatter {
// Attempts to set the formatting template and returns a string which contains the formatted
// version of the digits entered so far.
private String attemptToChooseFormattingPattern() {
private String attemptToChooseFormattingPattern(boolean rememberPosition) {
// We start to attempt to format only when as least 4 digits of national number (excluding
// national prefix) have been entered.
if (nationalNumber.length() >= 4) {
chooseFormatAndCreateTemplate(nationalNumber.substring(0, 4));
return inputAccruedNationalNumber();
return inputAccruedNationalNumber(rememberPosition);
} else {
if (rememberPosition) {
positionRemembered = prefixBeforeNationalNumber.length() + nationalNumber.length();
@ -280,23 +283,23 @@ public class AsYouTypeFormatter {
// Invokes inputDigitHelper on each digit of the national number accrued, and returns a formatted
// string in the end.
private String inputAccruedNationalNumber() {
private String inputAccruedNationalNumber(boolean rememberPosition) {
int lengthOfNationalNumber = nationalNumber.length();
if (lengthOfNationalNumber > 0) {
// The positionRemembered should be only adjusted once in the loop that follows.
Boolean positionAlreadyAdjusted = false;
for (int i = 0; i < lengthOfNationalNumber - 1; i++) {
String temp = inputDigitHelper(nationalNumber.charAt(i));
boolean positionAlreadyAdjusted = false;
String tempNationalNumber = "";
for (int i = 0; i < lengthOfNationalNumber; i++) {
tempNationalNumber = inputDigitHelper(nationalNumber.charAt(i), rememberPosition);
if (!positionAlreadyAdjusted &&
positionRemembered - prefixBeforeNationalNumber.length() == i + 1) {
positionRemembered = prefixBeforeNationalNumber.length() + temp.length();
positionRemembered = prefixBeforeNationalNumber.length() + tempNationalNumber.length();
positionAlreadyAdjusted = true;
}
}
String temp = inputDigitHelper(nationalNumber.charAt(lengthOfNationalNumber - 1));
return ableToFormat
? prefixBeforeNationalNumber + temp
: temp;
? prefixBeforeNationalNumber + tempNationalNumber
: tempNationalNumber;
} else {
if (rememberPosition) {
positionRemembered = prefixBeforeNationalNumber.length();
@ -309,13 +312,12 @@ public class AsYouTypeFormatter {
int startOfNationalNumber = 0;
if (currentMetaData.getCountryCode() == 1 && nationalNumber.charAt(0) == '1') {
startOfNationalNumber = 1;
prefixBeforeNationalNumber.append("1");
// Since a space will be inserted after the national prefix in this case, we increase the
prefixBeforeNationalNumber.append("1 ");
// Since a space is inserted after the national prefix in this case, we increase the
// remembered position by 1 for anything that is after the national prefix.
if (positionRemembered > prefixBeforeNationalNumber.length()) {
if (positionRemembered > prefixBeforeNationalNumber.length() - 1) {
positionRemembered++;
}
prefixBeforeNationalNumber.append(" ");
} else if (currentMetaData.hasNationalPrefix()) {
Matcher m = nationalPrefixForParsing.matcher(nationalNumber);
if (m.lookingAt()) {
@ -392,7 +394,7 @@ public class AsYouTypeFormatter {
return nextChar;
}
private String inputDigitHelper(char nextChar) {
private String inputDigitHelper(char nextChar, boolean rememberPosition) {
if (!PhoneNumberUtil.DIGIT_MAPPINGS.containsKey(nextChar)) {
return currentOutput.toString();
}
@ -408,10 +410,7 @@ public class AsYouTypeFormatter {
} else { // More digits are entered than we could handle.
currentOutput.append(nextChar);
ableToFormat = false;
if (positionRemembered > 0) {
positionRemembered = originalPosition;
currentOutput.setLength(0);
}
resetPositionOnFailureToFormat();
return accruedInput.toString();
}
}


BIN
java/src/com/google/i18n/phonenumbers/PhoneNumberMetadataProto View File


+ 170
- 164
java/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java View File

@ -26,6 +26,7 @@ import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber.CountryCodeSource;
import java.io.InputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@ -53,33 +54,19 @@ public class PhoneNumberUtil {
"/com/google/i18n/phonenumbers/PhoneNumberMetadataProto";
private static final Logger LOGGER = Logger.getLogger(PhoneNumberUtil.class.getName());
// A mapping from a country code to a region code which denotes the country/region
// represented by that country code. Note countries under NANPA share the country code 1,
// Russia and Kazakhstan share the country code 7, and many French territories in the Indian
// Ocean share the country code 262. Under this map, 1 is mapped to US, 7 is mapped to RU,
// and 262 is mapped to RE. The initial capacity is set to 300 as there are roughly 200 different
// 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. The initial capacity is set to 300 as there are roughly 200 different
// country codes, and this offers a load factor of roughly 0.75.
private final HashMap<Integer, String> countryCodeToRegionCodeMap =
new HashMap<Integer, String>(310);
private final Map<Integer, List<String> > countryCodeToRegionCodeMap =
new HashMap<Integer, List<String> >(310);
// The set of countries that share country code 1. There are roughly 26 countries of them and we
// set the initial capacity of the HashSet to 35 to offer a load factor of roughly 0.75.
private final HashSet<String> nanpaCountries = new HashSet<String>(35);
private static final int NANPA_COUNTRY_CODE = 1;
// The set of countries that share country code 7.
private final HashSet<String> russiaFederationCountries = new HashSet<String>(3);
private static final int RUSSIAN_FED_COUNTRY_CODE = 7;
// The set of countries that share country code 44.
private final HashSet<String> greatBritainAndDependencies = new HashSet<String>(7);
private static final int GREAT_BRITAIN_COUNTRY_CODE = 44;
// The set of countries that share country code 262.
private final HashSet<String> frenchIndianOceanTerritories = new HashSet<String>(6);
private static final int FRENCH_INDIAN_OCEAN_COUNTRY_CODE = 262;
// The PLUS_SIGN signifies the international prefix.
static final char PLUS_SIGN = '+';
@ -173,7 +160,6 @@ public class PhoneNumberUtil {
aSet.add(225); // Cote d'Ivoire
aSet.add(227); // Niger
aSet.add(228); // Togo
aSet.add(240); // Equatorial Guinea
aSet.add(241); // Gabon
aSet.add(379); // Vatican City
LEADING_ZERO_COUNTRIES = Collections.unmodifiableSet(aSet);
@ -214,7 +200,7 @@ public class PhoneNumberUtil {
// not include other punctuation, as this will be stripped later during parsing and is of no
// information value when parsing a number.
private static final String VALID_START_CHAR = "[" + PLUS_CHARS + VALID_DIGITS + "]";
private static final Pattern VALID_START_CHAR_PATTERN = Pattern.compile(VALID_START_CHAR);
static final Pattern VALID_START_CHAR_PATTERN = Pattern.compile(VALID_START_CHAR);
// Regular expression of characters typically used to start a second phone number for the purposes
// of parsing. This allows us to strip off parts of the number that are actually the start of
@ -260,8 +246,8 @@ public class PhoneNumberUtil {
// Note that the only capturing groups should be around the digits that you want to capture as
// part of the extension, or else parsing will fail!
private static final String KNOWN_EXTN_PATTERNS = "[ \u00A0\\t,]*(?:ext(?:ensio)?n?|" +
"\uFF45\uFF58\uFF54\uFF4E?|[,x\uFF58#\uFF03~\uFF5E]|int|\uFF49\uFF4E\uFF54)" +
"[:\\.\uFF0E]?[ \u00A0\\t,]*([" + VALID_DIGITS + "]{1,7})|[- ]+([" + VALID_DIGITS +
"\uFF45\uFF58\uFF54\uFF4E?|[,x\uFF58#\uFF03~\uFF5E]|int|anexo|\uFF49\uFF4E\uFF54)" +
"[:\\.\uFF0E]?[ \u00A0\\t,-]*([" + VALID_DIGITS + "]{1,7})|[- ]+([" + VALID_DIGITS +
"]{1,5})#";
// Regexp of all known extension prefixes used by different countries followed by 1 or more valid
@ -276,10 +262,12 @@ public class PhoneNumberUtil {
Pattern.compile(VALID_PHONE_NUMBER + "(?:" + KNOWN_EXTN_PATTERNS + ")?",
Pattern.UNICODE_CASE | Pattern.CASE_INSENSITIVE);
private static final Pattern NON_DIGIT_PATTERN = Pattern.compile("(\\D+)");
private static final Pattern NON_DIGITS_PATTERN = Pattern.compile("(\\D+)");
private static final Pattern FIRST_GROUP_PATTERN = Pattern.compile("(\\$1)");
private static final Pattern NP_PATTERN = Pattern.compile("\\$NP");
private static final Pattern FG_PATTERN = Pattern.compile("\\$FG");
private static final Pattern CC_PATTERN = Pattern.compile("\\$CC");
private static final Pattern NON_DIGIT_PATTERN = Pattern.compile("\\D");
private static PhoneNumberUtil instance = null;
@ -287,10 +275,10 @@ public class PhoneNumberUtil {
private HashMap<String, PhoneMetadata> countryToMetadataMap =
new HashMap<String, PhoneMetadata>();
// A cache for frequently used regular expressions. As most people use phone numbers primarily
// from one country, and there are roughly 30 regular expressions needed, the initial capacity of
// 50 offers a rough load factor of 0.75.
private RegexCache regexCache = new RegexCache(50);
// A cache for frequently used country-specific regular expressions. As most people use phone
// numbers primarily from one to two countries, and there are roughly 60 regular expressions
// needed, the initial capacity of 100 offers a rough load factor of 0.75.
private RegexCache regexCache = new RegexCache(100);
/**
* INTERNATIONAL and NATIONAL formats are consistent with the definition in ITU-T Recommendation
@ -371,34 +359,23 @@ public class PhoneNumberUtil {
countryToMetadataMap.put(regionCode, metadata);
countryToMetadataMap.put(regionCode.toLowerCase(), metadata);
int countryCode = metadata.getCountryCode();
switch (countryCode) {
case NANPA_COUNTRY_CODE:
nanpaCountries.add(regionCode);
nanpaCountries.add(regionCode.toLowerCase());
break;
case RUSSIAN_FED_COUNTRY_CODE:
russiaFederationCountries.add(regionCode);
russiaFederationCountries.add(regionCode.toLowerCase());
break;
case FRENCH_INDIAN_OCEAN_COUNTRY_CODE:
frenchIndianOceanTerritories.add(regionCode);
frenchIndianOceanTerritories.add(regionCode.toLowerCase());
break;
case GREAT_BRITAIN_COUNTRY_CODE:
greatBritainAndDependencies.add(regionCode);
break;
default:
countryCodeToRegionCodeMap.put(countryCode, regionCode);
break;
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 dialing code.
List<String> listWithRegionCode = new ArrayList<String>(1);
listWithRegionCode.add(regionCode);
countryCodeToRegionCodeMap.put(countryCode, listWithRegionCode);
}
if (countryCode == NANPA_COUNTRY_CODE) {
nanpaCountries.add(regionCode);
nanpaCountries.add(regionCode.toLowerCase());
}
}
// Override the value, so that 1 is always mapped to US, 7 is always mapped to RU, 44 to GB
// and 262 to RE.
countryCodeToRegionCodeMap.put(NANPA_COUNTRY_CODE, "US");
countryCodeToRegionCodeMap.put(RUSSIAN_FED_COUNTRY_CODE, "RU");
countryCodeToRegionCodeMap.put(FRENCH_INDIAN_OCEAN_COUNTRY_CODE, "RE");
countryCodeToRegionCodeMap.put(GREAT_BRITAIN_COUNTRY_CODE, "GB");
} catch (IOException e) {
LOGGER.log(Level.WARNING, e.toString());
} catch (ClassNotFoundException e) {
@ -422,14 +399,14 @@ public class PhoneNumberUtil {
* found in the number
*/
static String extractPossibleNumber(String number) {
// Remove trailing non-alpha non-numerical characters.
Matcher trailingCharsMatcher = UNWANTED_END_CHAR_PATTERN.matcher(number);
if (trailingCharsMatcher.find()) {
number = number.substring(0, trailingCharsMatcher.start());
}
Matcher m = VALID_START_CHAR_PATTERN.matcher(number);
if (m.find()) {
number = number.substring(m.start());
// Remove trailing non-alpha non-numerical characters.
Matcher trailingCharsMatcher = UNWANTED_END_CHAR_PATTERN.matcher(number);
if (trailingCharsMatcher.find()) {
number = number.substring(0, trailingCharsMatcher.start());
}
// Check for extra numbers at the end.
Matcher secondNumber = SECOND_NUMBER_START_PATTERN.matcher(number);
if (secondNumber.find()) {
@ -710,9 +687,9 @@ public Set<String> getSupportedCountries() {
formatNumberByFormat(countryCode, PhoneNumberFormat.E164, formattedNumber);
return;
}
// Note here that all NANPA formatting rules are contained by US, so we use that to format NANPA
// numbers. The same applies to Russian Fed countries - rules are contained by Russia. French
// Indian Ocean country rules are contained by Reunion.
// Note getRegionCodeForCountryCode() is used because formatting information for countries which
// share a country code is contained by only one country for performance reasons. For example,
// for NANPA countries it will be contained in the metadata for US.
String regionCode = getRegionCodeForCountryCode(countryCode);
if (!isValidRegionCode(regionCode)) {
formattedNumber.append(nationalSignificantNumber);
@ -740,11 +717,11 @@ public Set<String> getSupportedCountries() {
PhoneNumberFormat numberFormat,
List<NumberFormat> userDefinedFormats) {
int countryCode = number.getCountryCode();
String nationalSignificantNumber = getNationalSignificantNumber(number);
// Note getRegionCodeForCountryCode() is used because formatting information for countries which
// share a country code is contained by only one country for performance reasons. For example,
// for NANPA countries it will be contained in the metadata for US.
String regionCode = getRegionCodeForCountryCode(countryCode);
String nationalSignificantNumber = getNationalSignificantNumber(number);
if (!isValidRegionCode(regionCode)) {
return nationalSignificantNumber;
}
@ -772,6 +749,27 @@ public Set<String> getSupportedCountries() {
return formattedNumber.toString();
}
public String formatNationalNumberWithCarrierCode(PhoneNumber number, String carrierCode) {
int countryCode = number.getCountryCode();
String nationalSignificantNumber = getNationalSignificantNumber(number);
// Note getRegionCodeForCountryCode() is used because formatting information for countries which
// share a country code is contained by only one country for performance reasons. For example,
// for NANPA countries it will be contained in the metadata for US.
String regionCode = getRegionCodeForCountryCode(countryCode);
if (!isValidRegionCode(regionCode)) {
return nationalSignificantNumber;
}
StringBuffer formattedNumber = new StringBuffer(20);
formattedNumber.append(formatNationalNumber(nationalSignificantNumber,
regionCode,
PhoneNumberFormat.NATIONAL,
carrierCode));
maybeGetFormattedExtension(number, regionCode, formattedNumber);
formatNumberByFormat(countryCode, PhoneNumberFormat.NATIONAL, formattedNumber);
return formattedNumber.toString();
}
/**
* Formats a phone number for out-of-country dialing purpose. If no countryCallingFrom
* is supplied, we format the number in its INTERNATIONAL format. If the countryCallingFrom is
@ -796,55 +794,24 @@ public Set<String> getSupportedCountries() {
return format(number, PhoneNumberFormat.INTERNATIONAL);
}
int countryCode = number.getCountryCode();
if (countryCode == NANPA_COUNTRY_CODE && isNANPACountry(countryCallingFrom)) {
// For NANPA countries, return the national format for these countries but prefix it with the
// country code.
return countryCode + " " + format(number, PhoneNumberFormat.NATIONAL);
}
if (countryCode == FRENCH_INDIAN_OCEAN_COUNTRY_CODE &&
frenchIndianOceanTerritories.contains(countryCallingFrom)) {
// For dialling between FRENCH_INDIAN_OCEAN countries, the 10 digit number is all we need.
// Technically this is the case for dialling from la Reunion to other overseas departments of
// France (French Guiana, Martinique, Guadeloupe), but not vice versa - so we don't cover this
// edge case for now and for those cases return the version including country code.
// Details here: http://www.petitfute.com/voyage/225-info-pratiques-reunion
return format(number, PhoneNumberFormat.NATIONAL);
}
if (countryCode == GREAT_BRITAIN_COUNTRY_CODE &&
greatBritainAndDependencies.contains(countryCallingFrom)) {
// It seems that numbers can be dialled in national format between Great Britain and the crown
// dependencies with the same country code.
return format(number, PhoneNumberFormat.NATIONAL);
}
// If the country code is the Russian Fed country code, we check the number itself to determine
// which region code it is for. We don't do this for NANPA countries because of performance
// reasons, and instead use US rules for all NANPA numbers. Also, NANPA countries share the
// same national and international prefixes, which is not the case for Russian Fed countries.
// There is also a special case for toll-free and premium rate numbers dialled within Russian
// Fed countries.
String regionCode;
if (countryCode == RUSSIAN_FED_COUNTRY_CODE) {
if (russiaFederationCountries.contains(countryCallingFrom)) {
// For toll-free numbers and premium rate numbers dialled from within Russian Fed countries,
// we should format them as if they are local numbers.
// A toll-free number would be dialled from KZ as 8-800-080-7777 but from Russia as
// 0-800-080-7777. (Confirmation on government websites such as e.gov.kz).
PhoneNumberType numberType = getNumberType(number);
if (numberType == PhoneNumberType.TOLL_FREE || numberType == PhoneNumberType.PREMIUM_RATE) {
return format(number, PhoneNumberFormat.NATIONAL);
}
}
// Otherwise, we should find out what region the number really belongs to before continuing,
// since they have different formatting rules.
regionCode = getRegionCodeForNumber(number);
} else {
regionCode = getRegionCodeForCountryCode(countryCode);
}
String regionCode = getRegionCodeForCountryCode(countryCode);
String nationalSignificantNumber = getNationalSignificantNumber(number);
if (!isValidRegionCode(regionCode)) {
return nationalSignificantNumber;
}
if (regionCode.equalsIgnoreCase(countryCallingFrom)) {
if (countryCode == NANPA_COUNTRY_CODE) {
if (isNANPACountry(countryCallingFrom)) {
// For NANPA countries, return the national format for these countries but prefix it with
// the country code.
return countryCode + " " + format(number, PhoneNumberFormat.NATIONAL);
}
} else if (countryCode == getCountryCodeForRegion(countryCallingFrom)) {
// For countries that share a country calling code, the country code need not be dialled. This
// also applies when dialling within a country, so this if clause covers both these cases.
// Technically this is the case for dialling from la RŽunion to other overseas departments of
// France (French Guiana, Martinique, Guadeloupe), but not vice versa - so we don't cover this
// edge case for now and for those cases return the version including country code.
// Details here: http://www.petitfute.com/voyage/225-info-pratiques-reunion
return format(number, PhoneNumberFormat.NATIONAL);
}
String formattedNationalNumber =
@ -881,26 +848,25 @@ public Set<String> getSupportedCountries() {
* passed in. If such information is missing, the number will be formatted into the NATIONAL
* format by default.
*
* @param number The PhoneNumber that needs to be formatted in its original number format
* @param defaultCountry the country whose IDD needs to be appended if the original number has
* one
* @return The formatted phone number in its original number format
* @param number the PhoneNumber that needs to be formatted in its original number format
* @param countryCallingFrom the country whose IDD needs to be prefixed if the original number
* has one
* @return the formatted phone number in its original number format
*/
public String formatUsingOriginalNumberFormat(PhoneNumber number, String defaultCountry) {
if (!number.hasRawInput()) {
public String formatInOriginalFormat(PhoneNumber number, String countryCallingFrom) {
if (!number.hasCountryCodeSource()) {
return format(number, PhoneNumberFormat.NATIONAL);
}
switch (number.getCountryCodeSource()) {
case FROM_DEFAULT_COUNTRY:
return format(number, PhoneNumberFormat.NATIONAL);
case FROM_NUMBER_WITH_PLUS_SIGN:
return format(number, PhoneNumberFormat.INTERNATIONAL);
case FROM_NUMBER_WITH_IDD:
return formatOutOfCountryCallingNumber(number, defaultCountry);
return formatOutOfCountryCallingNumber(number, countryCallingFrom);
case FROM_NUMBER_WITHOUT_PLUS_SIGN:
return format(number, PhoneNumberFormat.INTERNATIONAL).substring(1);
case FROM_DEFAULT_COUNTRY:
default:
return number.getRawInput();
return format(number, PhoneNumberFormat.NATIONAL);
}
}
@ -947,12 +913,21 @@ public Set<String> getSupportedCountries() {
}
}
// Simple wrapper of formatNationalNumber for the common case of no carrier code.
private String formatNationalNumber(String number,
String regionCode,
PhoneNumberFormat numberFormat) {
return formatNationalNumber(number, regionCode, numberFormat, null);
}
// Note in some countries, the national number can be written in two completely different ways
// depending on whether it forms part of the NATIONAL format or INTERNATIONAL format. The
// numberFormat parameter here is used to specify which format to use for those cases.
// numberFormat parameter here is used to specify which format to use for those cases. If a
// carrierCode is specified, this will be inserted into the formatted string to replace $CC.
private String formatNationalNumber(String number,
String regionCode,
PhoneNumberFormat numberFormat) {
PhoneNumberFormat numberFormat,
String carrierCode) {
PhoneMetadata metadata = getMetadataForRegion(regionCode);
List<NumberFormat> intlNumberFormats = metadata.getIntlNumberFormatList();
// When the intlNumberFormats exists, we use that to format national number for the
@ -961,27 +936,48 @@ public Set<String> getSupportedCountries() {
(intlNumberFormats.size() == 0 || numberFormat == PhoneNumberFormat.NATIONAL)
? metadata.getNumberFormatList()
: metadata.getIntlNumberFormatList();
return formatAccordingToFormats(number, availableFormats, numberFormat);
return formatAccordingToFormats(number, availableFormats, numberFormat, carrierCode);
}
// Simple wrapper of formatAccordingToFormats for the common case of no carrier code.
private String formatAccordingToFormats(String nationalNumber,
List<NumberFormat> availableFormats,
PhoneNumberFormat numberFormat) {
return formatAccordingToFormats(nationalNumber, availableFormats, numberFormat, null);
}
// Note that carrierCode is optional - if NULL or an empty string, no carrier code replacement
// will take place. Carrier code replacement occurs before national prefix replacement.
private String formatAccordingToFormats(String nationalNumber,
List<NumberFormat> availableFormats,
PhoneNumberFormat numberFormat,
String carrierCode) {
for (NumberFormat numFormat : availableFormats) {
if (!numFormat.hasLeadingDigits() ||
regexCache.getPatternForRegex(numFormat.getLeadingDigits()).matcher(nationalNumber)
.lookingAt()) {
Pattern patternToMatch = regexCache.getPatternForRegex(numFormat.getPattern());
Matcher m = patternToMatch.matcher(nationalNumber);
Matcher m = regexCache.getPatternForRegex(numFormat.getPattern()).matcher(nationalNumber);
String numberFormatRule = numFormat.getFormat();
if (m.matches()) {
if (carrierCode != null && carrierCode.length() > 0 &&
numFormat.getDomesticCarrierCodeFormattingRule().length() > 0) {
// Replace the $CC in the formatting rule with the desired carrier code.
String carrierCodeFormattingRule = numFormat.getDomesticCarrierCodeFormattingRule();
carrierCodeFormattingRule =
CC_PATTERN.matcher(carrierCodeFormattingRule).replaceFirst(carrierCode);
// Now replace the $FG in the formatting rule with the first group and the carrier code
// combined in the appropriate way.
numberFormatRule = FIRST_GROUP_PATTERN.matcher(numberFormatRule)
.replaceFirst(carrierCodeFormattingRule);
}
String nationalPrefixFormattingRule = numFormat.getNationalPrefixFormattingRule();
if (nationalPrefixFormattingRule != null && nationalPrefixFormattingRule.length() > 0 &&
numberFormat == PhoneNumberFormat.NATIONAL) {
Matcher firstGroupMatcher =
FIRST_GROUP_PATTERN.matcher(numFormat.getFormat());
if (numberFormat == PhoneNumberFormat.NATIONAL &&
nationalPrefixFormattingRule != null &&
nationalPrefixFormattingRule.length() > 0) {
Matcher firstGroupMatcher = FIRST_GROUP_PATTERN.matcher(numberFormatRule);
return m.replaceAll(firstGroupMatcher.replaceFirst(nationalPrefixFormattingRule));
} else {
return m.replaceAll(numFormat.getFormat());
return m.replaceAll(numberFormatRule);
}
}
}
@ -1150,7 +1146,6 @@ public Set<String> getSupportedCountries() {
Matcher nationalNumberPatternMatcher =
regexCache.getPatternForRegex(numberDesc.getNationalNumberPattern())
.matcher(nationalNumber);
return possibleNumberPatternMatcher.matches() && nationalNumberPatternMatcher.matches();
}
@ -1203,39 +1198,34 @@ public Set<String> getSupportedCountries() {
* the country/region level.
*
* @param number the phone number whose origin we want to know
* @return the country/region where the phone number is from
* @return the country/region where the phone number is from, or null if no country matches this
* calling code.
*/
public String getRegionCodeForNumber(PhoneNumber number) {
int countryCode = number.getCountryCode();
switch (countryCode) {
case NANPA_COUNTRY_CODE:
// Override this and try the US case first, since it is more likely than other countries,
// for performance reasons.
String nationalNumber = getNationalSignificantNumber(number);
if (getNumberTypeHelper(nationalNumber,
getMetadataForRegion("US")) != PhoneNumberType.UNKNOWN) {
return "US";
}
HashSet<String> nanpaExceptUS = new HashSet<String>(nanpaCountries);
nanpaExceptUS.remove("US");
return getRegionCodeForNumberFromRegionList(number, nanpaExceptUS);
case RUSSIAN_FED_COUNTRY_CODE:
return getRegionCodeForNumberFromRegionList(number, russiaFederationCountries);
case FRENCH_INDIAN_OCEAN_COUNTRY_CODE:
return getRegionCodeForNumberFromRegionList(number, frenchIndianOceanTerritories);
case GREAT_BRITAIN_COUNTRY_CODE:
return getRegionCodeForNumberFromRegionList(number, greatBritainAndDependencies);
default:
return getRegionCodeForCountryCode(countryCode);
List<String> regions = countryCodeToRegionCodeMap.get(countryCode);
if (regions == null) {
return null;
}
if (regions.size() == 1) {
return regions.get(0);
} else {
return getRegionCodeForNumberFromRegionList(number, regions);
}
}
private String getRegionCodeForNumberFromRegionList(PhoneNumber number,
HashSet<String> regionCodes) {
List<String> regionCodes) {
String nationalNumber = String.valueOf(number.getNationalNumber());
for (String regionCode : regionCodes) {
if (getNumberTypeHelper(nationalNumber, getMetadataForRegion(regionCode)) !=
PhoneNumberType.UNKNOWN) {
// If leadingDigits is present, use this. Otherwise, do full validation.
PhoneMetadata metadata = getMetadataForRegion(regionCode);
if (metadata.hasLeadingDigits()) {
if (regexCache.getPatternForRegex(metadata.getLeadingDigits())
.matcher(nationalNumber).lookingAt()) {
return regionCode;
}
} else if (getNumberTypeHelper(nationalNumber, metadata) != PhoneNumberType.UNKNOWN) {
return regionCode;
}
}
@ -1244,11 +1234,12 @@ public Set<String> getSupportedCountries() {
/**
* Returns the region code that matches the specific country code. In the case of no region code
* being found, ZZ will be returned.
* being found, ZZ will be returned. In the case of multiple regions, the one designated in the
* metadata as the "main" country for this calling code will be returned.
*/
String getRegionCodeForCountryCode(int countryCode) {
String regionCode = countryCodeToRegionCodeMap.get(countryCode);
return regionCode == null ? "ZZ" : regionCode;
public String getRegionCodeForCountryCode(int countryCode) {
List<String> regionCodes = countryCodeToRegionCodeMap.get(countryCode);
return regionCodes == null ? "ZZ" : regionCodes.get(0);
}
/**
@ -1330,6 +1321,18 @@ public Set<String> getSupportedCountries() {
}
String nationalNumber = getNationalSignificantNumber(number);
PhoneNumberDesc generalNumDesc = getMetadataForRegion(regionCode).getGeneralDesc();
// Handling case of numbers with no metadata.
if (!generalNumDesc.hasNationalNumberPattern()) {
LOGGER.log(Level.FINER, "Checking if number is possible with incomplete metadata.");
int numberLength = nationalNumber.length();
if (numberLength < MIN_LENGTH_FOR_NSN) {
return ValidationResult.TOO_SHORT;
} else if (numberLength > MAX_LENGTH_FOR_NSN) {
return ValidationResult.TOO_LONG;
} else {
return ValidationResult.IS_POSSIBLE;
}
}
String possibleNumberPattern = generalNumDesc.getPossibleNumberPattern();
Matcher m = regexCache.getPatternForRegex(possibleNumberPattern).matcher(nationalNumber);
if (m.lookingAt()) {
@ -1622,8 +1625,7 @@ public Set<String> getSupportedCountries() {
// it is an extension.
if (m.find() && isViablePhoneNumber(number.substring(0, m.start()))) {
// The numbers are captured into groups in the regular expression.
int length = m.groupCount();
for (int i = 1; i <= length; i++) {
for (int i = 1, length = m.groupCount(); i <= length; i++) {
if (m.group(i) != null) {
// We go through the capturing groups until we find one that captured some digits. If none
// did, then we will return the empty string.
@ -1665,8 +1667,10 @@ public Set<String> getSupportedCountries() {
public void parse(String numberToParse, String defaultCountry, PhoneNumber phoneNumber)
throws NumberParseException {
if (!isValidRegionCode(defaultCountry)) {
throw new NumberParseException(NumberParseException.ErrorType.INVALID_COUNTRY_CODE,
"No default country was supplied.");
if (numberToParse.charAt(0) != PLUS_SIGN) {
throw new NumberParseException(NumberParseException.ErrorType.INVALID_COUNTRY_CODE,
"Missing or invalid default country.");
}
}
parseHelper(numberToParse, defaultCountry, false, phoneNumber);
}
@ -1700,8 +1704,10 @@ public Set<String> getSupportedCountries() {
PhoneNumber phoneNumber)
throws NumberParseException {
if (!isValidRegionCode(defaultCountry)) {
throw new NumberParseException(NumberParseException.ErrorType.INVALID_COUNTRY_CODE,
"No default country was supplied.");
if (numberToParse.charAt(0) != PLUS_SIGN) {
throw new NumberParseException(NumberParseException.ErrorType.INVALID_COUNTRY_CODE,
"Missing or invalid default country.");
}
}
parseHelper(numberToParse, defaultCountry, true, phoneNumber);
}


+ 56
- 0
java/src/com/google/i18n/phonenumbers/Phonemetadata.java View File

@ -77,6 +77,19 @@ public final class Phonemetadata {
return this;
}
// optional string domestic_carrier_code_formatting_rule = 5;
private boolean hasDomesticCarrierCodeFormattingRule;
private String domesticCarrierCodeFormattingRule_ = "";
public boolean hasDomesticCarrierCodeFormattingRule() {
return hasDomesticCarrierCodeFormattingRule; }
public String getDomesticCarrierCodeFormattingRule() {
return domesticCarrierCodeFormattingRule_; }
public NumberFormat setDomesticCarrierCodeFormattingRule(String value) {
hasDomesticCarrierCodeFormattingRule = true;
domesticCarrierCodeFormattingRule_ = value;
return this;
}
public void writeExternal(ObjectOutput objectOutput) throws IOException {
objectOutput.writeUTF(pattern_);
objectOutput.writeUTF(format_);
@ -88,6 +101,10 @@ public final class Phonemetadata {
if (hasNationalPrefixFormattingRule) {
objectOutput.writeUTF(nationalPrefixFormattingRule_);
}
objectOutput.writeBoolean(hasDomesticCarrierCodeFormattingRule);
if (hasDomesticCarrierCodeFormattingRule) {
objectOutput.writeUTF(domesticCarrierCodeFormattingRule_);
}
}
public void readExternal(ObjectInput objectInput) throws IOException, ClassNotFoundException {
@ -99,6 +116,9 @@ public final class Phonemetadata {
if (objectInput.readBoolean()) {
setNationalPrefixFormattingRule(objectInput.readUTF());
}
if (objectInput.readBoolean()) {
setDomesticCarrierCodeFormattingRule(objectInput.readUTF());
}
}
}
@ -441,6 +461,28 @@ public final class Phonemetadata {
return this;
}
// optional bool main_country_for_code = 22 [default = false];
private boolean hasMainCountryForCode;
private boolean mainCountryForCode_ = false;
public boolean hasMainCountryForCode() { return hasMainCountryForCode; }
public boolean getMainCountryForCode() { return mainCountryForCode_; }
public PhoneMetadata setMainCountryForCode(boolean value) {
hasMainCountryForCode = true;
mainCountryForCode_ = value;
return this;
}
// optional string leading_digits = 23;
private boolean hasLeadingDigits;
private String leadingDigits_ = "";
public boolean hasLeadingDigits() { return hasLeadingDigits; }
public String getLeadingDigits() { return leadingDigits_; }
public PhoneMetadata setLeadingDigits(String value) {
hasLeadingDigits = true;
leadingDigits_ = value;
return this;
}
public void writeExternal(ObjectOutput objectOutput) throws IOException {
objectOutput.writeBoolean(hasGeneralDesc);
if (hasGeneralDesc) {
@ -517,6 +559,13 @@ public final class Phonemetadata {
for (int i = 0; i < intlNumberFormatSize; i++) {
intlNumberFormat_.get(i).writeExternal(objectOutput);
}
objectOutput.writeBoolean(mainCountryForCode_);
objectOutput.writeBoolean(hasLeadingDigits);
if (hasLeadingDigits) {
objectOutput.writeUTF(leadingDigits_);
}
}
public void readExternal(ObjectInput objectInput) throws IOException, ClassNotFoundException {
@ -613,6 +662,13 @@ public final class Phonemetadata {
numFormat.readExternal(objectInput);
intlNumberFormat_.add(numFormat);
}
setMainCountryForCode(objectInput.readBoolean());
hasString = objectInput.readBoolean();
if (hasString) {
setLeadingDigits(objectInput.readUTF());
}
}
}


+ 95
- 106
java/test/com/google/i18n/phonenumbers/AsYouTypeFormatterTest.java View File

@ -67,6 +67,97 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("1 650 253 2222", formatter.inputDigit('2'));
formatter.clear();
assertEquals("0", formatter.inputDigit('0'));
assertEquals("01", formatter.inputDigit('1'));
assertEquals("011", formatter.inputDigit('1'));
assertEquals("0114", formatter.inputDigit('4'));
assertEquals("01144", formatter.inputDigit('4'));
assertEquals("011 44 6", formatter.inputDigit('6'));
assertEquals("011 44 61", formatter.inputDigit('1'));
assertEquals("011 44 612", formatter.inputDigit('2'));
assertEquals("011 44 6 123", formatter.inputDigit('3'));
assertEquals("011 44 6 123 1", formatter.inputDigit('1'));
assertEquals("011 44 6 123 12", formatter.inputDigit('2'));
assertEquals("011 44 6 123 123", formatter.inputDigit('3'));
assertEquals("011 44 6 123 123 1", formatter.inputDigit('1'));
assertEquals("011 44 6 123 123 12", formatter.inputDigit('2'));
assertEquals("011 44 6 123 123 123", formatter.inputDigit('3'));
formatter.clear();
assertEquals("0", formatter.inputDigit('0'));
assertEquals("01", formatter.inputDigit('1'));
assertEquals("011", formatter.inputDigit('1'));
assertEquals("0115", formatter.inputDigit('5'));
assertEquals("01154", formatter.inputDigit('4'));
assertEquals("011 54 9", formatter.inputDigit('9'));
assertEquals("011 54 91", formatter.inputDigit('1'));
assertEquals("011 54 911", formatter.inputDigit('1'));
assertEquals("011 54 9 11 2",
formatter.inputDigit('2'));
assertEquals("011 54 9 11 23", formatter.inputDigit('3'));
assertEquals("011 54 9 11 231", formatter.inputDigit('1'));
assertEquals("011 54 9 11 2312", formatter.inputDigit('2'));
assertEquals("011 54 9 11 2312 1", formatter.inputDigit('1'));
assertEquals("011 54 9 11 2312 12", formatter.inputDigit('2'));
assertEquals("011 54 9 11 2312 123", formatter.inputDigit('3'));
assertEquals("011 54 9 11 2312 1234", formatter.inputDigit('4'));
formatter.clear();
assertEquals("+", formatter.inputDigit('+'));
assertEquals("+4", formatter.inputDigit('4'));
assertEquals("+48", formatter.inputDigit('8'));
assertEquals("+488", formatter.inputDigit('8'));
assertEquals("+4888", formatter.inputDigit('8'));
assertEquals("+48 881", formatter.inputDigit('1'));
assertEquals("+48 88 12", formatter.inputDigit('2'));
assertEquals("+48 88 123", formatter.inputDigit('3'));
assertEquals("+48 88 123 1", formatter.inputDigit('1'));
assertEquals("+48 88 123 12", formatter.inputDigit('2'));
assertEquals("+48 88 123 12 1", formatter.inputDigit('1'));
assertEquals("+48 88 123 12 12", formatter.inputDigit('2'));
}
public void testAsYouTypeFormatterUSFullWidthCharacters() {
AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("US");
assertEquals("\uFF16", formatter.inputDigit('\uFF16'));
assertEquals("\uFF16\uFF15", formatter.inputDigit('\uFF15'));
assertEquals("\uFF16\uFF15\uFF10", formatter.inputDigit('\uFF10'));
assertEquals("\uFF16\uFF15\uFF10\uFF12", formatter.inputDigit('\uFF12'));
assertEquals("\uFF16\uFF15\uFF10\uFF12\uFF15", formatter.inputDigit('\uFF15'));
assertEquals("650 253", formatter.inputDigit('\uFF13'));
assertEquals("650 253 2", formatter.inputDigit('\uFF12'));
assertEquals("650 253 22", formatter.inputDigit('\uFF12'));
assertEquals("650 253 222", formatter.inputDigit('\uFF12'));
assertEquals("650 253 2222", formatter.inputDigit('\uFF12'));
}
public void testAsYouTypeFormatterUSMobileShortCode() {
AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("US");
assertEquals("*", formatter.inputDigit('*'));
assertEquals("*1", formatter.inputDigit('1'));
assertEquals("*12", formatter.inputDigit('2'));
assertEquals("*121", formatter.inputDigit('1'));
assertEquals("*121#", formatter.inputDigit('#'));
}
public void testAsYouTypeFormatterUSVanityNumber() {
AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("US");
assertEquals("8", formatter.inputDigit('8'));
assertEquals("80", formatter.inputDigit('0'));
assertEquals("800", formatter.inputDigit('0'));
assertEquals("800 ", formatter.inputDigit(' '));
assertEquals("800 M", formatter.inputDigit('M'));
assertEquals("800 MY", formatter.inputDigit('Y'));
assertEquals("800 MY ", formatter.inputDigit(' '));
assertEquals("800 MY A", formatter.inputDigit('A'));
assertEquals("800 MY AP", formatter.inputDigit('P'));
assertEquals("800 MY APP", formatter.inputDigit('P'));
assertEquals("800 MY APPL", formatter.inputDigit('L'));
assertEquals("800 MY APPLE", formatter.inputDigit('E'));
}
public void testAsYouTypeFormatterAndRememberPositionUS() {
AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("US");
assertEquals("1", formatter.inputDigitAndRememberPosition('1'));
assertEquals(1, formatter.getRememberedPosition());
assertEquals("16", formatter.inputDigit('6'));
@ -76,6 +167,8 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals(4, formatter.getRememberedPosition());
assertEquals("16502", formatter.inputDigit('2'));
assertEquals("1 650 25", formatter.inputDigit('5'));
// Note the remembered position for digit "0" changes from 4 to 5, because a space is now
// inserted in the front.
assertEquals(5, formatter.getRememberedPosition());
assertEquals("1 650 253", formatter.inputDigit('3'));
assertEquals("1 650 253 2", formatter.inputDigit('2'));
@ -90,26 +183,6 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("1650253222222", formatter.inputDigit('2'));
assertEquals(10, formatter.getRememberedPosition());
formatter.clear();
assertEquals("1", formatter.inputDigit('1'));
assertEquals("16", formatter.inputDigit('6'));
assertEquals("165", formatter.inputDigit('5'));
assertEquals("1650", formatter.inputDigitAndRememberPosition('0'));
assertEquals(4, formatter.getRememberedPosition());
assertEquals("16502", formatter.inputDigit('2'));
assertEquals("1 650 25", formatter.inputDigit('5'));
assertEquals(5, formatter.getRememberedPosition());
assertEquals("1 650 253", formatter.inputDigit('3'));
assertEquals("1 650 253 2", formatter.inputDigit('2'));
assertEquals("1 650 253 22", formatter.inputDigit('2'));
assertEquals(5, formatter.getRememberedPosition());
assertEquals("1 650 253 222", formatter.inputDigit('2'));
assertEquals("1 650 253 2222", formatter.inputDigit('2'));
assertEquals("165025322222", formatter.inputDigit('2'));
assertEquals(4, formatter.getRememberedPosition());
assertEquals("1650253222222", formatter.inputDigit('2'));
assertEquals(4, formatter.getRememberedPosition());
formatter.clear();
assertEquals("1", formatter.inputDigit('1'));
assertEquals("16", formatter.inputDigit('6'));
@ -184,41 +257,6 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("011 48 88 123 12 1", formatter.inputDigit('1'));
assertEquals("011 48 88 123 12 12", formatter.inputDigit('2'));
formatter.clear();
assertEquals("0", formatter.inputDigit('0'));
assertEquals("01", formatter.inputDigit('1'));
assertEquals("011", formatter.inputDigit('1'));
assertEquals("0114", formatter.inputDigit('4'));
assertEquals("01144", formatter.inputDigit('4'));
assertEquals("011 44 6", formatter.inputDigit('6'));
assertEquals("011 44 61", formatter.inputDigit('1'));
assertEquals("011 44 612", formatter.inputDigit('2'));
assertEquals("011 44 6 123", formatter.inputDigit('3'));
assertEquals("011 44 6 123 1", formatter.inputDigit('1'));
assertEquals("011 44 6 123 12", formatter.inputDigit('2'));
assertEquals("011 44 6 123 123", formatter.inputDigit('3'));
assertEquals("011 44 6 123 123 1", formatter.inputDigit('1'));
assertEquals("011 44 6 123 123 12", formatter.inputDigit('2'));
assertEquals("011 44 6 123 123 123", formatter.inputDigit('3'));
formatter.clear();
assertEquals("0", formatter.inputDigit('0'));
assertEquals("01", formatter.inputDigit('1'));
assertEquals("011", formatter.inputDigit('1'));
assertEquals("0115", formatter.inputDigit('5'));
assertEquals("01154", formatter.inputDigit('4'));
assertEquals("011 54 9", formatter.inputDigit('9'));
assertEquals("011 54 91", formatter.inputDigit('1'));
assertEquals("011 54 911", formatter.inputDigit('1'));
assertEquals("011 54 9 11 2", formatter.inputDigit('2'));
assertEquals("011 54 9 11 23", formatter.inputDigit('3'));
assertEquals("011 54 9 11 231", formatter.inputDigit('1'));
assertEquals("011 54 9 11 2312", formatter.inputDigit('2'));
assertEquals("011 54 9 11 2312 1", formatter.inputDigit('1'));
assertEquals("011 54 9 11 2312 12", formatter.inputDigit('2'));
assertEquals("011 54 9 11 2312 123", formatter.inputDigit('3'));
assertEquals("011 54 9 11 2312 1234", formatter.inputDigit('4'));
formatter.clear();
assertEquals("+", formatter.inputDigit('+'));
assertEquals("+1", formatter.inputDigit('1'));
@ -251,56 +289,6 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("+1 650 253 222", formatter.inputDigit('2'));
assertEquals("+1650253222;", formatter.inputDigit(';'));
assertEquals(3, formatter.getRememberedPosition());
formatter.clear();
assertEquals("+", formatter.inputDigit('+'));
assertEquals("+4", formatter.inputDigit('4'));
assertEquals("+48", formatter.inputDigit('8'));
assertEquals("+488", formatter.inputDigit('8'));
assertEquals("+4888", formatter.inputDigit('8'));
assertEquals("+48 881", formatter.inputDigit('1'));
assertEquals("+48 88 12", formatter.inputDigit('2'));
assertEquals("+48 88 123", formatter.inputDigit('3'));
assertEquals("+48 88 123 1", formatter.inputDigit('1'));
assertEquals("+48 88 123 12", formatter.inputDigit('2'));
assertEquals("+48 88 123 12 1", formatter.inputDigit('1'));
assertEquals("+48 88 123 12 12", formatter.inputDigit('2'));
// Test US number with full-width characters.
formatter.clear();
assertEquals("\uFF16", formatter.inputDigit('\uFF16'));
assertEquals("\uFF16\uFF15", formatter.inputDigit('\uFF15'));
assertEquals("\uFF16\uFF15\uFF10", formatter.inputDigit('\uFF10'));
assertEquals("\uFF16\uFF15\uFF10\uFF12", formatter.inputDigit('\uFF12'));
assertEquals("\uFF16\uFF15\uFF10\uFF12\uFF15", formatter.inputDigit('\uFF15'));
assertEquals("650 253", formatter.inputDigit('\uFF13'));
assertEquals("650 253 2", formatter.inputDigit('\uFF12'));
assertEquals("650 253 22", formatter.inputDigit('\uFF12'));
assertEquals("650 253 222", formatter.inputDigit('\uFF12'));
assertEquals("650 253 2222", formatter.inputDigit('\uFF12'));
// Mobile short code.
formatter.clear();
assertEquals("*", formatter.inputDigit('*'));
assertEquals("*1", formatter.inputDigit('1'));
assertEquals("*12", formatter.inputDigit('2'));
assertEquals("*121", formatter.inputDigit('1'));
assertEquals("*121#", formatter.inputDigit('#'));
// Test vanity numbers.
formatter.clear();
assertEquals("8", formatter.inputDigit('8'));
assertEquals("80", formatter.inputDigit('0'));
assertEquals("800", formatter.inputDigit('0'));
assertEquals("800 ", formatter.inputDigit(' '));
assertEquals("800 M", formatter.inputDigit('M'));
assertEquals("800 MY", formatter.inputDigit('Y'));
assertEquals("800 MY ", formatter.inputDigit(' '));
assertEquals("800 MY A", formatter.inputDigit('A'));
assertEquals("800 MY AP", formatter.inputDigit('P'));
assertEquals("800 MY APP", formatter.inputDigit('P'));
assertEquals("800 MY APPL", formatter.inputDigit('L'));
assertEquals("800 MY APPLE", formatter.inputDigit('E'));
}
public void testAsYouTypeFormatterGBFixedLine() {
@ -321,7 +309,7 @@ public class AsYouTypeFormatterTest extends TestCase {
}
public void testAsYouTypeFormatterGBTollFree() {
AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("gb");
AsYouTypeFormatter formatter = phoneUtil.getAsYouTypeFormatter("gb");
assertEquals("0", formatter.inputDigit('0'));
assertEquals("08", formatter.inputDigit('8'));
assertEquals("080", formatter.inputDigit('0'));
@ -357,6 +345,7 @@ public class AsYouTypeFormatterTest extends TestCase {
assertEquals("021", formatter.inputDigit('1'));
assertEquals("0211", formatter.inputDigit('1'));
assertEquals("02112", formatter.inputDigit('2'));
// Note the unittest is using fake metadata which might produce non-ideal results.
assertEquals("02-112 3", formatter.inputDigit('3'));
assertEquals("02-112 34", formatter.inputDigit('4'));
assertEquals("02-112 345", formatter.inputDigit('5'));


BIN
java/test/com/google/i18n/phonenumbers/PhoneNumberMetadataProtoForTesting View File


+ 104
- 6
java/test/com/google/i18n/phonenumbers/PhoneNumberUtilTest.java View File

@ -117,9 +117,9 @@ public class PhoneNumberUtilTest extends TestCase {
assertEquals("0", metadata.getNationalPrefix());
assertEquals("0(?:(11|343|3715)15)?", metadata.getNationalPrefixForParsing());
assertEquals("9$1", metadata.getNationalPrefixTransformRule());
assertEquals("$1 15 $2-$3", metadata.getNumberFormat(2).getFormat());
assertEquals("9(\\d{4})(\\d{2})(\\d{4})",
metadata.getNumberFormat(3).getPattern());
assertEquals("$1 15 $2-$3", metadata.getNumberFormat(3).getFormat());
assertEquals("(9)(\\d{4})(\\d{2})(\\d{4})",
metadata.getIntlNumberFormat(3).getPattern());
assertEquals("$1 $2 $3 $4", metadata.getIntlNumberFormat(3).getFormat());
@ -479,6 +479,25 @@ public class PhoneNumberUtilTest extends TestCase {
phoneUtil.formatOutOfCountryCallingNumber(itNumber, "AU"));
}
public void testFormatWithCarrierCode() {
// We only support this for AR in our test metadata.
PhoneNumber arNumber = new PhoneNumber();
arNumber.setCountryCode(54).setNationalNumber(91234125678L);
assertEquals("01234 12-5678", phoneUtil.format(arNumber,
PhoneNumberUtil.PhoneNumberFormat.NATIONAL));
// Test formatting with a carrier code.
assertEquals("01234 15 12-5678", phoneUtil.formatNationalNumberWithCarrierCode(arNumber, "15"));
// Here the international rule is used, so no carrier code should be present.
assertEquals("+5491234125678", phoneUtil.format(arNumber,
PhoneNumberUtil.PhoneNumberFormat.E164));
// We don't support this for the US so there should be no change.
PhoneNumber usNumber = new PhoneNumber();
usNumber.setCountryCode(1).setNationalNumber(4241231234L);
assertEquals("424 123 1234", phoneUtil.format(usNumber,
PhoneNumberUtil.PhoneNumberFormat.NATIONAL));
assertEquals("424 123 1234", phoneUtil.formatNationalNumberWithCarrierCode(usNumber, "15"));
}
public void testFormatByPattern() {
PhoneNumber usNumber = new PhoneNumber();
usNumber.setCountryCode(1).setNationalNumber(6502530000L);
@ -575,19 +594,19 @@ public class PhoneNumberUtilTest extends TestCase {
public void testFormatUsingOriginalNumberFormat() throws Exception {
PhoneNumber number1 = phoneUtil.parseAndKeepRawInput("+442087654321", "GB");
assertEquals("+44 20 8765 4321", phoneUtil.formatUsingOriginalNumberFormat(number1, "GB"));
assertEquals("+44 20 8765 4321", phoneUtil.formatInOriginalFormat(number1, "GB"));
PhoneNumber number2 = phoneUtil.parseAndKeepRawInput("02087654321", "GB");
assertEquals("(020) 8765 4321", phoneUtil.formatUsingOriginalNumberFormat(number2, "GB"));
assertEquals("(020) 8765 4321", phoneUtil.formatInOriginalFormat(number2, "GB"));
PhoneNumber number3 = phoneUtil.parseAndKeepRawInput("011442087654321", "US");
assertEquals("011 44 20 8765 4321", phoneUtil.formatUsingOriginalNumberFormat(number3, "US"));
assertEquals("011 44 20 8765 4321", phoneUtil.formatInOriginalFormat(number3, "US"));
PhoneNumber number4 = phoneUtil.parseAndKeepRawInput("442087654321", "GB");
assertEquals("44 20 8765 4321", phoneUtil.formatUsingOriginalNumberFormat(number4, "GB"));
assertEquals("44 20 8765 4321", phoneUtil.formatInOriginalFormat(number4, "GB"));
PhoneNumber number5 = phoneUtil.parse("+442087654321", "GB");
assertEquals("(020) 8765 4321", phoneUtil.formatUsingOriginalNumberFormat(number5, "GB"));
assertEquals("(020) 8765 4321", phoneUtil.formatInOriginalFormat(number5, "GB"));
}
public void testIsPremiumRate() {
@ -763,6 +782,28 @@ public class PhoneNumberUtilTest extends TestCase {
bsNumber.setNationalNumber(2421232345L);
// This number is no longer valid.
assertFalse(phoneUtil.isValidNumber(bsNumber));
// La Mayotte and RŽunion use 'leadingDigits' to differentiate them.
PhoneNumber reNumber = new PhoneNumber();
reNumber.setCountryCode(262).setNationalNumber(262123456L);
assertTrue(phoneUtil.isValidNumber(reNumber));
assertTrue(phoneUtil.isValidNumberForRegion(reNumber, "RE"));
assertFalse(phoneUtil.isValidNumberForRegion(reNumber, "YT"));
// Now change the number to be a number for La Mayotte.
reNumber.setNationalNumber(269601234L);
assertTrue(phoneUtil.isValidNumberForRegion(reNumber, "YT"));
assertFalse(phoneUtil.isValidNumberForRegion(reNumber, "RE"));
// This number is no longer valid for La RŽunion.
reNumber.setNationalNumber(269123456L);
assertFalse(phoneUtil.isValidNumberForRegion(reNumber, "YT"));
assertFalse(phoneUtil.isValidNumberForRegion(reNumber, "RE"));
assertFalse(phoneUtil.isValidNumber(reNumber));
// However, it should be recognised as from La Mayotte, since it is valid for this region.
assertEquals("YT", phoneUtil.getRegionCodeForNumber(reNumber));
// This number is valid in both places.
reNumber.setNationalNumber(800123456L);
assertTrue(phoneUtil.isValidNumberForRegion(reNumber, "YT"));
assertTrue(phoneUtil.isValidNumberForRegion(reNumber, "RE"));
}
public void testIsNotValidNumber() {
@ -874,6 +915,18 @@ public class PhoneNumberUtilTest extends TestCase {
number.setCountryCode(1).setNationalNumber(65025300000L);
assertEquals(PhoneNumberUtil.ValidationResult.TOO_LONG,
phoneUtil.isPossibleNumberWithReason(number));
// Try with number that we don't have metadata for.
PhoneNumber adNumber = new PhoneNumber();
adNumber.setCountryCode(376).setNationalNumber(12345L);
assertEquals(PhoneNumberUtil.ValidationResult.IS_POSSIBLE,
phoneUtil.isPossibleNumberWithReason(adNumber));
adNumber.setCountryCode(376).setNationalNumber(13L);
assertEquals(PhoneNumberUtil.ValidationResult.TOO_SHORT,
phoneUtil.isPossibleNumberWithReason(adNumber));
adNumber.setCountryCode(376).setNationalNumber(1234567890123456L);
assertEquals(PhoneNumberUtil.ValidationResult.TOO_LONG,
phoneUtil.isPossibleNumberWithReason(adNumber));
}
public void testIsNotPossibleNumber() {
@ -1307,6 +1360,16 @@ public class PhoneNumberUtilTest extends TestCase {
NumberParseException.ErrorType.TOO_LONG,
e.getErrorType());
}
try {
String plusMinusPhoneNumber = "+---";
phoneUtil.parse(plusMinusPhoneNumber, "DE");
fail("This should not parse without throwing an exception " + plusMinusPhoneNumber);
} catch (NumberParseException e) {
// Expected this exception.
assertEquals("Wrong error type stored in exception.",
NumberParseException.ErrorType.NOT_A_NUMBER,
e.getErrorType());
}
try {
String tooShortPhoneNumber = "+49 0";
phoneUtil.parse(tooShortPhoneNumber, "DE");
@ -1337,6 +1400,16 @@ public class PhoneNumberUtilTest extends TestCase {
NumberParseException.ErrorType.INVALID_COUNTRY_CODE,
e.getErrorType());
}
try {
String someNumber = "123 456 7890";
phoneUtil.parse(someNumber, "CS");
fail("Deprecated country code not allowed: should fail.");
} catch (NumberParseException e) {
// Expected this exception.
assertEquals("Wrong error type stored in exception.",
NumberParseException.ErrorType.INVALID_COUNTRY_CODE,
e.getErrorType());
}
try {
String someNumber = "123 456 7890";
phoneUtil.parse(someNumber, null);
@ -1389,6 +1462,20 @@ public class PhoneNumberUtilTest extends TestCase {
}
}
public void testParseNumbersWithPlusWithNoRegion() throws Exception {
PhoneNumber nzNumber = new PhoneNumber();
nzNumber.setCountryCode(64).setNationalNumber(33316005L);
// "ZZ" is allowed only if the number starts with a '+' - then the country code can be
// calculated.
assertEquals(nzNumber, phoneUtil.parse("+64 3 331 6005", "ZZ"));
assertEquals(nzNumber, phoneUtil.parse("+64 3 331 6005", null));
nzNumber.setRawInput("+64 3 331 6005").
setCountryCodeSource(CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN);
assertEquals(nzNumber, phoneUtil.parseAndKeepRawInput("+64 3 331 6005", "ZZ"));
// Null is also allowed for the region code in these cases.
assertEquals(nzNumber, phoneUtil.parseAndKeepRawInput("+64 3 331 6005", null));
}
public void testParseExtensions() throws Exception {
PhoneNumber nzNumber = new PhoneNumber();
nzNumber.setCountryCode(64).setNationalNumber(33316005L).setExtension("3456");
@ -1472,6 +1559,17 @@ public class PhoneNumberUtilTest extends TestCase {
setCountryCodeSource(CountryCodeSource.FROM_NUMBER_WITH_IDD);
assertEquals(alphaNumericNumber,
phoneUtil.parseAndKeepRawInput("001800 six-flag", "NZ"));
// Invalid region code supplied.
try {
phoneUtil.parseAndKeepRawInput("123 456 7890", "CS");
fail("Deprecated country code not allowed: should fail.");
} catch (NumberParseException e) {
// Expected this exception.
assertEquals("Wrong error type stored in exception.",
NumberParseException.ErrorType.INVALID_COUNTRY_CODE,
e.getErrorType());
}
}
public void testCountryWithNoNumberDesc() {


Loading…
Cancel
Save