diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/CountryCodeToRegionCodeMap.java b/java/libphonenumber/src/com/google/i18n/phonenumbers/CountryCodeToRegionCodeMap.java index 915c02b1e..922ab6dbc 100644 --- a/java/libphonenumber/src/com/google/i18n/phonenumbers/CountryCodeToRegionCodeMap.java +++ b/java/libphonenumber/src/com/google/i18n/phonenumbers/CountryCodeToRegionCodeMap.java @@ -31,10 +31,10 @@ public class CountryCodeToRegionCodeMap { // countries sharing a calling code, such as the NANPA countries, the one // indicated with "isMainCountryForCode" in the metadata should be first. static Map> 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> countryCodeToRegionCodeMap = - new HashMap>(280); + new HashMap>(281); ArrayList listWithRegionCode; @@ -253,6 +253,10 @@ public class CountryCodeToRegionCodeMap { listWithRegionCode.add("IR"); countryCodeToRegionCodeMap.put(98, listWithRegionCode); + listWithRegionCode = new ArrayList(1); + listWithRegionCode.add("SS"); + countryCodeToRegionCodeMap.put(211, listWithRegionCode); + listWithRegionCode = new ArrayList(1); listWithRegionCode.add("MA"); countryCodeToRegionCodeMap.put(212, listWithRegionCode); @@ -685,8 +689,10 @@ public class CountryCodeToRegionCodeMap { listWithRegionCode.add("UY"); countryCodeToRegionCodeMap.put(598, listWithRegionCode); - listWithRegionCode = new ArrayList(1); + listWithRegionCode = new ArrayList(3); + listWithRegionCode.add("CW"); listWithRegionCode.add("AN"); + listWithRegionCode.add("BQ"); countryCodeToRegionCodeMap.put(599, listWithRegionCode); listWithRegionCode = new ArrayList(1); diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberMatcher.java b/java/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberMatcher.java index e0892baee..d4bc57529 100644 --- a/java/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberMatcher.java +++ b/java/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberMatcher.java @@ -70,6 +70,14 @@ final class PhoneNumberMatcher implements Iterator { private static final Pattern SLASH_SEPARATED_DATES = 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 @@ -310,6 +318,13 @@ final class PhoneNumberMatcher implements Iterator { 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/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java b/java/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java index 76212c6b2..883a676ee 100644 --- a/java/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java +++ b/java/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java @@ -95,6 +95,8 @@ public class PhoneNumberUtil { // 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 @@ public class PhoneNumberUtil { // 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 @@ public class PhoneNumberUtil { return normalizedNumber.toString(); } + // @VisibleForTesting static synchronized PhoneNumberUtil getInstance( String baseFileLocation, Map> countryCallingCodeToRegionCodeMap) { @@ -1019,6 +1022,14 @@ public class PhoneNumberUtil { return supportedRegions; } + /** + * Convenience method to get a list of what global network calling codes the library has metadata + * for. + */ + public Set 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 @@ -1091,23 +1102,24 @@ public class PhoneNumberUtil { // 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 @@ public class PhoneNumberUtil { if (!hasValidCountryCallingCode(countryCallingCode)) { return nationalSignificantNumber; } - List userDefinedFormatsCopy = - new ArrayList(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 @@ public class PhoneNumberUtil { // 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 @@ public class PhoneNumberUtil { 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 class PhoneNumberUtil { 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 @@ public class PhoneNumberUtil { // 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 @@ public class PhoneNumberUtil { 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 @@ public class PhoneNumberUtil { * * 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 @@ public class PhoneNumberUtil { 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 availableFormats = - new ArrayList(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 @@ public class PhoneNumberUtil { 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 @@ public class PhoneNumberUtil { /** * 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 @@ public class PhoneNumberUtil { } } - // 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 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 @@ public class PhoneNumberUtil { (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 availableFormats, @@ -1698,50 +1708,58 @@ public class PhoneNumberUtil { return null; } - // Simple wrapper of formatAccordingToFormats for the common case of no carrier code. - private String formatAccordingToFormats(String nationalNumber, - List 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 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 class PhoneNumberUtil { 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 @@ public class PhoneNumberUtil { } 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 @@ public class PhoneNumberUtil { * 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 class PhoneNumberUtil { */ 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 class PhoneNumberUtil { */ 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 class PhoneNumberUtil { */ 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/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_AN b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_AN index ece2723b3..10f648f32 100644 Binary files a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_AN and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_AN differ diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_AX b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_AX index 91d11b5fd..18ca4d09a 100644 Binary files a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_AX and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_AX differ diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BF b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BF index 01ca8c3e9..802997eed 100644 Binary files a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BF and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BF differ diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BJ b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BJ index af2439c7a..09eee9039 100644 Binary files a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BJ and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BJ differ diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BQ b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BQ new file mode 100644 index 000000000..6ac0880a7 Binary files /dev/null and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BQ differ diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BR b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BR index 845377b1b..31ed9bc75 100644 Binary files a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BR and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BR differ diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BS b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BS index 2a8b97802..d28013574 100644 Binary files a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BS and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BS differ diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CW b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CW new file mode 100644 index 000000000..8f4170ea6 Binary files /dev/null and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CW differ diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_DJ b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_DJ index 57af3c095..0683a6431 100644 Binary files a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_DJ and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_DJ differ diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_FI b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_FI index 1e7307e9e..99957812a 100644 Binary files a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_FI and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_FI differ diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_IN b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_IN index 326d13125..f07407afc 100644 Binary files a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_IN and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_IN differ diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_LV b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_LV index 335c85d80..fd64fb83c 100644 Binary files a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_LV and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_LV differ diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_MW b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_MW index 2fb695207..fdb4093da 100644 Binary files a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_MW and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_MW differ diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_RS b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_RS index 81c0848bf..bf324e6b2 100644 Binary files a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_RS and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_RS differ diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_SC b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_SC index 39ee643e6..7f55e5ae9 100644 Binary files a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_SC and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_SC differ diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_SS b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_SS new file mode 100644 index 000000000..a8293fd49 Binary files /dev/null and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_SS differ diff --git a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_VN b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_VN index 2d29291ac..d775464fa 100644 Binary files a/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_VN and b/java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_VN differ diff --git a/java/libphonenumber/test/com/google/i18n/phonenumbers/ExampleNumbersTest.java b/java/libphonenumber/test/com/google/i18n/phonenumbers/ExampleNumbersTest.java index 7c30588e3..1d23fc2b3 100644 --- a/java/libphonenumber/test/com/google/i18n/phonenumbers/ExampleNumbersTest.java +++ b/java/libphonenumber/test/com/google/i18n/phonenumbers/ExampleNumbersTest.java @@ -141,6 +141,13 @@ public class ExampleNumbersTest extends TestCase { assertEquals(0, wrongTypeCases.size()); } + public void testVoicemail() throws Exception { + Set voicemailTypes = EnumSet.of(PhoneNumberType.VOICEMAIL); + checkNumbersValidAndCorrectType(PhoneNumberType.VOICEMAIL, voicemailTypes); + assertEquals(0, invalidCases.size()); + assertEquals(0, wrongTypeCases.size()); + } + public void testSharedCost() throws Exception { Set sharedCostTypes = EnumSet.of(PhoneNumberType.SHARED_COST); checkNumbersValidAndCorrectType(PhoneNumberType.SHARED_COST, sharedCostTypes); @@ -162,11 +169,44 @@ public class ExampleNumbersTest extends TestCase { } 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/libphonenumber/test/com/google/i18n/phonenumbers/PhoneNumberMatcherTest.java b/java/libphonenumber/test/com/google/i18n/phonenumbers/PhoneNumberMatcherTest.java index a9f841d07..9127f7799 100644 --- a/java/libphonenumber/test/com/google/i18n/phonenumbers/PhoneNumberMatcherTest.java +++ b/java/libphonenumber/test/com/google/i18n/phonenumbers/PhoneNumberMatcherTest.java @@ -370,6 +370,9 @@ public class PhoneNumberMatcherTest extends TestCase { 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 @@ public class PhoneNumberMatcherTest extends TestCase { 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/libphonenumber/test/com/google/i18n/phonenumbers/PhoneNumberUtilTest.java b/java/libphonenumber/test/com/google/i18n/phonenumbers/PhoneNumberUtilTest.java index be62ef847..d78e80f1e 100644 --- a/java/libphonenumber/test/com/google/i18n/phonenumbers/PhoneNumberUtilTest.java +++ b/java/libphonenumber/test/com/google/i18n/phonenumbers/PhoneNumberUtilTest.java @@ -709,6 +709,9 @@ public class PhoneNumberUtilTest extends TestCase { 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 @@ public class PhoneNumberUtilTest extends TestCase { 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 @@ public class PhoneNumberUtilTest extends TestCase { 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 @@ public class PhoneNumberUtilTest extends TestCase { // 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 { @@ -1702,6 +1728,26 @@ public class PhoneNumberUtilTest extends TestCase { NumberParseException.ErrorType.NOT_A_NUMBER, 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); diff --git a/java/libphonenumber/test/com/google/i18n/phonenumbers/ShortNumberUtilTest.java.orig b/java/libphonenumber/test/com/google/i18n/phonenumbers/ShortNumberUtilTest.java.orig deleted file mode 100644 index b1ae29b85..000000000 --- a/java/libphonenumber/test/com/google/i18n/phonenumbers/ShortNumberUtilTest.java.orig +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2011 The Libphonenumber Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.i18n.phonenumbers; - -import junit.framework.TestCase; - -import java.io.InputStream; - -/** - * @author Shaopeng Jia - */ -public class ShortNumberUtilTest extends TestCase { - private ShortNumberUtil shortUtil; - static final String TEST_META_DATA_FILE_PREFIX = - "/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProtoForTesting"; - - public ShortNumberUtilTest() { - PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance( - TEST_META_DATA_FILE_PREFIX, - CountryCodeToRegionCodeMapForTesting.getCountryCodeToRegionCodeMap()); - shortUtil = new ShortNumberUtil(phoneUtil); - } - - public void testConnectsToEmergencyNumber_US() { - assertTrue(shortUtil.connectsToEmergencyNumber("911", RegionCode.US)); - assertTrue(shortUtil.connectsToEmergencyNumber("119", RegionCode.US)); - assertFalse(shortUtil.connectsToEmergencyNumber("999", RegionCode.US)); - } - - public void testConnectsToEmergencyNumberLongNumber_US() { - assertTrue(shortUtil.connectsToEmergencyNumber("9116666666", RegionCode.US)); - assertTrue(shortUtil.connectsToEmergencyNumber("1196666666", RegionCode.US)); - assertFalse(shortUtil.connectsToEmergencyNumber("9996666666", RegionCode.US)); - } - - public void testConnectsToEmergencyNumberWithFormatting_US() { - assertTrue(shortUtil.connectsToEmergencyNumber("9-1-1", RegionCode.US)); - assertTrue(shortUtil.connectsToEmergencyNumber("1-1-9", RegionCode.US)); - assertFalse(shortUtil.connectsToEmergencyNumber("9-9-9", RegionCode.US)); - } - - public void testConnectsToEmergencyNumberWithPlusSign_US() { - assertFalse(shortUtil.connectsToEmergencyNumber("+911", RegionCode.US)); - assertFalse(shortUtil.connectsToEmergencyNumber("\uFF0B911", RegionCode.US)); - assertFalse(shortUtil.connectsToEmergencyNumber(" +911", RegionCode.US)); - assertFalse(shortUtil.connectsToEmergencyNumber("+119", RegionCode.US)); - assertFalse(shortUtil.connectsToEmergencyNumber("+999", RegionCode.US)); - } - - public void testConnectsToEmergencyNumber_BR() { - assertTrue(shortUtil.connectsToEmergencyNumber("911", RegionCode.BR)); - assertTrue(shortUtil.connectsToEmergencyNumber("190", RegionCode.BR)); - assertFalse(shortUtil.connectsToEmergencyNumber("999", RegionCode.BR)); - } - - public void testConnectsToEmergencyNumberLongNumber_BR() { - // Brazilian emergency numbers don't work when additional digits are appended. - assertFalse(shortUtil.connectsToEmergencyNumber("9111", RegionCode.BR)); - assertFalse(shortUtil.connectsToEmergencyNumber("1900", RegionCode.BR)); - assertFalse(shortUtil.connectsToEmergencyNumber("9996", RegionCode.BR)); - } -} diff --git a/java/release_notes.txt b/java/release_notes.txt index 4df4e8d94..5970332a7 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/resources/PhoneNumberMetaData.xml b/resources/PhoneNumberMetaData.xml index 8fcb1be4d..bdbf55cb1 100644 --- a/resources/PhoneNumberMetaData.xml +++ b/resources/PhoneNumberMetaData.xml @@ -669,92 +669,38 @@ + + - - - [13-7] - $1 $2 - - - 9 - $1 $2 $3 - - + - [13-79]\d{6,7} - \d{7,8} + 5\d{6} + \d{7} - - (?: - 318| - 5(?: - 25| - 4\d| - 8[239] - )| - 7(?: - 1[578]| - 50 - )| - 9(?: - [48]\d{2}| - 50\d| - 7(?: - 2[0-2]| - [34]\d| - 6[35-7]| - 77 - ) - ) - )\d{4}| - 416[0239]\d{3} + 5(?: + 4\d| + 8[239] + )\d{4} - 7151234 + 5451234 - (?: - 318| - 5(?: - 1[01]| - 2[0-7]| - 5\d| - 8[016-8] - )| - 7(?: - 0[01]| - [89]\d - )| - 9(?: - 5(?: - [1246]\d| - 3[01] - )| - 6(?: - [1679]\d| - 3[01] - ) - ) - )\d{4}| - 416[15-8]\d{3} + 5(?: + 1[01]| + 2[0-7]| + 5\d| + 8[016-8] + )\d{4} - 3181234 + 5101234 - - - - (?: - 10| - 69 - )\d{5} - - 1011234 - @@ -1770,6 +1716,31 @@ \d{5,12} + + + + [13]00\d{3,7}| + 2(?: + 0(?: + 0\d{3,7}| + 2[023]\d{1,6}| + 9[89]\d{1,6} + ) + )| + 60(?: + [12]\d{5,6}| + 6\d{7} + )| + 7(?: + 1\d{7}| + 3\d{8}| + 5[03-9]\d{2,7} + ) + + \d{5,10} + 100123 + 18[1-8]\d{3,9} \d{6,12} @@ -1795,18 +1766,22 @@ 600123456 + - 10[1-9]\d{3,7}| + [13]0\d{4,8}| 2(?: 0(?: - [16-8]\d{3,7}| - 2[14-9]\d{1,6}| - [3-5]\d{2,7}| - 9[0-7]\d{1,6} + [016-8]\d{3,7}| + [2-59]\d{2,7} )| 9\d{4,8} )| - 30[1-9]\d{3,7}| + 60(?: + [12]\d{5,6}| + 6\d{7} + )| 7(?: 1\d{7}| 3\d{8}| @@ -2451,7 +2426,7 @@ - [2457]\d{7} + [24-7]\d{7} \d{8} @@ -2478,11 +2453,14 @@ since diallable numbers have been found outside the range that the document specifies. Including 716 as well since many numbers seem to have this prefix. --> - 7(?: - [02-68]\d| - 1[0-4689]| - 7[0-6]| - 9[0-689] + (?: + 60[0-3]| + 7(?: + [02-68]\d| + 1[0-4689]| + 7[0-6]| + 9[0-689] + ) )\d{5} 70123456 @@ -2808,13 +2786,18 @@ \d{4} 7312 - 857[58]\d{4} \d{8} 85751234 + + + 81\d{6} + \d{8} + 81123456 + 11[78] \d{3} @@ -3031,6 +3014,51 @@ + + + + + + [347]\d{6} + \d{7} + + + + (?: + 318[023]| + 416[0239]| + 7(?: + 1[578]| + 50 + )\d + )\d{3} + + 7151234 + + + + (?: + 318[1456]| + 416[15-8]| + 7(?: + 0[01]| + [89]\d + )\d + )\d{3}| + + 3181234 + + + + + 112| + 911 + + \d{3} + 112 + + + [34]00 - - 400| - 3003 - $1-$2 \d{8,10} - - (?: - 400\d| - 3003 - )\d{4} - + [34]00\d{5} \d{8} 40041234 @@ -3124,12 +3143,7 @@ 300123456 - - (?: - 400\d| - 3003 - )\d{4} - + [34]00\d{5} \d{8} 40041234 @@ -3148,7 +3162,7 @@ - + @@ -3196,7 +3210,7 @@ 81 )| 5(?: - 2[34]| + 2[45]| 3[35]| 44| 5[1-9]| @@ -4911,6 +4925,81 @@ + + + + + + + [13-7] + $1 $2 + + + 9 + $1 $2 $3 + + + + [169]\d{6,7} + \d{7,8} + + + + 9(?: + [48]\d{2}| + 50\d| + 7(?: + 2[0-2]| + [34]\d| + 6[35-7]| + 77 + ) + )\d{4} + + 94151234 + + + + 9(?: + 5(?: + [1246]\d| + 3[01] + )| + 6(?: + [1679]\d| + 3[01] + ) + )\d{4} + + 95181234 + + + 955\d{5} + 95581234 + + + + + (?: + 10| + 69 + )\d{5} + + \d{7} + 1011234 + + + + + 112| + 911 + + \d{3} + 112 + + + @@ -5404,30 +5493,36 @@ - $1 $2 $3 + + $1 $2 $3 $4 + + - [1-8]\d{5} - \d{6} + [1-8]\d{5,7} + \d{6,8} - + (?: 1[05]| [2-5]\d - )\d{4} + )\d{4}| + 2(?: + 1[2-5]| + 7[45]| + )\d{5} - 251234 + 21360003 - [6-8]\d{5} - 601234 + (?:77)?[6-8]\d{5} + 77831001 1[78] @@ -6389,6 +6484,15 @@ + + + (?: + [1-3]00| + [6-8]0 + ) + + $1 $2 + 2[09]| @@ -6405,10 +6509,6 @@ $1 $2 - - [6-8]0 - $1 $2 - @@ -6417,6 +6517,31 @@ \d{5,12} + + + + [13]00\d{3,7}| + 2(?: + 0(?: + 0\d{3,7}| + 2[023]\d{1,6}| + 9[89]\d{1,6} + ) + )| + 60(?: + [12]\d{5,6}| + 6\d{7} + )| + 7(?: + 1\d{7}| + 3\d{8}| + 5[03-9]\d{2,7} + ) + + \d{5,10} + 100123 + @@ -6453,18 +6578,22 @@ 600123456 + - 10[1-9]\d{3,7}| + [13]0\d{4,8}| 2(?: 0(?: - [16-8]\d{3,7}| - 2[14-9]\d{1,6}| - [3-5]\d{2,7}| - 9[0-7]\d{1,6} + [016-8]\d{3,7}| + [2-59]\d{2,7} )| 9\d{4,8} )| - 30[1-9]\d{3,7}| + 60(?: + [12]\d{5,6}| + 6\d{7} + )| 7(?: 1\d{7}| 3\d{8}| @@ -9798,9 +9927,9 @@ 2[236-9]| 3[0479]| 4[0-68]| - 5[0-579] + 5[0-57-9] 6[05789] - 7[12569]| + 7[123569]| 8[0124-9]| 9[02-9] )| @@ -9822,7 +9951,7 @@ 9[689] )| 4(?: - 0[245789]| + 0[1245789]| 1[15-9]| [29][89]| 39| @@ -9877,7 +10006,7 @@ 8[567] )| 3(?: - 0[235-8]| + 0[0235-8]| 4[14789]| 74| 90 @@ -9894,6 +10023,7 @@ 30| 4[47]| 53| + 85| 7[45]| 9[015] )| @@ -9903,6 +10033,7 @@ )| 7(?: 1[24]| + 33| [2569] )| 8(?: @@ -9910,13 +10041,13 @@ 17| 2[024-8]| 44| - 5[389]| + 5[3589]| 6[0167] )| 9(?: [057-9]| 2[35-9]| - 3[09]| + 3[019]| 4[03678]| 6[0-46-9] ) @@ -10295,14 +10426,13 @@ + overlap. Extra prefixes added: 7277, 730[124-689], 735[0-2478], 738[1-57], 740[479], + 750[79], 7796, 787[34], 7896, 810[1-358], 811[56], 812[02569], 814[01], 8171, 8179, + 822[1-689], 823[0-24-9], 826[0-35-7], 827[0137], 829[0-2479], the ranges in 83X + excepting 830[037], 834[14] and 8390, 840[02-57-9], 842[24-689], 843[0-59], 844[0-489], + 845[0-24-689], 846[09], 847[0135], 848[0-8], 850[79], 852[02-589], 8530, 854[47], 8595, + 860[1259], 865[03-79], 867[09], 868[349], 869[0-46], 872[013-9], 875[1-35-9], + 876[0-47], 879[4589], 8824, 8859, 892[67], 894[6-8], 896[346]. --> (?: 7(?: @@ -10320,7 +10450,7 @@ 9[689] )| 4(?: - 0[245789]| + 0[1245789]| 1[15-9]| [29][89]| 39| @@ -10378,7 +10508,7 @@ 8[567] )| 3(?: - 0[235-8]| + 0[0235-8]| 4[14789]| 74| 90 @@ -10396,6 +10526,7 @@ 4[47]| 53| 7[45]| + 85| 9[015] )| 6(?: @@ -10404,6 +10535,7 @@ )| 7(?: 1[24]| + 33| [2569]\d )| 8(?: @@ -10411,13 +10543,13 @@ 17| 2[024-8]| 44| - 5[389]| + 5[3589]| 6[0167] )| 9(?: [057-9]\d| 2[35-9]| - 3[09]| + 3[019]| 4[036-8]| 6[0-46-9] ) @@ -14303,6 +14435,7 @@ + @@ -14314,8 +14447,8 @@ \d{8} - 6\d{7} - 61234567 + 6[3-8]\d{6} + 63123456 2\d{7} @@ -14329,6 +14462,10 @@ 90\d{6} 90123456 + + 81\d{6} + 81123456 + 0[123]| @@ -15917,7 +16054,7 @@ nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG"> - [13-5] + 1 $1 $2 $3 @@ -15928,25 +16065,14 @@ [1789] $1 $2 $3 $4 - - - [89] - $1 $2 $3 - - (?: - [3-5]| - [27]\d{2}| - [189](?: + 1(?: \d{2} - )? + )?| + [2789]\d{2} )\d{6} \d{7,9} @@ -15967,16 +16093,12 @@ (?: 111| - [3-5]| 77\d| - 8(?: - 8\d - )?| - 9(?: - 9\d - )? + 88\d| + 99\d )\d{6} + \d{9} 991234567 @@ -19092,13 +19214,44 @@ - [1-36-9]\d{4,11} + + + [126-9]\d{4,11}| + 3(?: + [0-79]\d{3,10}| + 8[2-9]\d{2,9} + ) + \d{5,12} - [1-3]\d{6,11} + + + (?: + 1(?: + [02-9][2-9]| + 1[1-9] + )\d| + 2(?: + [0-24-7][2-9]\d| + [389](?: + 0[2-9]| + [2-9]\d + ) + )| + 3(?: + [0-8][2-9]\d| + 9(?: + [2-9]\d| + 0[2-9] + ) + ) + )\d{3,8} + \d{5,12} - 101234567 + 10234567 @@ -19517,6 +19670,8 @@ 4217123 + 2(?: 5(?: @@ -19526,7 +19681,8 @@ 7(?: [0-79]\d| 8[24-9] - ) + )| + 8\d{2} )\d{3} \d{7} @@ -20544,6 +20700,59 @@ + + + + + + + + $1 $2 $3 + + + + [1489]\d{8} + \d{9} + + + + + (?: + 1[67]\d| + 811 + )\d{6} + + 811123456 + + + + + (?: + 1(?: + 02| + 2[1269] + )| + 477| + 9(?: + 0[03689]| + 1\d| + 2[024-9]| + 5[5-79]| + 77| + 98 + ) + )\d{6} + + 977123456 + + + @@ -20636,6 +20845,7 @@ + @@ -23205,6 +23415,10 @@ nationalPrefix="0" nationalPrefixFormattingRule="$NP$FG" nationalPrefixOptionalWhenFormatting="true"> + + [17]99 + $1 $2 + [48] $1 $2 $3 @@ -23214,7 +23428,7 @@ 2[025-79]| 3[0136-9]| 5[2-9]| - 6[0-46-9]| + 6[0-46-8]| 7[02-79] $1 $2 $3 @@ -23245,7 +23459,7 @@ 1(?: [26]| - 88| + 8[68]| 99 ) @@ -23259,11 +23473,20 @@ - 8\d{5,8}| - [1-79]\d{7,9} + [17]\d{6,9}| + [2-69]\d{7,9}| + 8\d{6,8} \d{7,10} + + + [17]99\d{4}| + 69\d{5,6} + + \d{7,8} + 1992000 + (?: @@ -23276,7 +23499,7 @@ [0136-9]| [25][01] )| - [48]\d| + 4\d| 5(?: [01][01]| [2-9] @@ -23288,12 +23511,11 @@ 7(?: [02-79]| [18][01] - ) - )\d{7}| - 69\d{5,6}| - 80\d{5} + )| + 8[1-9] + )\d{7} - \d{7,10} + \d{9,10} 2101234567 @@ -23303,7 +23525,7 @@ 1(?: 2\d| 6[3-9]| - 88| + 8[68]| 99 ) )\d{7} @@ -23321,6 +23543,18 @@ \d{8,10} 1900123456 + + + + [17]99\d{4}| + 69\d{5,6}| + 80\d{5} + + \d{7,8} + 1992000 + 11[345] \d{3}