summaryrefslogtreecommitdiff
path: root/phonenumberutil.cc
diff options
context:
space:
mode:
Diffstat (limited to 'phonenumberutil.cc')
-rw-r--r--phonenumberutil.cc348
1 files changed, 235 insertions, 113 deletions
diff --git a/phonenumberutil.cc b/phonenumberutil.cc
index 7946b04..a986009 100644
--- a/phonenumberutil.cc
+++ b/phonenumberutil.cc
@@ -17,9 +17,9 @@
#include "phonenumbers/phonenumberutil.h"
+#include <string.h>
#include <algorithm>
#include <cctype>
-#include <cstddef>
#include <fstream>
#include <iostream>
#include <iterator>
@@ -32,10 +32,10 @@
#include <unicode/uchar.h>
#include <unicode/utf8.h>
-#include "base/basictypes.h"
-#include "base/logging.h"
-#include "base/memory/singleton.h"
#include "phonenumbers/asyoutypeformatter.h"
+#include "phonenumbers/base/basictypes.h"
+#include "phonenumbers/base/logging.h"
+#include "phonenumbers/base/memory/singleton.h"
#include "phonenumbers/default_logger.h"
#include "phonenumbers/encoding_utils.h"
#include "phonenumbers/metadata.h"
@@ -73,10 +73,10 @@ const char PhoneNumberUtil::kPlusChars[] = "+\xEF\xBC\x8B"; /* "++" */
// unicode character.
// static
const char PhoneNumberUtil::kValidPunctuation[] =
- /* "-x‐-―−ー--/  <U+200B><U+2060> ()()[].\\[\\]/~⁓∼" */
+ /* "-x‐-―−ー--/  ­<U+200B><U+2060> ()()[].\\[\\]/~⁓∼" */
"-x\xE2\x80\x90-\xE2\x80\x95\xE2\x88\x92\xE3\x83\xBC\xEF\xBC\x8D-\xEF\xBC"
- "\x8F \xC2\xA0\xE2\x80\x8B\xE2\x81\xA0\xE3\x80\x80()\xEF\xBC\x88\xEF\xBC"
- "\x89\xEF\xBC\xBB\xEF\xBC\xBD.\\[\\]/~\xE2\x81\x93\xE2\x88\xBC";
+ "\x8F \xC2\xA0\xC2\xAD\xE2\x80\x8B\xE2\x81\xA0\xE3\x80\x80()\xEF\xBC\x88"
+ "\xEF\xBC\x89\xEF\xBC\xBB\xEF\xBC\xBD.\\[\\]/~\xE2\x81\x93\xE2\x88\xBC";
// static
const char PhoneNumberUtil::kCaptureUpToSecondNumberStart[] = "(.*)[\\\\/] *x";
@@ -97,9 +97,8 @@ const char kStarSign[] = "*";
const char kRfc3966ExtnPrefix[] = ";ext=";
const char kRfc3966Prefix[] = "tel:";
-// We include the "+" here since RFC3966 format specifies that the context must
-// be specified in international format.
-const char kRfc3966PhoneContext[] = ";phone-context=+";
+const char kRfc3966PhoneContext[] = ";phone-context=";
+const char kRfc3966IsdnSubaddress[] = ";isub=";
const char kDigits[] = "\\p{Nd}";
// We accept alpha characters in phone numbers, ASCII only. We store lower-case
@@ -164,17 +163,14 @@ void PrefixNumberWithCountryCallingCode(
string* formatted_number) {
switch (number_format) {
case PhoneNumberUtil::E164:
- formatted_number->insert(
- 0, StrCat(kPlusSign, SimpleItoa(country_calling_code)));
+ formatted_number->insert(0, StrCat(kPlusSign, country_calling_code));
return;
case PhoneNumberUtil::INTERNATIONAL:
- formatted_number->insert(
- 0, StrCat(kPlusSign, SimpleItoa(country_calling_code), " "));
+ formatted_number->insert(0, StrCat(kPlusSign, country_calling_code, " "));
return;
case PhoneNumberUtil::RFC3966:
- formatted_number->insert(
- 0, StrCat(kRfc3966Prefix, kPlusSign,
- SimpleItoa(country_calling_code), "-"));
+ formatted_number->insert(0, StrCat(kRfc3966Prefix, kPlusSign,
+ country_calling_code, "-"));
return;
case PhoneNumberUtil::NATIONAL:
default:
@@ -373,7 +369,8 @@ PhoneNumberUtil::ValidationResult TestNumberLengthAgainstPattern(
} // namespace
void PhoneNumberUtil::SetLogger(Logger* logger) {
- Logger::set_logger_impl(logger);
+ logger_.reset(logger);
+ Logger::set_logger_impl(logger_.get());
}
class PhoneNumberRegExpsAndMappings {
@@ -478,7 +475,10 @@ class PhoneNumberRegExpsAndMappings {
}
}
+ // Small string helpers since StrCat has a maximum number of arguments. These
+ // are both used to build valid_phone_number_.
const string punctuation_and_star_sign_;
+ const string min_length_phone_number_pattern_;
// Regular expression of viable phone numbers. This is location independent.
// Checks we have at least three leading digits, and only valid punctuation,
@@ -487,8 +487,15 @@ class PhoneNumberRegExpsAndMappings {
// used as a placeholder for carrier codes, for example in Brazilian phone
// numbers. We also allow multiple plus-signs at the start.
// Corresponds to the following:
+ // [digits]{minLengthNsn}|
// plus_sign*(([punctuation]|[star])*[digits]){3,}
// ([punctuation]|[star]|[digits]|[alpha])*
+ //
+ // The first reg-ex is to allow short numbers (two digits long) to be parsed
+ // if they are entered as "15" etc, but only if there is no punctuation in
+ // them. The second expression restricts the number of digits to three or
+ // more, but then allows them to be in international form, and to have
+ // alpha-characters and punctuation.
const string valid_phone_number_;
// Regexp of all possible ways to write extensions, for use when parsing. This
@@ -586,10 +593,13 @@ class PhoneNumberRegExpsAndMappings {
PhoneNumberRegExpsAndMappings()
: punctuation_and_star_sign_(StrCat(PhoneNumberUtil::kValidPunctuation,
kStarSign)),
+ min_length_phone_number_pattern_(
+ StrCat(kDigits, "{", PhoneNumberUtil::kMinLengthForNsn, "}")),
valid_phone_number_(
- StrCat("[", PhoneNumberUtil::kPlusChars, "]*(?:[",
- punctuation_and_star_sign_, "]*[",
- kDigits, "]){3,}[", kValidAlpha,
+ StrCat(min_length_phone_number_pattern_, "|[",
+ PhoneNumberUtil::kPlusChars, "]*(?:[",
+ punctuation_and_star_sign_, "]*",
+ kDigits, "){3,}[", kValidAlpha,
punctuation_and_star_sign_, kDigits,
"]*")),
extn_patterns_for_parsing_(
@@ -648,7 +658,7 @@ class PhoneNumberRegExpsAndMappings {
// Private constructor. Also takes care of initialisation.
PhoneNumberUtil::PhoneNumberUtil()
- : logger_(Logger::set_logger_impl(new StdoutLogger())),
+ : logger_(Logger::set_logger_impl(new NullLogger())),
reg_exps_(new PhoneNumberRegExpsAndMappings),
country_calling_code_to_region_code_map_(new vector<IntRegionsPair>()),
nanpa_regions_(new set<string>()),
@@ -730,16 +740,21 @@ void PhoneNumberUtil::GetSupportedRegions(set<string>* regions) const {
// Public wrapper function to get a PhoneNumberUtil instance with the default
// metadata file.
// static
-#ifdef USE_GOOGLE_BASE
PhoneNumberUtil* PhoneNumberUtil::GetInstance() {
- return Singleton<PhoneNumberUtil>::get();
+ return Singleton<PhoneNumberUtil>::GetInstance();
}
-#endif
const string& PhoneNumberUtil::GetExtnPatternsForMatching() const {
return reg_exps_->extn_patterns_for_matching_;
}
+bool PhoneNumberUtil::StartsWithPlusCharsPattern(const string& number)
+ const {
+ const scoped_ptr<RegExpInput> number_string_piece(
+ reg_exps_->regexp_factory_->CreateInput(number));
+ return reg_exps_->plus_chars_pattern_->Consume(number_string_piece.get());
+}
+
bool PhoneNumberUtil::ContainsOnlyValidDigits(const string& s) const {
return reg_exps_->digits_pattern_->FullMatch(s);
}
@@ -777,16 +792,28 @@ bool PhoneNumberUtil::IsFormatEligibleForAsYouTypeFormatter(
return eligible_format_pattern.FullMatch(format);
}
+bool PhoneNumberUtil::FormattingRuleHasFirstGroupOnly(
+ const string& national_prefix_formatting_rule) const {
+ // A pattern that is used to determine if the national prefix formatting rule
+ // has the first group only, i.e., does not start with the national prefix.
+ // Note that the pattern explicitly allows for unbalanced parentheses.
+ const RegExp& first_group_only_prefix_pattern =
+ reg_exps_->regexp_cache_->GetRegExp("\\(?\\$1\\)?");
+ return national_prefix_formatting_rule.empty() ||
+ first_group_only_prefix_pattern.FullMatch(
+ national_prefix_formatting_rule);
+}
+
void PhoneNumberUtil::GetNddPrefixForRegion(const string& region_code,
bool strip_non_digits,
string* national_prefix) const {
DCHECK(national_prefix);
- if (!IsValidRegionCode(region_code)) {
+ const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
+ if (!metadata) {
LOG(WARNING) << "Invalid or unknown region code (" << region_code
<< ") provided.";
return;
}
- const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
national_prefix->assign(metadata->national_prefix());
if (strip_non_digits) {
// Note: if any other non-numeric symbols are ever used in national
@@ -811,7 +838,8 @@ bool PhoneNumberUtil::HasValidCountryCallingCode(
target_pair, OrderByFirst()));
}
-// Returns a pointer to the phone metadata for the appropriate region.
+// Returns a pointer to the phone metadata for the appropriate region or NULL
+// if the region code is invalid or unknown.
const PhoneMetadata* PhoneNumberUtil::GetMetadataForRegion(
const string& region_code) const {
map<string, PhoneMetadata>::const_iterator it =
@@ -840,6 +868,11 @@ void PhoneNumberUtil::Format(const PhoneNumber& number,
if (number.national_number() == 0) {
const string& raw_input = number.raw_input();
if (!raw_input.empty()) {
+ // Unparseable numbers that kept their raw input just use that.
+ // This is the only case where a number can be formatted as E164 without a
+ // leading '+' symbol (but the original number wasn't parseable anyway).
+ // TODO: Consider removing the 'if' above so that unparseable
+ // strings without raw input format to the empty string instead of "+00".
formatted_number->assign(raw_input);
return;
}
@@ -848,23 +881,27 @@ void PhoneNumberUtil::Format(const PhoneNumber& number,
string national_significant_number;
GetNationalSignificantNumber(number, &national_significant_number);
if (number_format == E164) {
- // Early exit for E164 case since no formatting of the national number needs
- // to be applied. Extensions are not formatted.
+ // Early exit for E164 case (even if the country calling code is invalid)
+ // since no formatting of the national number needs to be applied.
+ // Extensions are not formatted.
formatted_number->assign(national_significant_number);
PrefixNumberWithCountryCallingCode(country_calling_code, E164,
formatted_number);
return;
}
+ if (!HasValidCountryCallingCode(country_calling_code)) {
+ formatted_number->assign(national_significant_number);
+ return;
+ }
// Note here that all NANPA formatting rules are contained by US, so we use
// that to format NANPA numbers. The same applies to Russian Fed regions -
// rules are contained by Russia. French Indian Ocean country rules are
// contained by Réunion.
string region_code;
GetRegionCodeForCountryCode(country_calling_code, &region_code);
- if (!HasValidCountryCallingCode(country_calling_code)) {
- formatted_number->assign(national_significant_number);
- return;
- }
+ // Metadata cannot be NULL because the country calling code is valid (which
+ // means that the region code cannot be ZZ and must be one of our supported
+ // region codes).
const PhoneMetadata* metadata =
GetMetadataForRegionOrCallingCode(country_calling_code, region_code);
FormatNsn(national_significant_number, *metadata, number_format,
@@ -886,14 +923,15 @@ void PhoneNumberUtil::FormatByPattern(
// for regions which share a country calling code is contained by only one
// region for performance reasons. For example, for NANPA regions it will be
// contained in the metadata for US.
- string region_code;
- GetRegionCodeForCountryCode(country_calling_code, &region_code);
string national_significant_number;
GetNationalSignificantNumber(number, &national_significant_number);
if (!HasValidCountryCallingCode(country_calling_code)) {
formatted_number->assign(national_significant_number);
return;
}
+ string region_code;
+ GetRegionCodeForCountryCode(country_calling_code, &region_code);
+ // Metadata cannot be NULL because the country calling code is valid.
const PhoneMetadata* metadata =
GetMetadataForRegionOrCallingCode(country_calling_code, region_code);
const NumberFormat* formatting_pattern =
@@ -941,15 +979,18 @@ void PhoneNumberUtil::FormatNationalNumberWithCarrierCode(
int country_calling_code = number.country_code();
string national_significant_number;
GetNationalSignificantNumber(number, &national_significant_number);
+ if (!HasValidCountryCallingCode(country_calling_code)) {
+ formatted_number->assign(national_significant_number);
+ return;
+ }
+
// Note GetRegionCodeForCountryCode() is used because formatting information
// for regions which share a country calling code is contained by only one
// region for performance reasons. For example, for NANPA regions it will be
// contained in the metadata for US.
string region_code;
GetRegionCodeForCountryCode(country_calling_code, &region_code);
- if (!HasValidCountryCallingCode(country_calling_code)) {
- formatted_number->assign(national_significant_number);
- }
+ // Metadata cannot be NULL because the country calling code is valid.
const PhoneMetadata* metadata =
GetMetadataForRegionOrCallingCode(country_calling_code, region_code);
FormatNsnWithCarrier(national_significant_number, *metadata, NATIONAL,
@@ -989,48 +1030,76 @@ void PhoneNumberUtil::FormatNumberForMobileDialing(
return;
}
+ formatted_number->assign("");
// Clear the extension, as that part cannot normally be dialed together with
// the main number.
PhoneNumber number_no_extension(number);
number_no_extension.clear_extension();
- PhoneNumberType number_type = GetNumberType(number_no_extension);
string region_code;
GetRegionCodeForCountryCode(country_calling_code, &region_code);
- if ((region_code == "CO") && (calling_from == "CO")) {
- if (number_type == FIXED_LINE) {
+ 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);
+ // Carrier codes may be needed in some countries. We handle this here.
+ if ((region_code == "CO") && (number_type == FIXED_LINE)) {
FormatNationalNumberWithCarrierCode(
number_no_extension, kColombiaMobileToFixedLinePrefix,
formatted_number);
- } else {
- // E164 doesn't work at all when dialing within Colombia.
- Format(number_no_extension, NATIONAL, formatted_number);
- }
- } else if ((region_code == "PE") && (calling_from == "PE")) {
- // In Peru, numbers cannot be dialled using E164 format from a mobile phone
- // for Movistar. Instead they must be dialled in national format.
- Format(number_no_extension, NATIONAL, formatted_number);
- } else if ((region_code == "BR") && (calling_from == "BR") &&
- ((number_type == FIXED_LINE) || (number_type == MOBILE) ||
- (number_type == FIXED_LINE_OR_MOBILE))) {
- if (number_no_extension.has_preferred_domestic_carrier_code()) {
+ } else if ((region_code == "BR") && (is_fixed_line_or_mobile)) {
+ if (number_no_extension.has_preferred_domestic_carrier_code()) {
FormatNationalNumberWithPreferredCarrierCode(number_no_extension, "",
formatted_number);
+ } else {
+ // Brazilian fixed line and mobile numbers need to be dialed with a
+ // carrier code when called within Brazil. Without that, most of the
+ // carriers won't connect the call. Because of that, we return an empty
+ // string here.
+ formatted_number->assign("");
+ }
+ } else if (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.
+ 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 {
- // Brazilian fixed line and mobile numbers need to be dialed with a
- // carrier code when called within Brazil. Without that, most of the
- // carriers won't connect the call. Because of that, we return an empty
- // string here.
- formatted_number->assign("");
+ // 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 ||
+ // 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 same
+ // local area. It is trickier to get that to work correctly than
+ // using international format, which is tested to work fine on all
+ // carriers.
+ // CL fixed line numbers need the national prefix when dialing in the
+ // national format, but don't have it when used for display. The
+ // reverse is true for mobile numbers. As a result, we output them in
+ // the international format to make it work.
+ ((region_code == "MX" || region_code == "CL") &&
+ is_fixed_line_or_mobile)) &&
+ CanBeInternationallyDialled(number_no_extension)) {
+ Format(number_no_extension, INTERNATIONAL, formatted_number);
+ } else {
+ Format(number_no_extension, NATIONAL, formatted_number);
+ }
+
}
} else if (CanBeInternationallyDialled(number_no_extension)) {
with_formatting
? Format(number_no_extension, INTERNATIONAL, formatted_number)
: Format(number_no_extension, E164, formatted_number);
return;
- } else if (calling_from == region_code) {
- Format(number_no_extension, NATIONAL, formatted_number);
- } else {
- formatted_number->assign("");
}
if (!with_formatting) {
NormalizeHelper(reg_exps_->diallable_char_mappings_,
@@ -1081,14 +1150,12 @@ void PhoneNumberUtil::FormatOutOfCountryCallingNumber(
Format(number, NATIONAL, formatted_number);
return;
}
- string region_code;
- GetRegionCodeForCountryCode(country_code, &region_code);
- const PhoneMetadata* metadata_for_region =
- GetMetadataForRegionOrCallingCode(country_code, region_code);
+ // Metadata cannot be NULL because we checked 'IsValidRegionCode()' above.
const PhoneMetadata* metadata_calling_from =
GetMetadataForRegion(calling_from);
const string& international_prefix =
metadata_calling_from->international_prefix();
+
// For regions that have multiple international prefixes, the international
// format of the number is returned, unless there is a preferred international
// prefix.
@@ -1096,6 +1163,12 @@ void PhoneNumberUtil::FormatOutOfCountryCallingNumber(
reg_exps_->unique_international_prefix_->FullMatch(international_prefix)
? international_prefix
: metadata_calling_from->preferred_international_prefix());
+
+ string region_code;
+ GetRegionCodeForCountryCode(country_code, &region_code);
+ // Metadata cannot be NULL because the country_code is valid.
+ const PhoneMetadata* metadata_for_region =
+ GetMetadataForRegionOrCallingCode(country_code, region_code);
FormatNsn(national_significant_number, *metadata_for_region, INTERNATIONAL,
formatted_number);
MaybeAppendFormattedExtension(number, *metadata_for_region, INTERNATIONAL,
@@ -1163,6 +1236,8 @@ void PhoneNumberUtil::FormatInOriginalFormat(const PhoneNumber& number,
Format(number, NATIONAL, formatted_number);
break;
}
+ // Metadata cannot be NULL here because GetNddPrefixForRegion() (above)
+ // leaves the prefix empty if there is no metadata for the region.
const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
string national_number;
GetNationalSignificantNumber(number, &national_number);
@@ -1171,6 +1246,14 @@ void PhoneNumberUtil::FormatInOriginalFormat(const PhoneNumber& number,
const NumberFormat* format_rule =
ChooseFormattingPatternForNumber(metadata->number_format(),
national_number);
+ // The format rule could still be NULL here if the national number was 0
+ // and there was no raw input (this should not be possible for numbers
+ // generated by the phonenumber library as they would also not have a
+ // country calling code and we would have exited earlier).
+ if (!format_rule) {
+ Format(number, NATIONAL, formatted_number);
+ break;
+ }
// When the format we apply to this number doesn't contain national
// prefix, we can just return the national format.
// TODO: Refactor the code below with the code in
@@ -1200,12 +1283,15 @@ void PhoneNumberUtil::FormatInOriginalFormat(const PhoneNumber& number,
// If no digit is inserted/removed/modified as a result of our formatting, we
// return the formatted phone number; otherwise we return the raw input the
// user entered.
- if (!formatted_number->empty()) {
- string formatted_number_copy(*formatted_number);
- NormalizeDigitsOnly(&formatted_number_copy);
- string raw_input_copy(number.raw_input());
- NormalizeDigitsOnly(&raw_input_copy);
- if (formatted_number_copy != raw_input_copy) {
+ if (!formatted_number->empty() && !number.raw_input().empty()) {
+ string normalized_formatted_number(*formatted_number);
+ NormalizeHelper(reg_exps_->diallable_char_mappings_,
+ true /* remove non matches */,
+ &normalized_formatted_number);
+ string normalized_raw_input(number.raw_input());
+ NormalizeHelper(reg_exps_->diallable_char_mappings_,
+ true /* remove non matches */, &normalized_raw_input);
+ if (normalized_formatted_number != normalized_raw_input) {
formatted_number->assign(number.raw_input());
}
}
@@ -1299,7 +1385,7 @@ void PhoneNumberUtil::FormatOutOfCountryKeepingAlphaChars(
StrAppend(formatted_number, country_code, " ", raw_input_copy);
return;
}
- } else if (IsValidRegionCode(calling_from) &&
+ } else if (metadata &&
country_code == GetCountryCodeForValidRegion(calling_from)) {
const NumberFormat* formatting_pattern =
ChooseFormattingPatternForNumber(metadata->number_format(),
@@ -1571,6 +1657,8 @@ void PhoneNumberUtil::GetRegionCodeForNumberFromRegionList(
GetNationalSignificantNumber(number, &national_number);
for (list<string>::const_iterator it = region_codes.begin();
it != region_codes.end(); ++it) {
+ // Metadata cannot be NULL because the region codes come from the country
+ // calling code map.
const PhoneMetadata* metadata = GetMetadataForRegion(*it);
if (metadata->has_leading_digits()) {
const scoped_ptr<RegExpInput> number(
@@ -1698,39 +1786,71 @@ bool PhoneNumberUtil::CheckRegionForParsing(
return true;
}
-PhoneNumberUtil::ErrorType PhoneNumberUtil::ParseHelper(
- const string& number_to_parse,
- const string& default_region,
- bool keep_raw_input,
- bool check_region,
- PhoneNumber* phone_number) const {
- DCHECK(phone_number);
-
+// Converts number_to_parse to a form that we can parse and write it to
+// national_number if it is written in RFC3966; otherwise extract a possible
+// number out of it and write to national_number.
+void PhoneNumberUtil::BuildNationalNumberForParsing(
+ const string& number_to_parse, string* national_number) const {
size_t index_of_phone_context = number_to_parse.find(kRfc3966PhoneContext);
- string national_number;
if (index_of_phone_context != string::npos) {
- // Prefix the number with the phone context. The offset here is because the
- // context we are expecting to match should start with a "+" sign, and we
- // want to include this at the start of the number.
- StrAppend(
- &national_number,
- number_to_parse.substr(
- index_of_phone_context + strlen(kRfc3966PhoneContext) - 1));
+ int phone_context_start =
+ index_of_phone_context + strlen(kRfc3966PhoneContext);
+ // If the phone context contains a phone number prefix, we need to capture
+ // it, whereas domains will be ignored.
+ if (number_to_parse.at(phone_context_start) == kPlusSign[0]) {
+ // Additional parameters might follow the phone context. If so, we will
+ // remove them here because the parameters after phone context are not
+ // important for parsing the phone number.
+ size_t phone_context_end = number_to_parse.find(';', phone_context_start);
+ if (phone_context_end != string::npos) {
+ StrAppend(
+ national_number, number_to_parse.substr(
+ phone_context_start, phone_context_end - phone_context_start));
+ } else {
+ StrAppend(national_number, number_to_parse.substr(phone_context_start));
+ }
+ }
+
// Now append everything between the "tel:" prefix and the phone-context.
+ // This should include the national number, an optional extension or
+ // isdn-subaddress component.
int end_of_rfc_prefix =
number_to_parse.find(kRfc3966Prefix) + strlen(kRfc3966Prefix);
StrAppend(
- &national_number,
+ national_number,
number_to_parse.substr(end_of_rfc_prefix,
index_of_phone_context - end_of_rfc_prefix));
- // Note that phone-contexts that are URLs will not be parsed -
- // IsViablePhoneNumber will throw an exception below.
} else {
// Extract a possible number from the string passed in (this strips leading
// characters that could not be the start of a phone number.)
- ExtractPossibleNumber(number_to_parse, &national_number);
+ ExtractPossibleNumber(number_to_parse, national_number);
}
+ // Delete the isdn-subaddress and everything after it if it is present. Note
+ // extension won't appear at the same time with isdn-subaddress according to
+ // paragraph 5.3 of the RFC3966 spec.
+ size_t index_of_isdn = national_number->find(kRfc3966IsdnSubaddress);
+ if (index_of_isdn != string::npos) {
+ national_number->erase(index_of_isdn);
+ }
+ // If both phone context and isdn-subaddress are absent but other parameters
+ // are present, the parameters are left in nationalNumber. This is because
+ // we are concerned about deleting content from a potential number string
+ // when there is no strong evidence that the number is actually written in
+ // RFC3966.
+}
+
+PhoneNumberUtil::ErrorType PhoneNumberUtil::ParseHelper(
+ const string& number_to_parse,
+ const string& default_region,
+ bool keep_raw_input,
+ bool check_region,
+ PhoneNumber* phone_number) const {
+ DCHECK(phone_number);
+
+ string national_number;
+ BuildNationalNumberForParsing(number_to_parse, &national_number);
+
if (!IsViablePhoneNumber(national_number)) {
VLOG(2) << "The string supplied did not seem to be a phone number.";
return NOT_A_NUMBER;
@@ -1902,6 +2022,7 @@ PhoneNumberUtil::ValidationResult PhoneNumberUtil::IsPossibleNumberWithReason(
}
string region_code;
GetRegionCodeForCountryCode(country_code, &region_code);
+ // Metadata cannot be NULL because the country calling code is valid.
const PhoneMetadata* metadata =
GetMetadataForRegionOrCallingCode(country_code, region_code);
const PhoneNumberDesc& general_num_desc = metadata->general_desc();
@@ -1944,14 +2065,13 @@ PhoneNumberUtil::PhoneNumberType PhoneNumberUtil::GetNumberType(
const PhoneNumber& number) const {
string region_code;
GetRegionCodeForNumber(number, &region_code);
- if (!IsValidRegionCode(region_code) &&
- kRegionCodeForNonGeoEntity != region_code) {
+ const PhoneMetadata* metadata =
+ GetMetadataForRegionOrCallingCode(number.country_code(), region_code);
+ if (!metadata) {
return UNKNOWN;
}
string national_significant_number;
GetNationalSignificantNumber(number, &national_significant_number);
- const PhoneMetadata* metadata =
- GetMetadataForRegionOrCallingCode(number.country_code(), region_code);
return GetNumberTypeHelper(national_significant_number,
*metadata,
reg_exps_->regexp_cache_.get());
@@ -1993,11 +2113,20 @@ bool PhoneNumberUtil::IsValidNumberForRegion(const PhoneNumber& number,
reg_exps_->regexp_cache_.get()) != UNKNOWN;
}
+bool PhoneNumberUtil::IsNumberGeographical(
+ const PhoneNumber& phone_number) const {
+ PhoneNumberType number_type = GetNumberType(phone_number);
+ // TODO: Include mobile phone numbers from countries like
+ // Indonesia, which has some mobile numbers that are geographical.
+ return number_type == PhoneNumberUtil::FIXED_LINE ||
+ number_type == PhoneNumberUtil::FIXED_LINE_OR_MOBILE;
+}
+
bool PhoneNumberUtil::IsLeadingZeroPossible(int country_calling_code) const {
string region_code;
GetRegionCodeForCountryCode(country_calling_code, &region_code);
- const PhoneMetadata* main_metadata_for_calling_code = GetMetadataForRegion(
- region_code);
+ const PhoneMetadata* main_metadata_for_calling_code =
+ GetMetadataForRegionOrCallingCode(country_calling_code, region_code);
if (!main_metadata_for_calling_code) return false;
return main_metadata_for_calling_code->leading_zero_possible();
}
@@ -2016,11 +2145,10 @@ int PhoneNumberUtil::GetLengthOfGeographicalAreaCode(
const PhoneNumber& number) const {
string region_code;
GetRegionCodeForNumber(number, &region_code);
- if (!IsValidRegionCode(region_code)) {
+ const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
+ if (!metadata) {
return 0;
}
- const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
- DCHECK(metadata);
// If a country doesn't use a national prefix, and this number doesn't have an
// Italian leading zero, we assume it is a closed dialling plan with no area
// codes.
@@ -2028,13 +2156,7 @@ int PhoneNumberUtil::GetLengthOfGeographicalAreaCode(
return 0;
}
- string national_significant_number;
- GetNationalSignificantNumber(number, &national_significant_number);
- PhoneNumberType type = GetNumberTypeHelper(national_significant_number,
- *metadata,
- reg_exps_->regexp_cache_.get());
- // Most numbers other than the two types below have to be dialled in full.
- if (type != FIXED_LINE && type != FIXED_LINE_OR_MOBILE) {
+ if (!IsNumberGeographical(number)) {
return 0;
}
@@ -2390,7 +2512,7 @@ PhoneNumberUtil::ErrorType PhoneNumberUtil::MaybeExtractCountryCode(
phone_number->set_country_code_source(country_code_source);
}
if (country_code_source != PhoneNumber::FROM_DEFAULT_COUNTRY) {
- if (national_number->length() < kMinLengthForNsn) {
+ if (national_number->length() <= kMinLengthForNsn) {
VLOG(2) << "Phone number had an IDD, but after this was not "
<< "long enough to be a viable phone number.";
return TOO_SHORT_AFTER_IDD;
@@ -2593,12 +2715,12 @@ bool PhoneNumberUtil::CanBeInternationallyDialled(
const PhoneNumber& number) const {
string region_code;
GetRegionCodeForNumber(number, &region_code);
- if (!IsValidRegionCode(region_code)) {
+ const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
+ if (!metadata) {
// Note numbers belonging to non-geographical entities (e.g. +800 numbers)
// are always internationally diallable, and will be caught here.
return true;
}
- const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
string national_significant_number;
GetNationalSignificantNumber(number, &national_significant_number);
return !IsNumberMatchingDesc(