Browse Source

JAVA/JS/CPP: Libphonenumber v5.9, code & metadata changes.

pull/567/head
Lara Scheidegger 12 years ago
committed by Mihaela Rosca
parent
commit
7123e3365f
173 changed files with 25109 additions and 21134 deletions
  1. +590
    -595
      cpp/src/phonenumbers/alternate_format.cc
  2. +8077
    -8031
      cpp/src/phonenumbers/lite_metadata.cc
  3. +8761
    -8714
      cpp/src/phonenumbers/metadata.cc
  4. +39
    -5
      cpp/src/phonenumbers/phonenumbermatcher.cc
  5. +173
    -115
      cpp/src/phonenumbers/phonenumberutil.cc
  6. +35
    -14
      cpp/src/phonenumbers/phonenumberutil.h
  7. +2472
    -2383
      cpp/src/phonenumbers/short_metadata.cc
  8. +221
    -5
      cpp/src/phonenumbers/shortnumberinfo.cc
  9. +106
    -3
      cpp/src/phonenumbers/shortnumberinfo.h
  10. +604
    -564
      cpp/src/phonenumbers/test_metadata.cc
  11. +4
    -0
      cpp/test/phonenumbers/phonenumbermatcher_test.cc
  12. +152
    -3
      cpp/test/phonenumbers/phonenumberutil_test.cc
  13. +240
    -1
      cpp/test/phonenumbers/shortnumberinfo_test.cc
  14. +12
    -0
      cpp/test/phonenumbers/test_util.h
  15. +40
    -0
      debian/changelog
  16. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/225_en
  17. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/256_en
  18. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/257_en
  19. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/261_en
  20. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/375_be
  21. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/375_en
  22. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/375_ru
  23. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/389_en
  24. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/57_en
  25. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/599_en
  26. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/61_en
  27. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/686_en
  28. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/92_en
  29. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/962_en
  30. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/965_ar
  31. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/965_en
  32. BIN
      java/carrier/src/com/google/i18n/phonenumbers/carrier/data/config
  33. +9
    -6
      java/geocoder/src/com/google/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder.java
  34. BIN
      java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/257_en
  35. BIN
      java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/264_en
  36. BIN
      java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/375_be
  37. BIN
      java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/375_en
  38. BIN
      java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/375_ru
  39. BIN
      java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/389_en
  40. BIN
      java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/599_en
  41. BIN
      java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/686_en
  42. BIN
      java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/962_en
  43. BIN
      java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/config
  44. +2
    -3
      java/libphonenumber/src/com/google/i18n/phonenumbers/AlternateFormatsCountryCodeSet.java
  45. +25
    -1
      java/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberMatcher.java
  46. +93
    -26
      java/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java
  47. +2
    -2
      java/libphonenumber/src/com/google/i18n/phonenumbers/Phonemetadata.java
  48. +26
    -1
      java/libphonenumber/src/com/google/i18n/phonenumbers/Phonenumber.java
  49. +114
    -45
      java/libphonenumber/src/com/google/i18n/phonenumbers/ShortNumberInfo.java
  50. +4
    -2
      java/libphonenumber/src/com/google/i18n/phonenumbers/ShortNumbersRegionCodeSet.java
  51. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_AU
  52. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BQ
  53. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BY
  54. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CI
  55. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CO
  56. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CW
  57. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_GN
  58. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_HN
  59. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_IN
  60. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_JO
  61. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_KI
  62. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_KW
  63. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_MG
  64. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_MK
  65. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_MM
  66. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_NA
  67. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_PK
  68. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_TC
  69. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_TM
  70. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_UG
  71. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_AF
  72. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_AG
  73. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_AI
  74. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_AM
  75. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_AR
  76. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_AS
  77. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_AW
  78. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_AZ
  79. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BD
  80. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BH
  81. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BI
  82. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BM
  83. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BO
  84. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BQ
  85. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BT
  86. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BW
  87. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BY
  88. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_CA
  89. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_CD
  90. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_CH
  91. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_CI
  92. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_CM
  93. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_CO
  94. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_CW
  95. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_DZ
  96. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_FJ
  97. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_GD
  98. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_GE
  99. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_GH
  100. BIN
      java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_GN

+ 590
- 595
cpp/src/phonenumbers/alternate_format.cc
File diff suppressed because it is too large
View File


+ 8077
- 8031
cpp/src/phonenumbers/lite_metadata.cc
File diff suppressed because it is too large
View File


+ 8761
- 8714
cpp/src/phonenumbers/metadata.cc
File diff suppressed because it is too large
View File


+ 39
- 5
cpp/src/phonenumbers/phonenumbermatcher.cc View File

@ -116,10 +116,15 @@ bool ContainsOnlyValidXChars(const PhoneNumber& number, const string& candidate,
bool AllNumberGroupsRemainGrouped(
const PhoneNumberUtil& util,
const PhoneNumber& phone_number,
const PhoneNumber& number,
const string& normalized_candidate,
const vector<string>& formatted_number_groups) {
size_t from_index = 0;
if (number.country_code_source() != PhoneNumber::FROM_DEFAULT_COUNTRY) {
// First skip the country code if the normalized candidate contained it.
string country_code = SimpleItoa(number.country_code());
from_index = normalized_candidate.find(country_code) + country_code.size();
}
// Check each group of consecutive digits are not broken into separate
// groupings in the normalized_candidate string.
for (size_t i = 0; i < formatted_number_groups.size(); ++i) {
@ -139,7 +144,7 @@ bool AllNumberGroupsRemainGrouped(
// different countries with the same country calling code and this is
// faster.
string region;
util.GetRegionCodeForCountryCode(phone_number.country_code(), &region);
util.GetRegionCodeForCountryCode(number.country_code(), &region);
string ndd_prefix;
util.GetNddPrefixForRegion(region, true, &ndd_prefix);
// Note although normalized_candidate might contain non-ASCII formatting
@ -151,8 +156,7 @@ bool AllNumberGroupsRemainGrouped(
// the number, except for extensions. This is only important for
// countries with national prefixes.
string national_significant_number;
util.GetNationalSignificantNumber(
phone_number, &national_significant_number);
util.GetNationalSignificantNumber(number, &national_significant_number);
return HasPrefixString(normalized_candidate.substr(
from_index - formatted_number_groups.at(i).length()),
national_significant_number);
@ -163,7 +167,7 @@ bool AllNumberGroupsRemainGrouped(
// extension to match the last group of the subscriber number. Note the
// extension cannot have formatting in-between digits.
return normalized_candidate.substr(from_index)
.find(phone_number.extension()) != string::npos;
.find(number.extension()) != string::npos;
}
bool LoadAlternateFormats(PhoneMetadataCollection* alternate_formats) {
@ -452,6 +456,36 @@ bool PhoneNumberMatcher::ParseAndVerify(const string& candidate, int offset,
PhoneNumberUtil::NO_PARSING_ERROR) {
return false;
}
// Check Israel * numbers: these are a special case in that they are
// four-digit numbers that our library supports, but they can only be dialled
// with a leading *. Since we don't actually store or detect the * in our
// phone number library, this means in practice we detect most four digit
// numbers as being valid for Israel. We are considering moving these numbers
// to ShortNumberInfo instead, in which case this problem would go away, but
// in the meantime we want to restrict the false matches so we only allow
// these numbers if they are preceded by a star. We enforce this for all
// leniency levels even though these numbers are technically accepted by
// isPossibleNumber and isValidNumber since we consider it to be a deficiency
// in those methods that they accept these numbers without the *.
// TODO: Remove this or make it significantly less hacky once
// we've decided how to handle these short codes going forward in
// ShortNumberInfo. We could use the formatting rules for instance, but that
// would be slower.
string region_code;
phone_util_.GetRegionCodeForCountryCode(number.country_code(), &region_code);
if (region_code == "IL") {
string national_number;
phone_util_.GetNationalSignificantNumber(number, &national_number);
if (national_number.length() == 4 &&
// Just check the previous char, since * is an ASCII character.
(offset == 0 || (offset > 0 && text_[offset - 1] != '*'))) {
// No match.
return false;
}
}
if (VerifyAccordingToLeniency(leniency_, number, candidate)) {
match->set_start(offset);
match->set_raw_string(candidate);


+ 173
- 115
cpp/src/phonenumbers/phonenumberutil.cc View File

@ -17,9 +17,9 @@
#include "phonenumbers/phonenumberutil.h"
#include <string.h>
#include <algorithm>
#include <cctype>
#include <cstring>
#include <iterator>
#include <map>
#include <utility>
@ -57,6 +57,12 @@ using std::sort;
using google::protobuf::RepeatedPtrField;
// static constants
const size_t PhoneNumberUtil::kMinLengthForNsn;
const size_t PhoneNumberUtil::kMaxLengthForNsn;
const size_t PhoneNumberUtil::kMaxLengthCountryCode;
const int PhoneNumberUtil::kNanpaCountryCode;
// static
const char PhoneNumberUtil::kPlusChars[] = "+\xEF\xBC\x8B"; /* "++" */
// To find out the unicode code-point of the characters below in vim, highlight
@ -187,92 +193,6 @@ bool IsNationalNumberSuffixOfTheOther(const PhoneNumber& first_number,
first_number_national_number);
}
bool IsNumberMatchingDesc(const string& national_number,
const PhoneNumberDesc& number_desc,
RegExpCache* regexp_cache) {
return regexp_cache->GetRegExp(number_desc.possible_number_pattern())
.FullMatch(national_number) &&
regexp_cache->GetRegExp(number_desc.national_number_pattern())
.FullMatch(national_number);
}
PhoneNumberUtil::PhoneNumberType GetNumberTypeHelper(
const string& national_number, const PhoneMetadata& metadata,
RegExpCache* regexp_cache) {
const PhoneNumberDesc& general_desc = metadata.general_desc();
if (!general_desc.has_national_number_pattern() ||
!IsNumberMatchingDesc(national_number, general_desc, regexp_cache)) {
VLOG(4) << "Number type unknown - doesn't match general national number"
<< " pattern.";
return PhoneNumberUtil::UNKNOWN;
}
if (IsNumberMatchingDesc(national_number, metadata.premium_rate(),
regexp_cache)) {
VLOG(4) << "Number is a premium number.";
return PhoneNumberUtil::PREMIUM_RATE;
}
if (IsNumberMatchingDesc(national_number, metadata.toll_free(),
regexp_cache)) {
VLOG(4) << "Number is a toll-free number.";
return PhoneNumberUtil::TOLL_FREE;
}
if (IsNumberMatchingDesc(national_number, metadata.shared_cost(),
regexp_cache)) {
VLOG(4) << "Number is a shared cost number.";
return PhoneNumberUtil::SHARED_COST;
}
if (IsNumberMatchingDesc(national_number, metadata.voip(), regexp_cache)) {
VLOG(4) << "Number is a VOIP (Voice over IP) number.";
return PhoneNumberUtil::VOIP;
}
if (IsNumberMatchingDesc(national_number, metadata.personal_number(),
regexp_cache)) {
VLOG(4) << "Number is a personal number.";
return PhoneNumberUtil::PERSONAL_NUMBER;
}
if (IsNumberMatchingDesc(national_number, metadata.pager(), regexp_cache)) {
VLOG(4) << "Number is a pager number.";
return PhoneNumberUtil::PAGER;
}
if (IsNumberMatchingDesc(national_number, metadata.uan(), regexp_cache)) {
VLOG(4) << "Number is a UAN.";
return PhoneNumberUtil::UAN;
}
if (IsNumberMatchingDesc(national_number, metadata.voicemail(),
regexp_cache)) {
VLOG(4) << "Number is a voicemail number.";
return PhoneNumberUtil::VOICEMAIL;
}
bool is_fixed_line =
IsNumberMatchingDesc(national_number, metadata.fixed_line(),
regexp_cache);
if (is_fixed_line) {
if (metadata.same_mobile_and_fixed_line_pattern()) {
VLOG(4) << "Fixed-line and mobile patterns equal, number is fixed-line"
<< " or mobile";
return PhoneNumberUtil::FIXED_LINE_OR_MOBILE;
} else if (IsNumberMatchingDesc(national_number, metadata.mobile(),
regexp_cache)) {
VLOG(4) << "Fixed-line and mobile patterns differ, but number is "
<< "still fixed-line or mobile";
return PhoneNumberUtil::FIXED_LINE_OR_MOBILE;
}
VLOG(4) << "Number is a fixed line number.";
return PhoneNumberUtil::FIXED_LINE;
}
// Otherwise, test to see if the number is mobile. Only do this if certain
// that the patterns for mobile and fixed line aren't the same.
if (!metadata.same_mobile_and_fixed_line_pattern() &&
IsNumberMatchingDesc(national_number, metadata.mobile(), regexp_cache)) {
VLOG(4) << "Number is a mobile number.";
return PhoneNumberUtil::MOBILE;
}
VLOG(4) << "Number type unknown - doesn\'t match any specific number type"
<< " pattern.";
return PhoneNumberUtil::UNKNOWN;
}
char32 ToUnicodeCodepoint(const char* unicode_char) {
char32 codepoint;
EncodingUtils::DecodeUTF8Char(unicode_char, &codepoint);
@ -1040,8 +960,9 @@ void PhoneNumberUtil::FormatNumberForMobileDialing(
number_no_extension.clear_extension();
string region_code;
GetRegionCodeForCountryCode(country_calling_code, &region_code);
PhoneNumberType number_type = GetNumberType(number_no_extension);
bool is_valid_number = (number_type != UNKNOWN);
if (calling_from == region_code) {
PhoneNumberType number_type = GetNumberType(number_no_extension);
bool is_fixed_line_or_mobile =
(number_type == FIXED_LINE) || (number_type == MOBILE) ||
(number_type == FIXED_LINE_OR_MOBILE);
@ -1061,23 +982,38 @@ void PhoneNumberUtil::FormatNumberForMobileDialing(
// string here.
formatted_number->assign("");
}
} else if (region_code == "HU") {
} else if (is_valid_number && region_code == "HU") {
// The national format for HU numbers doesn't contain the national prefix,
// because that is how numbers are normally written down. However, the
// national prefix is obligatory when dialing from a mobile phone. As a
// result, we add it back here.
// national prefix is obligatory when dialing from a mobile phone, except
// for short numbers. As a result, we add it back here if it is a valid
// regular length phone number.
Format(number_no_extension, NATIONAL, formatted_number);
string hu_national_prefix;
GetNddPrefixForRegion(region_code, true /* strip non-digits */,
&hu_national_prefix);
formatted_number->assign(
StrCat(hu_national_prefix, " ", *formatted_number));
} else if (country_calling_code == kNanpaCountryCode) {
// For NANPA countries, we output international format for numbers that
// can be dialed internationally, since that always works, except for
// numbers which might potentially be short numbers, which are always
// dialled in national format.
const PhoneMetadata* region_metadata = GetMetadataForRegion(calling_from);
string national_number;
GetNationalSignificantNumber(number_no_extension, &national_number);
if (CanBeInternationallyDialled(number_no_extension) &&
!IsShorterThanPossibleNormalNumber(region_metadata,
national_number)) {
Format(number_no_extension, INTERNATIONAL, formatted_number);
} else {
Format(number_no_extension, NATIONAL, formatted_number);
}
} else {
// For NANPA countries, non-geographical countries, Mexican and Chilean
// fixed line and mobile numbers, we output international format for
// numbers that can be dialed internationally as that always works.
if ((country_calling_code == kNanpaCountryCode ||
region_code == kRegionCodeForNonGeoEntity ||
// For non-geographical countries, and Mexican and Chilean fixed line and
// mobile numbers, we output international format for numbers that can be
// dialed internationally as that always works.
if ((region_code == kRegionCodeForNonGeoEntity ||
// MX fixed line and mobile numbers should always be formatted in
// international format, even when dialed within MX. For national
// format to work, a carrier code needs to be used, and the correct
@ -1097,7 +1033,11 @@ void PhoneNumberUtil::FormatNumberForMobileDialing(
Format(number_no_extension, NATIONAL, formatted_number);
}
}
} else if (CanBeInternationallyDialled(number_no_extension)) {
} else if (is_valid_number &&
CanBeInternationallyDialled(number_no_extension)) {
// We assume that short numbers are not diallable from outside their
// region, so if a number is not a valid regular length phone number, we
// treat it as if it cannot be internationally dialled.
with_formatting
? Format(number_no_extension, INTERNATIONAL, formatted_number)
: Format(number_no_extension, E164, formatted_number);
@ -1666,8 +1606,7 @@ void PhoneNumberUtil::GetRegionCodeForNumberFromRegionList(
*region_code = *it;
return;
}
} else if (GetNumberTypeHelper(national_number, *metadata,
reg_exps_->regexp_cache_.get()) != UNKNOWN) {
} else if (GetNumberTypeHelper(national_number, *metadata) != UNKNOWN) {
*region_code = *it;
return;
}
@ -1914,11 +1853,22 @@ PhoneNumberUtil::ErrorType PhoneNumberUtil::ParseHelper(
return TOO_SHORT_NSN;
}
if (country_metadata) {
string* carrier_code = keep_raw_input ?
temp_number.mutable_preferred_domestic_carrier_code() : NULL;
string carrier_code;
string potential_national_number(normalized_national_number);
MaybeStripNationalPrefixAndCarrierCode(*country_metadata,
&normalized_national_number,
carrier_code);
&potential_national_number,
&carrier_code);
// We require that the NSN remaining after stripping the national prefix
// and carrier code be of a possible length for the region. Otherwise, we
// don't do the stripping, since the original number could be a valid short
// number.
if (!IsShorterThanPossibleNormalNumber(country_metadata,
potential_national_number)) {
normalized_national_number.assign(potential_national_number);
if (keep_raw_input) {
temp_number.set_preferred_domestic_carrier_code(carrier_code);
}
}
}
size_t normalized_national_number_length =
normalized_national_number.length();
@ -1931,9 +1881,8 @@ PhoneNumberUtil::ErrorType PhoneNumberUtil::ParseHelper(
return TOO_LONG_NSN;
}
temp_number.set_country_code(country_code);
if (normalized_national_number[0] == '0') {
temp_number.set_italian_leading_zero(true);
}
SetItalianLeadingZerosForPhoneNumber(normalized_national_number,
&temp_number);
uint64 number_as_int;
safe_strtou64(normalized_national_number, &number_as_int);
temp_number.set_national_number(number_as_int);
@ -2070,9 +2019,7 @@ PhoneNumberUtil::PhoneNumberType PhoneNumberUtil::GetNumberType(
}
string national_significant_number;
GetNationalSignificantNumber(number, &national_significant_number);
return GetNumberTypeHelper(national_significant_number,
*metadata,
reg_exps_->regexp_cache_.get());
return GetNumberTypeHelper(national_significant_number, *metadata);
}
bool PhoneNumberUtil::IsValidNumber(const PhoneNumber& number) const {
@ -2107,8 +2054,7 @@ bool PhoneNumberUtil::IsValidNumberForRegion(const PhoneNumber& number,
return number_length > kMinLengthForNsn &&
number_length <= kMaxLengthForNsn;
}
return GetNumberTypeHelper(national_number, *metadata,
reg_exps_->regexp_cache_.get()) != UNKNOWN;
return GetNumberTypeHelper(national_number, *metadata) != UNKNOWN;
}
bool PhoneNumberUtil::IsNumberGeographical(
@ -2129,13 +2075,117 @@ bool PhoneNumberUtil::IsLeadingZeroPossible(int country_calling_code) const {
return main_metadata_for_calling_code->leading_zero_possible();
}
// A helper function to set the values related to leading zeros in a
// PhoneNumber.
void PhoneNumberUtil::SetItalianLeadingZerosForPhoneNumber(
const string& national_number, PhoneNumber* phone_number) const {
if (national_number.length() > 1 && national_number[0] == '0') {
phone_number->set_italian_leading_zero(true);
size_t number_of_leading_zeros = 1;
// Note that if the national number is all "0"s, the last "0" is not
// counted as a leading zero.
while (number_of_leading_zeros < national_number.length() - 1 &&
national_number[number_of_leading_zeros] == '0') {
number_of_leading_zeros++;
}
if (number_of_leading_zeros != 1) {
phone_number->set_number_of_leading_zeros(number_of_leading_zeros);
}
}
}
bool PhoneNumberUtil::IsNumberPossibleForDesc(
const string& national_number, const PhoneNumberDesc& number_desc) const {
return reg_exps_->regexp_cache_.get()->
GetRegExp(number_desc.possible_number_pattern())
.FullMatch(national_number);
}
bool PhoneNumberUtil::IsNumberMatchingDesc(
const string& national_number, const PhoneNumberDesc& number_desc) const {
return IsNumberPossibleForDesc(national_number, number_desc) &&
reg_exps_->regexp_cache_.get()->
GetRegExp(number_desc.national_number_pattern())
.FullMatch(national_number);
}
PhoneNumberUtil::PhoneNumberType PhoneNumberUtil::GetNumberTypeHelper(
const string& national_number, const PhoneMetadata& metadata) const {
const PhoneNumberDesc& general_desc = metadata.general_desc();
if (!general_desc.has_national_number_pattern() ||
!IsNumberMatchingDesc(national_number, general_desc)) {
VLOG(4) << "Number type unknown - doesn't match general national number"
<< " pattern.";
return PhoneNumberUtil::UNKNOWN;
}
if (IsNumberMatchingDesc(national_number, metadata.premium_rate())) {
VLOG(4) << "Number is a premium number.";
return PhoneNumberUtil::PREMIUM_RATE;
}
if (IsNumberMatchingDesc(national_number, metadata.toll_free())) {
VLOG(4) << "Number is a toll-free number.";
return PhoneNumberUtil::TOLL_FREE;
}
if (IsNumberMatchingDesc(national_number, metadata.shared_cost())) {
VLOG(4) << "Number is a shared cost number.";
return PhoneNumberUtil::SHARED_COST;
}
if (IsNumberMatchingDesc(national_number, metadata.voip())) {
VLOG(4) << "Number is a VOIP (Voice over IP) number.";
return PhoneNumberUtil::VOIP;
}
if (IsNumberMatchingDesc(national_number, metadata.personal_number())) {
VLOG(4) << "Number is a personal number.";
return PhoneNumberUtil::PERSONAL_NUMBER;
}
if (IsNumberMatchingDesc(national_number, metadata.pager())) {
VLOG(4) << "Number is a pager number.";
return PhoneNumberUtil::PAGER;
}
if (IsNumberMatchingDesc(national_number, metadata.uan())) {
VLOG(4) << "Number is a UAN.";
return PhoneNumberUtil::UAN;
}
if (IsNumberMatchingDesc(national_number, metadata.voicemail())) {
VLOG(4) << "Number is a voicemail number.";
return PhoneNumberUtil::VOICEMAIL;
}
bool is_fixed_line =
IsNumberMatchingDesc(national_number, metadata.fixed_line());
if (is_fixed_line) {
if (metadata.same_mobile_and_fixed_line_pattern()) {
VLOG(4) << "Fixed-line and mobile patterns equal, number is fixed-line"
<< " or mobile";
return PhoneNumberUtil::FIXED_LINE_OR_MOBILE;
} else if (IsNumberMatchingDesc(national_number, metadata.mobile())) {
VLOG(4) << "Fixed-line and mobile patterns differ, but number is "
<< "still fixed-line or mobile";
return PhoneNumberUtil::FIXED_LINE_OR_MOBILE;
}
VLOG(4) << "Number is a fixed line number.";
return PhoneNumberUtil::FIXED_LINE;
}
// Otherwise, test to see if the number is mobile. Only do this if certain
// that the patterns for mobile and fixed line aren't the same.
if (!metadata.same_mobile_and_fixed_line_pattern() &&
IsNumberMatchingDesc(national_number, metadata.mobile())) {
VLOG(4) << "Number is a mobile number.";
return PhoneNumberUtil::MOBILE;
}
VLOG(4) << "Number type unknown - doesn\'t match any specific number type"
<< " pattern.";
return PhoneNumberUtil::UNKNOWN;
}
void PhoneNumberUtil::GetNationalSignificantNumber(
const PhoneNumber& number,
string* national_number) const {
DCHECK(national_number);
// If a leading zero has been set, we prefix this now. Note this is not a
// If leading zero(s) have been set, we prefix this now. Note this is not a
// national prefix.
StrAppend(national_number, number.italian_leading_zero() ? "0" : "");
StrAppend(national_number, number.italian_leading_zero() ?
string(number.number_of_leading_zeros(), '0') : "");
StrAppend(national_number, number.national_number());
}
@ -2731,6 +2781,15 @@ AsYouTypeFormatter* PhoneNumberUtil::GetAsYouTypeFormatter(
return new AsYouTypeFormatter(region_code);
}
bool PhoneNumberUtil::IsShorterThanPossibleNormalNumber(
const PhoneMetadata* country_metadata, const string& number) const {
const RegExp& possible_number_pattern =
reg_exps_->regexp_cache_->GetRegExp(StrCat("(",
country_metadata->general_desc().possible_number_pattern(), ")"));
return TestNumberLengthAgainstPattern(possible_number_pattern, number) ==
PhoneNumberUtil::TOO_SHORT;
}
bool PhoneNumberUtil::CanBeInternationallyDialled(
const PhoneNumber& number) const {
string region_code;
@ -2744,8 +2803,7 @@ bool PhoneNumberUtil::CanBeInternationallyDialled(
string national_significant_number;
GetNationalSignificantNumber(number, &national_significant_number);
return !IsNumberMatchingDesc(
national_significant_number, metadata->no_international_dialling(),
reg_exps_->regexp_cache_.get());
national_significant_number, metadata->no_international_dialling());
}
} // namespace phonenumbers


+ 35
- 14
cpp/src/phonenumbers/phonenumberutil.h View File

@ -51,6 +51,7 @@ class AsYouTypeFormatter;
class Logger;
class NumberFormat;
class PhoneMetadata;
class PhoneNumberDesc;
class PhoneNumberRegExpsAndMappings;
class RegExp;
@ -154,14 +155,6 @@ class PhoneNumberUtil : public Singleton<PhoneNumberUtil> {
// for.
void GetSupportedRegions(set<string>* regions) const;
// Populates a list with the region codes that match the specific country
// calling code. For non-geographical country calling codes, the region code
// 001 is returned. Also, in the case of no region code being found, the list
// is left unchanged.
void GetRegionCodesForCountryCallingCode(
int country_calling_code,
list<string>* region_codes) const;
// Gets a 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, as specified by
@ -218,7 +211,7 @@ class PhoneNumberUtil : public Singleton<PhoneNumberUtil> {
// area_code = national_significant_number.substr(0, area_code_length);
// subscriber_number = national_significant_number.substr(
// area_code_length, string::npos);
// else {
// } else {
// area_code = "";
// subscriber_number = national_significant_number;
// }
@ -257,13 +250,13 @@ class PhoneNumberUtil : public Singleton<PhoneNumberUtil> {
// string subscriber_number;
//
// int national_destination_code_length =
// phone_util.GetLengthOfGeographicalAreaCode(number);
// phone_util.GetLengthOfNationalDestinationCode(number);
// if (national_destination_code_length > 0) {
// national_destination_code = national_significant_number.substr(
// 0, national_destination_code_length);
// subscriber_number = national_significant_number.substr(
// national_destination_code_length, string::npos);
// else {
// } else {
// national_destination_code = "";
// subscriber_number = national_significant_number;
// }
@ -394,7 +387,7 @@ class PhoneNumberUtil : public Singleton<PhoneNumberUtil> {
// doesn't verify the number is actually in use, which is impossible to tell
// by just looking at a number itself. If the country calling code is not the
// same as the country calling code for the region, this immediately exits
// with false. After this, the specific number pattern rules for the region
// with false. After this, the specific number pattern rules for the region
// are examined.
// This is useful for determining for example whether a particular number is
// valid for Canada, rather than just a valid NANPA number.
@ -418,9 +411,21 @@ class PhoneNumberUtil : public Singleton<PhoneNumberUtil> {
// Returns the region code that matches the specific country code. Note that
// it is possible that several regions share the same country calling code
// (e.g. US and Canada), and in that case, only one of the regions (normally
// the one with the largest population) is returned.
// the one with the largest population) is returned. If the
// countryCallingCode entered is valid but doesn't match a specific region
// (such as in the case of non-geographical calling codes like 800) the
// RegionCode 001 will be returned (corresponding to the value for World in
// the UN M.49 schema).
void GetRegionCodeForCountryCode(int country_code, string* region_code) const;
// Populates a list with the region codes that match the specific country
// calling code. For non-geographical country calling codes, the region code
// 001 is returned. Also, in the case of no region code being found, the list
// is left unchanged.
void GetRegionCodesForCountryCallingCode(
int country_calling_code,
list<string>* region_codes) const;
// Checks if this is a region under the North American Numbering Plan
// Administration (NANPA).
bool IsNANPACountry(const string& region_code) const;
@ -582,6 +587,15 @@ class PhoneNumberUtil : public Singleton<PhoneNumberUtil> {
// country is Italy.
bool IsLeadingZeroPossible(int country_calling_code) const;
bool IsNumberPossibleForDesc(const string& national_number,
const PhoneNumberDesc& number_desc) const;
bool IsNumberMatchingDesc(const string& national_number,
const PhoneNumberDesc& number_desc) const;
PhoneNumberUtil::PhoneNumberType GetNumberTypeHelper(
const string& national_number, const PhoneMetadata& metadata) const;
private:
scoped_ptr<Logger> logger_;
@ -648,6 +662,9 @@ class PhoneNumberUtil : public Singleton<PhoneNumberUtil> {
// Checks if a number matches the plus chars pattern.
bool StartsWithPlusCharsPattern(const string& number) const;
void SetItalianLeadingZerosForPhoneNumber(
const string& national_number, PhoneNumber* phone_number) const;
// Checks whether a string contains only valid digits.
bool ContainsOnlyValidDigits(const string& s) const;
@ -786,9 +803,13 @@ class PhoneNumberUtil : public Singleton<PhoneNumberUtil> {
void BuildNationalNumberForParsing(const string& number_to_parse,
string* national_number) const;
bool IsShorterThanPossibleNormalNumber(const PhoneMetadata* country_metadata,
const string& number) const;
// Returns true if the number can be dialled from outside the region, or
// unknown. If the number can only be dialled from within the region, returns
// false. Does not check the number is a valid number.
// false. Does not check the number is a valid number. Note that, at the
// moment, this method does not handle short numbers.
bool CanBeInternationallyDialled(const PhoneNumber& number) const;
DISALLOW_COPY_AND_ASSIGN(PhoneNumberUtil);


+ 2472
- 2383
cpp/src/phonenumbers/short_metadata.cc
File diff suppressed because it is too large
View File


+ 221
- 5
cpp/src/phonenumbers/shortnumberinfo.cc View File

@ -26,6 +26,7 @@
#include "phonenumbers/phonenumberutil.h"
#include "phonenumbers/regexp_adapter.h"
#include "phonenumbers/regexp_factory.h"
#include "phonenumbers/region_code.h"
#include "phonenumbers/short_metadata.h"
namespace i18n {
@ -43,10 +44,10 @@ bool LoadCompiledInMetadata(PhoneMetadataCollection* metadata) {
return true;
}
ShortNumberInfo::ShortNumberInfo()
: phone_util_(*PhoneNumberUtil::GetInstance()),
region_to_short_metadata_map_(new map<string, PhoneMetadata>()) {
region_to_short_metadata_map_(new map<string, PhoneMetadata>()),
regions_where_emergency_numbers_must_be_exact_(new set<string>()) {
PhoneMetadataCollection metadata_collection;
if (!LoadCompiledInMetadata(&metadata_collection)) {
LOG(DFATAL) << "Could not parse compiled-in metadata.";
@ -59,6 +60,9 @@ ShortNumberInfo::ShortNumberInfo()
const string& region_code = it->id();
region_to_short_metadata_map_->insert(make_pair(region_code, *it));
}
regions_where_emergency_numbers_must_be_exact_->insert("BR");
regions_where_emergency_numbers_must_be_exact_->insert("CL");
regions_where_emergency_numbers_must_be_exact_->insert("NI");
}
// Returns a pointer to the phone metadata for the appropriate region or NULL
@ -73,6 +77,204 @@ const PhoneMetadata* ShortNumberInfo::GetMetadataForRegion(
return NULL;
}
bool ShortNumberInfo::IsPossibleShortNumberForRegion(const string& short_number,
const string& region_dialing_from) const {
const PhoneMetadata* phone_metadata =
GetMetadataForRegion(region_dialing_from);
if (!phone_metadata) {
return false;
}
const PhoneNumberDesc& general_desc = phone_metadata->general_desc();
return phone_util_.IsNumberPossibleForDesc(short_number, general_desc);
}
bool ShortNumberInfo::IsPossibleShortNumber(const PhoneNumber& number) const {
list<string> region_codes;
phone_util_.GetRegionCodesForCountryCallingCode(number.country_code(),
&region_codes);
string short_number;
phone_util_.GetNationalSignificantNumber(number, &short_number);
for (list<string>::const_iterator it = region_codes.begin();
it != region_codes.end(); ++it) {
const PhoneMetadata* phone_metadata = GetMetadataForRegion(*it);
if (phone_util_.IsNumberPossibleForDesc(short_number,
phone_metadata->general_desc())) {
return true;
}
}
return false;
}
bool ShortNumberInfo::IsValidShortNumberForRegion(const string& short_number,
const string& region_dialing_from) const {
const PhoneMetadata* phone_metadata =
GetMetadataForRegion(region_dialing_from);
if (!phone_metadata) {
return false;
}
const PhoneNumberDesc& general_desc = phone_metadata->general_desc();
if (!general_desc.has_national_number_pattern() ||
!phone_util_.IsNumberMatchingDesc(short_number, general_desc)) {
return false;
}
const PhoneNumberDesc& short_number_desc = phone_metadata->short_code();
if (!short_number_desc.has_national_number_pattern()) {
LOG(WARNING) << "No short code national number pattern found for region: "
<< region_dialing_from;
return false;
}
return phone_util_.IsNumberMatchingDesc(short_number, short_number_desc);
}
bool ShortNumberInfo::IsValidShortNumber(const PhoneNumber& number) const {
list<string> region_codes;
phone_util_.GetRegionCodesForCountryCallingCode(number.country_code(),
&region_codes);
string short_number;
phone_util_.GetNationalSignificantNumber(number, &short_number);
string region_code;
GetRegionCodeForShortNumberFromRegionList(number,
region_codes, &region_code);
if (region_codes.size() > 1 && region_code != RegionCode::GetUnknown()) {
return true;
}
return IsValidShortNumberForRegion(short_number, region_code);
}
ShortNumberInfo::ShortNumberCost ShortNumberInfo::GetExpectedCostForRegion(
const string& short_number, const string& region_dialing_from) const {
const PhoneMetadata* phone_metadata = GetMetadataForRegion(
region_dialing_from);
if (!phone_metadata) {
return ShortNumberInfo::UNKNOWN_COST;
}
// The cost categories are tested in order of decreasing expense, since if
// for some reason the patterns overlap the most expensive matching cost
// category should be returned.
if (phone_util_.IsNumberMatchingDesc(short_number,
phone_metadata->premium_rate())) {
return ShortNumberInfo::PREMIUM_RATE;
}
if (phone_util_.IsNumberMatchingDesc(short_number,
phone_metadata->standard_rate())) {
return ShortNumberInfo::STANDARD_RATE;
}
if (phone_util_.IsNumberMatchingDesc(short_number,
phone_metadata->toll_free())) {
return ShortNumberInfo::TOLL_FREE;
}
if (IsEmergencyNumber(short_number, region_dialing_from)) {
// Emergency numbers are implicitly toll-free.
return ShortNumberInfo::TOLL_FREE;
}
return ShortNumberInfo::UNKNOWN_COST;
}
ShortNumberInfo::ShortNumberCost ShortNumberInfo::GetExpectedCost(
const PhoneNumber& number) const {
list<string> region_codes;
phone_util_.GetRegionCodesForCountryCallingCode(number.country_code(),
&region_codes);
if (region_codes.size() == 0) {
return ShortNumberInfo::UNKNOWN_COST;
}
string short_number;
phone_util_.GetNationalSignificantNumber(number, &short_number);
if (region_codes.size() == 1) {
return GetExpectedCostForRegion(short_number, region_codes.front());
}
ShortNumberInfo::ShortNumberCost cost =
ShortNumberInfo::TOLL_FREE;
for (list<string>::const_iterator it = region_codes.begin();
it != region_codes.end(); ++it) {
ShortNumberInfo::ShortNumberCost cost_for_region =
GetExpectedCostForRegion(short_number, *it);
switch (cost_for_region) {
case ShortNumberInfo::PREMIUM_RATE:
return ShortNumberInfo::PREMIUM_RATE;
case ShortNumberInfo::UNKNOWN_COST:
return ShortNumberInfo::UNKNOWN_COST;
case ShortNumberInfo::STANDARD_RATE:
if (cost != ShortNumberInfo::UNKNOWN_COST) {
cost = ShortNumberInfo::STANDARD_RATE;
}
break;
case ShortNumberInfo::TOLL_FREE:
// Do nothing.
break;
default:
LOG(ERROR) << "Unrecognised cost for region: "
<< static_cast<int>(cost_for_region);
break;
}
}
return cost;
}
void ShortNumberInfo::GetRegionCodeForShortNumberFromRegionList(
const PhoneNumber& number, const list<string>& region_codes,
string* region_code) const {
if (region_codes.size() == 0) {
region_code->assign(RegionCode::GetUnknown());
} else if (region_codes.size() == 1) {
region_code->assign(region_codes.front());
}
string national_number;
phone_util_.GetNationalSignificantNumber(number, &national_number);
for (list<string>::const_iterator it = region_codes.begin();
it != region_codes.end(); ++it) {
const PhoneMetadata* phone_metadata = GetMetadataForRegion(*it);
if (phone_metadata != NULL &&
phone_util_.IsNumberMatchingDesc(national_number,
phone_metadata->short_code())) {
// The number is valid for this region.
region_code->assign(*it);
}
}
region_code->assign(RegionCode::GetUnknown());
}
string ShortNumberInfo::GetExampleShortNumber(const string& region_code) const {
const PhoneMetadata* phone_metadata = GetMetadataForRegion(region_code);
if (!phone_metadata) {
return "";
}
const PhoneNumberDesc& desc = phone_metadata->short_code();
if (desc.has_example_number()) {
return desc.example_number();
}
return "";
}
string ShortNumberInfo::GetExampleShortNumberForCost(const string& region_code,
ShortNumberInfo::ShortNumberCost cost) const {
const PhoneMetadata* phone_metadata = GetMetadataForRegion(region_code);
if (!phone_metadata) {
return "";
}
const PhoneNumberDesc* desc = NULL;
switch (cost) {
case TOLL_FREE:
desc = &(phone_metadata->toll_free());
break;
case STANDARD_RATE:
desc = &(phone_metadata->standard_rate());
break;
case PREMIUM_RATE:
desc = &(phone_metadata->premium_rate());
break;
default:
// UNKNOWN_COST numbers are computed by the process of elimination from
// the other cost categories.
break;
}
if (desc != NULL && desc->has_example_number()) {
return desc->example_number();
}
return "";
}
bool ShortNumberInfo::ConnectsToEmergencyNumber(const string& number,
const string& region_code) const {
return MatchesEmergencyNumberHelper(number, region_code,
@ -109,13 +311,27 @@ bool ShortNumberInfo::MatchesEmergencyNumberHelper(const string& number,
const scoped_ptr<RegExpInput> normalized_number_input(
regexp_factory->CreateInput(extracted_number));
// In Brazil and Chile, emergency numbers don't work when additional digits
// are appended.
return (!allow_prefix_match ||
region_code == "BR" || region_code == "CL")
regions_where_emergency_numbers_must_be_exact_->find(region_code)
!= regions_where_emergency_numbers_must_be_exact_->end())
? emergency_number_pattern->FullMatch(extracted_number)
: emergency_number_pattern->Consume(normalized_number_input.get());
}
bool ShortNumberInfo::IsCarrierSpecific(const PhoneNumber& number) const {
list<string> region_codes;
phone_util_.GetRegionCodesForCountryCallingCode(number.country_code(),
&region_codes);
string region_code;
GetRegionCodeForShortNumberFromRegionList(number,
region_codes, &region_code);
string national_number;
phone_util_.GetNationalSignificantNumber(number, &national_number);
const PhoneMetadata* phone_metadata = GetMetadataForRegion(region_code);
return phone_metadata &&
phone_util_.IsNumberMatchingDesc(national_number,
phone_metadata->carrier_specific());
}
} // namespace phonenumbers
} // namespace i18n

+ 106
- 3
cpp/src/phonenumbers/shortnumberinfo.h View File

@ -12,14 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Utility for international short phone numbers, such as short codes and
// emergency numbers. Note most commercial short numbers are not handled here,
// but by the phonenumberutil.
// Library for obtaining information about international short phone numbers,
// such as short codes and emergency numbers. Note most commercial short
// numbers are not handled here, but by the phonenumberutil.
#ifndef I18N_PHONENUMBERS_SHORTNUMBERINFO_H_
#define I18N_PHONENUMBERS_SHORTNUMBERINFO_H_
#include <list>
#include <map>
#include <set>
#include <string>
#include "phonenumbers/base/basictypes.h"
@ -29,15 +31,97 @@
namespace i18n {
namespace phonenumbers {
using std::list;
using std::map;
using std::set;
using std::string;
class PhoneNumber;
class PhoneNumberUtil;
class ShortNumberInfo {
public:
ShortNumberInfo();
// Cost categories of short numbers.
enum ShortNumberCost {
TOLL_FREE,
STANDARD_RATE,
PREMIUM_RATE,
UNKNOWN_COST
};
// Check whether a short number is a possible number when dialled from a
// region, given the number in the form of a string, and the region where the
// number is dialed from. This provides a more lenient check than
// IsValidShortNumberForRegion.
bool IsPossibleShortNumberForRegion(
const string& short_number,
const string& region_dialing_from) const;
// Check whether a short number is a possible number. If a country calling
// code is shared by multiple regions, this returns true if it's possible in
// any of them. This provides a more lenient check than IsValidShortNumber.
// See IsPossibleShortNumberForRegion for details.
bool IsPossibleShortNumber(const PhoneNumber& number) const;
// Tests whether a short number matches a valid pattern in a region. Note
// that this doesn't verify the number is actually in use, which is
// impossible to tell by just looking at the number itself.
bool IsValidShortNumberForRegion(
const string& short_number,
const string& region_dialing_from) const;
// Tests whether a short number matches a valid pattern. If a country calling
// code is shared by multiple regions, this returns true if it's valid in any
// of them. Note that this doesn't verify the number is actually in use,
// which is impossible to tell by just looking at the number itself. See
// IsValidShortNumberForRegion for details.
bool IsValidShortNumber(const PhoneNumber& number) const;
// Gets the expected cost category of a short number when dialled from a
// region (however, nothing is implied about its validity). If it is
// important that the number is valid, then its validity must first be
// checked using IsValidShortNumberForRegion. Note that emergency numbers are
// always considered toll-free. Example usage:
//
// ShortNumberInfo short_info;
// string short_number("110");
// RegionCode region_code = RegionCode::FR();
// if (short_info.IsValidShortNumberForRegion(short_number, region_code)) {
// ShortNumberInfo::ShortNumberCost cost =
// short_info.GetExpectedCostForRegion(short_number, region_code);
// // Do something with the cost information here.
// }
ShortNumberCost GetExpectedCostForRegion(
const string& short_number,
const string& region_dialing_from) const;
// Gets the expected cost category of a short number (however, nothing is
// implied about its validity). If the country calling code is unique to a
// region, this method behaves exactly the same as GetExpectedCostForRegion.
// However, if the country calling code is shared by multiple regions, then
// it returns the highest cost in the sequence PREMIUM_RATE, UNKNOWN_COST,
// STANDARD_RATE, TOLL_FREE. The reason for the position of UNKNOWN_COST in
// this order is that if a number is UNKNOWN_COST in one region but
// STANDARD_RATE or TOLL_FREE in another, its expected cost cannot be
// estimated as one of the latter since it might be a PREMIUM_RATE number.
//
// For example, if a number is STANDARD_RATE in the US, but TOLL_FREE in
// Canada, the expected cost returned by this method will be STANDARD_RATE,
// since the NANPA countries share the same country calling code.
//
// Note: If the region from which the number is dialed is known, it is highly
// preferable to call GetExpectedCostForRegion instead.
ShortNumberCost GetExpectedCost(const PhoneNumber& number) const;
// Gets a valid short number for the specified region.
string GetExampleShortNumber(const string& region_code) const;
// Gets a valid short number for the specified cost category.
string GetExampleShortNumberForCost(const string& region_code,
ShortNumberCost cost) const;
// Returns true if the number might be used to connect to an emergency service
// in the given region.
//
@ -55,6 +139,12 @@ class ShortNumberInfo {
bool IsEmergencyNumber(const string& number,
const string& region_code) const;
// Given a valid short number, determines whether it is carrier-specific
// (however, nothing is implied about its validity). If it is important that
// the number is valid, then its validity must first be checked using
// IsValidShortNumber or IsValidShortNumberForRegion.
bool IsCarrierSpecific(const PhoneNumber& number) const;
private:
const PhoneNumberUtil& phone_util_;
@ -62,9 +152,22 @@ class ShortNumberInfo {
scoped_ptr<map<string, PhoneMetadata> >
region_to_short_metadata_map_;
// In these countries, if extra digits are added to an emergency number, it no
// longer connects to the emergency service.
scoped_ptr<set<string> >
regions_where_emergency_numbers_must_be_exact_;
const i18n::phonenumbers::PhoneMetadata* GetMetadataForRegion(
const string& region_code) const;
// Helper method to get the region code for a given phone number, from a list
// of possible region codes. If the list contains more than one region, the
// first region for which the number is valid is returned.
void GetRegionCodeForShortNumberFromRegionList(
const PhoneNumber& number,
const list<string>& region_codes,
string* region_code) const;
bool MatchesEmergencyNumberHelper(const string& number,
const string& region_code,
bool allow_prefix_match) const;


+ 604
- 564
cpp/src/phonenumbers/test_metadata.cc
File diff suppressed because it is too large
View File


+ 4
- 0
cpp/test/phonenumbers/phonenumbermatcher_test.cc View File

@ -812,6 +812,9 @@ static const NumberTest kStrictGroupingCases[] = {
NumberTest("0900-1 123123", RegionCode::DE()),
NumberTest("(0)900-1 123123", RegionCode::DE()),
NumberTest("0 900-1 123123", RegionCode::DE()),
// NDC also found as part of the country calling code; this shouldn't ruin the
// grouping expectations.
NumberTest("+33 3 34 2312", RegionCode::FR()),
};
// Strings with number-like things that should be found at all levels.
@ -850,6 +853,7 @@ static const NumberTest kExactGroupingCases[] = {
NumberTest("0900-1 123 123", RegionCode::DE()),
NumberTest("(0)900-1 123 123", RegionCode::DE()),
NumberTest("0 900-1 123 123", RegionCode::DE()),
NumberTest("+33 3 34 23 12", RegionCode::FR()),
};
TEST_F(PhoneNumberMatcherTest, MatchesWithPossibleLeniency) {


+ 152
- 3
cpp/test/phonenumbers/phonenumberutil_test.cc View File

@ -1075,6 +1075,72 @@ TEST_F(PhoneNumberUtilTest, FormatNumberForMobileDialing) {
phone_util_.FormatNumberForMobileDialing(
test_number, RegionCode::UN001(), false, &formatted_number);
EXPECT_EQ("+80012345678", formatted_number);
// Test that a short number is formatted correctly for mobile dialing within
// the region, and is not diallable from outside the region.
test_number.set_country_code(49);
test_number.set_national_number(123L);
phone_util_.FormatNumberForMobileDialing(
test_number, RegionCode::DE(), false, &formatted_number);
EXPECT_EQ("123", formatted_number);
phone_util_.FormatNumberForMobileDialing(
test_number, RegionCode::IT(), false, &formatted_number);
EXPECT_EQ("", formatted_number);
// Test the special logic for Hungary, where the national prefix must be
// added before dialing from a mobile phone for regular length numbers, but
// not for short numbers.
test_number.set_country_code(36);
test_number.set_national_number(301234567L);
phone_util_.FormatNumberForMobileDialing(
test_number, RegionCode::HU(), false, &formatted_number);
EXPECT_EQ("06301234567", formatted_number);
phone_util_.FormatNumberForMobileDialing(
test_number, RegionCode::JP(), false, &formatted_number);
EXPECT_EQ("+36301234567", formatted_number);
test_number.set_national_number(104L);
phone_util_.FormatNumberForMobileDialing(
test_number, RegionCode::HU(), false, &formatted_number);
EXPECT_EQ("104", formatted_number);
phone_util_.FormatNumberForMobileDialing(
test_number, RegionCode::JP(), false, &formatted_number);
EXPECT_EQ("", formatted_number);
// Test the special logic for NANPA countries, for which regular length phone
// numbers are always output in international format, but short numbers are
// in national format.
test_number.set_country_code(1);
test_number.set_national_number(6502530000L);
phone_util_.FormatNumberForMobileDialing(
test_number, RegionCode::US(), false, &formatted_number);
EXPECT_EQ("+16502530000", formatted_number);
phone_util_.FormatNumberForMobileDialing(
test_number, RegionCode::CA(), false, &formatted_number);
EXPECT_EQ("+16502530000", formatted_number);
phone_util_.FormatNumberForMobileDialing(
test_number, RegionCode::BR(), false, &formatted_number);
EXPECT_EQ("+16502530000", formatted_number);
test_number.set_national_number(911L);
phone_util_.FormatNumberForMobileDialing(
test_number, RegionCode::US(), false, &formatted_number);
EXPECT_EQ("911", formatted_number);
phone_util_.FormatNumberForMobileDialing(
test_number, RegionCode::CA(), false, &formatted_number);
EXPECT_EQ("", formatted_number);
phone_util_.FormatNumberForMobileDialing(
test_number, RegionCode::BR(), false, &formatted_number);
EXPECT_EQ("", formatted_number);
// Test that the Australian emergency number 000 is formatted correctly.
test_number.set_country_code(61);
test_number.set_national_number(0L);
test_number.set_italian_leading_zero(true);
test_number.set_number_of_leading_zeros(2);
phone_util_.FormatNumberForMobileDialing(
test_number, RegionCode::AU(), false, &formatted_number);
EXPECT_EQ("000", formatted_number);
phone_util_.FormatNumberForMobileDialing(
test_number, RegionCode::NZ(), false, &formatted_number);
EXPECT_EQ("", formatted_number);
}
TEST_F(PhoneNumberUtilTest, FormatByPattern) {
@ -1333,8 +1399,8 @@ TEST_F(PhoneNumberUtilTest, GetCountryMobileToken) {
phone_util_.GetCountryMobileToken(country_calling_code, &mobile_token);
EXPECT_EQ("1", mobile_token);
// Country calling code for United States, which has no mobile token.
country_calling_code = phone_util_.GetCountryCodeForRegion(RegionCode::US());
// Country calling code for Sweden, which has no mobile token.
country_calling_code = phone_util_.GetCountryCodeForRegion(RegionCode::SE());
phone_util_.GetCountryMobileToken(country_calling_code, &mobile_token);
EXPECT_EQ("", mobile_token);
}
@ -2646,7 +2712,7 @@ TEST_F(PhoneNumberUtilTest, UnknownCountryCallingCode) {
}
TEST_F(PhoneNumberUtilTest, IsNumberMatchMatches) {
// Test simple matches where formatting is different, or leading zeroes, or
// Test simple matches where formatting is different, or leading zeros, or
// country code has been specified.
EXPECT_EQ(PhoneNumberUtil::EXACT_MATCH,
phone_util_.IsNumberMatchWithTwoStrings("+64 3 331 6005",
@ -3523,6 +3589,44 @@ TEST_F(PhoneNumberUtilTest, ParseNumbersWithPlusWithNoRegion) {
EXPECT_EQ(nz_number, result_proto);
}
TEST_F(PhoneNumberUtilTest, ParseNumberTooShortIfNationalPrefixStripped) {
PhoneNumber test_number;
// Test that a number whose first digits happen to coincide with the national
// prefix does not get them stripped if doing so would result in a number too
// short to be a possible (regular length) phone number for that region.
PhoneNumber by_number;
by_number.set_country_code(375);
by_number.set_national_number(8123L);
EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR,
phone_util_.Parse("8123", RegionCode::BY(),
&test_number));
EXPECT_EQ(by_number, test_number);
by_number.set_national_number(81234L);
test_number.Clear();
EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR,
phone_util_.Parse("81234", RegionCode::BY(),
&test_number));
EXPECT_EQ(by_number, test_number);
// The prefix doesn't get stripped, since the input is a viable 6-digit
// number, whereas the result of stripping is only 5 digits.
by_number.set_national_number(812345L);
test_number.Clear();
EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR,
phone_util_.Parse("812345", RegionCode::BY(),
&test_number));
EXPECT_EQ(by_number, test_number);
// The prefix gets stripped, since only 6-digit numbers are possible.
by_number.set_national_number(123456L);
test_number.Clear();
EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR,
phone_util_.Parse("8123456", RegionCode::BY(),
&test_number));
EXPECT_EQ(by_number, test_number);
}
TEST_F(PhoneNumberUtilTest, ParseExtensions) {
PhoneNumber nz_number;
nz_number.set_country_code(64);
@ -3805,6 +3909,51 @@ TEST_F(PhoneNumberUtilTest, ParseAndKeepRaw) {
EXPECT_EQ(korean_number, test_number);
}
TEST_F(PhoneNumberUtilTest, ParseItalianLeadingZeros) {
PhoneNumber zeros_number;
zeros_number.set_country_code(61);
PhoneNumber test_number;
// Test the number "011".
zeros_number.set_national_number(11L);
zeros_number.set_italian_leading_zero(true);
EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR,
phone_util_.Parse("011", RegionCode::AU(),
&test_number));
EXPECT_EQ(zeros_number, test_number);
test_number.Clear();
// Test the number "001".
zeros_number.set_national_number(1L);
zeros_number.set_italian_leading_zero(true);
zeros_number.set_number_of_leading_zeros(2);
EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR,
phone_util_.Parse("001", RegionCode::AU(),
&test_number));
EXPECT_EQ(zeros_number, test_number);
test_number.Clear();
// Test the number "000". This number has 2 leading zeros.
zeros_number.set_national_number(0L);
zeros_number.set_italian_leading_zero(true);
zeros_number.set_number_of_leading_zeros(2);
EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR,
phone_util_.Parse("000", RegionCode::AU(),
&test_number));
EXPECT_EQ(zeros_number, test_number);
test_number.Clear();
// Test the number "0000". This number has 3 leading zeros.
zeros_number.set_national_number(0L);
zeros_number.set_italian_leading_zero(true);
zeros_number.set_number_of_leading_zeros(3);
EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR,
phone_util_.Parse("0000", RegionCode::AU(),
&test_number));
EXPECT_EQ(zeros_number, test_number);
test_number.Clear();
}
TEST_F(PhoneNumberUtilTest, CanBeInternationallyDialled) {
PhoneNumber test_number;
test_number.set_country_code(1);


+ 240
- 1
cpp/test/phonenumbers/shortnumberinfo_test.cc View File

@ -14,11 +14,16 @@
// Author: David Yonge-Mallo
// Note that these tests use the test metadata, not the normal metadata file, so
// should not be used for regression test purposes - these tests are
// illustrative only and test functionality.
#include "phonenumbers/shortnumberinfo.h"
#include <gtest/gtest.h>
#include "phonenumbers/default_logger.h"
#include "phonenumbers/phonenumberutil.h"
#include "phonenumbers/shortnumberinfo.h"
#include "phonenumbers/test_util.h"
namespace i18n {
@ -36,6 +41,207 @@ class ShortNumberInfoTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(ShortNumberInfoTest);
};
TEST_F(ShortNumberInfoTest, IsPossibleShortNumber) {
PhoneNumber possible_number;
possible_number.set_country_code(33);
possible_number.set_national_number(123456ULL);
EXPECT_TRUE(short_info_.IsPossibleShortNumber(possible_number));
EXPECT_TRUE(short_info_.IsPossibleShortNumberForRegion("123456",
RegionCode::FR()));
PhoneNumber impossible_number;
impossible_number.set_country_code(33);
impossible_number.set_national_number(9ULL);
EXPECT_FALSE(short_info_.IsPossibleShortNumber(impossible_number));
EXPECT_FALSE(short_info_.IsPossibleShortNumberForRegion("9",
RegionCode::FR()));
// Note that GB and GG share the country calling code 44, and that this
// number is possible but not valid.
PhoneNumber shared_number;
shared_number.set_country_code(44);
shared_number.set_national_number(11001ULL);
EXPECT_TRUE(short_info_.IsPossibleShortNumber(shared_number));
}
TEST_F(ShortNumberInfoTest, IsValidShortNumber) {
PhoneNumber valid_number;
valid_number.set_country_code(33);
valid_number.set_national_number(1010ULL);
EXPECT_TRUE(short_info_.IsValidShortNumber(valid_number));
EXPECT_TRUE(short_info_.IsValidShortNumberForRegion("1010",
RegionCode::FR()));
PhoneNumber invalid_number;
invalid_number.set_country_code(33);
invalid_number.set_national_number(123456ULL);
EXPECT_FALSE(short_info_.IsValidShortNumber(invalid_number));
EXPECT_FALSE(short_info_.IsValidShortNumberForRegion("123456",
RegionCode::FR()));
// Note that GB and GG share the country calling code 44.
PhoneNumber shared_number;
shared_number.set_country_code(44);
shared_number.set_national_number(18001ULL);
EXPECT_TRUE(short_info_.IsValidShortNumber(shared_number));
}
TEST_F(ShortNumberInfoTest, GetExpectedCost) {
uint64 national_number;
const string& premium_rate_example =
short_info_.GetExampleShortNumberForCost(
RegionCode::FR(), ShortNumberInfo::ShortNumberCost::PREMIUM_RATE);
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::PREMIUM_RATE,
short_info_.GetExpectedCostForRegion(premium_rate_example,
RegionCode::FR()));
PhoneNumber premium_rate_number;
premium_rate_number.set_country_code(33);
safe_strtou64(premium_rate_example, &national_number);
premium_rate_number.set_national_number(national_number);
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::PREMIUM_RATE,
short_info_.GetExpectedCost(premium_rate_number));
const string& standard_rate_example =
short_info_.GetExampleShortNumberForCost(
RegionCode::FR(), ShortNumberInfo::ShortNumberCost::STANDARD_RATE);
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::STANDARD_RATE,
short_info_.GetExpectedCostForRegion(standard_rate_example,
RegionCode::FR()));
PhoneNumber standard_rate_number;
standard_rate_number.set_country_code(33);
safe_strtou64(standard_rate_example, &national_number);
standard_rate_number.set_national_number(national_number);
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::STANDARD_RATE,
short_info_.GetExpectedCost(standard_rate_number));
const string& toll_free_example =
short_info_.GetExampleShortNumberForCost(
RegionCode::FR(), ShortNumberInfo::ShortNumberCost::TOLL_FREE);
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::TOLL_FREE,
short_info_.GetExpectedCostForRegion(toll_free_example,
RegionCode::FR()));
PhoneNumber toll_free_number;
toll_free_number.set_country_code(33);
safe_strtou64(toll_free_example, &national_number);
toll_free_number.set_national_number(national_number);
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::TOLL_FREE,
short_info_.GetExpectedCost(toll_free_number));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::UNKNOWN_COST,
short_info_.GetExpectedCostForRegion("12345", RegionCode::FR()));
PhoneNumber unknown_cost_number;
unknown_cost_number.set_country_code(33);
unknown_cost_number.set_national_number(12345ULL);
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::UNKNOWN_COST,
short_info_.GetExpectedCost(unknown_cost_number));
// Test that an invalid number may nevertheless have a cost other than
// UNKNOWN_COST.
EXPECT_FALSE(short_info_.IsValidShortNumberForRegion("116123",
RegionCode::FR()));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::TOLL_FREE,
short_info_.GetExpectedCostForRegion("116123", RegionCode::FR()));
PhoneNumber invalid_number;
invalid_number.set_country_code(33);
invalid_number.set_national_number(116123ULL);
EXPECT_FALSE(short_info_.IsValidShortNumber(invalid_number));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::TOLL_FREE,
short_info_.GetExpectedCost(invalid_number));
// Test a nonexistent country code.
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::UNKNOWN_COST,
short_info_.GetExpectedCostForRegion("911", RegionCode::ZZ()));
unknown_cost_number.clear();
unknown_cost_number.set_country_code(123);
unknown_cost_number.set_national_number(911ULL);
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::UNKNOWN_COST,
short_info_.GetExpectedCost(unknown_cost_number));
}
TEST_F(ShortNumberInfoTest, GetExpectedCostForSharedCountryCallingCode) {
// Test some numbers which have different costs in countries sharing the same
// country calling code. In Australia, 1234 is premium-rate, 1194 is
// standard-rate, and 733 is toll-free. These are not known to be valid
// numbers in the Christmas Islands.
string ambiguous_premium_rate_string("1234");
PhoneNumber ambiguous_premium_rate_number;
ambiguous_premium_rate_number.set_country_code(61);
ambiguous_premium_rate_number.set_national_number(1234ULL);
string ambiguous_standard_rate_string("1194");
PhoneNumber ambiguous_standard_rate_number;
ambiguous_standard_rate_number.set_country_code(61);
ambiguous_standard_rate_number.set_national_number(1194ULL);
string ambiguous_toll_free_string("733");
PhoneNumber ambiguous_toll_free_number;
ambiguous_toll_free_number.set_country_code(61);
ambiguous_toll_free_number.set_national_number(733ULL);
EXPECT_TRUE(short_info_.IsValidShortNumber(ambiguous_premium_rate_number));
EXPECT_TRUE(short_info_.IsValidShortNumber(ambiguous_standard_rate_number));
EXPECT_TRUE(short_info_.IsValidShortNumber(ambiguous_toll_free_number));
EXPECT_TRUE(short_info_.IsValidShortNumberForRegion(
ambiguous_premium_rate_string, RegionCode::AU()));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::PREMIUM_RATE,
short_info_.GetExpectedCostForRegion(ambiguous_premium_rate_string,
RegionCode::AU()));
EXPECT_FALSE(short_info_.IsValidShortNumberForRegion(
ambiguous_premium_rate_string, RegionCode::CX()));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::UNKNOWN_COST,
short_info_.GetExpectedCostForRegion(ambiguous_premium_rate_string,
RegionCode::CX()));
// PREMIUM_RATE takes precedence over UNKNOWN_COST.
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::PREMIUM_RATE,
short_info_.GetExpectedCost(ambiguous_premium_rate_number));
EXPECT_TRUE(short_info_.IsValidShortNumberForRegion(
ambiguous_standard_rate_string, RegionCode::AU()));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::STANDARD_RATE,
short_info_.GetExpectedCostForRegion(ambiguous_standard_rate_string,
RegionCode::AU()));
EXPECT_FALSE(short_info_.IsValidShortNumberForRegion(
ambiguous_standard_rate_string, RegionCode::CX()));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::UNKNOWN_COST,
short_info_.GetExpectedCostForRegion(ambiguous_standard_rate_string,
RegionCode::CX()));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::UNKNOWN_COST,
short_info_.GetExpectedCost(ambiguous_standard_rate_number));
EXPECT_TRUE(short_info_.IsValidShortNumberForRegion(
ambiguous_toll_free_string, RegionCode::AU()));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::TOLL_FREE,
short_info_.GetExpectedCostForRegion(ambiguous_toll_free_string,
RegionCode::AU()));
EXPECT_FALSE(short_info_.IsValidShortNumberForRegion(
ambiguous_toll_free_string, RegionCode::CX()));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::UNKNOWN_COST,
short_info_.GetExpectedCostForRegion(ambiguous_toll_free_string,
RegionCode::CX()));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::UNKNOWN_COST,
short_info_.GetExpectedCost(ambiguous_toll_free_number));
}
TEST_F(ShortNumberInfoTest, GetExampleShortNumber) {
EXPECT_EQ("8711", short_info_.GetExampleShortNumber(RegionCode::AM()));
EXPECT_EQ("1010", short_info_.GetExampleShortNumber(RegionCode::FR()));
EXPECT_EQ("", short_info_.GetExampleShortNumber(RegionCode::UN001()));
EXPECT_EQ("", short_info_.GetExampleShortNumber(RegionCode::GetUnknown()));
}
TEST_F(ShortNumberInfoTest, GetExampleShortNumberForCost) {
EXPECT_EQ("3010",
short_info_.GetExampleShortNumberForCost(RegionCode::FR(),
ShortNumberInfo::ShortNumberCost::TOLL_FREE));
EXPECT_EQ("1023",
short_info_.GetExampleShortNumberForCost(RegionCode::FR(),
ShortNumberInfo::ShortNumberCost::STANDARD_RATE));
EXPECT_EQ("42000",
short_info_.GetExampleShortNumberForCost(RegionCode::FR(),
ShortNumberInfo::ShortNumberCost::PREMIUM_RATE));
EXPECT_EQ("", short_info_.GetExampleShortNumberForCost(RegionCode::FR(),
ShortNumberInfo::ShortNumberCost::UNKNOWN_COST));
}
TEST_F(ShortNumberInfoTest, ConnectsToEmergencyNumber_US) {
EXPECT_TRUE(short_info_.ConnectsToEmergencyNumber("911", RegionCode::US()));
EXPECT_TRUE(short_info_.ConnectsToEmergencyNumber("112", RegionCode::US()));
@ -170,5 +376,38 @@ TEST_F(ShortNumberInfoTest, IsEmergencyNumber_ZW) {
EXPECT_FALSE(short_info_.IsEmergencyNumber("0711234567", RegionCode::ZW()));
}
TEST_F(ShortNumberInfoTest, EmergencyNumberForSharedCountryCallingCode) {
// Test the emergency number 112, which is valid in both Australia and the
// Christmas Islands.
EXPECT_TRUE(short_info_.IsEmergencyNumber("112", RegionCode::AU()));
EXPECT_TRUE(short_info_.IsValidShortNumberForRegion("112", RegionCode::AU()));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::TOLL_FREE,
short_info_.GetExpectedCostForRegion("112", RegionCode::AU()));
EXPECT_TRUE(short_info_.IsEmergencyNumber("112", RegionCode::CX()));
EXPECT_TRUE(short_info_.IsValidShortNumberForRegion("112", RegionCode::CX()));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::TOLL_FREE,
short_info_.GetExpectedCostForRegion("112", RegionCode::CX()));
PhoneNumber shared_emergency_number;
shared_emergency_number.set_country_code(61);
shared_emergency_number.set_national_number(112ULL);
EXPECT_TRUE(short_info_.IsValidShortNumber(shared_emergency_number));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::TOLL_FREE,
short_info_.GetExpectedCost(shared_emergency_number));
}
TEST_F(ShortNumberInfoTest, OverlappingNANPANumber) {
// 211 is an emergency number in Barbados, while it is a toll-free
// information line in Canada and the USA.
EXPECT_TRUE(short_info_.IsEmergencyNumber("211", RegionCode::BB()));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::TOLL_FREE,
short_info_.GetExpectedCostForRegion("211", RegionCode::BB()));
EXPECT_FALSE(short_info_.IsEmergencyNumber("211", RegionCode::US()));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::UNKNOWN_COST,
short_info_.GetExpectedCostForRegion("211", RegionCode::US()));
EXPECT_FALSE(short_info_.IsEmergencyNumber("211", RegionCode::CA()));
EXPECT_EQ(ShortNumberInfo::ShortNumberCost::UNKNOWN_COST,
short_info_.GetExpectedCostForRegion("211", RegionCode::CA()));
}
} // namespace phonenumbers
} // namespace i18n

+ 12
- 0
cpp/test/phonenumbers/test_util.h View File

@ -109,10 +109,18 @@ class RegionCode {
return "DE";
}
static const char* FR() {
return "FR";
}
static const char* GB() {
return "GB";
}
static const char* HU() {
return "HU";
}
static const char* IT() {
return "IT";
}
@ -141,6 +149,10 @@ class RegionCode {
return "RE";
}
static const char* SE() {
return "SE";
}
static const char* SG() {
return "SG";
}


+ 40
- 0
debian/changelog View File

@ -1,3 +1,43 @@
libphonenumber (5.9) precise; urgency=low
* Code changes:
- Adding support for numbers with multiple Italian leading zeros, by
adding a field to the phone number proto to allow an arbitrary number of
leading zeros, and supporting this when parsing, validating and
formatting.
- Adding more functionality to ShortNumberInfo -> such as
GetExpectedCostForRegion.
- Fix for parsing short numbers that start with the national prefix.
- Updating FormatNumberForMobileDialing to work with short numbers.
- Stop finding Israeli 4-digit "star" numbers in text when no star is in
fact present.
- Bug fix for finding phone numbers where the area code was also part of
the country calling code.
* Metadata changes:
- Updated phone metadata for region code(s):
AU, BQ, BY, CI, CO, CW, GN, HN, IN, JO, KI, KW, MG, MK, MM, NA, PK,
TC, TM,
UG
- New short number metadata for region code(s): CD, GN
- Updated short number metadata for region code(s):
AF, AG, AI, AM, AR, AS, AW, AZ, BD, BH, BI, BM, BO, BQ, BT, BW, BY,
CA, CH,
CI, CM, CO, CW, DZ, FJ, GD, GE, GH, GT, GY, HN, HT, ID, IE, IM, IN,
IQ, JM,
JO, KE, KG, KH, KI, KW, KZ, LR, SV
- New geocoding data for country calling code(s):
257 (en), 389 (en), 599 (en), 686 (en), 962 (en)
- Updated geocoding data for country calling code(s):
264 (en), 375 (be, en, ru)
- New carrier data for country calling code(s):
375 (be, ru), 389 (en), 599 (en), 965 (ar)
- Updated carrier data for country calling code(s):
57 (en), 61 (en), 92 (en), 225 (en), 256 (en), 257 (en), 261 (en),
375 (en),
686 (en), 962 (en), 965 (en)
-- Lara Scheidegger <lararennie@google.com> Tue, 12 Nov 2013 12:09:15 +0100
libphonenumber (5.8.8) precise; urgency=low
* Metadata changes:


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/225_en View File


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/256_en View File


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/257_en View File


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/261_en View File


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/375_be View File


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/375_en View File


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/375_ru View File


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/389_en View File


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/57_en View File


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/599_en View File


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/61_en View File


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/686_en View File


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/92_en View File


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/962_en View File


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/965_ar View File


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/965_en View File


BIN
java/carrier/src/com/google/i18n/phonenumbers/carrier/data/config View File


+ 9
- 6
java/geocoder/src/com/google/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder.java View File

@ -16,6 +16,7 @@
package com.google.i18n.phonenumbers.geocoding;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberType;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
@ -33,7 +34,7 @@ public class PhoneNumberOfflineGeocoder {
private static final String MAPPING_DATA_DIRECTORY =
"/com/google/i18n/phonenumbers/geocoding/data/";
private PrefixFileReader prefixFileReader = null;
private final PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
// @VisibleForTesting
@ -101,11 +102,13 @@ public class PhoneNumberOfflineGeocoder {
// In some countries, eg. Argentina, mobile numbers have a mobile token before the national
// destination code, this should be removed before geocoding.
nationalNumber = nationalNumber.substring(mobileToken.length());
PhoneNumber copiedNumber = new PhoneNumber();
copiedNumber.setCountryCode(number.getCountryCode());
copiedNumber.setNationalNumber(Long.parseLong(nationalNumber));
if (nationalNumber.startsWith("0")) {
copiedNumber.setItalianLeadingZero(true);
String region = phoneUtil.getRegionCodeForCountryCode(number.getCountryCode());
PhoneNumber copiedNumber;
try {
copiedNumber = phoneUtil.parse(nationalNumber, region);
} catch (NumberParseException e) {
// If this happens, just reuse what we had.
copiedNumber = number;
}
areaDescription = prefixFileReader.getDescriptionForNumber(copiedNumber, langStr, scriptStr,
regionStr);


BIN
java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/257_en View File


BIN
java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/264_en View File


BIN
java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/375_be View File


BIN
java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/375_en View File


BIN
java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/375_ru View File


BIN
java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/389_en View File


BIN
java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/599_en View File


BIN
java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/686_en View File


BIN
java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/962_en View File


BIN
java/geocoder/src/com/google/i18n/phonenumbers/geocoding/data/config View File


+ 2
- 3
java/libphonenumber/src/com/google/i18n/phonenumbers/AlternateFormatsCountryCodeSet.java View File

@ -26,9 +26,9 @@ import java.util.Set;
public class AlternateFormatsCountryCodeSet {
// A set of all country codes for which data is available.
static Set<Integer> getCountryCodeSet() {
// The capacity is set to 45 as there are 34 different entries,
// The capacity is set to 44 as there are 33 different entries,
// and this offers a load factor of roughly 0.75.
Set<Integer> countryCodeSet = new HashSet<Integer>(45);
Set<Integer> countryCodeSet = new HashSet<Integer>(44);
countryCodeSet.add(7);
countryCodeSet.add(30);
@ -55,7 +55,6 @@ public class AlternateFormatsCountryCodeSet {
countryCodeSet.add(359);
countryCodeSet.add(372);
countryCodeSet.add(373);
countryCodeSet.add(375);
countryCodeSet.add(380);
countryCodeSet.add(385);
countryCodeSet.add(595);


+ 25
- 1
java/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberMatcher.java View File

@ -416,6 +416,25 @@ final class PhoneNumberMatcher implements Iterator<PhoneNumberMatch> {
}
PhoneNumber number = phoneUtil.parseAndKeepRawInput(candidate, preferredRegion);
// Check Israel * numbers: these are a special case in that they are four-digit numbers that
// our library supports, but they can only be dialled with a leading *. Since we don't
// actually store or detect the * in our phone number library, this means in practice we
// detect most four digit numbers as being valid for Israel. We are considering moving these
// numbers to ShortNumberInfo instead, in which case this problem would go away, but in the
// meantime we want to restrict the false matches so we only allow these numbers if they are
// preceded by a star. We enforce this for all leniency levels even though these numbers are
// technically accepted by isPossibleNumber and isValidNumber since we consider it to be a
// deficiency in those methods that they accept these numbers without the *.
// TODO: Remove this or make it significantly less hacky once we've decided how to
// handle these short codes going forward in ShortNumberInfo. We could use the formatting
// rules for instance, but that would be slower.
if (phoneUtil.getRegionCodeForCountryCode(number.getCountryCode()).equals("IL") &&
phoneUtil.getNationalSignificantNumber(number).length() == 4 &&
(offset == 0 || (offset > 0 && text.charAt(offset - 1) != '*'))) {
// No match.
return null;
}
if (leniency.verify(number, candidate, phoneUtil)) {
// We used parseAndKeepRawInput to create this number, but for now we don't return the extra
// values parsed. TODO: stop clearing all values here and switch all users over
@ -456,6 +475,11 @@ final class PhoneNumberMatcher implements Iterator<PhoneNumberMatch> {
StringBuilder normalizedCandidate,
String[] formattedNumberGroups) {
int fromIndex = 0;
if (number.getCountryCodeSource() != CountryCodeSource.FROM_DEFAULT_COUNTRY) {
// First skip the country code if the normalized candidate contained it.
String countryCode = Integer.toString(number.getCountryCode());
fromIndex = normalizedCandidate.indexOf(countryCode) + countryCode.length();
}
// Check each group of consecutive digits are not broken into separate groupings in the
// {@code normalizedCandidate} string.
for (int i = 0; i < formattedNumberGroups.length; i++) {
@ -621,7 +645,7 @@ final class PhoneNumberMatcher implements Iterator<PhoneNumberMatch> {
// extension number.
} else if (!PhoneNumberUtil.normalizeDigitsOnly(candidate.substring(index)).equals(
number.getExtension())) {
return false;
return false;
}
}
}


+ 93
- 26
java/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java View File

@ -25,6 +25,7 @@ import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber.CountryCodeSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Arrays;
@ -608,8 +609,7 @@ public class PhoneNumberUtil {
ObjectInputStream in = null;
try {
in = new ObjectInputStream(source);
PhoneMetadataCollection metadataCollection = new PhoneMetadataCollection();
metadataCollection.readExternal(in);
PhoneMetadataCollection metadataCollection = loadMetadataAndCloseInput(in);
List<PhoneMetadata> metadataList = metadataCollection.getMetadataList();
if (metadataList.isEmpty()) {
logger.log(Level.SEVERE, "empty metadata: " + fileName);
@ -627,17 +627,30 @@ public class PhoneNumberUtil {
} catch (IOException e) {
logger.log(Level.SEVERE, "cannot load/parse metadata: " + fileName, e);
throw new RuntimeException("cannot load/parse metadata: " + fileName, e);
} finally {
close(in);
}
}
private static void close(InputStream in) {
if (in != null) {
/**
* Loads the metadata protocol buffer from the given stream and closes the stream afterwards. Any
* exceptions that occur while reading the stream are propagated (though exceptions that occur
* when the stream is closed will be ignored).
*
* @param source the non-null stream from which metadata is to be read.
* @return the loaded metadata protocol buffer.
*/
private static PhoneMetadataCollection loadMetadataAndCloseInput(ObjectInput source) {
PhoneMetadataCollection metadataCollection = new PhoneMetadataCollection();
try {
metadataCollection.readExternal(source);
} catch (IOException e) {
logger.log(Level.WARNING, "error reading input (ignored)", e);
} finally {
try {
in.close();
source.close();
} catch (IOException e) {
logger.log(Level.WARNING, "error closing input stream (ignored)", e);
} finally {
return metadataCollection;
}
}
}
@ -1256,8 +1269,9 @@ public class PhoneNumberUtil {
// Clear the extension, as that part cannot normally be dialed together with the main number.
PhoneNumber numberNoExt = new PhoneNumber().mergeFrom(number).clearExtension();
String regionCode = getRegionCodeForCountryCode(countryCallingCode);
PhoneNumberType numberType = getNumberType(numberNoExt);
boolean isValidNumber = (numberType != PhoneNumberType.UNKNOWN);
if (regionCallingFrom.equals(regionCode)) {
PhoneNumberType numberType = getNumberType(numberNoExt);
boolean isFixedLineOrMobile =
(numberType == PhoneNumberType.FIXED_LINE) || (numberType == PhoneNumberType.MOBILE) ||
(numberType == PhoneNumberType.FIXED_LINE_OR_MOBILE);
@ -1272,19 +1286,31 @@ public class PhoneNumberUtil {
// called within Brazil. Without that, most of the carriers won't connect the call.
// Because of that, we return an empty string here.
: "";
} else if (regionCode.equals("HU")) {
} else if (isValidNumber && regionCode.equals("HU")) {
// The national format for HU numbers doesn't contain the national prefix, because that is
// how numbers are normally written down. However, the national prefix is obligatory when
// dialing from a mobile phone. As a result, we add it back here.
// dialing from a mobile phone, except for short numbers. As a result, we add it back here
// if it is a valid regular length phone number.
formattedNumber =
getNddPrefixForRegion(regionCode, true /* strip non-digits */) +
" " + format(numberNoExt, PhoneNumberFormat.NATIONAL);
} else if (countryCallingCode == NANPA_COUNTRY_CODE) {
// For NANPA countries, we output international format for numbers that can be dialed
// internationally, since that always works, except for numbers which might potentially be
// short numbers, which are always dialled in national format.
PhoneMetadata regionMetadata = getMetadataForRegion(regionCallingFrom);
if (canBeInternationallyDialled(numberNoExt) &&
!isShorterThanPossibleNormalNumber(regionMetadata,
getNationalSignificantNumber(numberNoExt))) {
formattedNumber = format(numberNoExt, PhoneNumberFormat.INTERNATIONAL);
} else {
formattedNumber = format(numberNoExt, PhoneNumberFormat.NATIONAL);
}
} else {
// For NANPA countries, non-geographical countries, Mexican and Chilean fixed line and
// mobile numbers, we output international format for numbers that can be dialed
// internationally as that always works.
if ((countryCallingCode == NANPA_COUNTRY_CODE ||
regionCode.equals(REGION_CODE_FOR_NON_GEO_ENTITY) ||
// For non-geographical countries, and Mexican and Chilean fixed line and mobile numbers, we
// output international format for numbers that can be dialed internationally as that always
// works.
if ((regionCode.equals(REGION_CODE_FOR_NON_GEO_ENTITY) ||
// MX fixed line and mobile numbers should always be formatted in international format,
// even when dialed within MX. For national format to work, a carrier code needs to be
// used, and the correct carrier code depends on if the caller and callee are from the
@ -1300,7 +1326,10 @@ public class PhoneNumberUtil {
formattedNumber = format(numberNoExt, PhoneNumberFormat.NATIONAL);
}
}
} else if (canBeInternationallyDialled(numberNoExt)) {
} else if (isValidNumber && canBeInternationallyDialled(numberNoExt)) {
// We assume that short numbers are not diallable from outside their region, so if a number
// is not a valid regular length phone number, we treat it as if it cannot be internationally
// dialled.
return withFormatting ? format(numberNoExt, PhoneNumberFormat.INTERNATIONAL)
: format(numberNoExt, PhoneNumberFormat.E164);
}
@ -1662,8 +1691,13 @@ public class PhoneNumberUtil {
* @return the national significant number of the PhoneNumber object passed in
*/
public String getNationalSignificantNumber(PhoneNumber number) {
// If a leading zero has been set, we prefix this now. Note this is not a national prefix.
StringBuilder nationalNumber = new StringBuilder(number.isItalianLeadingZero() ? "0" : "");
// If leading zero(s) have been set, we prefix this now. Note this is not a national prefix.
StringBuilder nationalNumber = new StringBuilder();
if (number.isItalianLeadingZero()) {
char[] zeros = new char[number.getNumberOfLeadingZeros()];
Arrays.fill(zeros, '0');
nationalNumber.append(new String(zeros));
}
nationalNumber.append(number.getNationalNumber());
return nationalNumber.toString();
}
@ -1996,7 +2030,6 @@ public class PhoneNumberUtil {
return countryCodeToNonGeographicalMetadataMap.get(countryCallingCode);
}
// @VisibleForTesting
boolean isNumberPossibleForDesc(String nationalNumber, PhoneNumberDesc numberDesc) {
Matcher possibleNumberPatternMatcher =
regexCache.getPatternForRegex(numberDesc.getPossibleNumberPattern())
@ -2004,7 +2037,6 @@ public class PhoneNumberUtil {
return possibleNumberPatternMatcher.matches();
}
// @VisibleForTesting
boolean isNumberMatchingDesc(String nationalNumber, PhoneNumberDesc numberDesc) {
Matcher nationalNumberPatternMatcher =
regexCache.getPatternForRegex(numberDesc.getNationalNumberPattern())
@ -2273,6 +2305,17 @@ public class PhoneNumberUtil {
}
}
/**
* Helper method to check whether a number is too short to be a regular length phone number in a
* region.
*/
private boolean isShorterThanPossibleNormalNumber(PhoneMetadata regionMetadata, String number) {
Pattern possibleNumberPattern = regexCache.getPatternForRegex(
regionMetadata.getGeneralDesc().getPossibleNumberPattern());
return testNumberLengthAgainstPattern(possibleNumberPattern, number) ==
ValidationResult.TOO_SHORT;
}
/**
* Check whether a phone number is a possible number. It provides a more lenient check than
* {@link #isValidNumber} in the following sense:
@ -2787,6 +2830,25 @@ public class PhoneNumberUtil {
};
}
/**
* A helper function to set the values related to leading zeros in a PhoneNumber.
*/
static void setItalianLeadingZerosForPhoneNumber(String nationalNumber, PhoneNumber phoneNumber) {
if (nationalNumber.length() > 1 && nationalNumber.charAt(0) == '0') {
phoneNumber.setItalianLeadingZero(true);
int numberOfLeadingZeros = 1;
// Note that if the national number is all "0"s, the last "0" is not counted as a leading
// zero.
while (numberOfLeadingZeros < nationalNumber.length() - 1 &&
nationalNumber.charAt(numberOfLeadingZeros) == '0') {
numberOfLeadingZeros++;
}
if (numberOfLeadingZeros != 1) {
phoneNumber.setNumberOfLeadingZeros(numberOfLeadingZeros);
}
}
}
/**
* Parses a string and fills up the phoneNumber. This method is the same as the public
* parse() method, with the exception that it allows the default region to be null, for use by
@ -2880,9 +2942,16 @@ public class PhoneNumberUtil {
}
if (regionMetadata != null) {
StringBuilder carrierCode = new StringBuilder();
maybeStripNationalPrefixAndCarrierCode(normalizedNationalNumber, regionMetadata, carrierCode);
if (keepRawInput) {
phoneNumber.setPreferredDomesticCarrierCode(carrierCode.toString());
StringBuilder potentialNationalNumber = new StringBuilder(normalizedNationalNumber);
maybeStripNationalPrefixAndCarrierCode(potentialNationalNumber, regionMetadata, carrierCode);
// We require that the NSN remaining after stripping the national prefix and carrier code be
// of a possible length for the region. Otherwise, we don't do the stripping, since the
// original number could be a valid short number.
if (!isShorterThanPossibleNormalNumber(regionMetadata, potentialNationalNumber.toString())) {
normalizedNationalNumber = potentialNationalNumber;
if (keepRawInput) {
phoneNumber.setPreferredDomesticCarrierCode(carrierCode.toString());
}
}
}
int lengthOfNationalNumber = normalizedNationalNumber.length();
@ -2894,9 +2963,7 @@ public class PhoneNumberUtil {
throw new NumberParseException(NumberParseException.ErrorType.TOO_LONG,
"The string supplied is too long to be a phone number.");
}
if (normalizedNationalNumber.charAt(0) == '0') {
phoneNumber.setItalianLeadingZero(true);
}
setItalianLeadingZerosForPhoneNumber(normalizedNationalNumber.toString(), phoneNumber);
phoneNumber.setNationalNumber(Long.parseLong(normalizedNationalNumber.toString()));
}


+ 2
- 2
java/libphonenumber/src/com/google/i18n/phonenumbers/Phonemetadata.java View File

@ -542,7 +542,7 @@ public final class Phonemetadata {
return this;
}
// required int32 country_code = 10;
// optional int32 country_code = 10;
private boolean hasCountryCode;
private int countryCode_ = 0;
public boolean hasCountryCode() { return hasCountryCode; }
@ -553,7 +553,7 @@ public final class Phonemetadata {
return this;
}
// required string international_prefix = 11;
// optional string international_prefix = 11;
private boolean hasInternationalPrefix;
private String internationalPrefix_ = "";
public boolean hasInternationalPrefix() { return hasInternationalPrefix; }


+ 26
- 1
java/libphonenumber/src/com/google/i18n/phonenumbers/Phonenumber.java View File

@ -106,6 +106,22 @@ public final class Phonenumber {
return this;
}
// optional int32 number_of_leading_zeros = 8 [default = 1];
private boolean hasNumberOfLeadingZeros;
private int numberOfLeadingZeros_ = 1;
public boolean hasNumberOfLeadingZeros() { return hasNumberOfLeadingZeros; }
public int getNumberOfLeadingZeros() { return numberOfLeadingZeros_; }
public PhoneNumber setNumberOfLeadingZeros(int value) {
hasNumberOfLeadingZeros = true;
numberOfLeadingZeros_ = value;
return this;
}
public PhoneNumber clearNumberOfLeadingZeros() {
hasNumberOfLeadingZeros = false;
numberOfLeadingZeros_ = 1;
return this;
}
// optional string raw_input = 5;
private boolean hasRawInput;
private String rawInput_ = "";
@ -168,6 +184,7 @@ public final class Phonenumber {
clearNationalNumber();
clearExtension();
clearItalianLeadingZero();
clearNumberOfLeadingZeros();
clearRawInput();
clearCountryCodeSource();
clearPreferredDomesticCarrierCode();
@ -187,6 +204,9 @@ public final class Phonenumber {
if (other.hasItalianLeadingZero()) {
setItalianLeadingZero(other.isItalianLeadingZero());
}
if (other.hasNumberOfLeadingZeros()) {
setNumberOfLeadingZeros(other.getNumberOfLeadingZeros());
}
if (other.hasRawInput()) {
setRawInput(other.getRawInput());
}
@ -208,6 +228,7 @@ public final class Phonenumber {
}
return (countryCode_ == other.countryCode_ && nationalNumber_ == other.nationalNumber_ &&
extension_.equals(other.extension_) && italianLeadingZero_ == other.italianLeadingZero_ &&
numberOfLeadingZeros_ == other.numberOfLeadingZeros_ &&
rawInput_.equals(other.rawInput_) && countryCodeSource_ == other.countryCodeSource_ &&
preferredDomesticCarrierCode_.equals(other.preferredDomesticCarrierCode_) &&
hasPreferredDomesticCarrierCode() == other.hasPreferredDomesticCarrierCode());
@ -229,6 +250,7 @@ public final class Phonenumber {
hash = (53 * hash) + Long.valueOf(getNationalNumber()).hashCode();
hash = (53 * hash) + getExtension().hashCode();
hash = (53 * hash) + (isItalianLeadingZero() ? 1231 : 1237);
hash = (53 * hash) + getNumberOfLeadingZeros();
hash = (53 * hash) + getRawInput().hashCode();
hash = (53 * hash) + getCountryCodeSource().hashCode();
hash = (53 * hash) + getPreferredDomesticCarrierCode().hashCode();
@ -242,7 +264,10 @@ public final class Phonenumber {
outputString.append("Country Code: ").append(countryCode_);
outputString.append(" National Number: ").append(nationalNumber_);
if (hasItalianLeadingZero() && isItalianLeadingZero()) {
outputString.append(" Leading Zero: true");
outputString.append(" Leading Zero(s): true");
}
if (hasNumberOfLeadingZeros()) {
outputString.append(" Number of leading zeros: ").append(numberOfLeadingZeros_);
}
if (hasExtension()) {
outputString.append(" Extension: ").append(extension_);


+ 114
- 45
java/libphonenumber/src/com/google/i18n/phonenumbers/ShortNumberInfo.java View File

@ -21,6 +21,7 @@ import com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
@ -41,6 +42,16 @@ public class ShortNumberInfo {
private static final ShortNumberInfo INSTANCE =
new ShortNumberInfo(PhoneNumberUtil.getInstance());
// In these countries, if extra digits are added to an emergency number, it no longer connects
// to the emergency service.
private static final Set<String> REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT =
new HashSet<String>();
static {
REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT.add("BR");
REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT.add("CL");
REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT.add("NI");
}
/** Cost categories of short numbers. */
public enum ShortNumberCost {
TOLL_FREE,
@ -62,15 +73,15 @@ public class ShortNumberInfo {
}
/**
* Check whether a short number is a possible number, given the number in the form of a string,
* and the region where the number is dialed from. This provides a more lenient check than
* {@link #isValidShortNumber}.
* Check whether a short number is a possible number when dialled from a region, given the number
* in the form of a string, and the region where the number is dialed from. This provides a more
* lenient check than {@link #isValidShortNumberForRegion}.
*
* @param shortNumber the short number to check as a string
* @param regionDialingFrom the region from which the number is dialed
* @return whether the number is a possible short number
*/
public boolean isPossibleShortNumber(String shortNumber, String regionDialingFrom) {
public boolean isPossibleShortNumberForRegion(String shortNumber, String regionDialingFrom) {
PhoneMetadata phoneMetadata =
MetadataManager.getShortNumberMetadataForRegion(regionDialingFrom);
if (phoneMetadata == null) {
@ -81,9 +92,10 @@ public class ShortNumberInfo {
}
/**
* Check whether a short number is a possible number. This provides a more lenient check than
* {@link #isValidShortNumber}. See {@link #isPossibleShortNumber(String, String)} for
* details.
* Check whether a short number is a possible number. If a country calling code is shared by
* multiple regions, this returns true if it's possible in any of them. This provides a more
* lenient check than {@link #isValidShortNumber}. See {@link
* #isPossibleShortNumberForRegion(String, String)} for details.
*
* @param number the short number to check
* @return whether the number is a possible short number
@ -91,24 +103,25 @@ public class ShortNumberInfo {
public boolean isPossibleShortNumber(PhoneNumber number) {
List<String> regionCodes = phoneUtil.getRegionCodesForCountryCode(number.getCountryCode());
String shortNumber = phoneUtil.getNationalSignificantNumber(number);
String regionCode = getRegionCodeForShortNumberFromRegionList(number, regionCodes);
if (regionCodes.size() > 1 && regionCode != null) {
// If a matching region had been found for the phone number from among two or more regions,
// then we have already implicitly verified its validity for that region.
return true;
for (String region : regionCodes) {
PhoneMetadata phoneMetadata = MetadataManager.getShortNumberMetadataForRegion(region);
if (phoneUtil.isNumberPossibleForDesc(shortNumber, phoneMetadata.getGeneralDesc())) {
return true;
}
}
return isPossibleShortNumber(shortNumber, regionCode);
return false;
}
/**
* Tests whether a short number matches a valid pattern. Note that this doesn't verify the number
* is actually in use, which is impossible to tell by just looking at the number itself.
* Tests whether a short number matches a valid pattern in a region. Note that this doesn't verify
* the number is actually in use, which is impossible to tell by just looking at the number
* itself.
*
* @param shortNumber the short number to check as a string
* @param regionDialingFrom the region from which the number is dialed
* @return whether the short number matches a valid pattern
*/
public boolean isValidShortNumber(String shortNumber, String regionDialingFrom) {
public boolean isValidShortNumberForRegion(String shortNumber, String regionDialingFrom) {
PhoneMetadata phoneMetadata =
MetadataManager.getShortNumberMetadataForRegion(regionDialingFrom);
if (phoneMetadata == null) {
@ -129,9 +142,10 @@ public class ShortNumberInfo {
}
/**
* Tests whether a short number matches a valid pattern. Note that this doesn't verify the number
* is actually in use, which is impossible to tell by just looking at the number itself. See
* {@link #isValidShortNumber(String, String)} for details.
* Tests whether a short number matches a valid pattern. If a country calling code is shared by
* multiple regions, this returns true if it's valid in any of them. Note that this doesn't verify
* the number is actually in use, which is impossible to tell by just looking at the number
* itself. See {@link #isValidShortNumberForRegion(String, RegionCode)} for details.
*
* @param number the short number for which we want to test the validity
* @return whether the short number matches a valid pattern
@ -145,56 +159,111 @@ public class ShortNumberInfo {
// then we have already implicitly verified its validity for that region.
return true;
}
return isValidShortNumber(shortNumber, regionCode);
return isValidShortNumberForRegion(shortNumber, regionCode);
}
/**
* Gets the expected cost category of a short number (however, nothing is implied about its
* validity). If it is important that the number is valid, then its validity must first be checked
* using {@link isValidShortNumber}. Note that emergency numbers are always considered toll-free.
* Example usage:
* Gets the expected cost category of a short number when dialled from a region (however, nothing
* is implied about its validity). If it is important that the number is valid, then its validity
* must first be checked using {@link isValidShortNumberForRegion}. Note that emergency numbers
* are always considered toll-free. Example usage:
* <pre>{@code
* PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
* ShortNumberInfo shortInfo = ShortNumberInfo.getInstance();
* PhoneNumber number = phoneUtil.parse("110", "FR");
* if (shortInfo.isValidShortNumber(number)) {
* ShortNumberInfo.ShortNumberCost cost = shortInfo.getExpectedCost(number);
* String shortNumber = "110";
* String regionCode = "FR";
* if (shortInfo.isValidShortNumberForRegion(shortNumber, regionCode)) {
* ShortNumberInfo.ShortNumberCost cost = shortInfo.getExpectedCostForRegion(shortNumber,
* regionCode);
* // Do something with the cost information here.
* }}</pre>
*
* @param number the short number for which we want to know the expected cost category
* @return the expected cost category of the short number. Returns UNKNOWN_COST if the number does
* not match a cost category. Note that an invalid number may match any cost category.
* @param shortNumber the short number for which we want to know the expected cost category,
* as a string
* @param regionDialingFrom the region from which the number is dialed
* @return the expected cost category for that region of the short number. Returns UNKNOWN_COST if
* the number does not match a cost category. Note that an invalid number may match any cost
* category.
*/
public ShortNumberCost getExpectedCost(PhoneNumber number) {
List<String> regionCodes = phoneUtil.getRegionCodesForCountryCode(number.getCountryCode());
String regionCode = getRegionCodeForShortNumberFromRegionList(number, regionCodes);
// Note that regionCode may be null, in which case phoneMetadata will also be null.
PhoneMetadata phoneMetadata = MetadataManager.getShortNumberMetadataForRegion(regionCode);
public ShortNumberCost getExpectedCostForRegion(String shortNumber, String regionDialingFrom) {
// Note that regionDialingFrom may be null, in which case phoneMetadata will also be null.
PhoneMetadata phoneMetadata = MetadataManager.getShortNumberMetadataForRegion(
regionDialingFrom);
if (phoneMetadata == null) {
return ShortNumberCost.UNKNOWN_COST;
}
String nationalNumber = phoneUtil.getNationalSignificantNumber(number);
// The cost categories are tested in order of decreasing expense, since if for some reason the
// patterns overlap the most expensive matching cost category should be returned.
if (phoneUtil.isNumberMatchingDesc(nationalNumber, phoneMetadata.getPremiumRate())) {
if (phoneUtil.isNumberMatchingDesc(shortNumber, phoneMetadata.getPremiumRate())) {
return ShortNumberCost.PREMIUM_RATE;
}
if (phoneUtil.isNumberMatchingDesc(nationalNumber, phoneMetadata.getStandardRate())) {
if (phoneUtil.isNumberMatchingDesc(shortNumber, phoneMetadata.getStandardRate())) {
return ShortNumberCost.STANDARD_RATE;
}
if (phoneUtil.isNumberMatchingDesc(nationalNumber, phoneMetadata.getTollFree())) {
if (phoneUtil.isNumberMatchingDesc(shortNumber, phoneMetadata.getTollFree())) {
return ShortNumberCost.TOLL_FREE;
}
if (isEmergencyNumber(nationalNumber, regionCode)) {
if (isEmergencyNumber(shortNumber, regionDialingFrom)) {
// Emergency numbers are implicitly toll-free.
return ShortNumberCost.TOLL_FREE;
}
return ShortNumberCost.UNKNOWN_COST;
}
/**
* Gets the expected cost category of a short number (however, nothing is implied about its
* validity). If the country calling code is unique to a region, this method behaves exactly the
* same as {@link #getExpectedCostForRegion(String, RegionCode)}. However, if the country calling
* code is shared by multiple regions, then it returns the highest cost in the sequence
* PREMIUM_RATE, UNKNOWN_COST, STANDARD_RATE, TOLL_FREE. The reason for the position of
* UNKNOWN_COST in this order is that if a number is UNKNOWN_COST in one region but STANDARD_RATE
* or TOLL_FREE in another, its expected cost cannot be estimated as one of the latter since it
* might be a PREMIUM_RATE number.
*
* For example, if a number is STANDARD_RATE in the US, but TOLL_FREE in Canada, the expected cost
* returned by this method will be STANDARD_RATE, since the NANPA countries share the same country
* calling code.
*
* Note: If the region from which the number is dialed is known, it is highly preferable to call
* {@link #getExpectedCostForRegion(String, RegionCode)} instead.
*
* @param number the short number for which we want to know the expected cost category
* @return the highest expected cost category of the short number in the region(s) with the given
* country calling code
*/
public ShortNumberCost getExpectedCost(PhoneNumber number) {
List<String> regionCodes = phoneUtil.getRegionCodesForCountryCode(number.getCountryCode());
if (regionCodes.size() == 0) {
return ShortNumberCost.UNKNOWN_COST;
}
String shortNumber = phoneUtil.getNationalSignificantNumber(number);
if (regionCodes.size() == 1) {
return getExpectedCostForRegion(shortNumber, regionCodes.get(0));
}
ShortNumberCost cost = ShortNumberCost.TOLL_FREE;
for (String regionCode : regionCodes) {
ShortNumberCost costForRegion = getExpectedCostForRegion(shortNumber, regionCode);
switch (costForRegion) {
case PREMIUM_RATE:
return ShortNumberCost.PREMIUM_RATE;
case UNKNOWN_COST:
cost = ShortNumberCost.UNKNOWN_COST;
break;
case STANDARD_RATE:
if (cost != ShortNumberCost.UNKNOWN_COST) {
cost = ShortNumberCost.STANDARD_RATE;
}
break;
case TOLL_FREE:
// Do nothing.
break;
default:
logger.log(Level.SEVERE, "Unrecognised cost for region: " + costForRegion);
}
}
return cost;
}
// Helper method to get the region code for a given phone number, from a list of possible region
// codes. If the list contains more than one region, the first region for which the number is
// valid is returned.
@ -324,8 +393,7 @@ public class ShortNumberInfo {
Pattern emergencyNumberPattern =
Pattern.compile(metadata.getEmergency().getNationalNumberPattern());
String normalizedNumber = PhoneNumberUtil.normalizeDigitsOnly(number);
// In Brazil and Chile, emergency numbers don't work when additional digits are appended.
return (!allowPrefixMatch || regionCode == "BR" || regionCode == "CL")
return (!allowPrefixMatch || REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT.contains(regionCode))
? emergencyNumberPattern.matcher(normalizedNumber).matches()
: emergencyNumberPattern.matcher(normalizedNumber).lookingAt();
}
@ -333,7 +401,8 @@ public class ShortNumberInfo {
/**
* Given a valid short number, determines whether it is carrier-specific (however, nothing is
* implied about its validity). If it is important that the number is valid, then its validity
* must first be checked using {@link isValidShortNumber}.
* must first be checked using {@link #isValidShortNumber} or
* {@link #isValidShortNumberForRegion}.
*
* @param number the valid short number to check
* @return whether the short number is carrier-specific (assuming the input was a valid short


+ 4
- 2
java/libphonenumber/src/com/google/i18n/phonenumbers/ShortNumbersRegionCodeSet.java View File

@ -26,9 +26,9 @@ import java.util.Set;
public class ShortNumbersRegionCodeSet {
// A set of all region codes for which data is available.
static Set<String> getRegionCodeSet() {
// The capacity is set to 305 as there are 229 different entries,
// The capacity is set to 308 as there are 231 different entries,
// and this offers a load factor of roughly 0.75.
Set<String> regionCodeSet = new HashSet<String>(305);
Set<String> regionCodeSet = new HashSet<String>(308);
regionCodeSet.add("AC");
regionCodeSet.add("AD");
@ -68,6 +68,7 @@ public class ShortNumbersRegionCodeSet {
regionCodeSet.add("BZ");
regionCodeSet.add("CA");
regionCodeSet.add("CC");
regionCodeSet.add("CD");
regionCodeSet.add("CH");
regionCodeSet.add("CI");
regionCodeSet.add("CK");
@ -110,6 +111,7 @@ public class ShortNumbersRegionCodeSet {
regionCodeSet.add("GI");
regionCodeSet.add("GL");
regionCodeSet.add("GM");
regionCodeSet.add("GN");
regionCodeSet.add("GP");
regionCodeSet.add("GR");
regionCodeSet.add("GT");


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_AU View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BQ View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_BY View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CI View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CO View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_CW View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_GN View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_HN View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_IN View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_JO View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_KI View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_KW View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_MG View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_MK View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_MM View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_NA View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_PK View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_TC View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_TM View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/PhoneNumberMetadataProto_UG View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_AF View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_AG View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_AI View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_AM View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_AR View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_AS View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_AW View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_AZ View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BD View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BH View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BI View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BM View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BO View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BQ View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BT View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BW View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_BY View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_CA View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_CD View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_CH View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_CI View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_CM View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_CO View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_CW View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_DZ View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_FJ View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_GD View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_GE View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_GH View File


BIN
java/libphonenumber/src/com/google/i18n/phonenumbers/data/ShortNumberMetadataProto_GN View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save