Update libphonenumber to v4.6
Change-Id: Ibc040a53cce8b85d901c0155dcb06f4d609e20fb
diff --git a/README.android b/README.android
index 9978794..e84d172 100644
--- a/README.android
+++ b/README.android
@@ -1,5 +1,5 @@
URL: http://code.google.com/p/libphonenumber/
-Version: 4.5 (r415)
+Version: 4.6 (r429)
License: Apache 2
Description: Google Phone Number Library.
Local Modification:
diff --git a/java/release_notes.txt b/java/release_notes.txt
index 4df4e8d..5970332 100644
--- a/java/release_notes.txt
+++ b/java/release_notes.txt
@@ -1,3 +1,19 @@
+February 9th, 2012: libphonenumber-4.6
+* Bug fixes
+ - Fix for formatByPattern to enable RFC formatting to work
+ - Fix for RFC formatting to work even when the international formatting rule starts with
+ punctuation
+ - Logging consistency changes - some warnings are no longer printed, others have become only
+ WARNINGS
+ - Fix for isValidNumberForRegion potentially throwing a NPE
+ - Parsing Israeli * numbers written in international format now works
+ - PhoneNumberMatcher doesn't match timestamps as phone-numbers
+* Metadata changes
+ - Updates for AN, AX, BF, BJ, BR, BS, DJ, FI, IN, LV, MW, RS, SC, VN
+ - New countries supported: SS (South Sudan), CW (Curaçao) and BQ (Bonaire, Sint Eustatius and Saba)
+* Refactoring of the private formatting functions in PhoneNumberUtil to ensure names are more
+ descriptive and to reduce code duplication.
+
January 19th, 2012: libphonenumber-4.5
* Code changes
- Support for non-geographical country calling codes (e.g. +800).
diff --git a/java/src/com/android/i18n/phonenumbers/CountryCodeToRegionCodeMap.java b/java/src/com/android/i18n/phonenumbers/CountryCodeToRegionCodeMap.java
index 10b492e..69bb351 100644
--- a/java/src/com/android/i18n/phonenumbers/CountryCodeToRegionCodeMap.java
+++ b/java/src/com/android/i18n/phonenumbers/CountryCodeToRegionCodeMap.java
@@ -31,10 +31,10 @@
// countries sharing a calling code, such as the NANPA countries, the one
// indicated with "isMainCountryForCode" in the metadata should be first.
static Map<Integer, List<String>> getCountryCodeToRegionCodeMap() {
- // The capacity is set to 280 as there are 210 different country codes,
+ // The capacity is set to 281 as there are 211 different country codes,
// and this offers a load factor of roughly 0.75.
Map<Integer, List<String>> countryCodeToRegionCodeMap =
- new HashMap<Integer, List<String>>(280);
+ new HashMap<Integer, List<String>>(281);
ArrayList<String> listWithRegionCode;
@@ -254,6 +254,10 @@
countryCodeToRegionCodeMap.put(98, listWithRegionCode);
listWithRegionCode = new ArrayList<String>(1);
+ listWithRegionCode.add("SS");
+ countryCodeToRegionCodeMap.put(211, listWithRegionCode);
+
+ listWithRegionCode = new ArrayList<String>(1);
listWithRegionCode.add("MA");
countryCodeToRegionCodeMap.put(212, listWithRegionCode);
@@ -685,8 +689,10 @@
listWithRegionCode.add("UY");
countryCodeToRegionCodeMap.put(598, listWithRegionCode);
- listWithRegionCode = new ArrayList<String>(1);
+ listWithRegionCode = new ArrayList<String>(3);
+ listWithRegionCode.add("CW");
listWithRegionCode.add("AN");
+ listWithRegionCode.add("BQ");
countryCodeToRegionCodeMap.put(599, listWithRegionCode);
listWithRegionCode = new ArrayList<String>(1);
diff --git a/java/src/com/android/i18n/phonenumbers/PhoneNumberMatcher.java b/java/src/com/android/i18n/phonenumbers/PhoneNumberMatcher.java
index 3ddc773..64eaf1b 100644
--- a/java/src/com/android/i18n/phonenumbers/PhoneNumberMatcher.java
+++ b/java/src/com/android/i18n/phonenumbers/PhoneNumberMatcher.java
@@ -71,6 +71,14 @@
Pattern.compile("(?:(?:[0-3]?\\d/[01]?\\d)|(?:[01]?\\d/[0-3]?\\d))/(?:[12]\\d)?\\d{2}");
/**
+ * Matches timestamps. Examples: "2012-01-02 08:00". Note that the reg-ex does not include the
+ * trailing ":\d\d" -- that is covered by TIME_STAMPS_SUFFIX.
+ */
+ private static final Pattern TIME_STAMPS =
+ Pattern.compile("[12]\\d{3}[-/]?[01]\\d[-/]?[0-3]\\d [0-2]\\d$");
+ private static final Pattern TIME_STAMPS_SUFFIX = Pattern.compile(":[0-5]\\d");
+
+ /**
* Pattern to check that brackets match. Opening brackets should be closed within a phone number.
* This also checks that there is something inside the brackets. Having no brackets at all is also
* fine.
@@ -310,6 +318,13 @@
if (PUB_PAGES.matcher(candidate).find() || SLASH_SEPARATED_DATES.matcher(candidate).find()) {
return null;
}
+ // Skip potential time-stamps.
+ if (TIME_STAMPS.matcher(candidate).find()) {
+ String followingText = text.toString().substring(offset + candidate.length());
+ if (TIME_STAMPS_SUFFIX.matcher(followingText).lookingAt()) {
+ return null;
+ }
+ }
// Try to come up with a valid match given the entire candidate.
String rawString = candidate.toString();
diff --git a/java/src/com/android/i18n/phonenumbers/PhoneNumberUtil.java b/java/src/com/android/i18n/phonenumbers/PhoneNumberUtil.java
index 53e72c5..5120aab 100644
--- a/java/src/com/android/i18n/phonenumbers/PhoneNumberUtil.java
+++ b/java/src/com/android/i18n/phonenumbers/PhoneNumberUtil.java
@@ -95,6 +95,8 @@
// The PLUS_SIGN signifies the international prefix.
static final char PLUS_SIGN = '+';
+ private static final char STAR_SIGN = '*';
+
private static final String RFC3966_EXTN_PREFIX = ";ext=";
// A map that contains characters that are essential when dialling. That means any of the
@@ -256,11 +258,11 @@
// carrier codes, for example in Brazilian phone numbers. We also allow multiple "+" characters at
// the start.
// Corresponds to the following:
- // plus_sign*([punctuation]*[digits]){3,}([punctuation]|[digits]|[alpha])*
+ // plus_sign*(([punctuation]|[star])*[digits]){3,}([punctuation]|[star]|[digits]|[alpha])*
// Note VALID_PUNCTUATION starts with a -, so must be the first in the range.
private static final String VALID_PHONE_NUMBER =
- "[" + PLUS_CHARS + "]*(?:[" + VALID_PUNCTUATION + "]*" + DIGITS + "){3,}[" +
- VALID_PUNCTUATION + VALID_ALPHA + DIGITS + "]*";
+ "[" + PLUS_CHARS + "]*(?:[" + VALID_PUNCTUATION + STAR_SIGN + "]*" + DIGITS + "){3,}[" +
+ VALID_PUNCTUATION + STAR_SIGN + VALID_ALPHA + DIGITS + "]*";
// Default extension prefix to use when formatting. This will be put in front of any extension
// component of the number, after the main national number is formatted. For example, if you wish
@@ -993,6 +995,7 @@
return normalizedNumber.toString();
}
+ // @VisibleForTesting
static synchronized PhoneNumberUtil getInstance(
String baseFileLocation,
Map<Integer, List<String>> countryCallingCodeToRegionCodeMap) {
@@ -1020,6 +1023,14 @@
}
/**
+ * Convenience method to get a list of what global network calling codes the library has metadata
+ * for.
+ */
+ public Set<Integer> getSupportedGlobalNetworkCallingCodes() {
+ return countryCodeToNonGeographicalMetadataMap.keySet();
+ }
+
+ /**
* Gets a {@link PhoneNumberUtil} instance to carry out international phone number formatting,
* parsing, or validation. The instance is loaded with phone number metadata for a number of most
* commonly used regions.
@@ -1091,23 +1102,24 @@
// Early exit for E164 case since no formatting of the national number needs to be applied.
// Extensions are not formatted.
formattedNumber.append(nationalSignificantNumber);
- formatNumberByFormat(countryCallingCode, PhoneNumberFormat.E164, formattedNumber);
+ prefixNumberWithCountryCallingCode(countryCallingCode, PhoneNumberFormat.E164,
+ formattedNumber);
return;
}
// Note getRegionCodeForCountryCode() is used because formatting information for regions which
// share a country calling code is contained by only one region for performance reasons. For
// example, for NANPA regions it will be contained in the metadata for US.
String regionCode = getRegionCodeForCountryCode(countryCallingCode);
- if (!hasValidCountryCallingCode(countryCallingCode)) {
+ if (!hasValidCountryCallingCode(countryCallingCode)) {
formattedNumber.append(nationalSignificantNumber);
return;
}
PhoneMetadata metadata =
getMetadataForRegionOrCallingCode(countryCallingCode, regionCode);
- formattedNumber.append(formatNationalNumber(nationalSignificantNumber, metadata, numberFormat));
- maybeGetFormattedExtension(number, metadata, numberFormat, formattedNumber);
- formatNumberByFormat(countryCallingCode, numberFormat, formattedNumber);
+ formattedNumber.append(formatNsn(nationalSignificantNumber, metadata, numberFormat));
+ maybeAppendFormattedExtension(number, metadata, numberFormat, formattedNumber);
+ prefixNumberWithCountryCallingCode(countryCallingCode, numberFormat, formattedNumber);
}
/**
@@ -1133,18 +1145,24 @@
if (!hasValidCountryCallingCode(countryCallingCode)) {
return nationalSignificantNumber;
}
- List<NumberFormat> userDefinedFormatsCopy =
- new ArrayList<NumberFormat>(userDefinedFormats.size());
PhoneMetadata metadata =
getMetadataForRegionOrCallingCode(countryCallingCode, regionCode);
- for (NumberFormat numFormat : userDefinedFormats) {
- String nationalPrefixFormattingRule = numFormat.getNationalPrefixFormattingRule();
+
+ StringBuilder formattedNumber = new StringBuilder(20);
+
+ NumberFormat formattingPattern =
+ chooseFormattingPatternForNumber(userDefinedFormats, nationalSignificantNumber);
+ if (formattingPattern == null) {
+ // If no pattern above is matched, we format the number as a whole.
+ formattedNumber.append(nationalSignificantNumber);
+ } else {
+ NumberFormat numFormatCopy = new NumberFormat();
+ // Before we do a replacement of the national prefix pattern $NP with the national prefix, we
+ // need to copy the rule so that subsequent replacements for different numbers have the
+ // appropriate national prefix.
+ numFormatCopy.mergeFrom(formattingPattern);
+ String nationalPrefixFormattingRule = formattingPattern.getNationalPrefixFormattingRule();
if (nationalPrefixFormattingRule.length() > 0) {
- // Before we do a replacement of the national prefix pattern $NP with the national prefix,
- // we need to copy the rule so that subsequent replacements for different numbers have the
- // appropriate national prefix.
- NumberFormat numFormatCopy = new NumberFormat();
- numFormatCopy.mergeFrom(numFormat);
String nationalPrefix = metadata.getNationalPrefix();
if (nationalPrefix.length() > 0) {
// Replace $NP with national prefix and $FG with the first group ($1).
@@ -1157,19 +1175,12 @@
// We don't want to have a rule for how to format the national prefix if there isn't one.
numFormatCopy.clearNationalPrefixFormattingRule();
}
- userDefinedFormatsCopy.add(numFormatCopy);
- } else {
- // Otherwise, we just add the original rule to the modified list of formats.
- userDefinedFormatsCopy.add(numFormat);
}
+ formattedNumber.append(
+ formatNsnUsingPattern(nationalSignificantNumber, numFormatCopy, numberFormat));
}
-
- StringBuilder formattedNumber =
- new StringBuilder(formatAccordingToFormats(nationalSignificantNumber,
- userDefinedFormatsCopy,
- numberFormat));
- maybeGetFormattedExtension(number, metadata, numberFormat, formattedNumber);
- formatNumberByFormat(countryCallingCode, numberFormat, formattedNumber);
+ maybeAppendFormattedExtension(number, metadata, numberFormat, formattedNumber);
+ prefixNumberWithCountryCallingCode(countryCallingCode, numberFormat, formattedNumber);
return formattedNumber.toString();
}
@@ -1197,12 +1208,11 @@
StringBuilder formattedNumber = new StringBuilder(20);
PhoneMetadata metadata = getMetadataForRegionOrCallingCode(countryCallingCode, regionCode);
- formattedNumber.append(formatNationalNumber(nationalSignificantNumber,
- metadata,
- PhoneNumberFormat.NATIONAL,
- carrierCode));
- maybeGetFormattedExtension(number, metadata, PhoneNumberFormat.NATIONAL, formattedNumber);
- formatNumberByFormat(countryCallingCode, PhoneNumberFormat.NATIONAL, formattedNumber);
+ formattedNumber.append(formatNsn(nationalSignificantNumber, metadata,
+ PhoneNumberFormat.NATIONAL, carrierCode));
+ maybeAppendFormattedExtension(number, metadata, PhoneNumberFormat.NATIONAL, formattedNumber);
+ prefixNumberWithCountryCallingCode(countryCallingCode, PhoneNumberFormat.NATIONAL,
+ formattedNumber);
return formattedNumber.toString();
}
@@ -1314,6 +1324,10 @@
public String formatOutOfCountryCallingNumber(PhoneNumber number,
String regionCallingFrom) {
if (!isValidRegionCode(regionCallingFrom)) {
+ LOGGER.log(Level.WARNING,
+ "Trying to format number from invalid region "
+ + regionCallingFrom
+ + ". International formatting applied.");
return format(number, PhoneNumberFormat.INTERNATIONAL);
}
int countryCallingCode = number.getCountryCode();
@@ -1327,7 +1341,7 @@
// country calling code.
return countryCallingCode + " " + format(number, PhoneNumberFormat.NATIONAL);
}
- } else if (countryCallingCode == getCountryCodeForRegion(regionCallingFrom)) {
+ } else if (countryCallingCode == getCountryCodeForValidRegion(regionCallingFrom)) {
// For regions that share a country calling code, the country calling code need not be dialled.
// This also applies when dialling within a region, so this if clause covers both these cases.
// Technically this is the case for dialling from La Reunion to other overseas departments of
@@ -1353,18 +1367,17 @@
PhoneMetadata metadataForRegion =
getMetadataForRegionOrCallingCode(countryCallingCode, regionCode);
String formattedNationalNumber =
- formatNationalNumber(nationalSignificantNumber,
- metadataForRegion, PhoneNumberFormat.INTERNATIONAL);
+ formatNsn(nationalSignificantNumber, metadataForRegion, PhoneNumberFormat.INTERNATIONAL);
StringBuilder formattedNumber = new StringBuilder(formattedNationalNumber);
- maybeGetFormattedExtension(number, metadataForRegion, PhoneNumberFormat.INTERNATIONAL,
- formattedNumber);
+ maybeAppendFormattedExtension(number, metadataForRegion, PhoneNumberFormat.INTERNATIONAL,
+ formattedNumber);
if (internationalPrefixForFormatting.length() > 0) {
formattedNumber.insert(0, " ").insert(0, countryCallingCode).insert(0, " ")
.insert(0, internationalPrefixForFormatting);
} else {
- formatNumberByFormat(countryCallingCode,
- PhoneNumberFormat.INTERNATIONAL,
- formattedNumber);
+ prefixNumberWithCountryCallingCode(countryCallingCode,
+ PhoneNumberFormat.INTERNATIONAL,
+ formattedNumber);
}
return formattedNumber.toString();
}
@@ -1379,6 +1392,7 @@
*
* Note this method guarantees no digit will be inserted, removed or modified as a result of
* formatting.
+ *
* @param number the phone number that needs to be formatted in its original number format
* @param regionCallingFrom the region whose IDD needs to be prefixed if the original number
* has one
@@ -1565,26 +1579,27 @@
if (isNANPACountry(regionCallingFrom)) {
return countryCode + " " + rawInput;
}
- } else if (countryCode == getCountryCodeForRegion(regionCallingFrom)) {
- // Here we copy the formatting rules so we can modify the pattern we expect to match against.
- List<NumberFormat> availableFormats =
- new ArrayList<NumberFormat>(metadataForRegionCallingFrom.numberFormatSize());
- for (NumberFormat format : metadataForRegionCallingFrom.numberFormats()) {
- NumberFormat newFormat = new NumberFormat();
- newFormat.mergeFrom(format);
- // The first group is the first group of digits that the user determined.
- newFormat.setPattern("(\\d+)(.*)");
- // Here we just concatenate them back together after the national prefix has been fixed.
- newFormat.setFormat("$1$2");
- availableFormats.add(newFormat);
+ } else if (isValidRegionCode(regionCallingFrom) &&
+ countryCode == getCountryCodeForValidRegion(regionCallingFrom)) {
+ NumberFormat formattingPattern =
+ chooseFormattingPatternForNumber(metadataForRegionCallingFrom.numberFormats(),
+ nationalNumber);
+ if (formattingPattern == null) {
+ // If no pattern above is matched, we format the original input.
+ return rawInput;
}
- // Now we format using these patterns instead of the default pattern, but with the national
- // prefix prefixed if necessary, by choosing the format rule based on the leading digits
- // present in the unformatted national number.
+ NumberFormat newFormat = new NumberFormat();
+ newFormat.mergeFrom(formattingPattern);
+ // The first group is the first group of digits that the user wrote together.
+ newFormat.setPattern("(\\d+)(.*)");
+ // Here we just concatenate them back together after the national prefix has been fixed.
+ newFormat.setFormat("$1$2");
+ // Now we format using this pattern instead of the default pattern, but with the national
+ // prefix prefixed if necessary.
// This will not work in the cases where the pattern (and not the leading digits) decide
// whether a national prefix needs to be used, since we have overridden the pattern to match
// anything, but that is not the case in the metadata to date.
- return formatAccordingToFormats(rawInput, availableFormats, PhoneNumberFormat.NATIONAL);
+ return formatNsnUsingPattern(rawInput, newFormat, PhoneNumberFormat.NATIONAL);
}
String internationalPrefixForFormatting = "";
// If an unsupported region-calling-from is entered, or a country with multiple international
@@ -1600,15 +1615,15 @@
StringBuilder formattedNumber = new StringBuilder(rawInput);
String regionCode = getRegionCodeForCountryCode(countryCode);
PhoneMetadata metadataForRegion = getMetadataForRegionOrCallingCode(countryCode, regionCode);
- maybeGetFormattedExtension(number, metadataForRegion,
- PhoneNumberFormat.INTERNATIONAL, formattedNumber);
+ maybeAppendFormattedExtension(number, metadataForRegion,
+ PhoneNumberFormat.INTERNATIONAL, formattedNumber);
if (internationalPrefixForFormatting.length() > 0) {
formattedNumber.insert(0, " ").insert(0, countryCode).insert(0, " ")
.insert(0, internationalPrefixForFormatting);
} else {
// Invalid region entered as country-calling-from (so no metadata was found for it) or the
// region chosen has multiple international dialling prefixes.
- formatNumberByFormat(countryCode,
+ prefixNumberWithCountryCallingCode(countryCode,
PhoneNumberFormat.INTERNATIONAL,
formattedNumber);
}
@@ -1632,9 +1647,9 @@
/**
* A helper function that is used by format and formatByPattern.
*/
- private void formatNumberByFormat(int countryCallingCode,
- PhoneNumberFormat numberFormat,
- StringBuilder formattedNumber) {
+ private void prefixNumberWithCountryCallingCode(int countryCallingCode,
+ PhoneNumberFormat numberFormat,
+ StringBuilder formattedNumber) {
switch (numberFormat) {
case E164:
formattedNumber.insert(0, countryCallingCode).insert(0, PLUS_SIGN);
@@ -1651,21 +1666,19 @@
}
}
- // Simple wrapper of formatNationalNumber for the common case of no carrier code.
- private String formatNationalNumber(String number,
- PhoneMetadata metadata,
- PhoneNumberFormat numberFormat) {
- return formatNationalNumber(number, metadata, numberFormat, null);
+ // Simple wrapper of formatNsn for the common case of no carrier code.
+ private String formatNsn(String number, PhoneMetadata metadata, PhoneNumberFormat numberFormat) {
+ return formatNsn(number, metadata, numberFormat, null);
}
// Note in some regions, 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. If a
// carrierCode is specified, this will be inserted into the formatted string to replace $CC.
- private String formatNationalNumber(String number,
- PhoneMetadata metadata,
- PhoneNumberFormat numberFormat,
- String carrierCode) {
+ private String formatNsn(String number,
+ PhoneMetadata metadata,
+ PhoneNumberFormat numberFormat,
+ String carrierCode) {
List<NumberFormat> intlNumberFormats = metadata.intlNumberFormats();
// When the intlNumberFormats exists, we use that to format national number for the
// INTERNATIONAL format instead of using the numberDesc.numberFormats.
@@ -1673,13 +1686,10 @@
(intlNumberFormats.size() == 0 || numberFormat == PhoneNumberFormat.NATIONAL)
? metadata.numberFormats()
: metadata.intlNumberFormats();
- String formattedNationalNumber =
- formatAccordingToFormats(number, availableFormats, numberFormat, carrierCode);
- if (numberFormat == PhoneNumberFormat.RFC3966) {
- formattedNationalNumber =
- SEPARATOR_PATTERN.matcher(formattedNationalNumber).replaceAll("-");
- }
- return formattedNationalNumber;
+ NumberFormat formattingPattern = chooseFormattingPatternForNumber(availableFormats, number);
+ return (formattingPattern == null)
+ ? number
+ : formatNsnUsingPattern(number, formattingPattern, numberFormat, carrierCode);
}
private NumberFormat chooseFormattingPatternForNumber(List<NumberFormat> availableFormats,
@@ -1698,50 +1708,58 @@
return null;
}
- // 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);
+ // Simple wrapper of formatNsnUsingPattern for the common case of no carrier code.
+ private String formatNsnUsingPattern(String nationalNumber,
+ NumberFormat formattingPattern,
+ PhoneNumberFormat numberFormat) {
+ return formatNsnUsingPattern(nationalNumber, formattingPattern, numberFormat, null);
}
// Note that carrierCode is optional - if NULL or an empty string, no carrier code replacement
// will take place.
- private String formatAccordingToFormats(String nationalNumber,
- List<NumberFormat> availableFormats,
- PhoneNumberFormat numberFormat,
- String carrierCode) {
- NumberFormat numFormat = chooseFormattingPatternForNumber(availableFormats, nationalNumber);
- if (numFormat == null) {
- // If no pattern above is matched, we format the number as a whole.
- return nationalNumber;
- }
- String numberFormatRule = numFormat.getFormat();
- Matcher m = regexCache.getPatternForRegex(numFormat.getPattern()).matcher(nationalNumber);
+ private String formatNsnUsingPattern(String nationalNumber,
+ NumberFormat formattingPattern,
+ PhoneNumberFormat numberFormat,
+ String carrierCode) {
+ String numberFormatRule = formattingPattern.getFormat();
+ Matcher m =
+ regexCache.getPatternForRegex(formattingPattern.getPattern()).matcher(nationalNumber);
+ String formattedNationalNumber = "";
if (numberFormat == PhoneNumberFormat.NATIONAL &&
carrierCode != null && carrierCode.length() > 0 &&
- numFormat.getDomesticCarrierCodeFormattingRule().length() > 0) {
+ formattingPattern.getDomesticCarrierCodeFormattingRule().length() > 0) {
// Replace the $CC in the formatting rule with the desired carrier code.
- String carrierCodeFormattingRule = numFormat.getDomesticCarrierCodeFormattingRule();
+ String carrierCodeFormattingRule = formattingPattern.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);
- return m.replaceAll(numberFormatRule);
+ formattedNationalNumber = m.replaceAll(numberFormatRule);
} else {
// Use the national prefix formatting rule instead.
- String nationalPrefixFormattingRule = numFormat.getNationalPrefixFormattingRule();
+ String nationalPrefixFormattingRule = formattingPattern.getNationalPrefixFormattingRule();
if (numberFormat == PhoneNumberFormat.NATIONAL &&
nationalPrefixFormattingRule != null &&
nationalPrefixFormattingRule.length() > 0) {
Matcher firstGroupMatcher = FIRST_GROUP_PATTERN.matcher(numberFormatRule);
- return m.replaceAll(firstGroupMatcher.replaceFirst(nationalPrefixFormattingRule));
+ formattedNationalNumber =
+ m.replaceAll(firstGroupMatcher.replaceFirst(nationalPrefixFormattingRule));
} else {
- return m.replaceAll(numberFormatRule);
+ formattedNationalNumber = m.replaceAll(numberFormatRule);
}
}
+ if (numberFormat == PhoneNumberFormat.RFC3966) {
+ // Strip any leading punctuation.
+ Matcher matcher = SEPARATOR_PATTERN.matcher(formattedNationalNumber);
+ if (matcher.lookingAt()) {
+ formattedNationalNumber = matcher.replaceFirst("");
+ }
+ // Replace the rest with a dash between each number group.
+ formattedNationalNumber = matcher.reset(formattedNationalNumber).replaceAll("-");
+ }
+ return formattedNationalNumber;
}
/**
@@ -1769,7 +1787,7 @@
public PhoneNumber getExampleNumberForType(String regionCode, PhoneNumberType type) {
// Check the region code is valid.
if (!isValidRegionCode(regionCode)) {
- LOGGER.log(Level.SEVERE, "Invalid or unknown region code provided: " + regionCode);
+ LOGGER.log(Level.WARNING, "Invalid or unknown region code provided: " + regionCode);
return null;
}
PhoneNumberDesc desc = getNumberDescByType(getMetadataForRegion(regionCode), type);
@@ -1802,6 +1820,9 @@
} catch (NumberParseException e) {
LOGGER.log(Level.SEVERE, e.toString());
}
+ } else {
+ LOGGER.log(Level.WARNING,
+ "Invalid or unknown country calling code provided: " + countryCallingCode);
}
return null;
}
@@ -1810,32 +1831,22 @@
* Appends the formatted extension of a phone number to formattedNumber, if the phone number had
* an extension specified.
*/
- private void maybeGetFormattedExtension(PhoneNumber number, PhoneMetadata metadata,
- PhoneNumberFormat numberFormat,
- StringBuilder formattedNumber) {
+ private void maybeAppendFormattedExtension(PhoneNumber number, PhoneMetadata metadata,
+ PhoneNumberFormat numberFormat,
+ StringBuilder formattedNumber) {
if (number.hasExtension() && number.getExtension().length() > 0) {
if (numberFormat == PhoneNumberFormat.RFC3966) {
formattedNumber.append(RFC3966_EXTN_PREFIX).append(number.getExtension());
} else {
- formatExtension(number.getExtension(), metadata, formattedNumber);
+ if (metadata.hasPreferredExtnPrefix()) {
+ formattedNumber.append(metadata.getPreferredExtnPrefix()).append(number.getExtension());
+ } else {
+ formattedNumber.append(DEFAULT_EXTN_PREFIX).append(number.getExtension());
+ }
}
}
}
- /**
- * Formats the extension part of the phone number by prefixing it with the appropriate extension
- * prefix. This will be the default extension prefix, unless overridden by a preferred
- * extension prefix for this region.
- */
- private void formatExtension(String extensionDigits, PhoneMetadata metadata,
- StringBuilder extension) {
- if (metadata.hasPreferredExtnPrefix()) {
- extension.append(metadata.getPreferredExtnPrefix()).append(extensionDigits);
- } else {
- extension.append(DEFAULT_EXTN_PREFIX).append(extensionDigits);
- }
- }
-
PhoneNumberDesc getNumberDescByType(PhoneMetadata metadata, PhoneNumberType type) {
switch (type) {
case PREMIUM_RATE:
@@ -1992,12 +2003,14 @@
*/
public boolean isValidNumberForRegion(PhoneNumber number, String regionCode) {
int countryCode = number.getCountryCode();
- if (countryCode == 0 ||
+ PhoneMetadata metadata = getMetadataForRegionOrCallingCode(countryCode, regionCode);
+ if ((metadata == null) ||
(!REGION_CODE_FOR_NON_GEO_ENTITY.equals(regionCode) &&
- countryCode != getCountryCodeForRegion(regionCode))) {
+ countryCode != getCountryCodeForValidRegion(regionCode))) {
+ // Either the region code was invalid, or the country calling code for this number does not
+ // match that of the region code.
return false;
}
- PhoneMetadata metadata = getMetadataForRegionOrCallingCode(countryCode, regionCode);
PhoneNumberDesc generalNumDesc = metadata.getGeneralDesc();
String nationalSignificantNumber = getNationalSignificantNumber(number);
@@ -2072,12 +2085,23 @@
*/
public int getCountryCodeForRegion(String regionCode) {
if (!isValidRegionCode(regionCode)) {
- LOGGER.log(Level.SEVERE,
+ LOGGER.log(Level.WARNING,
"Invalid or missing region code ("
+ ((regionCode == null) ? "null" : regionCode)
+ ") provided.");
return 0;
}
+ return getCountryCodeForValidRegion(regionCode);
+ }
+
+ /**
+ * Returns the country calling code for a specific region. For example, this would be 1 for the
+ * United States, and 64 for New Zealand. Assumes the region is already valid.
+ *
+ * @param regionCode the region that we want to get the country calling code for
+ * @return the country calling code for the region denoted by regionCode
+ */
+ private int getCountryCodeForValidRegion(String regionCode) {
PhoneMetadata metadata = getMetadataForRegion(regionCode);
return metadata.getCountryCode();
}
@@ -2098,7 +2122,7 @@
*/
public String getNddPrefixForRegion(String regionCode, boolean stripNonDigits) {
if (!isValidRegionCode(regionCode)) {
- LOGGER.log(Level.SEVERE,
+ LOGGER.log(Level.WARNING,
"Invalid or missing region code ("
+ ((regionCode == null) ? "null" : regionCode)
+ ") provided.");
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_AN b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_AN
index ece2723..10f648f 100644
--- a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_AN
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_AN
Binary files differ
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_AX b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_AX
index 91d11b5..18ca4d0 100644
--- a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_AX
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_AX
Binary files differ
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BF b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BF
index 01ca8c3..802997e 100644
--- a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BF
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BF
Binary files differ
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BJ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BJ
index af2439c..09eee90 100644
--- a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BJ
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BJ
Binary files differ
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BQ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BQ
new file mode 100644
index 0000000..6ac0880
--- /dev/null
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BQ
Binary files differ
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BR b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BR
index 845377b..31ed9bc 100644
--- a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BR
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BR
Binary files differ
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BS b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BS
index 2a8b978..d280135 100644
--- a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BS
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_BS
Binary files differ
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_CW b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_CW
new file mode 100644
index 0000000..8f4170e
--- /dev/null
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_CW
Binary files differ
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_DJ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_DJ
index 57af3c0..0683a64 100644
--- a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_DJ
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_DJ
Binary files differ
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_FI b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_FI
index 1e7307e..9995781 100644
--- a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_FI
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_FI
Binary files differ
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_IN b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_IN
index 326d131..f07407a 100644
--- a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_IN
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_IN
Binary files differ
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_LV b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_LV
index 335c85d..fd64fb8 100644
--- a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_LV
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_LV
Binary files differ
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_MW b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_MW
index 2fb6952..fdb4093 100644
--- a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_MW
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_MW
Binary files differ
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_RS b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_RS
index 81c0848..bf324e6 100644
--- a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_RS
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_RS
Binary files differ
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_SC b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_SC
index 39ee643..7f55e5a 100644
--- a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_SC
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_SC
Binary files differ
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_SS b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_SS
new file mode 100644
index 0000000..a8293fd
--- /dev/null
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_SS
Binary files differ
diff --git a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_VN b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_VN
index 2d29291..d775464 100644
--- a/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_VN
+++ b/java/src/com/android/i18n/phonenumbers/data/PhoneNumberMetadataProto_VN
Binary files differ
diff --git a/java/test/com/android/i18n/phonenumbers/ExampleNumbersTest.java b/java/test/com/android/i18n/phonenumbers/ExampleNumbersTest.java
index 9f19826..04e2c7f 100644
--- a/java/test/com/android/i18n/phonenumbers/ExampleNumbersTest.java
+++ b/java/test/com/android/i18n/phonenumbers/ExampleNumbersTest.java
@@ -141,6 +141,13 @@
assertEquals(0, wrongTypeCases.size());
}
+ public void testVoicemail() throws Exception {
+ Set<PhoneNumberType> voicemailTypes = EnumSet.of(PhoneNumberType.VOICEMAIL);
+ checkNumbersValidAndCorrectType(PhoneNumberType.VOICEMAIL, voicemailTypes);
+ assertEquals(0, invalidCases.size());
+ assertEquals(0, wrongTypeCases.size());
+ }
+
public void testSharedCost() throws Exception {
Set<PhoneNumberType> sharedCostTypes = EnumSet.of(PhoneNumberType.SHARED_COST);
checkNumbersValidAndCorrectType(PhoneNumberType.SHARED_COST, sharedCostTypes);
@@ -162,11 +169,44 @@
}
if (exampleNumber != null && phoneNumberUtil.canBeInternationallyDialled(exampleNumber)) {
wrongTypeCases.add(exampleNumber);
+ LOGGER.log(Level.SEVERE, "Number " + exampleNumber.toString()
+ + " should not be internationally diallable");
}
}
assertEquals(0, wrongTypeCases.size());
}
+ // TODO: Update this to use connectsToEmergencyNumber or similar once that is
+ // implemented.
+ public void testEmergency() throws Exception {
+ int wrongTypeCounter = 0;
+ for (String regionCode : phoneNumberUtil.getSupportedRegions()) {
+ PhoneNumberDesc desc =
+ phoneNumberUtil.getMetadataForRegion(regionCode).getEmergency();
+ if (desc.hasExampleNumber()) {
+ String exampleNumber = desc.getExampleNumber();
+ if (!exampleNumber.matches(desc.getPossibleNumberPattern()) ||
+ !exampleNumber.matches(desc.getNationalNumberPattern())) {
+ wrongTypeCounter++;
+ LOGGER.log(Level.SEVERE, "Emergency example number test failed for " + regionCode);
+ }
+ }
+ }
+ assertEquals(0, wrongTypeCounter);
+ }
+
+ public void testGlobalNetworkNumbers() throws Exception {
+ for (Integer callingCode : phoneNumberUtil.getSupportedGlobalNetworkCallingCodes()) {
+ PhoneNumber exampleNumber =
+ phoneNumberUtil.getExampleNumberForNonGeoEntity(callingCode);
+ assertNotNull("No example phone number for calling code " + callingCode, exampleNumber);
+ if (!phoneNumberUtil.isValidNumber(exampleNumber)) {
+ invalidCases.add(exampleNumber);
+ LOGGER.log(Level.SEVERE, "Failed validation for " + exampleNumber.toString());
+ }
+ }
+ }
+
public void testEveryRegionHasAnExampleNumber() throws Exception {
for (String regionCode : phoneNumberUtil.getSupportedRegions()) {
PhoneNumber exampleNumber = phoneNumberUtil.getExampleNumber(regionCode);
diff --git a/java/test/com/android/i18n/phonenumbers/PhoneNumberMatcherTest.java b/java/test/com/android/i18n/phonenumbers/PhoneNumberMatcherTest.java
index a00ef33..dea226d 100644
--- a/java/test/com/android/i18n/phonenumbers/PhoneNumberMatcherTest.java
+++ b/java/test/com/android/i18n/phonenumbers/PhoneNumberMatcherTest.java
@@ -370,6 +370,9 @@
new NumberTest("1/12/2011", RegionCode.US),
new NumberTest("10/12/82", RegionCode.DE),
new NumberTest("650x2531234", RegionCode.US),
+ new NumberTest("2012-01-02 08:00", RegionCode.US),
+ new NumberTest("2012/01/02 08:00", RegionCode.US),
+ new NumberTest("20120102 08:00", RegionCode.US),
};
/**
@@ -398,12 +401,13 @@
new NumberTest("9002309. 158", RegionCode.US),
new NumberTest("12 7/8 - 14 12/34 - 5", RegionCode.US),
new NumberTest("12.1 - 23.71 - 23.45", RegionCode.US),
-
new NumberTest("800 234 1 111x1111", RegionCode.US),
new NumberTest("1979-2011 100", RegionCode.US),
new NumberTest("+494949-4-94", RegionCode.DE), // National number in wrong format
new NumberTest("\uFF14\uFF11\uFF15\uFF16\uFF16\uFF16\uFF16-\uFF17\uFF17\uFF17", RegionCode.US),
-
+ new NumberTest("2012-0102 08", RegionCode.US), // Very strange formatting.
+ new NumberTest("2012-01-02 08", RegionCode.US),
+ new NumberTest("1800-10-10 22", RegionCode.AU), // Breakdown assistance number.
};
/**
diff --git a/java/test/com/android/i18n/phonenumbers/PhoneNumberUtilTest.java b/java/test/com/android/i18n/phonenumbers/PhoneNumberUtilTest.java
index 66693e5..00718bb 100644
--- a/java/test/com/android/i18n/phonenumbers/PhoneNumberUtilTest.java
+++ b/java/test/com/android/i18n/phonenumbers/PhoneNumberUtilTest.java
@@ -709,6 +709,9 @@
assertEquals("+1 (650) 253-0000", phoneUtil.formatByPattern(US_NUMBER,
PhoneNumberFormat.INTERNATIONAL,
newNumberFormats));
+ assertEquals("+1-650-253-0000", phoneUtil.formatByPattern(US_NUMBER,
+ PhoneNumberFormat.RFC3966,
+ newNumberFormats));
// $NP is set to '1' for the US. Here we check that for other NANPA countries the US rules are
// followed.
@@ -1027,6 +1030,18 @@
assertTrue(phoneUtil.isValidNumberForRegion(reNumber, RegionCode.RE));
assertTrue(phoneUtil.isValidNumberForRegion(INTERNATIONAL_TOLL_FREE, RegionCode.UN001));
assertFalse(phoneUtil.isValidNumberForRegion(INTERNATIONAL_TOLL_FREE, RegionCode.US));
+ assertFalse(phoneUtil.isValidNumberForRegion(INTERNATIONAL_TOLL_FREE, RegionCode.ZZ));
+
+ PhoneNumber invalidNumber = new PhoneNumber();
+ // Invalid country calling codes.
+ invalidNumber.setCountryCode(3923).setNationalNumber(2366L);
+ assertFalse(phoneUtil.isValidNumberForRegion(invalidNumber, RegionCode.ZZ));
+ invalidNumber.setCountryCode(3923).setNationalNumber(2366L);
+ assertFalse(phoneUtil.isValidNumberForRegion(invalidNumber, RegionCode.UN001));
+ invalidNumber.setCountryCode(0).setNationalNumber(2366L);
+ assertFalse(phoneUtil.isValidNumberForRegion(invalidNumber, RegionCode.UN001));
+ invalidNumber.setCountryCode(0);
+ assertFalse(phoneUtil.isValidNumberForRegion(invalidNumber, RegionCode.ZZ));
}
public void testIsNotValidNumber() {
@@ -1048,6 +1063,13 @@
invalidNumber.setCountryCode(64).setNationalNumber(3316005L);
assertFalse(phoneUtil.isValidNumber(invalidNumber));
+ invalidNumber.clear();
+ // Invalid country calling codes.
+ invalidNumber.setCountryCode(3923).setNationalNumber(2366L);
+ assertFalse(phoneUtil.isValidNumber(invalidNumber));
+ invalidNumber.setCountryCode(0);
+ assertFalse(phoneUtil.isValidNumber(invalidNumber));
+
assertFalse(phoneUtil.isValidNumber(INTERNATIONAL_TOLL_FREE_TOO_LONG));
}
@@ -1551,6 +1573,10 @@
// already possible.
usNumber.setCountryCode(1).setNationalNumber(1234567890L);
assertEquals(usNumber, phoneUtil.parse("123-456-7890", RegionCode.US));
+
+ // Test star numbers. Although this is not strictly valid, we would like to make sure we can
+ // parse the output we produce when formatting the number.
+ assertEquals(JP_STAR_NUMBER, phoneUtil.parse("+81 *2345", RegionCode.JP));
}
public void testParseNumberWithAlphaCharacters() throws Exception {
@@ -1703,6 +1729,26 @@
e.getErrorType());
}
try {
+ String plusStar = "+***";
+ phoneUtil.parse(plusStar, RegionCode.DE);
+ fail("This should not parse without throwing an exception " + plusStar);
+ } catch (NumberParseException e) {
+ // Expected this exception.
+ assertEquals("Wrong error type stored in exception.",
+ NumberParseException.ErrorType.NOT_A_NUMBER,
+ e.getErrorType());
+ }
+ try {
+ String plusStarPhoneNumber = "+*******91";
+ phoneUtil.parse(plusStarPhoneNumber, RegionCode.DE);
+ fail("This should not parse without throwing an exception " + plusStarPhoneNumber);
+ } 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, RegionCode.DE);
fail("This should not parse without throwing an exception " + tooShortPhoneNumber);