#include "phonenumbers/phonenumberutil.h"
+#include <string.h>
#include <algorithm>
#include <cctype>
-#include <cstddef>
-#include <fstream>
-#include <iostream>
#include <iterator>
#include <map>
-#include <sstream>
#include <utility>
#include <vector>
#include <unicode/uchar.h>
#include <unicode/utf8.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"
namespace i18n {
namespace phonenumbers {
-using std::cerr;
-using std::endl;
-using std::ifstream;
-using std::make_pair;
-using std::sort;
-using std::stringstream;
-
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
// 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";
-namespace {
-
-scoped_ptr<Logger> logger_;
-scoped_ptr<const AbstractRegExpFactory> regexp_factory;
-scoped_ptr<RegExpCache> regexp_cache;
-
-// These objects are created in the function InitializeStaticMapsAndSets.
+// static
+const char PhoneNumberUtil::kRegionCodeForNonGeoEntity[] = "001";
-// These mappings map a character (key) to a specific digit that should replace
-// it for normalization purposes.
-scoped_ptr<map<char32, char> > alpha_mappings;
-// For performance reasons, store a map of combining alpha_mappings with ASCII
-// digits.
-scoped_ptr<map<char32, char> > alpha_phone_mappings;
-// Separate map of all symbols that we wish to retain when formatting alpha
-// numbers. This includes digits, ascii letters and number grouping symbols such
-// as "-" and " ".
-scoped_ptr<map<char32, char> > all_plus_number_grouping_symbols;
+namespace {
// The prefix that needs to be inserted in front of a Colombian landline
// number when dialed from a mobile phone in Colombia.
// The kPlusSign signifies the international prefix.
const char kPlusSign[] = "+";
-scoped_ptr<const RegExp> plus_chars_pattern;
+const char kStarSign[] = "*";
const char kRfc3966ExtnPrefix[] = ";ext=";
-
-// Pattern that makes it easy to distinguish whether a region has a unique
-// international dialing prefix or not. If a region has a unique international
-// prefix (e.g. 011 in USA), it will be represented as a string that contains a
-// sequence of ASCII digits. If there are multiple available international
-// prefixes in a region, they will be represented as a regex string that always
-// contains character(s) other than ASCII digits.
-// Note this regex also includes tilde, which signals waiting for the tone.
-scoped_ptr<const RegExp> unique_international_prefix;
+const char kRfc3966Prefix[] = "tel:";
+const char kRfc3966PhoneContext[] = ";phone-context=";
+const char kRfc3966IsdnSubaddress[] = ";isub=";
const char kDigits[] = "\\p{Nd}";
-scoped_ptr<const RegExp> digits_pattern;
// We accept alpha characters in phone numbers, ASCII only. We store lower-case
// here only since our regular expressions are case-insensitive.
const char kValidAlpha[] = "a-z";
-scoped_ptr<const RegExp> capturing_digit_pattern;
-scoped_ptr<const RegExp> capturing_ascii_digits_pattern;
-
-// Regular expression of acceptable characters that may start a phone number
-// for the purposes of parsing. This allows us to strip away meaningless
-// prefixes to phone numbers that may be mistakenly given to us. This
-// consists of digits, the plus symbol and arabic-indic digits. This does
-// not contain alpha characters, although they may be used later in the
-// number. It also does not include other punctuation, as this will be
-// stripped later during parsing and is of no information value when parsing
-// a number. The string starting with this valid character is captured.
-// This corresponds to VALID_START_CHAR in the java version.
-scoped_ptr<const string> valid_start_char;
-scoped_ptr<const RegExp> valid_start_char_pattern;
-
-// Regular expression of valid characters before a marker that might indicate a
-// second number.
-scoped_ptr<const RegExp> capture_up_to_second_number_start_pattern;
-
-// Regular expression of trailing characters that we want to remove. We remove
-// all characters that are not alpha or numerical characters. The hash
-// character is retained here, as it may signify the previous block was an
-// extension. Note the capturing block at the start to capture the rest of the
-// number if this was a match.
-// This corresponds to UNWANTED_END_CHARS in the java version.
-const char kUnwantedEndChar[] = "[^\\p{N}\\p{L}#]";
-scoped_ptr<const RegExp> unwanted_end_char_pattern;
-
-// Regular expression of groups of valid punctuation characters.
-scoped_ptr<const RegExp> separator_pattern;
-
-// Regular expression of viable phone numbers. This is location independent.
-// Checks we have at least three leading digits, and only valid punctuation,
-// alpha characters and digits in the phone number. Does not include extension
-// data. The symbol 'x' is allowed here as valid punctuation since it is often
-// 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:
-// plus_sign*([punctuation]*[digits]){3,}([punctuation]|[digits]|[alpha])*
-scoped_ptr<const string> valid_phone_number;
// Default extension prefix to use when formatting. This will be put in front of
// any extension component of the number, after the main national number is
// One-character symbols that can be used to indicate an extension.
const char kSingleExtnSymbolsForMatching[] =
"x\xEF\xBD\x98#\xEF\xBC\x83~\xEF\xBD\x9E";
-// Regexp of all possible ways to write extensions, for use when parsing. This
-// will be run as a case-insensitive regexp match. Wide character versions are
-// also provided after each ASCII version.
-scoped_ptr<const string> extn_patterns_for_parsing;
-scoped_ptr<const string> extn_patterns_for_matching;
-// Regexp of all known extension prefixes used by different regions followed
-// by 1 or more valid digits, for use when parsing.
-scoped_ptr<const RegExp> extn_pattern;
-
-// We append optionally the extension pattern to the end here, as a valid phone
-// number may have an extension prefix appended, followed by 1 or more digits.
-scoped_ptr<const RegExp> valid_phone_number_pattern;
-
-// We use this pattern to check if the phone number has at least three letters
-// in it - if so, then we treat it as a number where some phone-number digits
-// are represented by letters.
-scoped_ptr<const RegExp> valid_alpha_phone_pattern;
-
-scoped_ptr<const RegExp> first_group_capturing_pattern;
-
-scoped_ptr<const RegExp> carrier_code_pattern;
bool LoadCompiledInMetadata(PhoneMetadataCollection* metadata) {
if (!metadata->ParseFromArray(metadata_get(), metadata_size())) {
- cerr << "Could not parse binary data." << endl;
+ LOG(ERROR) << "Could not parse binary data.";
return false;
}
return true;
return &metadata.pager();
case PhoneNumberUtil::UAN:
return &metadata.uan();
+ case PhoneNumberUtil::VOICEMAIL:
+ return &metadata.voicemail();
default:
return &metadata.general_desc();
}
}
// A helper function that is used by Format and FormatByPattern.
-void FormatNumberByFormat(int country_calling_code,
- PhoneNumberUtil::PhoneNumberFormat number_format,
- const string& formatted_national_number,
- const string& formatted_extension,
- string* formatted_number) {
+void PrefixNumberWithCountryCallingCode(
+ int country_calling_code,
+ PhoneNumberUtil::PhoneNumberFormat number_format,
+ string* formatted_number) {
switch (number_format) {
case PhoneNumberUtil::E164:
- formatted_number->assign(StrCat(kPlusSign,
- SimpleItoa(country_calling_code),
- formatted_national_number,
- formatted_extension));
+ formatted_number->insert(0, StrCat(kPlusSign, country_calling_code));
return;
case PhoneNumberUtil::INTERNATIONAL:
- formatted_number->assign(StrCat(kPlusSign,
- SimpleItoa(country_calling_code),
- " ",
- formatted_national_number,
- formatted_extension));
+ formatted_number->insert(0, StrCat(kPlusSign, country_calling_code, " "));
return;
case PhoneNumberUtil::RFC3966:
- formatted_number->assign(StrCat(kPlusSign,
- SimpleItoa(country_calling_code),
- "-",
- formatted_national_number,
- formatted_extension));
+ formatted_number->insert(0, StrCat(kRfc3966Prefix, kPlusSign,
+ country_calling_code, "-"));
return;
case PhoneNumberUtil::NATIONAL:
default:
- formatted_number->assign(StrCat(formatted_national_number,
- formatted_extension));
- }
-}
-
-// The number_for_leading_digits_match is a separate parameter, because for
-// alpha numbers we want to pass in the numeric version to select the right
-// formatting rule, but then we actually apply the formatting pattern to the
-// national_number (which in this case has alpha characters in it).
-//
-// Note that carrier_code is optional - if an empty string, no carrier code
-// replacement will take place.
-void FormatAccordingToFormatsWithCarrier(
- const string& number_for_leading_digits_match,
- const RepeatedPtrField<NumberFormat>& available_formats,
- PhoneNumberUtil::PhoneNumberFormat number_format,
- const string& national_number,
- const string& carrier_code,
- string* formatted_number) {
- DCHECK(formatted_number);
- for (RepeatedPtrField<NumberFormat>::const_iterator
- it = available_formats.begin(); it != available_formats.end(); ++it) {
- int size = it->leading_digits_pattern_size();
- if (size > 0) {
- const scoped_ptr<RegExpInput> number_copy(
- regexp_factory->CreateInput(number_for_leading_digits_match));
- // We always use the last leading_digits_pattern, as it is the most
- // detailed.
- if (!regexp_cache->GetRegExp(it->leading_digits_pattern(size - 1))
- .Consume(number_copy.get())) {
- continue;
- }
- }
- const RegExp& pattern_to_match(regexp_cache->GetRegExp(it->pattern()));
- if (pattern_to_match.FullMatch(national_number)) {
- string formatting_pattern(it->format());
- if (number_format == PhoneNumberUtil::NATIONAL &&
- carrier_code.length() > 0 &&
- it->domestic_carrier_code_formatting_rule().length() > 0) {
- // Replace the $CC in the formatting rule with the desired carrier code.
- string carrier_code_formatting_rule =
- it->domestic_carrier_code_formatting_rule();
- carrier_code_pattern->Replace(&carrier_code_formatting_rule,
- carrier_code);
- first_group_capturing_pattern->Replace(&formatting_pattern,
- carrier_code_formatting_rule);
- } else {
- // Use the national prefix formatting rule instead.
- string national_prefix_formatting_rule =
- it->national_prefix_formatting_rule();
- if (number_format == PhoneNumberUtil::NATIONAL &&
- national_prefix_formatting_rule.length() > 0) {
- // Apply the national_prefix_formatting_rule as the formatting_pattern
- // contains only information on how the national significant number
- // should be formatted at this point.
- first_group_capturing_pattern->Replace(
- &formatting_pattern, national_prefix_formatting_rule);
- }
- }
- formatted_number->assign(national_number);
- pattern_to_match.GlobalReplace(formatted_number, formatting_pattern);
+ // Do nothing.
return;
- }
}
- // If no pattern above is matched, we format the number as a whole.
- formatted_number->assign(national_number);
-}
-
-// Simple wrapper of FormatAccordingToFormatsWithCarrier for the common case of
-// no carrier code.
-void FormatAccordingToFormats(
- const string& number_for_leading_digits_match,
- const RepeatedPtrField<NumberFormat>& available_formats,
- PhoneNumberUtil::PhoneNumberFormat number_format,
- const string& national_number,
- string* formatted_number) {
- DCHECK(formatted_number);
- FormatAccordingToFormatsWithCarrier(number_for_leading_digits_match,
- available_formats, number_format,
- national_number, "", formatted_number);
}
// Returns true when one national number is the suffix of the other or both are
first_number_national_number);
}
-bool IsNumberMatchingDesc(const string& national_number,
- const PhoneNumberDesc& number_desc) {
- 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) {
- 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;
- }
-
- 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;
-}
-
char32 ToUnicodeCodepoint(const char* unicode_char) {
char32 codepoint;
EncodingUtils::DecodeUTF8Char(unicode_char, &codepoint);
return codepoint;
}
-void InitializeStaticMapsAndSets() {
- // Create global objects.
- regexp_factory.reset(new RegExpFactory());
- regexp_cache.reset(new RegExpCache(*regexp_factory.get(), 128));
- all_plus_number_grouping_symbols.reset(new map<char32, char>);
- alpha_mappings.reset(new map<char32, char>);
- alpha_phone_mappings.reset(new map<char32, char>);
-
- // Punctuation that we wish to respect in alpha numbers, as they show number
- // groupings are mapped here.
- all_plus_number_grouping_symbols->insert(
- make_pair(ToUnicodeCodepoint("-"), '-'));
- all_plus_number_grouping_symbols->insert(
- make_pair(ToUnicodeCodepoint("\xEF\xBC\x8D" /* "-" */), '-'));
- all_plus_number_grouping_symbols->insert(
- make_pair(ToUnicodeCodepoint("\xE2\x80\x90" /* "‐" */), '-'));
- all_plus_number_grouping_symbols->insert(
- make_pair(ToUnicodeCodepoint("\xE2\x80\x91" /* "‑" */), '-'));
- all_plus_number_grouping_symbols->insert(
- make_pair(ToUnicodeCodepoint("\xE2\x80\x92" /* "‒" */), '-'));
- all_plus_number_grouping_symbols->insert(
- make_pair(ToUnicodeCodepoint("\xE2\x80\x93" /* "–" */), '-'));
- all_plus_number_grouping_symbols->insert(
- make_pair(ToUnicodeCodepoint("\xE2\x80\x94" /* "—" */), '-'));
- all_plus_number_grouping_symbols->insert(
- make_pair(ToUnicodeCodepoint("\xE2\x80\x95" /* "―" */), '-'));
- all_plus_number_grouping_symbols->insert(
- make_pair(ToUnicodeCodepoint("\xE2\x88\x92" /* "−" */), '-'));
- all_plus_number_grouping_symbols->insert(
- make_pair(ToUnicodeCodepoint("/"), '/'));
- all_plus_number_grouping_symbols->insert(
- make_pair(ToUnicodeCodepoint("\xEF\xBC\x8F" /* "/" */), '/'));
- all_plus_number_grouping_symbols->insert(
- make_pair(ToUnicodeCodepoint(" "), ' '));
- all_plus_number_grouping_symbols->insert(
- make_pair(ToUnicodeCodepoint("\xE3\x80\x80" /* " " */), ' '));
- all_plus_number_grouping_symbols->insert(
- make_pair(ToUnicodeCodepoint("\xE2\x81\xA0"), ' '));
- all_plus_number_grouping_symbols->insert(
- make_pair(ToUnicodeCodepoint("."), '.'));
- all_plus_number_grouping_symbols->insert(
- make_pair(ToUnicodeCodepoint("\xEF\xBC\x8E" /* "." */), '.'));
- // Only the upper-case letters are added here - the lower-case versions are
- // added programmatically.
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("A"), '2'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("B"), '2'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("C"), '2'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("D"), '3'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("E"), '3'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("F"), '3'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("G"), '4'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("H"), '4'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("I"), '4'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("J"), '5'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("K"), '5'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("L"), '5'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("M"), '6'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("N"), '6'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("O"), '6'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("P"), '7'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("Q"), '7'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("R"), '7'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("S"), '7'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("T"), '8'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("U"), '8'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("V"), '8'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("W"), '9'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("X"), '9'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("Y"), '9'));
- alpha_mappings->insert(make_pair(ToUnicodeCodepoint("Z"), '9'));
- map<char32, char> lower_case_mappings;
- map<char32, char> alpha_letters;
- for (map<char32, char>::const_iterator it = alpha_mappings->begin();
- it != alpha_mappings->end();
- ++it) {
- // Convert all the upper-case ASCII letters to lower-case.
- if (it->first < 128) {
- char letter_as_upper = static_cast<char>(it->first);
- char32 letter_as_lower = static_cast<char32>(tolower(letter_as_upper));
- lower_case_mappings.insert(make_pair(letter_as_lower, it->second));
- // Add the letters in both variants to the alpha_letters map. This just
- // pairs each letter with its upper-case representation so that it can be
- // retained when normalising alpha numbers.
- alpha_letters.insert(make_pair(letter_as_lower, letter_as_upper));
- alpha_letters.insert(make_pair(it->first, letter_as_upper));
- }
- }
- // In the Java version we don't insert the lower-case mappings in the map,
- // because we convert to upper case on the fly. Doing this here would involve
- // pulling in all of ICU, which we don't want to do if we don't have to.
- alpha_mappings->insert(lower_case_mappings.begin(),
- lower_case_mappings.end());
- alpha_phone_mappings->insert(alpha_mappings->begin(),
- alpha_mappings->end());
- all_plus_number_grouping_symbols->insert(alpha_letters.begin(),
- alpha_letters.end());
- // Add the ASCII digits so that they don't get deleted by NormalizeHelper().
- for (char c = '0'; c <= '9'; ++c) {
- alpha_phone_mappings->insert(make_pair(c, c));
- all_plus_number_grouping_symbols->insert(make_pair(c, c));
- }
-}
-
// Helper initialiser method to create the regular-expression pattern to match
// extensions, allowing the one-codepoint extension symbols provided by
// single_extn_symbols.
// unicode decomposed form with the combining acute accent.
return (StrCat(
kRfc3966ExtnPrefix, capturing_extn_digits, "|"
- /* "[ \\t,]*(?:ext(?:ensi(?:ó?|ó))?n?|extn?|single_extn_symbols|"
+ /* "[ \\t,]*(?:e?xt(?:ensi(?:ó?|ó))?n?|e?xtn?|single_extn_symbols|"
"int|int|anexo)"
"[:\\..]?[ \\t,-]*", capturing_extn_digits, "#?|" */
- "[ \xC2\xA0\\t,]*(?:ext(?:ensi(?:o\xCC\x81?|\xC3\xB3))?n?|\xEF\xBD"
- "\x85\xEF\xBD\x98\xEF\xBD\x94\xEF\xBD\x8E?|"
+ "[ \xC2\xA0\\t,]*(?:e?xt(?:ensi(?:o\xCC\x81?|\xC3\xB3))?n?|"
+ "(?:\xEF\xBD\x85)?\xEF\xBD\x98\xEF\xBD\x94(?:\xEF\xBD\x8E)?|"
"[", single_extn_symbols, "]|int|"
"\xEF\xBD\x89\xEF\xBD\x8E\xEF\xBD\x94|anexo)"
"[:\\.\xEF\xBC\x8E]?[ \xC2\xA0\\t,-]*", capturing_extn_digits,
} // namespace
void PhoneNumberUtil::SetLogger(Logger* logger) {
- Logger::set_logger_impl(logger);
+ logger_.reset(logger);
+ Logger::set_logger_impl(logger_.get());
}
+class PhoneNumberRegExpsAndMappings {
+ private:
+ void InitializeMapsAndSets() {
+ diallable_char_mappings_.insert(std::make_pair('+', '+'));
+ diallable_char_mappings_.insert(std::make_pair('*', '*'));
+ // Here we insert all punctuation symbols that we wish to respect when
+ // formatting alpha numbers, as they show the intended number groupings.
+ all_plus_number_grouping_symbols_.insert(
+ std::make_pair(ToUnicodeCodepoint("-"), '-'));
+ all_plus_number_grouping_symbols_.insert(
+ std::make_pair(ToUnicodeCodepoint("\xEF\xBC\x8D" /* "-" */), '-'));
+ all_plus_number_grouping_symbols_.insert(
+ std::make_pair(ToUnicodeCodepoint("\xE2\x80\x90" /* "‐" */), '-'));
+ all_plus_number_grouping_symbols_.insert(
+ std::make_pair(ToUnicodeCodepoint("\xE2\x80\x91" /* "‑" */), '-'));
+ all_plus_number_grouping_symbols_.insert(
+ std::make_pair(ToUnicodeCodepoint("\xE2\x80\x92" /* "‒" */), '-'));
+ all_plus_number_grouping_symbols_.insert(
+ std::make_pair(ToUnicodeCodepoint("\xE2\x80\x93" /* "–" */), '-'));
+ all_plus_number_grouping_symbols_.insert(
+ std::make_pair(ToUnicodeCodepoint("\xE2\x80\x94" /* "—" */), '-'));
+ all_plus_number_grouping_symbols_.insert(
+ std::make_pair(ToUnicodeCodepoint("\xE2\x80\x95" /* "―" */), '-'));
+ all_plus_number_grouping_symbols_.insert(
+ std::make_pair(ToUnicodeCodepoint("\xE2\x88\x92" /* "−" */), '-'));
+ all_plus_number_grouping_symbols_.insert(
+ std::make_pair(ToUnicodeCodepoint("/"), '/'));
+ all_plus_number_grouping_symbols_.insert(
+ std::make_pair(ToUnicodeCodepoint("\xEF\xBC\x8F" /* "/" */), '/'));
+ all_plus_number_grouping_symbols_.insert(
+ std::make_pair(ToUnicodeCodepoint(" "), ' '));
+ all_plus_number_grouping_symbols_.insert(
+ std::make_pair(ToUnicodeCodepoint("\xE3\x80\x80" /* " " */), ' '));
+ all_plus_number_grouping_symbols_.insert(
+ std::make_pair(ToUnicodeCodepoint("\xE2\x81\xA0"), ' '));
+ all_plus_number_grouping_symbols_.insert(
+ std::make_pair(ToUnicodeCodepoint("."), '.'));
+ all_plus_number_grouping_symbols_.insert(
+ std::make_pair(ToUnicodeCodepoint("\xEF\xBC\x8E" /* "." */), '.'));
+ // Only the upper-case letters are added here - the lower-case versions are
+ // added programmatically.
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("A"), '2'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("B"), '2'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("C"), '2'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("D"), '3'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("E"), '3'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("F"), '3'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("G"), '4'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("H"), '4'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("I"), '4'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("J"), '5'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("K"), '5'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("L"), '5'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("M"), '6'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("N"), '6'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("O"), '6'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("P"), '7'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("Q"), '7'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("R"), '7'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("S"), '7'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("T"), '8'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("U"), '8'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("V"), '8'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("W"), '9'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("X"), '9'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("Y"), '9'));
+ alpha_mappings_.insert(std::make_pair(ToUnicodeCodepoint("Z"), '9'));
+ map<char32, char> lower_case_mappings;
+ map<char32, char> alpha_letters;
+ for (map<char32, char>::const_iterator it = alpha_mappings_.begin();
+ it != alpha_mappings_.end();
+ ++it) {
+ // Convert all the upper-case ASCII letters to lower-case.
+ if (it->first < 128) {
+ char letter_as_upper = static_cast<char>(it->first);
+ char32 letter_as_lower = static_cast<char32>(tolower(letter_as_upper));
+ lower_case_mappings.insert(std::make_pair(letter_as_lower, it->second));
+ // Add the letters in both variants to the alpha_letters map. This just
+ // pairs each letter with its upper-case representation so that it can
+ // be retained when normalising alpha numbers.
+ alpha_letters.insert(std::make_pair(letter_as_lower, letter_as_upper));
+ alpha_letters.insert(std::make_pair(it->first, letter_as_upper));
+ }
+ }
+ // In the Java version we don't insert the lower-case mappings in the map,
+ // because we convert to upper case on the fly. Doing this here would
+ // involve pulling in all of ICU, which we don't want to do if we don't have
+ // to.
+ alpha_mappings_.insert(lower_case_mappings.begin(),
+ lower_case_mappings.end());
+ alpha_phone_mappings_.insert(alpha_mappings_.begin(),
+ alpha_mappings_.end());
+ all_plus_number_grouping_symbols_.insert(alpha_letters.begin(),
+ alpha_letters.end());
+ // Add the ASCII digits so that they don't get deleted by NormalizeHelper().
+ for (char c = '0'; c <= '9'; ++c) {
+ diallable_char_mappings_.insert(std::make_pair(c, c));
+ alpha_phone_mappings_.insert(std::make_pair(c, c));
+ all_plus_number_grouping_symbols_.insert(std::make_pair(c, c));
+ }
+
+ mobile_token_mappings_.insert(std::make_pair(52, '1'));
+ mobile_token_mappings_.insert(std::make_pair(54, '9'));
+ }
+
+ // 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,
+ // alpha characters and digits in the phone number. Does not include extension
+ // data. The symbol 'x' is allowed here as valid punctuation since it is often
+ // 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
+ // will be run as a case-insensitive regexp match. Wide character versions are
+ // also provided after each ASCII version.
+ // For parsing, we are slightly more lenient in our interpretation than for
+ // matching. Here we allow a "comma" as a possible extension indicator. When
+ // matching, this is hardly ever used to indicate this.
+ const string extn_patterns_for_parsing_;
+
+ public:
+ scoped_ptr<const AbstractRegExpFactory> regexp_factory_;
+ scoped_ptr<RegExpCache> regexp_cache_;
+
+ // A map that contains characters that are essential when dialling. That means
+ // any of the characters in this map must not be removed from a number when
+ // dialing, otherwise the call will not reach the intended destination.
+ map<char32, char> diallable_char_mappings_;
+ // These mappings map a character (key) to a specific digit that should
+ // replace it for normalization purposes.
+ map<char32, char> alpha_mappings_;
+ // For performance reasons, store a map of combining alpha_mappings with ASCII
+ // digits.
+ map<char32, char> alpha_phone_mappings_;
+
+ // Separate map of all symbols that we wish to retain when formatting alpha
+ // numbers. This includes digits, ascii letters and number grouping symbols
+ // such as "-" and " ".
+ map<char32, char> all_plus_number_grouping_symbols_;
+
+ // Map of country calling codes that use a mobile token before the area code.
+ // One example of when this is relevant is when determining the length of the
+ // national destination code, which should be the length of the area code plus
+ // the length of the mobile token.
+ map<int, char> mobile_token_mappings_;
+
+ // Pattern that makes it easy to distinguish whether a region has a unique
+ // international dialing prefix or not. If a region has a unique international
+ // prefix (e.g. 011 in USA), it will be represented as a string that contains
+ // a sequence of ASCII digits. If there are multiple available international
+ // prefixes in a region, they will be represented as a regex string that
+ // always contains character(s) other than ASCII digits.
+ // Note this regex also includes tilde, which signals waiting for the tone.
+ scoped_ptr<const RegExp> unique_international_prefix_;
+
+ scoped_ptr<const RegExp> digits_pattern_;
+ scoped_ptr<const RegExp> capturing_digit_pattern_;
+ scoped_ptr<const RegExp> capturing_ascii_digits_pattern_;
+
+ // Regular expression of acceptable characters that may start a phone number
+ // for the purposes of parsing. This allows us to strip away meaningless
+ // prefixes to phone numbers that may be mistakenly given to us. This consists
+ // of digits, the plus symbol and arabic-indic digits. This does not contain
+ // alpha characters, although they may be used later in the number. It also
+ // does not include other punctuation, as this will be stripped later during
+ // parsing and is of no information value when parsing a number. The string
+ // starting with this valid character is captured.
+ // This corresponds to VALID_START_CHAR in the java version.
+ scoped_ptr<const RegExp> valid_start_char_pattern_;
+
+ // Regular expression of valid characters before a marker that might indicate
+ // a second number.
+ scoped_ptr<const RegExp> capture_up_to_second_number_start_pattern_;
+
+ // Regular expression of trailing characters that we want to remove. We remove
+ // all characters that are not alpha or numerical characters. The hash
+ // character is retained here, as it may signify the previous block was an
+ // extension. Note the capturing block at the start to capture the rest of the
+ // number if this was a match.
+ // This corresponds to UNWANTED_END_CHAR_PATTERN in the java version.
+ scoped_ptr<const RegExp> unwanted_end_char_pattern_;
+
+ // Regular expression of groups of valid punctuation characters.
+ scoped_ptr<const RegExp> separator_pattern_;
+
+ // Regexp of all possible ways to write extensions, for use when finding phone
+ // numbers in text. This will be run as a case-insensitive regexp match. Wide
+ // character versions are also provided after each ASCII version.
+ const string extn_patterns_for_matching_;
+
+ // Regexp of all known extension prefixes used by different regions followed
+ // by 1 or more valid digits, for use when parsing.
+ scoped_ptr<const RegExp> extn_pattern_;
+
+ // We append optionally the extension pattern to the end here, as a valid
+ // phone number may have an extension prefix appended, followed by 1 or more
+ // digits.
+ scoped_ptr<const RegExp> valid_phone_number_pattern_;
+
+ // We use this pattern to check if the phone number has at least three letters
+ // in it - if so, then we treat it as a number where some phone-number digits
+ // are represented by letters.
+ scoped_ptr<const RegExp> valid_alpha_phone_pattern_;
+
+ scoped_ptr<const RegExp> first_group_capturing_pattern_;
+
+ scoped_ptr<const RegExp> carrier_code_pattern_;
+
+ scoped_ptr<const RegExp> plus_chars_pattern_;
+
+ PhoneNumberRegExpsAndMappings()
+ : punctuation_and_star_sign_(StrCat(PhoneNumberUtil::kValidPunctuation,
+ kStarSign)),
+ min_length_phone_number_pattern_(
+ StrCat(kDigits, "{", PhoneNumberUtil::kMinLengthForNsn, "}")),
+ valid_phone_number_(
+ StrCat(min_length_phone_number_pattern_, "|[",
+ PhoneNumberUtil::kPlusChars, "]*(?:[",
+ punctuation_and_star_sign_, "]*",
+ kDigits, "){3,}[", kValidAlpha,
+ punctuation_and_star_sign_, kDigits,
+ "]*")),
+ extn_patterns_for_parsing_(
+ CreateExtnPattern(StrCat(",", kSingleExtnSymbolsForMatching))),
+ regexp_factory_(new RegExpFactory()),
+ regexp_cache_(new RegExpCache(*regexp_factory_.get(), 128)),
+ diallable_char_mappings_(),
+ alpha_mappings_(),
+ alpha_phone_mappings_(),
+ all_plus_number_grouping_symbols_(),
+ mobile_token_mappings_(),
+ unique_international_prefix_(regexp_factory_->CreateRegExp(
+ /* "[\\d]+(?:[~⁓∼~][\\d]+)?" */
+ "[\\d]+(?:[~\xE2\x81\x93\xE2\x88\xBC\xEF\xBD\x9E][\\d]+)?")),
+ digits_pattern_(
+ regexp_factory_->CreateRegExp(StrCat("[", kDigits, "]*"))),
+ capturing_digit_pattern_(
+ regexp_factory_->CreateRegExp(StrCat("([", kDigits, "])"))),
+ capturing_ascii_digits_pattern_(
+ regexp_factory_->CreateRegExp("(\\d+)")),
+ valid_start_char_pattern_(regexp_factory_->CreateRegExp(
+ StrCat("[", PhoneNumberUtil::kPlusChars, kDigits, "]"))),
+ capture_up_to_second_number_start_pattern_(
+ regexp_factory_->CreateRegExp(
+ PhoneNumberUtil::kCaptureUpToSecondNumberStart)),
+ unwanted_end_char_pattern_(
+ regexp_factory_->CreateRegExp("[^\\p{N}\\p{L}#]")),
+ separator_pattern_(
+ regexp_factory_->CreateRegExp(
+ StrCat("[", PhoneNumberUtil::kValidPunctuation, "]+"))),
+ extn_patterns_for_matching_(
+ CreateExtnPattern(kSingleExtnSymbolsForMatching)),
+ extn_pattern_(regexp_factory_->CreateRegExp(
+ StrCat("(?i)(?:", extn_patterns_for_parsing_, ")$"))),
+ valid_phone_number_pattern_(regexp_factory_->CreateRegExp(
+ StrCat("(?i)", valid_phone_number_,
+ "(?:", extn_patterns_for_parsing_, ")?"))),
+ valid_alpha_phone_pattern_(regexp_factory_->CreateRegExp(
+ StrCat("(?i)(?:.*?[", kValidAlpha, "]){3}"))),
+ // The first_group_capturing_pattern was originally set to $1 but there
+ // are some countries for which the first group is not used in the
+ // national pattern (e.g. Argentina) so the $1 group does not match
+ // correctly. Therefore, we use \d, so that the first group actually
+ // used in the pattern will be matched.
+ first_group_capturing_pattern_(
+ regexp_factory_->CreateRegExp("(\\$\\d)")),
+ carrier_code_pattern_(regexp_factory_->CreateRegExp("\\$CC")),
+ plus_chars_pattern_(
+ regexp_factory_->CreateRegExp(
+ StrCat("[", PhoneNumberUtil::kPlusChars, "]+"))) {
+ InitializeMapsAndSets();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PhoneNumberRegExpsAndMappings);
+};
+
// Private constructor. Also takes care of initialisation.
PhoneNumberUtil::PhoneNumberUtil()
- : country_calling_code_to_region_code_map_(new vector<IntRegionsPair>()),
+ : 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>()),
- region_to_metadata_map_(new map<string, PhoneMetadata>()) {
- logger_.reset(new StdoutLogger());
+ region_to_metadata_map_(new map<string, PhoneMetadata>()),
+ country_code_to_non_geographical_metadata_map_(
+ new map<int, PhoneMetadata>) {
Logger::set_logger_impl(logger_.get());
+ // TODO: Update the java version to put the contents of the init
+ // method inside the constructor as well to keep both in sync.
PhoneMetadataCollection metadata_collection;
if (!LoadCompiledInMetadata(&metadata_collection)) {
LOG(DFATAL) << "Could not parse compiled-in metadata.";
metadata_collection.metadata().begin();
it != metadata_collection.metadata().end();
++it) {
- const PhoneMetadata& phone_metadata = *it;
- const string& region_code = phone_metadata.id();
- region_to_metadata_map_->insert(make_pair(region_code, *it));
+ const string& region_code = it->id();
+ if (region_code == RegionCode::GetUnknown()) {
+ continue;
+ }
+
int country_calling_code = it->country_code();
- map<int, list<string>*>::iterator calling_code_in_map =
+ if (kRegionCodeForNonGeoEntity == region_code) {
+ country_code_to_non_geographical_metadata_map_->insert(
+ std::make_pair(country_calling_code, *it));
+ } else {
+ region_to_metadata_map_->insert(std::make_pair(region_code, *it));
+ }
+ map<int, list<string>* >::iterator calling_code_in_map =
country_calling_code_to_region_map.find(country_calling_code);
if (calling_code_in_map != country_calling_code_to_region_map.end()) {
if (it->main_country_for_code()) {
list<string>* list_with_region_code = new list<string>();
list_with_region_code->push_back(region_code);
country_calling_code_to_region_map.insert(
- make_pair(country_calling_code, list_with_region_code));
+ std::make_pair(country_calling_code, list_with_region_code));
}
if (country_calling_code == kNanpaCountryCode) {
nanpa_regions_->insert(region_code);
country_calling_code_to_region_map.begin(),
country_calling_code_to_region_map.end());
// Sort all the pairs in ascending order according to country calling code.
- sort(country_calling_code_to_region_code_map_->begin(),
- country_calling_code_to_region_code_map_->end(),
- OrderByFirst());
-
- InitializeStaticMapsAndSets();
- CreateRegularExpressions();
+ std::sort(country_calling_code_to_region_code_map_->begin(),
+ country_calling_code_to_region_code_map_->end(),
+ OrderByFirst());
}
PhoneNumberUtil::~PhoneNumberUtil() {
country_calling_code_to_region_code_map_->end());
}
+void PhoneNumberUtil::GetSupportedRegions(set<string>* regions) const {
+ DCHECK(regions);
+ for (map<string, PhoneMetadata>::const_iterator it =
+ region_to_metadata_map_->begin(); it != region_to_metadata_map_->end();
+ ++it) {
+ regions->insert(it->first);
+ }
+}
+
+void PhoneNumberUtil::GetSupportedGlobalNetworkCallingCodes(
+ set<int>* calling_codes) const {
+ DCHECK(calling_codes);
+ for (map<int, PhoneMetadata>::const_iterator it =
+ country_code_to_non_geographical_metadata_map_->begin();
+ it != country_code_to_non_geographical_metadata_map_->end(); ++it) {
+ calling_codes->insert(it->first);
+ }
+}
+
// 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();
-}
-#endif
-
-void PhoneNumberUtil::CreateRegularExpressions() const {
- unique_international_prefix.reset(regexp_factory->CreateRegExp(
- /* "[\\d]+(?:[~⁓∼~][\\d]+)?" */
- "[\\d]+(?:[~\xE2\x81\x93\xE2\x88\xBC\xEF\xBD\x9E][\\d]+)?"));
- // The first_group_capturing_pattern was originally set to $1 but there are
- // some countries for which the first group is not used in the national
- // pattern (e.g. Argentina) so the $1 group does not match correctly.
- // Therefore, we use \d, so that the first group actually used in the pattern
- // will be matched.
- first_group_capturing_pattern.reset(regexp_factory->CreateRegExp("(\\$\\d)"));
- carrier_code_pattern.reset(regexp_factory->CreateRegExp("\\$CC"));
- digits_pattern.reset(
- regexp_factory->CreateRegExp(StrCat("[", kDigits, "]*")));
- capturing_digit_pattern.reset(
- regexp_factory->CreateRegExp(StrCat("([", kDigits, "])")));
- capturing_ascii_digits_pattern.reset(regexp_factory->CreateRegExp("(\\d+)"));
- valid_start_char.reset(new string(StrCat("[", kPlusChars, kDigits, "]")));
- valid_start_char_pattern.reset(
- regexp_factory->CreateRegExp(*valid_start_char));
- capture_up_to_second_number_start_pattern.reset(regexp_factory->CreateRegExp(
- kCaptureUpToSecondNumberStart));
- unwanted_end_char_pattern.reset(
- regexp_factory->CreateRegExp(kUnwantedEndChar));
- separator_pattern.reset(
- regexp_factory->CreateRegExp(StrCat("[", kValidPunctuation, "]+")));
- valid_phone_number.reset(new string(
- StrCat("[", kPlusChars, "]*(?:[", kValidPunctuation, "]*[", kDigits,
- "]){3,}[", kValidAlpha, kValidPunctuation, kDigits, "]*")));
- // For parsing, we are slightly more lenient in our interpretation than for
- // matching. Here we allow a "comma" as a possible extension indicator. When
- // matching, this is hardly ever used to indicate this.
- const string single_extn_symbols_for_parsing =
- StrCat(",", kSingleExtnSymbolsForMatching);
- extn_patterns_for_parsing.reset(
- new string(CreateExtnPattern(single_extn_symbols_for_parsing)));
- extn_patterns_for_matching.reset(
- new string(CreateExtnPattern(kSingleExtnSymbolsForMatching)));
-
- extn_pattern.reset(regexp_factory->CreateRegExp(
- StrCat("(?i)(?:", *extn_patterns_for_parsing, ")$")));
- valid_phone_number_pattern.reset(regexp_factory->CreateRegExp(
- StrCat("(?i)", *valid_phone_number,
- "(?:", *extn_patterns_for_parsing, ")?")));
- valid_alpha_phone_pattern.reset(regexp_factory->CreateRegExp(
- StrCat("(?i)(?:.*?[", kValidAlpha, "]){3}")));
- plus_chars_pattern.reset(
- regexp_factory->CreateRegExp(StrCat("[", kPlusChars, "]+")));
+ return Singleton<PhoneNumberUtil>::GetInstance();
}
const string& PhoneNumberUtil::GetExtnPatternsForMatching() const {
- return *(extn_patterns_for_matching.get());
+ 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 digits_pattern->FullMatch(s);
+ return reg_exps_->digits_pattern_->FullMatch(s);
}
void PhoneNumberUtil::TrimUnwantedEndChars(string* number) const {
for (; reverse_it.base() != number_as_unicode.begin(); ++reverse_it) {
len = reverse_it.get_utf8(current_char);
current_char[len] = '\0';
- if (!unwanted_end_char_pattern->FullMatch(current_char)) {
+ if (!reg_exps_->unwanted_end_char_pattern_->FullMatch(current_char)) {
break;
}
}
// followed by a single digit, separated by valid phone number punctuation.
// This prevents invalid punctuation (such as the star sign in Israeli star
// numbers) getting into the output of the AYTF.
- const RegExp& eligible_format_pattern = regexp_cache->GetRegExp(
+ const RegExp& eligible_format_pattern = reg_exps_->regexp_cache_->GetRegExp(
StrCat("[", kValidPunctuation, "]*", "(\\$\\d", "[",
kValidPunctuation, "]*)+"));
return eligible_format_pattern.FullMatch(format);
}
-void PhoneNumberUtil::GetSupportedRegions(set<string>* regions) const {
- DCHECK(regions);
- for (map<string, PhoneMetadata>::const_iterator it =
- region_to_metadata_map_->begin(); it != region_to_metadata_map_->end();
- ++it) {
- regions->insert(it->first);
- }
+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)) {
- LOG(ERROR) << "Invalid region code provided.";
+ 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
region_to_metadata_map_->end());
}
-bool PhoneNumberUtil::HasValidRegionCode(const string& region_code,
- int country_calling_code,
- const string& number) const {
- if (!IsValidRegionCode(region_code)) {
- VLOG(1) << "Number " << number
- << " has invalid or missing country calling code ("
- << country_calling_code
- << ")";
- return false;
- }
- return true;
+bool PhoneNumberUtil::HasValidCountryCallingCode(
+ int country_calling_code) const {
+ // Create an IntRegionsPair with the country_code passed in, and use it to
+ // locate the pair with the same country_code in the sorted vector.
+ IntRegionsPair target_pair;
+ target_pair.first = country_calling_code;
+ return (binary_search(country_calling_code_to_region_code_map_->begin(),
+ country_calling_code_to_region_code_map_->end(),
+ 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 =
return NULL;
}
+const PhoneMetadata* PhoneNumberUtil::GetMetadataForNonGeographicalRegion(
+ int country_calling_code) const {
+ map<int, PhoneMetadata>::const_iterator it =
+ country_code_to_non_geographical_metadata_map_->find(
+ country_calling_code);
+ if (it != country_code_to_non_geographical_metadata_map_->end()) {
+ return &it->second;
+ }
+ return NULL;
+}
+
void PhoneNumberUtil::Format(const PhoneNumber& number,
PhoneNumberFormat number_format,
string* formatted_number) const {
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;
}
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.
- FormatNumberByFormat(country_calling_code, E164,
- national_significant_number, "", formatted_number);
+ // 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
// contained by Réunion.
string region_code;
GetRegionCodeForCountryCode(country_calling_code, ®ion_code);
- if (!HasValidRegionCode(region_code, country_calling_code,
- national_significant_number)) {
- formatted_number->assign(national_significant_number);
- return;
- }
- string formatted_extension;
- MaybeGetFormattedExtension(number, region_code, number_format,
- &formatted_extension);
- string formatted_national_number;
- FormatNationalNumber(national_significant_number, region_code, number_format,
- &formatted_national_number);
- FormatNumberByFormat(country_calling_code, number_format,
- formatted_national_number,
- formatted_extension, formatted_number);
+ // 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,
+ formatted_number);
+ MaybeAppendFormattedExtension(number, *metadata, number_format,
+ formatted_number);
+ PrefixNumberWithCountryCallingCode(country_calling_code, number_format,
+ formatted_number);
}
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, ®ion_code);
string national_significant_number;
GetNationalSignificantNumber(number, &national_significant_number);
- if (!HasValidRegionCode(region_code, country_calling_code,
- national_significant_number)) {
+ if (!HasValidCountryCallingCode(country_calling_code)) {
formatted_number->assign(national_significant_number);
return;
}
- RepeatedPtrField<NumberFormat> user_defined_formats_copy;
- for (RepeatedPtrField<NumberFormat>::const_iterator it =
- user_defined_formats.begin();
- it != user_defined_formats.end();
- ++it) {
+ string region_code;
+ GetRegionCodeForCountryCode(country_calling_code, ®ion_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 =
+ ChooseFormattingPatternForNumber(user_defined_formats,
+ national_significant_number);
+ if (!formatting_pattern) {
+ // If no pattern above is matched, we format the number as a whole.
+ formatted_number->assign(national_significant_number);
+ } else {
+ NumberFormat num_format_copy;
+ // Before we do a replacement of the national prefix pattern $NP with the
+ // national prefix, we need to copy the rule so that subsequent replacements
+ // for different numbers have the appropriate national prefix.
+ num_format_copy.MergeFrom(*formatting_pattern);
string national_prefix_formatting_rule(
- it->national_prefix_formatting_rule());
+ formatting_pattern->national_prefix_formatting_rule());
if (!national_prefix_formatting_rule.empty()) {
- const string& national_prefix =
- GetMetadataForRegion(region_code)->national_prefix();
- NumberFormat* num_format_copy = user_defined_formats_copy.Add();
- num_format_copy->MergeFrom(*it);
+ const string& national_prefix = metadata->national_prefix();
if (!national_prefix.empty()) {
// Replace $NP with national prefix and $FG with the first group ($1).
GlobalReplaceSubstring("$NP", national_prefix,
&national_prefix_formatting_rule);
GlobalReplaceSubstring("$FG", "$1",
&national_prefix_formatting_rule);
- num_format_copy->set_national_prefix_formatting_rule(
+ num_format_copy.set_national_prefix_formatting_rule(
national_prefix_formatting_rule);
} else {
// We don't want to have a rule for how to format the national prefix if
// there isn't one.
- num_format_copy->clear_national_prefix_formatting_rule();
+ num_format_copy.clear_national_prefix_formatting_rule();
}
- } else {
- user_defined_formats_copy.Add()->MergeFrom(*it);
}
+ FormatNsnUsingPattern(national_significant_number, num_format_copy,
+ number_format, formatted_number);
}
- string formatted_number_without_extension;
- FormatAccordingToFormats(national_significant_number,
- user_defined_formats_copy,
- number_format, national_significant_number,
- &formatted_number_without_extension);
- string formatted_extension;
- MaybeGetFormattedExtension(number, region_code, NATIONAL,
- &formatted_extension);
- FormatNumberByFormat(country_calling_code, number_format,
- formatted_number_without_extension, formatted_extension,
- formatted_number);
+ MaybeAppendFormattedExtension(number, *metadata, NATIONAL, formatted_number);
+ PrefixNumberWithCountryCallingCode(country_calling_code, number_format,
+ formatted_number);
}
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, ®ion_code);
- if (!HasValidRegionCode(region_code, country_calling_code,
- national_significant_number)) {
- formatted_number->assign(national_significant_number);
- }
- string formatted_extension;
- MaybeGetFormattedExtension(number, region_code, NATIONAL,
- &formatted_extension);
- string formatted_national_number;
- FormatNationalNumberWithCarrier(national_significant_number, region_code,
- NATIONAL, carrier_code,
- &formatted_national_number);
- FormatNumberByFormat(country_calling_code, NATIONAL,
- formatted_national_number, formatted_extension,
- formatted_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,
+ carrier_code, formatted_number);
+ MaybeAppendFormattedExtension(number, *metadata, NATIONAL, formatted_number);
+ PrefixNumberWithCountryCallingCode(country_calling_code, NATIONAL,
+ formatted_number);
+}
+
+const PhoneMetadata* PhoneNumberUtil::GetMetadataForRegionOrCallingCode(
+ int country_calling_code, const string& region_code) const {
+ return kRegionCodeForNonGeoEntity == region_code
+ ? GetMetadataForNonGeographicalRegion(country_calling_code)
+ : GetMetadataForRegion(region_code);
}
void PhoneNumberUtil::FormatNationalNumberWithPreferredCarrierCode(
const string& calling_from,
bool with_formatting,
string* formatted_number) const {
- string region_code;
- GetRegionCodeForNumber(number, ®ion_code);
- if (!IsValidRegionCode(region_code)) {
+ int country_calling_code = number.country_code();
+ if (!HasValidCountryCallingCode(country_calling_code)) {
formatted_number->assign(number.has_raw_input() ? number.raw_input() : "");
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();
+ string region_code;
+ GetRegionCodeForCountryCode(country_calling_code, ®ion_code);
PhoneNumberType number_type = GetNumberType(number_no_extension);
- if ((region_code == "CO") && (calling_from == "CO") &&
- (number_type == FIXED_LINE)) {
+ bool is_valid_number = (number_type != UNKNOWN);
+ if (calling_from == region_code) {
+ 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 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 (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, 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 {
- // 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 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
+ // 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)) {
+ } 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);
return;
- } else if (calling_from == region_code) {
- Format(number_no_extension, NATIONAL, formatted_number);
- } else {
- formatted_number->assign("");
}
if (!with_formatting) {
- NormalizeDigitsOnly(formatted_number);
+ NormalizeDiallableCharsOnly(formatted_number);
}
}
string* formatted_number) const {
DCHECK(formatted_number);
if (!IsValidRegionCode(calling_from)) {
- VLOG(1) << "Trying to format number from invalid region. International"
- << " formatting applied.";
+ LOG(WARNING) << "Trying to format number from invalid region "
+ << calling_from
+ << ". International formatting applied.";
Format(number, INTERNATIONAL, formatted_number);
return;
}
int country_code = number.country_code();
- string region_code;
- GetRegionCodeForCountryCode(country_code, ®ion_code);
string national_significant_number;
GetNationalSignificantNumber(number, &national_significant_number);
- if (!HasValidRegionCode(region_code, country_code,
- national_significant_number)) {
+ if (!HasValidCountryCallingCode(country_code)) {
formatted_number->assign(national_significant_number);
return;
}
if (IsNANPACountry(calling_from)) {
// For NANPA regions, return the national format for these regions but
// prefix it with the country calling code.
- string national_number;
- Format(number, NATIONAL, &national_number);
- formatted_number->assign(StrCat(country_code, " ", national_number));
+ Format(number, NATIONAL, formatted_number);
+ formatted_number->insert(0, StrCat(country_code, " "));
return;
}
- } else if (country_code == GetCountryCodeForRegion(calling_from)) {
+ } else if (country_code == GetCountryCodeForValidRegion(calling_from)) {
// If neither region is a NANPA region, then we check to see if the
// country calling code of the number and the country calling code of the
// region we are calling from are the same.
Format(number, NATIONAL, formatted_number);
return;
}
- string formatted_national_number;
- FormatNationalNumber(national_significant_number, region_code, INTERNATIONAL,
- &formatted_national_number);
- const PhoneMetadata* metadata = GetMetadataForRegion(calling_from);
- const string& international_prefix = metadata->international_prefix();
- string formatted_extension;
- MaybeGetFormattedExtension(number, region_code, INTERNATIONAL,
- &formatted_extension);
+ // 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.
const string international_prefix_for_formatting(
- unique_international_prefix->FullMatch(international_prefix)
+ reg_exps_->unique_international_prefix_->FullMatch(international_prefix)
? international_prefix
- : metadata->preferred_international_prefix());
+ : metadata_calling_from->preferred_international_prefix());
+
+ string region_code;
+ GetRegionCodeForCountryCode(country_code, ®ion_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,
+ formatted_number);
if (!international_prefix_for_formatting.empty()) {
- formatted_number->assign(
- StrCat(international_prefix_for_formatting, " ", country_code, " ",
- formatted_national_number, formatted_extension));
+ formatted_number->insert(
+ 0, StrCat(international_prefix_for_formatting, " ", country_code, " "));
} else {
- FormatNumberByFormat(country_code, INTERNATIONAL, formatted_national_number,
- formatted_extension, formatted_number);
+ PrefixNumberWithCountryCallingCode(country_code, INTERNATIONAL,
+ formatted_number);
}
}
string* formatted_number) const {
DCHECK(formatted_number);
- if (number.has_raw_input() && !IsValidNumber(number)) {
+ if (number.has_raw_input() &&
+ (HasUnexpectedItalianLeadingZero(number) ||
+ !HasFormattingPatternForNumber(number))) {
+ // We check if we have the formatting pattern because without that, we might
+ // format the number as a group without national prefix.
formatted_number->assign(number.raw_input());
return;
}
switch (number.country_code_source()) {
case PhoneNumber::FROM_NUMBER_WITH_PLUS_SIGN:
Format(number, INTERNATIONAL, formatted_number);
- return;
+ break;
case PhoneNumber::FROM_NUMBER_WITH_IDD:
FormatOutOfCountryCallingNumber(number, region_calling_from,
formatted_number);
- return;
+ break;
case PhoneNumber::FROM_NUMBER_WITHOUT_PLUS_SIGN:
Format(number, INTERNATIONAL, formatted_number);
formatted_number->erase(formatted_number->begin());
- return;
+ break;
case PhoneNumber::FROM_DEFAULT_COUNTRY:
+ // Fall-through to default case.
default:
- Format(number, NATIONAL, formatted_number);
+ string region_code;
+ GetRegionCodeForCountryCode(number.country_code(), ®ion_code);
+ // We strip non-digits from the NDD here, and from the raw input later, so
+ // that we can compare them easily.
+ string national_prefix;
+ GetNddPrefixForRegion(region_code, true /* strip non-digits */,
+ &national_prefix);
+ if (national_prefix.empty()) {
+ // If the region doesn't have a national prefix at all, we can safely
+ // return the national format without worrying about a national prefix
+ // being added.
+ Format(number, NATIONAL, formatted_number);
+ break;
+ }
+ // Otherwise, we check if the original number was entered with a national
+ // prefix.
+ if (RawInputContainsNationalPrefix(number.raw_input(), national_prefix,
+ region_code)) {
+ // If so, we can safely return the national format.
+ 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);
+ // This shouldn't be NULL, because we have checked that above with
+ // HasFormattingPatternForNumber.
+ 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
+ // IsNationalPrefixPresentIfRequired.
+ string candidate_national_prefix_rule(
+ format_rule->national_prefix_formatting_rule());
+ // We assume that the first-group symbol will never be _before_ the
+ // national prefix.
+ if (!candidate_national_prefix_rule.empty()) {
+ candidate_national_prefix_rule.erase(
+ candidate_national_prefix_rule.find("$1"));
+ NormalizeDigitsOnly(&candidate_national_prefix_rule);
+ }
+ if (candidate_national_prefix_rule.empty()) {
+ // National prefix not used when formatting this number.
+ Format(number, NATIONAL, formatted_number);
+ break;
+ }
+ // Otherwise, we need to remove the national prefix from our output.
+ RepeatedPtrField<NumberFormat> number_formats;
+ NumberFormat* number_format = number_formats.Add();
+ number_format->MergeFrom(*format_rule);
+ number_format->clear_national_prefix_formatting_rule();
+ FormatByPattern(number, NATIONAL, number_formats, formatted_number);
+ break;
+ }
+ // 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() && !number.raw_input().empty()) {
+ string normalized_formatted_number(*formatted_number);
+ NormalizeDiallableCharsOnly(&normalized_formatted_number);
+ string normalized_raw_input(number.raw_input());
+ NormalizeDiallableCharsOnly(&normalized_raw_input);
+ if (normalized_formatted_number != normalized_raw_input) {
+ formatted_number->assign(number.raw_input());
+ }
+ }
+}
+
+// Check if raw_input, which is assumed to be in the national format, has a
+// national prefix. The national prefix is assumed to be in digits-only form.
+bool PhoneNumberUtil::RawInputContainsNationalPrefix(
+ const string& raw_input,
+ const string& national_prefix,
+ const string& region_code) const {
+ string normalized_national_number(raw_input);
+ NormalizeDigitsOnly(&normalized_national_number);
+ if (HasPrefixString(normalized_national_number, national_prefix)) {
+ // Some Japanese numbers (e.g. 00777123) might be mistaken to contain
+ // the national prefix when written without it (e.g. 0777123) if we just
+ // do prefix matching. To tackle that, we check the validity of the
+ // number if the assumed national prefix is removed (777123 won't be
+ // valid in Japan).
+ PhoneNumber number_without_national_prefix;
+ if (Parse(normalized_national_number.substr(national_prefix.length()),
+ region_code, &number_without_national_prefix)
+ == NO_PARSING_ERROR) {
+ return IsValidNumber(number_without_national_prefix);
+ }
}
+ return false;
+}
+
+bool PhoneNumberUtil::HasUnexpectedItalianLeadingZero(
+ const PhoneNumber& number) const {
+ return number.has_italian_leading_zero() &&
+ !IsLeadingZeroPossible(number.country_code());
+}
+
+bool PhoneNumberUtil::HasFormattingPatternForNumber(
+ const PhoneNumber& number) const {
+ int country_calling_code = number.country_code();
+ string region_code;
+ GetRegionCodeForCountryCode(country_calling_code, ®ion_code);
+ const PhoneMetadata* metadata =
+ GetMetadataForRegionOrCallingCode(country_calling_code, region_code);
+ if (!metadata) {
+ return false;
+ }
+ string national_number;
+ GetNationalSignificantNumber(number, &national_number);
+ const NumberFormat* format_rule =
+ ChooseFormattingPatternForNumber(metadata->number_format(),
+ national_number);
+ return format_rule;
}
void PhoneNumberUtil::FormatOutOfCountryKeepingAlphaChars(
FormatOutOfCountryCallingNumber(number, calling_from, formatted_number);
return;
}
- string region_code;
- GetRegionCodeForCountryCode(number.country_code(), ®ion_code);
- if (!HasValidRegionCode(region_code, number.country_code(),
- number.raw_input())) {
+ int country_code = number.country_code();
+ if (!HasValidCountryCallingCode(country_code)) {
formatted_number->assign(number.raw_input());
return;
}
// this by comparing the number in raw_input with the parsed number.
string raw_input_copy(number.raw_input());
// Normalize punctuation. We retain number grouping symbols such as " " only.
- NormalizeHelper(*all_plus_number_grouping_symbols, true, &raw_input_copy);
+ NormalizeHelper(reg_exps_->all_plus_number_grouping_symbols_, true,
+ &raw_input_copy);
// Now we trim everything before the first three digits in the parsed number.
// We choose three because all valid alpha numbers have 3 digits at the start
// - if it does not, then we don't trim anything at all. Similarly, if the
}
}
const PhoneMetadata* metadata = GetMetadataForRegion(calling_from);
- if (number.country_code() == kNanpaCountryCode) {
+ if (country_code == kNanpaCountryCode) {
if (IsNANPACountry(calling_from)) {
- formatted_number->assign(StrCat(number.country_code(), " ",
- raw_input_copy));
+ StrAppend(formatted_number, country_code, " ", raw_input_copy);
return;
}
- } else if (number.country_code() == GetCountryCodeForRegion(calling_from)) {
- // Here we copy the formatting rules so we can modify the pattern we expect
- // to match against.
- RepeatedPtrField<NumberFormat> available_formats =
- metadata->number_format();
- for (RepeatedPtrField<NumberFormat>::iterator
- it = available_formats.begin(); it != available_formats.end(); ++it) {
- // The first group is the first group of digits that the user determined.
- it->set_pattern("(\\d+)(.*)");
- // Here we just concatenate them back together after the national prefix
- // has been fixed.
- it->set_format("$1$2");
+ } else if (metadata &&
+ country_code == GetCountryCodeForValidRegion(calling_from)) {
+ const NumberFormat* formatting_pattern =
+ ChooseFormattingPatternForNumber(metadata->number_format(),
+ national_number);
+ if (!formatting_pattern) {
+ // If no pattern above is matched, we format the original input.
+ formatted_number->assign(raw_input_copy);
+ return;
}
- // Now we format using these patterns instead of the default pattern, but
- // with the national prefix prefixed if necessary, by choosing the format
- // rule based on the leading digits present in the unformatted national
- // number.
+ NumberFormat new_format;
+ new_format.MergeFrom(*formatting_pattern);
+ // The first group is the first group of digits that the user wrote
+ // together.
+ new_format.set_pattern("(\\d+)(.*)");
+ // Here we just concatenate them back together after the national prefix
+ // has been fixed.
+ new_format.set_format("$1$2");
+ // Now we format using this pattern instead of the default pattern, but
+ // with the national prefix prefixed if necessary.
// This will not work in the cases where the pattern (and not the
// leading digits) decide whether a national prefix needs to be used, since
// we have overridden the pattern to match anything, but that is not the
// case in the metadata to date.
- FormatAccordingToFormats(national_number, available_formats,
- NATIONAL, raw_input_copy, formatted_number);
+ FormatNsnUsingPattern(raw_input_copy, new_format, NATIONAL,
+ formatted_number);
return;
}
- const string& international_prefix = metadata->international_prefix();
- // For regions that have multiple international prefixes, the international
- // format of the number is returned, unless there is a preferred international
- // prefix.
- const string international_prefix_for_formatting(
- unique_international_prefix->FullMatch(international_prefix)
- ? international_prefix
- : metadata->preferred_international_prefix());
+ string international_prefix_for_formatting;
+ // If an unsupported region-calling-from is entered, or a country with
+ // multiple international prefixes, the international format of the number is
+ // returned, unless there is a preferred international prefix.
+ if (metadata) {
+ const string& international_prefix = metadata->international_prefix();
+ international_prefix_for_formatting =
+ reg_exps_->unique_international_prefix_->FullMatch(international_prefix)
+ ? international_prefix
+ : metadata->preferred_international_prefix();
+ }
if (!international_prefix_for_formatting.empty()) {
- formatted_number->assign(
- StrCat(international_prefix_for_formatting, " ", number.country_code(),
- " ", raw_input_copy));
+ StrAppend(formatted_number, international_prefix_for_formatting, " ",
+ country_code, " ", raw_input_copy);
} else {
- FormatNumberByFormat(number.country_code(), INTERNATIONAL, raw_input_copy,
- "", formatted_number);
+ // Invalid region entered as country-calling-from (so no metadata was found
+ // for it) or the region chosen has multiple international dialling
+ // prefixes.
+ LOG(WARNING) << "Trying to format number from invalid region "
+ << calling_from
+ << ". International formatting applied.";
+ formatted_number->assign(raw_input_copy);
+ PrefixNumberWithCountryCallingCode(country_code, INTERNATIONAL,
+ formatted_number);
}
}
-void PhoneNumberUtil::FormatNationalNumber(
- const string& number,
- const string& region_code,
- PhoneNumberFormat number_format,
+const NumberFormat* PhoneNumberUtil::ChooseFormattingPatternForNumber(
+ const RepeatedPtrField<NumberFormat>& available_formats,
+ const string& national_number) const {
+ for (RepeatedPtrField<NumberFormat>::const_iterator
+ it = available_formats.begin(); it != available_formats.end(); ++it) {
+ int size = it->leading_digits_pattern_size();
+ if (size > 0) {
+ const scoped_ptr<RegExpInput> number_copy(
+ reg_exps_->regexp_factory_->CreateInput(national_number));
+ // We always use the last leading_digits_pattern, as it is the most
+ // detailed.
+ if (!reg_exps_->regexp_cache_->GetRegExp(
+ it->leading_digits_pattern(size - 1)).Consume(
+ number_copy.get())) {
+ continue;
+ }
+ }
+ const RegExp& pattern_to_match(
+ reg_exps_->regexp_cache_->GetRegExp(it->pattern()));
+ if (pattern_to_match.FullMatch(national_number)) {
+ return &(*it);
+ }
+ }
+ return NULL;
+}
+
+// Note that carrier_code is optional - if an empty string, no carrier code
+// replacement will take place.
+void PhoneNumberUtil::FormatNsnUsingPatternWithCarrier(
+ const string& national_number,
+ const NumberFormat& formatting_pattern,
+ PhoneNumberUtil::PhoneNumberFormat number_format,
+ const string& carrier_code,
string* formatted_number) const {
DCHECK(formatted_number);
- FormatNationalNumberWithCarrier(number, region_code, number_format, "",
- formatted_number);
+ string number_format_rule(formatting_pattern.format());
+ if (number_format == PhoneNumberUtil::NATIONAL &&
+ carrier_code.length() > 0 &&
+ formatting_pattern.domestic_carrier_code_formatting_rule().length() > 0) {
+ // Replace the $CC in the formatting rule with the desired carrier code.
+ string carrier_code_formatting_rule =
+ formatting_pattern.domestic_carrier_code_formatting_rule();
+ reg_exps_->carrier_code_pattern_->Replace(&carrier_code_formatting_rule,
+ carrier_code);
+ reg_exps_->first_group_capturing_pattern_->
+ Replace(&number_format_rule, carrier_code_formatting_rule);
+ } else {
+ // Use the national prefix formatting rule instead.
+ string national_prefix_formatting_rule =
+ formatting_pattern.national_prefix_formatting_rule();
+ if (number_format == PhoneNumberUtil::NATIONAL &&
+ national_prefix_formatting_rule.length() > 0) {
+ // Apply the national_prefix_formatting_rule as the formatting_pattern
+ // contains only information on how the national significant number
+ // should be formatted at this point.
+ reg_exps_->first_group_capturing_pattern_->Replace(
+ &number_format_rule, national_prefix_formatting_rule);
+ }
+ }
+ formatted_number->assign(national_number);
+
+ const RegExp& pattern_to_match(
+ reg_exps_->regexp_cache_->GetRegExp(formatting_pattern.pattern()));
+ pattern_to_match.GlobalReplace(formatted_number, number_format_rule);
+
+ if (number_format == RFC3966) {
+ // First consume any leading punctuation, if any was present.
+ const scoped_ptr<RegExpInput> number(
+ reg_exps_->regexp_factory_->CreateInput(*formatted_number));
+ if (reg_exps_->separator_pattern_->Consume(number.get())) {
+ formatted_number->assign(number->ToString());
+ }
+ // Then replace all separators with a "-".
+ reg_exps_->separator_pattern_->GlobalReplace(formatted_number, "-");
+ }
+}
+
+// Simple wrapper of FormatNsnUsingPatternWithCarrier for the common case of
+// no carrier code.
+void PhoneNumberUtil::FormatNsnUsingPattern(
+ const string& national_number,
+ const NumberFormat& formatting_pattern,
+ PhoneNumberUtil::PhoneNumberFormat number_format,
+ string* formatted_number) const {
+ DCHECK(formatted_number);
+ FormatNsnUsingPatternWithCarrier(national_number, formatting_pattern,
+ number_format, "", formatted_number);
+}
+
+void PhoneNumberUtil::FormatNsn(const string& number,
+ const PhoneMetadata& metadata,
+ PhoneNumberFormat number_format,
+ string* formatted_number) const {
+ DCHECK(formatted_number);
+ FormatNsnWithCarrier(number, metadata, number_format, "", formatted_number);
}
// Note in some regions, the national number can be written in two completely
// INTERNATIONAL format. The number_format parameter here is used to specify
// which format to use for those cases. If a carrier_code is specified, this
// will be inserted into the formatted string to replace $CC.
-void PhoneNumberUtil::FormatNationalNumberWithCarrier(
- const string& number,
- const string& region_code,
- PhoneNumberFormat number_format,
- const string& carrier_code,
- string* formatted_number) const {
+void PhoneNumberUtil::FormatNsnWithCarrier(const string& number,
+ const PhoneMetadata& metadata,
+ PhoneNumberFormat number_format,
+ const string& carrier_code,
+ string* formatted_number) const {
DCHECK(formatted_number);
- const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
// When the intl_number_formats exists, we use that to format national number
// for the INTERNATIONAL format instead of using the number_formats.
const RepeatedPtrField<NumberFormat> available_formats =
- (metadata->intl_number_format_size() == 0 || number_format == NATIONAL)
- ? metadata->number_format()
- : metadata->intl_number_format();
- FormatAccordingToFormatsWithCarrier(number, available_formats, number_format,
- number, carrier_code, formatted_number);
- if (number_format == RFC3966) {
- // Replace all separators with a "-".
- separator_pattern->GlobalReplace(formatted_number, "-");
+ (metadata.intl_number_format_size() == 0 || number_format == NATIONAL)
+ ? metadata.number_format()
+ : metadata.intl_number_format();
+ const NumberFormat* formatting_pattern =
+ ChooseFormattingPatternForNumber(available_formats, number);
+ if (!formatting_pattern) {
+ formatted_number->assign(number);
+ } else {
+ FormatNsnUsingPatternWithCarrier(number, *formatting_pattern, number_format,
+ carrier_code, formatted_number);
}
}
-// Gets the formatted extension of a phone number, if the phone number had an
-// extension specified. If not, it returns an empty string.
-void PhoneNumberUtil::MaybeGetFormattedExtension(
+// Appends the formatted extension of a phone number, if the phone number had an
+// extension specified.
+void PhoneNumberUtil::MaybeAppendFormattedExtension(
const PhoneNumber& number,
- const string& region_code,
+ const PhoneMetadata& metadata,
PhoneNumberFormat number_format,
- string* extension) const {
- DCHECK(extension);
- if (!number.has_extension() || number.extension().length() == 0) {
- extension->assign("");
- } else {
+ string* formatted_number) const {
+ DCHECK(formatted_number);
+ if (number.has_extension() && number.extension().length() > 0) {
if (number_format == RFC3966) {
- StrAppend(extension, kRfc3966ExtnPrefix, number.extension());
- return;
+ StrAppend(formatted_number, kRfc3966ExtnPrefix, number.extension());
+ } else {
+ if (metadata.has_preferred_extn_prefix()) {
+ StrAppend(formatted_number, metadata.preferred_extn_prefix(),
+ number.extension());
+ } else {
+ StrAppend(formatted_number, kDefaultExtnPrefix, number.extension());
+ }
}
- FormatExtension(number.extension(), region_code, extension);
- }
-}
-
-// Formats the extension part of the phone number by prefixing it with the
-// appropriate extension prefix. This will be the default extension prefix,
-// unless overridden by a preferred extension prefix for this region.
-void PhoneNumberUtil::FormatExtension(const string& extension_digits,
- const string& region_code,
- string* extension) const {
- DCHECK(extension);
- const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
- if (metadata->has_preferred_extn_prefix()) {
- extension->assign(StrCat(metadata->preferred_extn_prefix(),
- extension_digits));
- } else {
- extension->assign(StrCat(kDefaultExtnPrefix, extension_digits));
}
}
list<string> region_codes;
GetRegionCodesForCountryCallingCode(country_calling_code, ®ion_codes);
- *region_code = (region_codes.size() > 0)
- ? region_codes.front() : RegionCode::GetUnknown();
+ *region_code = (region_codes.size() > 0) ?
+ region_codes.front() : RegionCode::GetUnknown();
}
void PhoneNumberUtil::GetRegionCodeForNumber(const PhoneNumber& number,
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(
- regexp_factory->CreateInput(national_number));
- if (regexp_cache->GetRegExp(metadata->leading_digits()).Consume(
- number.get())) {
+ reg_exps_->regexp_factory_->CreateInput(national_number));
+ if (reg_exps_->regexp_cache_->
+ GetRegExp(metadata->leading_digits()).Consume(number.get())) {
*region_code = *it;
return;
}
int PhoneNumberUtil::GetCountryCodeForRegion(const string& region_code) const {
if (!IsValidRegionCode(region_code)) {
- LOG(ERROR) << "Invalid or unknown region code provided.";
+ LOG(WARNING) << "Invalid or unknown region code (" << region_code
+ << ") provided.";
return 0;
}
+ return GetCountryCodeForValidRegion(region_code);
+}
+
+int PhoneNumberUtil::GetCountryCodeForValidRegion(
+ const string& region_code) const {
const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
return metadata->country_code();
}
// Gets a valid fixed-line number for the specified region_code. Returns false
-// if the country was unknown or if no number exists.
+// if the region was unknown or 001 (representing non-geographical regions), or
+// if no number exists.
bool PhoneNumberUtil::GetExampleNumber(const string& region_code,
PhoneNumber* number) const {
DCHECK(number);
- return GetExampleNumberForType(region_code,
- FIXED_LINE,
- number);
+ return GetExampleNumberForType(region_code, FIXED_LINE, number);
}
// Gets a valid number for the specified region_code and type. Returns false if
-// the country was unknown or if no number exists.
+// the country was unknown or 001 (representing non-geographical regions), or if
+// no number exists.
bool PhoneNumberUtil::GetExampleNumberForType(
const string& region_code,
PhoneNumberUtil::PhoneNumberType type,
PhoneNumber* number) const {
DCHECK(number);
if (!IsValidRegionCode(region_code)) {
- LOG(WARNING) << "Invalid or unknown region code provided.";
+ LOG(WARNING) << "Invalid or unknown region code (" << region_code
+ << ") provided.";
return false;
}
const PhoneMetadata* region_metadata = GetMetadataForRegion(region_code);
- const PhoneNumberDesc* description =
- GetNumberDescByType(*region_metadata, type);
- if (description && description->has_example_number()) {
- return (Parse(description->example_number(),
- region_code,
- number) == NO_PARSING_ERROR);
+ const PhoneNumberDesc* desc = GetNumberDescByType(*region_metadata, type);
+ if (desc && desc->has_example_number()) {
+ ErrorType success = Parse(desc->example_number(), region_code, number);
+ if (success == NO_PARSING_ERROR) {
+ return true;
+ } else {
+ LOG(ERROR) << "Error parsing example number ("
+ << static_cast<int>(success) << ")";
+ }
+ }
+ return false;
+}
+
+bool PhoneNumberUtil::GetExampleNumberForNonGeoEntity(
+ int country_calling_code, PhoneNumber* number) const {
+ DCHECK(number);
+ const PhoneMetadata* metadata =
+ GetMetadataForNonGeographicalRegion(country_calling_code);
+ if (metadata) {
+ const PhoneNumberDesc& desc = metadata->general_desc();
+ if (desc.has_example_number()) {
+ ErrorType success = Parse(StrCat(kPlusSign,
+ SimpleItoa(country_calling_code),
+ desc.example_number()),
+ RegionCode::ZZ(), number);
+ if (success == NO_PARSING_ERROR) {
+ return true;
+ } else {
+ LOG(ERROR) << "Error parsing example number ("
+ << static_cast<int>(success) << ")";
+ }
+ }
+ } else {
+ LOG(WARNING) << "Invalid or unknown country calling code provided: "
+ << country_calling_code;
}
return false;
}
const string& default_region) const {
if (!IsValidRegionCode(default_region) && !number_to_parse.empty()) {
const scoped_ptr<RegExpInput> number(
- regexp_factory->CreateInput(number_to_parse));
- if (!plus_chars_pattern->Consume(number.get())) {
+ reg_exps_->regexp_factory_->CreateInput(number_to_parse));
+ if (!reg_exps_->plus_chars_pattern_->Consume(number.get())) {
return false;
}
}
return true;
}
+// 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);
+ if (index_of_phone_context != string::npos) {
+ 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. Note we also handle the case when "tel:" is
+ // missing, as we have seen in some of the phone number inputs. In that
+ // case, we append everything from the beginning.
+ size_t index_of_rfc_prefix = number_to_parse.find(kRfc3966Prefix);
+ int index_of_national_number = (index_of_rfc_prefix != string::npos) ?
+ index_of_rfc_prefix + strlen(kRfc3966Prefix) : 0;
+ StrAppend(
+ national_number,
+ number_to_parse.substr(
+ index_of_national_number,
+ index_of_phone_context - index_of_national_number));
+ } 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);
+ }
+
+ // 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 check_region,
PhoneNumber* phone_number) const {
DCHECK(phone_number);
- // Extract a possible number from the string passed in (this strips leading
- // characters that could not be the start of a phone number.)
+
string national_number;
- ExtractPossibleNumber(number_to_parse, &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;
&normalized_national_number, &temp_number);
if (country_code_error != NO_PARSING_ERROR) {
const scoped_ptr<RegExpInput> number_string_piece(
- regexp_factory->CreateInput(national_number));
+ reg_exps_->regexp_factory_->CreateInput(national_number));
if ((country_code_error == INVALID_COUNTRY_CODE_ERROR) &&
- (plus_chars_pattern->Consume(number_string_piece.get()))) {
+ (reg_exps_->plus_chars_pattern_->Consume(number_string_piece.get()))) {
normalized_national_number.assign(number_string_piece->ToString());
// Strip the plus-char, and try again.
MaybeExtractCountryCode(country_metadata,
string phone_number_region;
GetRegionCodeForCountryCode(country_code, &phone_number_region);
if (phone_number_region != default_region) {
- country_metadata = GetMetadataForRegion(phone_number_region);
+ country_metadata =
+ GetMetadataForRegionOrCallingCode(country_code, phone_number_region);
}
} else if (country_metadata) {
// If no extracted country calling code, use the region supplied instead.
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();
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);
- phone_number->MergeFrom(temp_number);
+ phone_number->Swap(&temp_number);
return NO_PARSING_ERROR;
}
for (it = number_as_unicode.begin(); it != number_as_unicode.end(); ++it) {
len = it.get_utf8(current_char);
current_char[len] = '\0';
- if (valid_start_char_pattern->FullMatch(current_char)) {
+ if (reg_exps_->valid_start_char_pattern_->FullMatch(current_char)) {
break;
}
}
<< *extracted_number;
// Now remove any extra numbers at the end.
- capture_up_to_second_number_start_pattern->PartialMatch(*extracted_number,
- extracted_number);
+ reg_exps_->capture_up_to_second_number_start_pattern_->
+ PartialMatch(*extracted_number, extracted_number);
}
bool PhoneNumberUtil::IsPossibleNumber(const PhoneNumber& number) const {
// work if the number is possible but not valid. This would need to be
// revisited if the possible number pattern ever differed between various
// regions within those plans.
- string region_code;
- GetRegionCodeForCountryCode(country_code, ®ion_code);
- if (!HasValidRegionCode(region_code, country_code, national_number)) {
+ if (!HasValidCountryCallingCode(country_code)) {
return INVALID_COUNTRY_CODE;
}
- const PhoneNumberDesc& general_num_desc =
- GetMetadataForRegion(region_code)->general_desc();
- // Handling case of numbers with no metadata.
- if (!general_num_desc.has_national_number_pattern()) {
- size_t number_length = national_number.length();
- if (number_length < kMinLengthForNsn) {
- return TOO_SHORT;
- } else if (number_length > kMaxLengthForNsn) {
- return TOO_LONG;
- } else {
- return IS_POSSIBLE;
- }
- }
- const RegExp& possible_number_pattern = regexp_cache->GetRegExp(
- StrCat("(", general_num_desc.possible_number_pattern(), ")"));
+ string region_code;
+ GetRegionCodeForCountryCode(country_code, ®ion_code);
+ // Metadata cannot be NULL because the country calling code is valid.
+ const PhoneMetadata* metadata =
+ GetMetadataForRegionOrCallingCode(country_code, region_code);
+ const RegExp& possible_number_pattern = reg_exps_->regexp_cache_->GetRegExp(
+ StrCat("(", metadata->general_desc().possible_number_pattern(), ")"));
return TestNumberLengthAgainstPattern(possible_number_pattern,
national_number);
}
const PhoneNumber& number) const {
string region_code;
GetRegionCodeForNumber(number, ®ion_code);
- if (!IsValidRegionCode(region_code)) {
+ const PhoneMetadata* metadata =
+ GetMetadataForRegionOrCallingCode(number.country_code(), region_code);
+ if (!metadata) {
return UNKNOWN;
}
string national_significant_number;
GetNationalSignificantNumber(number, &national_significant_number);
- return GetNumberTypeHelper(national_significant_number,
- *GetMetadataForRegion(region_code));
+ return GetNumberTypeHelper(national_significant_number, *metadata);
}
bool PhoneNumberUtil::IsValidNumber(const PhoneNumber& number) const {
string region_code;
GetRegionCodeForNumber(number, ®ion_code);
- return IsValidRegionCode(region_code) &&
- IsValidNumberForRegion(number, region_code);
+ return IsValidNumberForRegion(number, region_code);
}
bool PhoneNumberUtil::IsValidNumberForRegion(const PhoneNumber& number,
const string& region_code) const {
- if (number.country_code() != GetCountryCodeForRegion(region_code)) {
+ int country_code = number.country_code();
+ const PhoneMetadata* metadata =
+ GetMetadataForRegionOrCallingCode(country_code, region_code);
+ if (!metadata ||
+ ((kRegionCodeForNonGeoEntity != region_code) &&
+ country_code != GetCountryCodeForValidRegion(region_code))) {
+ // Either the region code was invalid, or the country calling code for this
+ // number does not match that of the region code.
return false;
}
- const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
- const PhoneNumberDesc& general_desc = metadata->general_desc();
string national_number;
GetNationalSignificantNumber(number, &national_number);
- // For regions where we don't have metadata for PhoneNumberDesc, we treat
- // any number passed in as a valid number if its national significant number
- // is between the minimum and maximum lengths defined by ITU for a national
- // significant number.
- if (!general_desc.has_national_number_pattern()) {
- VLOG(3) << "Validating number with incomplete metadata.";
- size_t number_length = national_number.length();
- return number_length > kMinLengthForNsn &&
- number_length <= kMaxLengthForNsn;
- }
return GetNumberTypeHelper(national_number, *metadata) != 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, ®ion_code);
const PhoneMetadata* main_metadata_for_calling_code =
- GetMetadataForRegion(region_code);
+ GetMetadataForRegionOrCallingCode(country_calling_code, region_code);
if (!main_metadata_for_calling_code) return false;
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 {
+ if (!IsNumberMatchingDesc(national_number, metadata.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());
}
const PhoneNumber& number) const {
string region_code;
GetRegionCodeForNumber(number, ®ion_code);
- if (!IsValidRegionCode(region_code)) {
+ const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
+ if (!metadata) {
return 0;
}
- const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
- DCHECK(metadata);
- if (!metadata->has_national_prefix()) {
+ // 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.
+ if (!metadata->has_national_prefix() && !number.italian_leading_zero()) {
return 0;
}
- string national_significant_number;
- GetNationalSignificantNumber(number, &national_significant_number);
- PhoneNumberType type = GetNumberTypeHelper(national_significant_number,
- *metadata);
- // 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;
}
string formatted_number;
Format(copied_proto, INTERNATIONAL, &formatted_number);
const scoped_ptr<RegExpInput> i18n_number(
- regexp_factory->CreateInput(formatted_number));
+ reg_exps_->regexp_factory_->CreateInput(formatted_number));
string digit_group;
string ndc;
string third_group;
for (int i = 0; i < 3; ++i) {
- if (!capturing_ascii_digits_pattern->FindAndConsume(i18n_number.get(),
- &digit_group)) {
+ if (!reg_exps_->capturing_ascii_digits_pattern_->FindAndConsume(
+ i18n_number.get(), &digit_group)) {
// We should find at least three groups.
return 0;
}
third_group = digit_group;
}
}
- string region_code;
- GetRegionCodeForNumber(number, ®ion_code);
- if (region_code == "AR" &&
- GetNumberType(number) == MOBILE) {
- // Argentinian mobile numbers, when formatted in the international format,
- // are in the form of +54 9 NDC XXXX.... As a result, we take the length of
- // the third group (NDC) and add 1 for the digit 9, which also forms part of
- // the national significant number.
- return third_group.size() + 1;
+
+ if (GetNumberType(number) == MOBILE) {
+ // For example Argentinian mobile numbers, when formatted in the
+ // international format, are in the form of +54 9 NDC XXXX.... As a result,
+ // we take the length of the third group (NDC) and add the length of the
+ // mobile token, which also forms part of the national significant number.
+ // This assumes that the mobile token is always formatted separately from
+ // the rest of the phone number.
+ string mobile_token;
+ GetCountryMobileToken(number.country_code(), &mobile_token);
+ if (!mobile_token.empty()) {
+ return third_group.size() + mobile_token.size();
+ }
}
return ndc.size();
}
+void PhoneNumberUtil::GetCountryMobileToken(int country_calling_code,
+ string* mobile_token) const {
+ DCHECK(mobile_token);
+ map<int, char>::iterator it = reg_exps_->mobile_token_mappings_.find(
+ country_calling_code);
+ if (it != reg_exps_->mobile_token_mappings_.end()) {
+ *mobile_token = it->second;
+ } else {
+ mobile_token->assign("");
+ }
+}
+
void PhoneNumberUtil::NormalizeDigitsOnly(string* number) const {
DCHECK(number);
- const RegExp& non_digits_pattern = regexp_cache->GetRegExp(
+ const RegExp& non_digits_pattern = reg_exps_->regexp_cache_->GetRegExp(
StrCat("[^", kDigits, "]"));
// Delete everything that isn't valid digits.
non_digits_pattern.GlobalReplace(number, "");
number->assign(NormalizeUTF8::NormalizeDecimalDigits(*number));
}
+void PhoneNumberUtil::NormalizeDiallableCharsOnly(string* number) const {
+ DCHECK(number);
+ NormalizeHelper(reg_exps_->diallable_char_mappings_,
+ true /* remove non matches */, number);
+}
+
bool PhoneNumberUtil::IsAlphaNumber(const string& number) const {
if (!IsViablePhoneNumber(number)) {
// Number is too short, or doesn't match the basic phone number pattern.
string number_copy(number);
string extension;
MaybeStripExtension(&number_copy, &extension);
- return valid_alpha_phone_pattern->FullMatch(number_copy);
+ return reg_exps_->valid_alpha_phone_pattern_->FullMatch(number_copy);
}
void PhoneNumberUtil::ConvertAlphaCharactersInNumber(string* number) const {
DCHECK(number);
- NormalizeHelper(*alpha_phone_mappings, false, number);
+ NormalizeHelper(reg_exps_->alpha_phone_mappings_, false, number);
}
// Normalizes a string of characters representing a phone number. This performs
// - Spurious alpha characters are stripped.
void PhoneNumberUtil::Normalize(string* number) const {
DCHECK(number);
- if (valid_alpha_phone_pattern->PartialMatch(*number)) {
- NormalizeHelper(*alpha_phone_mappings, true, number);
+ if (reg_exps_->valid_alpha_phone_pattern_->PartialMatch(*number)) {
+ NormalizeHelper(reg_exps_->alpha_phone_mappings_, true, number);
}
NormalizeDigitsOnly(number);
}
VLOG(2) << "Number too short to be viable:" << number;
return false;
}
- return valid_phone_number_pattern->FullMatch(number);
+ return reg_exps_->valid_phone_number_pattern_->FullMatch(number);
}
// Strips the IDD from the start of the number if present. Helper function used
string* number) const {
DCHECK(number);
const scoped_ptr<RegExpInput> number_copy(
- regexp_factory->CreateInput(*number));
+ reg_exps_->regexp_factory_->CreateInput(*number));
// First attempt to strip the idd_pattern at the start, if present. We make a
// copy so that we can revert to the original string if necessary.
if (idd_pattern.Consume(number_copy.get())) {
// Only strip this if the first digit after the match is not a 0, since
// country calling codes cannot begin with 0.
string extracted_digit;
- if (capturing_digit_pattern->PartialMatch(number_copy->ToString(),
- &extracted_digit)) {
+ if (reg_exps_->capturing_digit_pattern_->PartialMatch(
+ number_copy->ToString(), &extracted_digit)) {
NormalizeDigitsOnly(&extracted_digit);
if (extracted_digit == "0") {
return false;
return PhoneNumber::FROM_DEFAULT_COUNTRY;
}
const scoped_ptr<RegExpInput> number_string_piece(
- regexp_factory->CreateInput(*number));
- if (plus_chars_pattern->Consume(number_string_piece.get())) {
+ reg_exps_->regexp_factory_->CreateInput(*number));
+ if (reg_exps_->plus_chars_pattern_->Consume(number_string_piece.get())) {
number->assign(number_string_piece->ToString());
// Can now normalize the rest of the number since we've consumed the "+"
// sign at the start.
return PhoneNumber::FROM_NUMBER_WITH_PLUS_SIGN;
}
// Attempt to parse the first digits as an international prefix.
- const RegExp& idd_pattern = regexp_cache->GetRegExp(possible_idd_prefix);
+ const RegExp& idd_pattern =
+ reg_exps_->regexp_cache_->GetRegExp(possible_idd_prefix);
Normalize(number);
return ParsePrefixAsIdd(idd_pattern, number)
? PhoneNumber::FROM_NUMBER_WITH_IDD
// Strips any national prefix (such as 0, 1) present in the number provided.
// The number passed in should be the normalized telephone number that we wish
// to strip any national dialing prefix from. The metadata should be for the
-// region that we think this number is from.
-void PhoneNumberUtil::MaybeStripNationalPrefixAndCarrierCode(
+// region that we think this number is from. Returns true if a national prefix
+// and/or carrier code was stripped.
+bool PhoneNumberUtil::MaybeStripNationalPrefixAndCarrierCode(
const PhoneMetadata& metadata,
string* number,
string* carrier_code) const {
if (number->empty() || possible_national_prefix.empty()) {
// Early return for numbers of zero length or with no national prefix
// possible.
- return;
+ return false;
}
// We use two copies here since Consume modifies the phone number, and if the
// first if-clause fails the number will already be changed.
const scoped_ptr<RegExpInput> number_copy(
- regexp_factory->CreateInput(*number));
+ reg_exps_->regexp_factory_->CreateInput(*number));
const scoped_ptr<RegExpInput> number_copy_without_transform(
- regexp_factory->CreateInput(*number));
+ reg_exps_->regexp_factory_->CreateInput(*number));
string number_string_copy(*number);
string captured_part_of_prefix;
- const RegExp& national_number_rule = regexp_cache->GetRegExp(
+ const RegExp& national_number_rule = reg_exps_->regexp_cache_->GetRegExp(
metadata.general_desc().national_number_pattern());
// Check if the original number is viable.
bool is_viable_original_number = national_number_rule.FullMatch(*number);
// copy so that we can revert to the original string if necessary.
const string& transform_rule = metadata.national_prefix_transform_rule();
const RegExp& possible_national_prefix_pattern =
- regexp_cache->GetRegExp(possible_national_prefix);
+ reg_exps_->regexp_cache_->GetRegExp(possible_national_prefix);
if (!transform_rule.empty() &&
(possible_national_prefix_pattern.Consume(
number_copy.get(), &carrier_code_temp, &captured_part_of_prefix) ||
transform_rule);
if (is_viable_original_number &&
!national_number_rule.FullMatch(number_string_copy)) {
- return;
+ return false;
}
number->assign(number_string_copy);
if (carrier_code) {
number_copy_without_transform->ToString();
if (is_viable_original_number &&
!national_number_rule.FullMatch(number_copy_as_string)) {
- return;
+ return false;
}
number->assign(number_copy_as_string);
if (carrier_code) {
carrier_code->assign(carrier_code_temp);
}
} else {
+ return false;
VLOG(4) << "The first digits did not match the national prefix.";
}
+ return true;
}
// Strips any extension (as in, the part of the number dialled after the call is
string possible_extension_three;
string number_copy(*number);
const scoped_ptr<RegExpInput> number_copy_as_regexp_input(
- regexp_factory->CreateInput(number_copy));
- if (extn_pattern->Consume(number_copy_as_regexp_input.get(),
+ reg_exps_->regexp_factory_->CreateInput(number_copy));
+ if (reg_exps_->extn_pattern_->Consume(number_copy_as_regexp_input.get(),
false,
&possible_extension_one,
&possible_extension_two,
&possible_extension_three)) {
// Replace the extensions in the original string here.
- extn_pattern->Replace(&number_copy, "");
+ reg_exps_->extn_pattern_->Replace(&number_copy, "");
VLOG(4) << "Found an extension. Possible extension one: "
<< possible_extension_one
<< ". Possible extension two: " << possible_extension_two
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;
const PhoneNumberDesc& general_num_desc =
default_region_metadata->general_desc();
const RegExp& valid_number_pattern =
- regexp_cache->GetRegExp(general_num_desc.national_number_pattern());
+ reg_exps_->regexp_cache_->GetRegExp(
+ general_num_desc.national_number_pattern());
MaybeStripNationalPrefixAndCarrierCode(*default_region_metadata,
&potential_national_number,
NULL);
VLOG(4) << "Number without country calling code prefix: "
<< potential_national_number;
- const RegExp& possible_number_pattern = regexp_cache->GetRegExp(
- StrCat("(", general_num_desc.possible_number_pattern(), ")"));
+ const RegExp& possible_number_pattern =
+ reg_exps_->regexp_cache_->GetRegExp(
+ StrCat("(", general_num_desc.possible_number_pattern(), ")"));
// If the number was not valid before but is valid now, or if it was too
// long before, we consider the number with the country code stripped to
// be a better result and keep that instead.
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;
GetRegionCodeForNumber(number, ®ion_code);
- string national_significant_number;
- GetNationalSignificantNumber(number, &national_significant_number);
- if (!HasValidRegionCode(region_code, number.country_code(),
- national_significant_number)) {
+ 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(
national_significant_number, metadata->no_international_dialling());
}