blob: 30625940d5986d8f4042b566e56174eeb7fddd3a [file] [log] [blame]
// Copyright (C) 2009 The Libphonenumber Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Author: Shaopeng Jia
// Open-sourced by: Philippe Liard
#include "phonenumbers/phonenumberutil.h"
#include <algorithm>
#include <cctype>
#include <cstddef>
#include <fstream>
#include <iostream>
#include <iterator>
#include <map>
#include <sstream>
#include <utility>
#include <vector>
#include <google/protobuf/message_lite.h>
#include <unicode/uchar.h>
#include <unicode/utf8.h>
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "phonenumbers/asyoutypeformatter.h"
#include "phonenumbers/default_logger.h"
#include "phonenumbers/encoding_utils.h"
#include "phonenumbers/metadata.h"
#include "phonenumbers/normalize_utf8.h"
#include "phonenumbers/phonemetadata.pb.h"
#include "phonenumbers/phonenumber.h"
#include "phonenumbers/phonenumber.pb.h"
#include "phonenumbers/regexp_adapter.h"
#include "phonenumbers/regexp_cache.h"
#include "phonenumbers/regexp_factory.h"
#include "phonenumbers/region_code.h"
#include "phonenumbers/stl_util.h"
#include "phonenumbers/stringutil.h"
#include "phonenumbers/utf/unicodetext.h"
#include "phonenumbers/utf/utf.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
const char PhoneNumberUtil::kPlusChars[] = "+\xEF\xBC\x8B"; /* "++" */
// To find out the unicode code-point of the characters below in vim, highlight
// the character and type 'ga'. Note that the - is used to express ranges of
// full-width punctuation below, as well as being present in the expression
// itself. In emacs, you can use M-x unicode-what to query information about the
// unicode character.
// static
const char PhoneNumberUtil::kValidPunctuation[] =
/* "-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";
// 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.
// 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;
// The prefix that needs to be inserted in front of a Colombian landline
// number when dialed from a mobile phone in Colombia.
const char kColombiaMobileToFixedLinePrefix[] = "3";
// The kPlusSign signifies the international prefix.
const char kPlusSign[] = "+";
scoped_ptr<const RegExp> plus_chars_pattern;
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 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
// formatted. For example, if you wish the default extension formatting to be "
// extn: 3456", then you should specify " extn: " here as the default extension
// prefix. This can be overridden by region-specific preferences.
const char kDefaultExtnPrefix[] = " ext. ";
// 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;
return false;
}
return true;
}
// Returns a pointer to the description inside the metadata of the appropriate
// type.
const PhoneNumberDesc* GetNumberDescByType(
const PhoneMetadata& metadata,
PhoneNumberUtil::PhoneNumberType type) {
switch (type) {
case PhoneNumberUtil::PREMIUM_RATE:
return &metadata.premium_rate();
case PhoneNumberUtil::TOLL_FREE:
return &metadata.toll_free();
case PhoneNumberUtil::MOBILE:
return &metadata.mobile();
case PhoneNumberUtil::FIXED_LINE:
case PhoneNumberUtil::FIXED_LINE_OR_MOBILE:
return &metadata.fixed_line();
case PhoneNumberUtil::SHARED_COST:
return &metadata.shared_cost();
case PhoneNumberUtil::VOIP:
return &metadata.voip();
case PhoneNumberUtil::PERSONAL_NUMBER:
return &metadata.personal_number();
case PhoneNumberUtil::PAGER:
return &metadata.pager();
case PhoneNumberUtil::UAN:
return &metadata.uan();
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) {
switch (number_format) {
case PhoneNumberUtil::E164:
formatted_number->assign(StrCat(kPlusSign,
SimpleItoa(country_calling_code),
formatted_national_number,
formatted_extension));
return;
case PhoneNumberUtil::INTERNATIONAL:
formatted_number->assign(StrCat(kPlusSign,
SimpleItoa(country_calling_code),
" ",
formatted_national_number,
formatted_extension));
return;
case PhoneNumberUtil::RFC3966:
formatted_number->assign(StrCat(kPlusSign,
SimpleItoa(country_calling_code),
"-",
formatted_national_number,
formatted_extension));
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);
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
// the same.
bool IsNationalNumberSuffixOfTheOther(const PhoneNumber& first_number,
const PhoneNumber& second_number) {
const string& first_number_national_number =
SimpleItoa(static_cast<uint64>(first_number.national_number()));
const string& second_number_national_number =
SimpleItoa(static_cast<uint64>(second_number.national_number()));
// Note that HasSuffixString returns true if the numbers are equal.
return HasSuffixString(first_number_national_number,
second_number_national_number) ||
HasSuffixString(second_number_national_number,
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.
// Note that there are currently three capturing groups for the extension itself
// - if this number is changed, MaybeStripExtension needs to be updated.
string CreateExtnPattern(const string& single_extn_symbols) {
static const string capturing_extn_digits = StrCat("([", kDigits, "]{1,7})");
// The first regular expression covers RFC 3966 format, where the extension is
// added using ";ext=". The second more generic one starts with optional white
// space and ends with an optional full stop (.), followed by zero or more
// spaces/tabs and then the numbers themselves. The third one covers the
// special case of American numbers where the extension is written with a hash
// at the end, such as "- 503#".
// Note that the only capturing groups should be around the digits that you
// want to capture as part of the extension, or else parsing will fail!
// Canonical-equivalence doesn't seem to be an option with RE2, so we allow
// two options for representing the ó - the character itself, and one in the
// unicode decomposed form with the combining acute accent.
return (StrCat(
kRfc3966ExtnPrefix, capturing_extn_digits, "|"
/* "[  \\t,]*(?:ext(?:ensi(?:ó?|ó))?n?|extn?|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?|"
"[", single_extn_symbols, "]|int|"
"\xEF\xBD\x89\xEF\xBD\x8E\xEF\xBD\x94|anexo)"
"[:\\.\xEF\xBC\x8E]?[ \xC2\xA0\\t,-]*", capturing_extn_digits,
"#?|[- ]+([", kDigits, "]{1,5})#"));
}
// Normalizes a string of characters representing a phone number by replacing
// all characters found in the accompanying map with the values therein, and
// stripping all other characters if remove_non_matches is true.
// Parameters:
// number - a pointer to a string of characters representing a phone number to
// be normalized.
// normalization_replacements - a mapping of characters to what they should be
// replaced by in the normalized version of the phone number
// remove_non_matches - indicates whether characters that are not able to be
// replaced should be stripped from the number. If this is false, they will be
// left unchanged in the number.
void NormalizeHelper(const map<char32, char>& normalization_replacements,
bool remove_non_matches,
string* number) {
DCHECK(number);
UnicodeText number_as_unicode;
number_as_unicode.PointToUTF8(number->data(), number->size());
string normalized_number;
char unicode_char[5];
for (UnicodeText::const_iterator it = number_as_unicode.begin();
it != number_as_unicode.end();
++it) {
map<char32, char>::const_iterator found_glyph_pair =
normalization_replacements.find(*it);
if (found_glyph_pair != normalization_replacements.end()) {
normalized_number.push_back(found_glyph_pair->second);
} else if (!remove_non_matches) {
// Find out how long this unicode char is so we can append it all.
int char_len = it.get_utf8(unicode_char);
normalized_number.append(unicode_char, char_len);
}
// If neither of the above are true, we remove this character.
}
number->assign(normalized_number);
}
PhoneNumberUtil::ValidationResult TestNumberLengthAgainstPattern(
const RegExp& number_pattern, const string& number) {
string extracted_number;
if (number_pattern.FullMatch(number, &extracted_number)) {
return PhoneNumberUtil::IS_POSSIBLE;
}
if (number_pattern.PartialMatch(number, &extracted_number)) {
return PhoneNumberUtil::TOO_LONG;
} else {
return PhoneNumberUtil::TOO_SHORT;
}
}
} // namespace
void PhoneNumberUtil::SetLogger(Logger* logger) {
Logger::set_logger_impl(logger);
}
// Private constructor. Also takes care of initialisation.
PhoneNumberUtil::PhoneNumberUtil()
: 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());
Logger::set_logger_impl(logger_.get());
PhoneMetadataCollection metadata_collection;
if (!LoadCompiledInMetadata(&metadata_collection)) {
LOG(DFATAL) << "Could not parse compiled-in metadata.";
return;
}
// Storing data in a temporary map to make it easier to find other regions
// that share a country calling code when inserting data.
map<int, list<string>* > country_calling_code_to_region_map;
for (RepeatedPtrField<PhoneMetadata>::const_iterator it =
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));
int country_calling_code = it->country_code();
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()) {
calling_code_in_map->second->push_front(region_code);
} else {
calling_code_in_map->second->push_back(region_code);
}
} else {
// For most country calling codes, there will be only one region 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));
}
if (country_calling_code == kNanpaCountryCode) {
nanpa_regions_->insert(region_code);
}
}
country_calling_code_to_region_code_map_->insert(
country_calling_code_to_region_code_map_->begin(),
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();
}
PhoneNumberUtil::~PhoneNumberUtil() {
STLDeleteContainerPairSecondPointers(
country_calling_code_to_region_code_map_->begin(),
country_calling_code_to_region_code_map_->end());
}
// 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, "]+")));
}
const string& PhoneNumberUtil::GetExtnPatternsForMatching() const {
return *(extn_patterns_for_matching.get());
}
bool PhoneNumberUtil::ContainsOnlyValidDigits(const string& s) const {
return digits_pattern->FullMatch(s);
}
void PhoneNumberUtil::TrimUnwantedEndChars(string* number) const {
DCHECK(number);
UnicodeText number_as_unicode;
number_as_unicode.PointToUTF8(number->data(), number->size());
char current_char[5];
int len;
UnicodeText::const_reverse_iterator reverse_it(number_as_unicode.end());
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)) {
break;
}
}
number->assign(UnicodeText::UTF8Substring(number_as_unicode.begin(),
reverse_it.base()));
}
bool PhoneNumberUtil::IsFormatEligibleForAsYouTypeFormatter(
const string& format) const {
// A pattern that is used to determine if a numberFormat under
// availableFormats is eligible to be used by the AYTF. It is eligible when
// the format element under numberFormat contains groups of the dollar sign
// 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(
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);
}
}
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.";
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
// prefixes, these would have to be removed here as well.
strrmm(national_prefix, "~");
}
}
bool PhoneNumberUtil::IsValidRegionCode(const string& region_code) const {
return (region_to_metadata_map_->find(region_code) !=
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;
}
// Returns a pointer to the phone metadata for the appropriate region.
const PhoneMetadata* PhoneNumberUtil::GetMetadataForRegion(
const string& region_code) const {
map<string, PhoneMetadata>::const_iterator it =
region_to_metadata_map_->find(region_code);
if (it != region_to_metadata_map_->end()) {
return &it->second;
}
return NULL;
}
void PhoneNumberUtil::Format(const PhoneNumber& number,
PhoneNumberFormat number_format,
string* formatted_number) const {
DCHECK(formatted_number);
if (number.national_number() == 0) {
const string& raw_input = number.raw_input();
if (!raw_input.empty()) {
formatted_number->assign(raw_input);
return;
}
}
int country_calling_code = number.country_code();
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);
return;
}
// Note here that all NANPA formatting rules are contained by US, so we use
// that to format NANPA numbers. The same applies to Russian Fed regions -
// rules are contained by Russia. French Indian Ocean country rules are
// contained by Réunion.
string region_code;
GetRegionCodeForCountryCode(country_calling_code, &region_code);
if (!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);
}
void PhoneNumberUtil::FormatByPattern(
const PhoneNumber& number,
PhoneNumberFormat number_format,
const RepeatedPtrField<NumberFormat>& user_defined_formats,
string* formatted_number) const {
DCHECK(formatted_number);
int country_calling_code = number.country_code();
// Note GetRegionCodeForCountryCode() is used because formatting information
// for regions which share a country calling code is contained by only one
// region for performance reasons. For example, for NANPA regions it will be
// contained in the metadata for US.
string region_code;
GetRegionCodeForCountryCode(country_calling_code, &region_code);
string national_significant_number;
GetNationalSignificantNumber(number, &national_significant_number);
if (!HasValidRegionCode(region_code, country_calling_code,
national_significant_number)) {
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 national_prefix_formatting_rule(
it->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);
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(
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();
}
} else {
user_defined_formats_copy.Add()->MergeFrom(*it);
}
}
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);
}
void PhoneNumberUtil::FormatNationalNumberWithCarrierCode(
const PhoneNumber& number,
const string& carrier_code,
string* formatted_number) const {
int country_calling_code = number.country_code();
string national_significant_number;
GetNationalSignificantNumber(number, &national_significant_number);
// Note GetRegionCodeForCountryCode() is used because formatting information
// for regions which share a country calling code is contained by only one
// region for performance reasons. For example, for NANPA regions it will be
// contained in the metadata for US.
string region_code;
GetRegionCodeForCountryCode(country_calling_code, &region_code);
if (!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);
}
void PhoneNumberUtil::FormatNationalNumberWithPreferredCarrierCode(
const PhoneNumber& number,
const string& fallback_carrier_code,
string* formatted_number) const {
FormatNationalNumberWithCarrierCode(
number,
number.has_preferred_domestic_carrier_code()
? number.preferred_domestic_carrier_code()
: fallback_carrier_code,
formatted_number);
}
void PhoneNumberUtil::FormatNumberForMobileDialing(
const PhoneNumber& number,
const string& calling_from,
bool with_formatting,
string* formatted_number) const {
string region_code;
GetRegionCodeForNumber(number, &region_code);
if (!IsValidRegionCode(region_code)) {
formatted_number->assign(number.has_raw_input() ? number.raw_input() : "");
return;
}
// Clear the extension, as that part cannot normally be dialed together with
// the main number.
PhoneNumber number_no_extension(number);
number_no_extension.clear_extension();
PhoneNumberType number_type = GetNumberType(number_no_extension);
if ((region_code == "CO") && (calling_from == "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()) {
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 (CanBeInternationallyDialled(number_no_extension)) {
with_formatting
? Format(number_no_extension, INTERNATIONAL, formatted_number)
: Format(number_no_extension, E164, formatted_number);
return;
} else if (calling_from == region_code) {
Format(number_no_extension, NATIONAL, formatted_number);
} else {
formatted_number->assign("");
}
if (!with_formatting) {
NormalizeDigitsOnly(formatted_number);
}
}
void PhoneNumberUtil::FormatOutOfCountryCallingNumber(
const PhoneNumber& number,
const string& calling_from,
string* formatted_number) const {
DCHECK(formatted_number);
if (!IsValidRegionCode(calling_from)) {
VLOG(1) << "Trying to format number from invalid region. International"
<< " formatting applied.";
Format(number, INTERNATIONAL, formatted_number);
return;
}
int country_code = number.country_code();
string region_code;
GetRegionCodeForCountryCode(country_code, &region_code);
string national_significant_number;
GetNationalSignificantNumber(number, &national_significant_number);
if (!HasValidRegionCode(region_code, country_code,
national_significant_number)) {
formatted_number->assign(national_significant_number);
return;
}
if (country_code == kNanpaCountryCode) {
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));
return;
}
} else if (country_code == GetCountryCodeForRegion(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.
// For regions that share a country calling code, the country calling code
// need not be dialled. This also applies when dialling within a region, so
// this if clause covers both these cases.
// Technically this is the case for dialling from la Réunion to other
// overseas departments of France (French Guiana, Martinique, Guadeloupe),
// but not vice versa - so we don't cover this edge case for now and for
// those cases return the version including country calling code.
// Details here:
// http://www.petitfute.com/voyage/225-info-pratiques-reunion
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);
// 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());
if (!international_prefix_for_formatting.empty()) {
formatted_number->assign(
StrCat(international_prefix_for_formatting, " ", country_code, " ",
formatted_national_number, formatted_extension));
} else {
FormatNumberByFormat(country_code, INTERNATIONAL, formatted_national_number,
formatted_extension, formatted_number);
}
}
void PhoneNumberUtil::FormatInOriginalFormat(const PhoneNumber& number,
const string& region_calling_from,
string* formatted_number) const {
DCHECK(formatted_number);
if (number.has_raw_input() && !IsValidNumber(number)) {
formatted_number->assign(number.raw_input());
return;
}
if (!number.has_country_code_source()) {
Format(number, NATIONAL, formatted_number);
return;
}
switch (number.country_code_source()) {
case PhoneNumber::FROM_NUMBER_WITH_PLUS_SIGN:
Format(number, INTERNATIONAL, formatted_number);
return;
case PhoneNumber::FROM_NUMBER_WITH_IDD:
FormatOutOfCountryCallingNumber(number, region_calling_from,
formatted_number);
return;
case PhoneNumber::FROM_NUMBER_WITHOUT_PLUS_SIGN:
Format(number, INTERNATIONAL, formatted_number);
formatted_number->erase(formatted_number->begin());
return;
case PhoneNumber::FROM_DEFAULT_COUNTRY:
default:
Format(number, NATIONAL, formatted_number);
}
}
void PhoneNumberUtil::FormatOutOfCountryKeepingAlphaChars(
const PhoneNumber& number,
const string& calling_from,
string* formatted_number) const {
// If there is no raw input, then we can't keep alpha characters because there
// aren't any. In this case, we return FormatOutOfCountryCallingNumber.
if (number.raw_input().empty()) {
FormatOutOfCountryCallingNumber(number, calling_from, formatted_number);
return;
}
string region_code;
GetRegionCodeForCountryCode(number.country_code(), &region_code);
if (!HasValidRegionCode(region_code, number.country_code(),
number.raw_input())) {
formatted_number->assign(number.raw_input());
return;
}
// Strip any prefix such as country calling code, IDD, that was present. We do
// 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);
// 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
// national number was less than three digits, we don't trim anything at all.
string national_number;
GetNationalSignificantNumber(number, &national_number);
if (national_number.length() > 3) {
size_t first_national_number_digit =
raw_input_copy.find(national_number.substr(0, 3));
if (first_national_number_digit != string::npos) {
raw_input_copy = raw_input_copy.substr(first_national_number_digit);
}
}
const PhoneMetadata* metadata = GetMetadataForRegion(calling_from);
if (number.country_code() == kNanpaCountryCode) {
if (IsNANPACountry(calling_from)) {
formatted_number->assign(StrCat(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");
}
// 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.
// 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);
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());
if (!international_prefix_for_formatting.empty()) {
formatted_number->assign(
StrCat(international_prefix_for_formatting, " ", number.country_code(),
" ", raw_input_copy));
} else {
FormatNumberByFormat(number.country_code(), INTERNATIONAL, raw_input_copy,
"", formatted_number);
}
}
void PhoneNumberUtil::FormatNationalNumber(
const string& number,
const string& region_code,
PhoneNumberFormat number_format,
string* formatted_number) const {
DCHECK(formatted_number);
FormatNationalNumberWithCarrier(number, region_code, number_format, "",
formatted_number);
}
// Note in some regions, the national number can be written in two completely
// different ways depending on whether it forms part of the NATIONAL format or
// INTERNATIONAL format. The 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 {
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, "-");
}
}
// 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(
const PhoneNumber& number,
const string& region_code,
PhoneNumberFormat number_format,
string* extension) const {
DCHECK(extension);
if (!number.has_extension() || number.extension().length() == 0) {
extension->assign("");
} else {
if (number_format == RFC3966) {
StrAppend(extension, kRfc3966ExtnPrefix, number.extension());
return;
}
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));
}
}
bool PhoneNumberUtil::IsNANPACountry(const string& region_code) const {
return nanpa_regions_->find(region_code) != nanpa_regions_->end();
}
// Returns the region codes that matches the specific country calling code. In
// the case of no region code being found, region_codes will be left empty.
void PhoneNumberUtil::GetRegionCodesForCountryCallingCode(
int country_calling_code,
list<string>* region_codes) const {
DCHECK(region_codes);
// Create a 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;
typedef vector<IntRegionsPair>::const_iterator ConstIterator;
pair<ConstIterator, ConstIterator> range = equal_range(
country_calling_code_to_region_code_map_->begin(),
country_calling_code_to_region_code_map_->end(),
target_pair, OrderByFirst());
if (range.first != range.second) {
region_codes->insert(region_codes->begin(),
range.first->second->begin(),
range.first->second->end());
}
}
// Returns the region code that matches the specific country calling code. In
// the case of no region code being found, the unknown region code will be
// returned.
void PhoneNumberUtil::GetRegionCodeForCountryCode(
int country_calling_code,
string* region_code) const {
DCHECK(region_code);
list<string> region_codes;
GetRegionCodesForCountryCallingCode(country_calling_code, &region_codes);
*region_code = (region_codes.size() > 0)
? region_codes.front() : RegionCode::GetUnknown();
}
void PhoneNumberUtil::GetRegionCodeForNumber(const PhoneNumber& number,
string* region_code) const {
DCHECK(region_code);
int country_calling_code = number.country_code();
list<string> region_codes;
GetRegionCodesForCountryCallingCode(country_calling_code, &region_codes);
if (region_codes.size() == 0) {
string number_string;
GetNationalSignificantNumber(number, &number_string);
LOG(WARNING) << "Missing/invalid country calling code ("
<< country_calling_code
<< ") for number " << number_string;
*region_code = RegionCode::GetUnknown();
return;
}
if (region_codes.size() == 1) {
*region_code = region_codes.front();
} else {
GetRegionCodeForNumberFromRegionList(number, region_codes, region_code);
}
}
void PhoneNumberUtil::GetRegionCodeForNumberFromRegionList(
const PhoneNumber& number, const list<string>& region_codes,
string* region_code) const {
DCHECK(region_code);
string national_number;
GetNationalSignificantNumber(number, &national_number);
for (list<string>::const_iterator it = region_codes.begin();
it != region_codes.end(); ++it) {
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())) {
*region_code = *it;
return;
}
} else if (GetNumberTypeHelper(national_number, *metadata) != UNKNOWN) {
*region_code = *it;
return;
}
}
*region_code = RegionCode::GetUnknown();
}
int PhoneNumberUtil::GetCountryCodeForRegion(const string& region_code) const {
if (!IsValidRegionCode(region_code)) {
LOG(ERROR) << "Invalid or unknown region code provided.";
return 0;
}
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.
bool PhoneNumberUtil::GetExampleNumber(const string& region_code,
PhoneNumber* number) const {
DCHECK(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.
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.";
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);
}
return false;
}
PhoneNumberUtil::ErrorType PhoneNumberUtil::Parse(const string& number_to_parse,
const string& default_region,
PhoneNumber* number) const {
DCHECK(number);
return ParseHelper(number_to_parse, default_region, false, true, number);
}
PhoneNumberUtil::ErrorType PhoneNumberUtil::ParseAndKeepRawInput(
const string& number_to_parse,
const string& default_region,
PhoneNumber* number) const {
DCHECK(number);
return ParseHelper(number_to_parse, default_region, true, true, number);
}
// Checks to see that the region code used is valid, or if it is not valid, that
// the number to parse starts with a + symbol so that we can attempt to infer
// the country from the number. Returns false if it cannot use the region
// provided and the region cannot be inferred.
bool PhoneNumberUtil::CheckRegionForParsing(
const string& number_to_parse,
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())) {
return false;
}
}
return true;
}
PhoneNumberUtil::ErrorType PhoneNumberUtil::ParseHelper(
const string& number_to_parse,
const string& default_region,
bool keep_raw_input,
bool check_region,
PhoneNumber* phone_number) const {
DCHECK(phone_number);
// 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);
if (!IsViablePhoneNumber(national_number)) {
VLOG(2) << "The string supplied did not seem to be a phone number.";
return NOT_A_NUMBER;
}
if (check_region &&
!CheckRegionForParsing(national_number, default_region)) {
VLOG(1) << "Missing or invalid default country.";
return INVALID_COUNTRY_CODE_ERROR;
}
PhoneNumber temp_number;
if (keep_raw_input) {
temp_number.set_raw_input(number_to_parse);
}
// Attempt to parse extension first, since it doesn't require country-specific
// data and we want to have the non-normalised number here.
string extension;
MaybeStripExtension(&national_number, &extension);
if (!extension.empty()) {
temp_number.set_extension(extension);
}
const PhoneMetadata* country_metadata = GetMetadataForRegion(default_region);
// Check to see if the number is given in international format so we know
// whether this number is from the default country or not.
string normalized_national_number(national_number);
ErrorType country_code_error =
MaybeExtractCountryCode(country_metadata, keep_raw_input,
&normalized_national_number, &temp_number);
if (country_code_error != NO_PARSING_ERROR) {
const scoped_ptr<RegExpInput> number_string_piece(
regexp_factory->CreateInput(national_number));
if ((country_code_error == INVALID_COUNTRY_CODE_ERROR) &&
(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,
keep_raw_input,
&normalized_national_number,
&temp_number);
if (temp_number.country_code() == 0) {
return INVALID_COUNTRY_CODE_ERROR;
}
} else {
return country_code_error;
}
}
int country_code = temp_number.country_code();
if (country_code != 0) {
string phone_number_region;
GetRegionCodeForCountryCode(country_code, &phone_number_region);
if (phone_number_region != default_region) {
country_metadata = GetMetadataForRegion(phone_number_region);
}
} else if (country_metadata) {
// If no extracted country calling code, use the region supplied instead.
// Note that the national number was already normalized by
// MaybeExtractCountryCode.
country_code = country_metadata->country_code();
}
if (normalized_national_number.length() < kMinLengthForNsn) {
VLOG(2) << "The string supplied is too short to be a phone number.";
return TOO_SHORT_NSN;
}
if (country_metadata) {
string* carrier_code = keep_raw_input ?
temp_number.mutable_preferred_domestic_carrier_code() : NULL;
MaybeStripNationalPrefixAndCarrierCode(*country_metadata,
&normalized_national_number,
carrier_code);
}
size_t normalized_national_number_length =
normalized_national_number.length();
if (normalized_national_number_length < kMinLengthForNsn) {
VLOG(2) << "The string supplied is too short to be a phone number.";
return TOO_SHORT_NSN;
}
if (normalized_national_number_length > kMaxLengthForNsn) {
VLOG(2) << "The string supplied is too long to be a phone number.";
return TOO_LONG_NSN;
}
temp_number.set_country_code(country_code);
if (normalized_national_number[0] == '0') {
temp_number.set_italian_leading_zero(true);
}
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);
return NO_PARSING_ERROR;
}
// Attempts to extract a possible number from the string passed in. This
// currently strips all leading characters that could not be used to start a
// phone number. Characters that can be used to start a phone number are
// defined in the valid_start_char_pattern. If none of these characters are
// found in the number passed in, an empty string is returned. This function
// also attempts to strip off any alternative extensions or endings if two or
// more are present, such as in the case of: (530) 583-6985 x302/x2303. The
// second extension here makes this actually two phone numbers, (530) 583-6985
// x302 and (530) 583-6985 x2303. We remove the second extension so that the
// first number is parsed correctly.
void PhoneNumberUtil::ExtractPossibleNumber(const string& number,
string* extracted_number) const {
DCHECK(extracted_number);
UnicodeText number_as_unicode;
number_as_unicode.PointToUTF8(number.data(), number.size());
char current_char[5];
int len;
UnicodeText::const_iterator it;
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)) {
break;
}
}
if (it == number_as_unicode.end()) {
// No valid start character was found. extracted_number should be set to
// empty string.
extracted_number->assign("");
return;
}
extracted_number->assign(
UnicodeText::UTF8Substring(it, number_as_unicode.end()));
TrimUnwantedEndChars(extracted_number);
if (extracted_number->length() == 0) {
return;
}
VLOG(3) << "After stripping starting and trailing characters, left with: "
<< *extracted_number;
// Now remove any extra numbers at the end.
capture_up_to_second_number_start_pattern->PartialMatch(*extracted_number,
extracted_number);
}
bool PhoneNumberUtil::IsPossibleNumber(const PhoneNumber& number) const {
return IsPossibleNumberWithReason(number) == IS_POSSIBLE;
}
bool PhoneNumberUtil::IsPossibleNumberForString(
const string& number,
const string& region_dialing_from) const {
PhoneNumber number_proto;
if (Parse(number, region_dialing_from, &number_proto) == NO_PARSING_ERROR) {
return IsPossibleNumber(number_proto);
} else {
return false;
}
}
PhoneNumberUtil::ValidationResult PhoneNumberUtil::IsPossibleNumberWithReason(
const PhoneNumber& number) const {
string national_number;
GetNationalSignificantNumber(number, &national_number);
int country_code = number.country_code();
// Note: For Russian Fed and NANPA numbers, we just use the rules from the
// default region (US or Russia) since the GetRegionCodeForNumber will not
// 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, &region_code);
if (!HasValidRegionCode(region_code, country_code, national_number)) {
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(), ")"));
return TestNumberLengthAgainstPattern(possible_number_pattern,
national_number);
}
bool PhoneNumberUtil::TruncateTooLongNumber(PhoneNumber* number) const {
if (IsValidNumber(*number)) {
return true;
}
PhoneNumber number_copy(*number);
uint64 national_number = number->national_number();
do {
national_number /= 10;
number_copy.set_national_number(national_number);
if (IsPossibleNumberWithReason(number_copy) == TOO_SHORT ||
national_number == 0) {
return false;
}
} while (!IsValidNumber(number_copy));
number->set_national_number(national_number);
return true;
}
PhoneNumberUtil::PhoneNumberType PhoneNumberUtil::GetNumberType(
const PhoneNumber& number) const {
string region_code;
GetRegionCodeForNumber(number, &region_code);
if (!IsValidRegionCode(region_code)) {
return UNKNOWN;
}
string national_significant_number;
GetNationalSignificantNumber(number, &national_significant_number);
return GetNumberTypeHelper(national_significant_number,
*GetMetadataForRegion(region_code));
}
bool PhoneNumberUtil::IsValidNumber(const PhoneNumber& number) const {
string region_code;
GetRegionCodeForNumber(number, &region_code);
return IsValidRegionCode(region_code) &&
IsValidNumberForRegion(number, region_code);
}
bool PhoneNumberUtil::IsValidNumberForRegion(const PhoneNumber& number,
const string& region_code) const {
if (number.country_code() != GetCountryCodeForRegion(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::IsLeadingZeroPossible(int country_calling_code) const {
string region_code;
GetRegionCodeForCountryCode(country_calling_code, &region_code);
const PhoneMetadata* main_metadata_for_calling_code =
GetMetadataForRegion(region_code);
if (!main_metadata_for_calling_code) return false;
return main_metadata_for_calling_code->leading_zero_possible();
}
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
// national prefix.
StrAppend(national_number, number.italian_leading_zero() ? "0" : "");
StrAppend(national_number, number.national_number());
}
int PhoneNumberUtil::GetLengthOfGeographicalAreaCode(
const PhoneNumber& number) const {
string region_code;
GetRegionCodeForNumber(number, &region_code);
if (!IsValidRegionCode(region_code)) {
return 0;
}
const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
DCHECK(metadata);
if (!metadata->has_national_prefix()) {
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) {
return 0;
}
return GetLengthOfNationalDestinationCode(number);
}
int PhoneNumberUtil::GetLengthOfNationalDestinationCode(
const PhoneNumber& number) const {
PhoneNumber copied_proto(number);
if (number.has_extension()) {
// Clear the extension so it's not included when formatting.
copied_proto.clear_extension();
}
string formatted_number;
Format(copied_proto, INTERNATIONAL, &formatted_number);
const scoped_ptr<RegExpInput> i18n_number(
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)) {
// We should find at least three groups.
return 0;
}
if (i == 1) {
ndc = digit_group;
} else if (i == 2) {
third_group = digit_group;
}
}
string region_code;
GetRegionCodeForNumber(number, &region_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;
}
return ndc.size();
}
void PhoneNumberUtil::NormalizeDigitsOnly(string* number) const {
DCHECK(number);
const RegExp& non_digits_pattern = regexp_cache->GetRegExp(
StrCat("[^", kDigits, "]"));
// Delete everything that isn't valid digits.
non_digits_pattern.GlobalReplace(number, "");
// Normalize all decimal digits to ASCII digits.
number->assign(NormalizeUTF8::NormalizeDecimalDigits(*number));
}
bool PhoneNumberUtil::IsAlphaNumber(const string& number) const {
if (!IsViablePhoneNumber(number)) {
// Number is too short, or doesn't match the basic phone number pattern.
return false;
}
// Copy the number, since we are going to try and strip the extension from it.
string number_copy(number);
string extension;
MaybeStripExtension(&number_copy, &extension);
return valid_alpha_phone_pattern->FullMatch(number_copy);
}
void PhoneNumberUtil::ConvertAlphaCharactersInNumber(string* number) const {
DCHECK(number);
NormalizeHelper(*alpha_phone_mappings, false, number);
}
// Normalizes a string of characters representing a phone number. This performs
// the following conversions:
// - Punctuation is stripped.
// For ALPHA/VANITY numbers:
// - Letters are converted to their numeric representation on a telephone
// keypad. The keypad used here is the one defined in ITU Recommendation
// E.161. This is only done if there are 3 or more letters in the number, to
// lessen the risk that such letters are typos.
// For other numbers:
// - Wide-ascii digits are converted to normal ASCII (European) digits.
// - Arabic-Indic numerals are converted to European numerals.
// - 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);
}
NormalizeDigitsOnly(number);
}
// Checks to see if the string of characters could possibly be a phone number at
// all. At the moment, checks to see that the string begins with at least 3
// digits, ignoring any punctuation commonly found in phone numbers. This
// method does not require the number to be normalized in advance - but does
// assume that leading non-number symbols have been removed, such as by the
// method ExtractPossibleNumber.
bool PhoneNumberUtil::IsViablePhoneNumber(const string& number) const {
if (number.length() < kMinLengthForNsn) {
VLOG(2) << "Number too short to be viable:" << number;
return false;
}
return valid_phone_number_pattern->FullMatch(number);
}
// Strips the IDD from the start of the number if present. Helper function used
// by MaybeStripInternationalPrefixAndNormalize.
bool PhoneNumberUtil::ParsePrefixAsIdd(const RegExp& idd_pattern,
string* number) const {
DCHECK(number);
const scoped_ptr<RegExpInput> number_copy(
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)) {
NormalizeDigitsOnly(&extracted_digit);
if (extracted_digit == "0") {
return false;
}
}
number->assign(number_copy->ToString());
return true;
}
return false;
}
// Strips any international prefix (such as +, 00, 011) present in the number
// provided, normalizes the resulting number, and indicates if an international
// prefix was present.
//
// possible_idd_prefix represents the international direct dialing prefix from
// the region we think this number may be dialed in.
// Returns true if an international dialing prefix could be removed from the
// number, otherwise false if the number did not seem to be in international
// format.
PhoneNumber::CountryCodeSource
PhoneNumberUtil::MaybeStripInternationalPrefixAndNormalize(
const string& possible_idd_prefix,
string* number) const {
DCHECK(number);
if (number->empty()) {
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())) {
number->assign(number_string_piece->ToString());
// Can now normalize the rest of the number since we've consumed the "+"
// sign at the start.
Normalize(number);
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);
Normalize(number);
return ParsePrefixAsIdd(idd_pattern, number)
? PhoneNumber::FROM_NUMBER_WITH_IDD
: PhoneNumber::FROM_DEFAULT_COUNTRY;
}
// 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(
const PhoneMetadata& metadata,
string* number,
string* carrier_code) const {
DCHECK(number);
string carrier_code_temp;
const string& possible_national_prefix =
metadata.national_prefix_for_parsing();
if (number->empty() || possible_national_prefix.empty()) {
// Early return for numbers of zero length or with no national prefix
// possible.
return;
}
// 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));
const scoped_ptr<RegExpInput> number_copy_without_transform(
regexp_factory->CreateInput(*number));
string number_string_copy(*number);
string captured_part_of_prefix;
const RegExp& national_number_rule = 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);
// Attempt to parse the first digits as a national prefix. We make a
// 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);
if (!transform_rule.empty() &&
(possible_national_prefix_pattern.Consume(
number_copy.get(), &carrier_code_temp, &captured_part_of_prefix) ||
possible_national_prefix_pattern.Consume(
number_copy.get(), &captured_part_of_prefix)) &&
!captured_part_of_prefix.empty()) {
// If this succeeded, then we must have had a transform rule and there must
// have been some part of the prefix that we captured.
// We make the transformation and check that the resultant number is still
// viable. If so, replace the number and return.
possible_national_prefix_pattern.Replace(&number_string_copy,
transform_rule);
if (is_viable_original_number &&
!national_number_rule.FullMatch(number_string_copy)) {
return;
}
number->assign(number_string_copy);
if (carrier_code) {
carrier_code->assign(carrier_code_temp);
}
} else if (possible_national_prefix_pattern.Consume(
number_copy_without_transform.get(), &carrier_code_temp) ||
possible_national_prefix_pattern.Consume(
number_copy_without_transform.get())) {
VLOG(4) << "Parsed the first digits as a national prefix.";
// If captured_part_of_prefix is empty, this implies nothing was captured by
// the capturing groups in possible_national_prefix; therefore, no
// transformation is necessary, and we just remove the national prefix.
const string number_copy_as_string =
number_copy_without_transform->ToString();
if (is_viable_original_number &&
!national_number_rule.FullMatch(number_copy_as_string)) {
return;
}
number->assign(number_copy_as_string);
if (carrier_code) {
carrier_code->assign(carrier_code_temp);
}
} else {
VLOG(4) << "The first digits did not match the national prefix.";
}
}
// Strips any extension (as in, the part of the number dialled after the call is
// connected, usually indicated with extn, ext, x or similar) from the end of
// the number, and returns it. The number passed in should be non-normalized.
bool PhoneNumberUtil::MaybeStripExtension(string* number, string* extension)
const {
DCHECK(number);
DCHECK(extension);
// There are three extension capturing groups in the regular expression.
string possible_extension_one;
string possible_extension_two;
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(),
false,
&possible_extension_one,
&possible_extension_two,
&possible_extension_three)) {
// Replace the extensions in the original string here.
extn_pattern->Replace(&number_copy, "");
VLOG(4) << "Found an extension. Possible extension one: "
<< possible_extension_one
<< ". Possible extension two: " << possible_extension_two
<< ". Possible extension three: " << possible_extension_three
<< ". Remaining number: " << number_copy;
// If we find a potential extension, and the number preceding this is a
// viable number, we assume it is an extension.
if ((!possible_extension_one.empty() || !possible_extension_two.empty() ||
!possible_extension_three.empty()) &&
IsViablePhoneNumber(number_copy)) {
number->assign(number_copy);
if (!possible_extension_one.empty()) {
extension->assign(possible_extension_one);
} else if (!possible_extension_two.empty()) {
extension->assign(possible_extension_two);
} else if (!possible_extension_three.empty()) {
extension->assign(possible_extension_three);
}
return true;
}
}
return false;
}
// Extracts country calling code from national_number, and returns it. It
// assumes that the leading plus sign or IDD has already been removed. Returns 0
// if national_number doesn't start with a valid country calling code, and
// leaves national_number unmodified. Assumes the national_number is at least 3
// characters long.
int PhoneNumberUtil::ExtractCountryCode(string* national_number) const {
int potential_country_code;
if (national_number->empty() || (national_number->at(0) == '0')) {
// Country codes do not begin with a '0'.
return 0;
}
for (size_t i = 1; i <= kMaxLengthCountryCode; ++i) {
safe_strto32(national_number->substr(0, i), &potential_country_code);
string region_code;
GetRegionCodeForCountryCode(potential_country_code, &region_code);
if (region_code != RegionCode::GetUnknown()) {
national_number->erase(0, i);
return potential_country_code;
}
}
return 0;
}
// Tries to extract a country calling code from a number. Country calling codes
// are extracted in the following ways:
// - by stripping the international dialing prefix of the region the person
// is dialing from, if this is present in the number, and looking at the next
// digits
// - by stripping the '+' sign if present and then looking at the next digits
// - by comparing the start of the number and the country calling code of the
// default region. If the number is not considered possible for the numbering
// plan of the default region initially, but starts with the country calling
// code of this region, validation will be reattempted after stripping this
// country calling code. If this number is considered a possible number, then
// the first digits will be considered the country calling code and removed as
// such.
//
// Returns NO_PARSING_ERROR if a country calling code was successfully
// extracted or none was present, or the appropriate error otherwise, such as
// if a + was present but it was not followed by a valid country calling code.
// If NO_PARSING_ERROR is returned, the national_number without the country
// calling code is populated, and the country_code of the phone_number passed
// in is set to the country calling code if found, otherwise to 0.
PhoneNumberUtil::ErrorType PhoneNumberUtil::MaybeExtractCountryCode(
const PhoneMetadata* default_region_metadata,
bool keep_raw_input,
string* national_number,
PhoneNumber* phone_number) const {
DCHECK(national_number);
DCHECK(phone_number);
// Set the default prefix to be something that will never match if there is no
// default region.
string possible_country_idd_prefix = default_region_metadata
? default_region_metadata->international_prefix()
: "NonMatch";
PhoneNumber::CountryCodeSource country_code_source =
MaybeStripInternationalPrefixAndNormalize(possible_country_idd_prefix,
national_number);
if (keep_raw_input) {
phone_number->set_country_code_source(country_code_source);
}
if (country_code_source != PhoneNumber::FROM_DEFAULT_COUNTRY) {
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;
}
int potential_country_code = ExtractCountryCode(national_number);
if (potential_country_code != 0) {
phone_number->set_country_code(potential_country_code);
return NO_PARSING_ERROR;
}
// If this fails, they must be using a strange country calling code that we
// don't recognize, or that doesn't exist.
return INVALID_COUNTRY_CODE_ERROR;
} else if (default_region_metadata) {
// Check to see if the number starts with the country calling code for the
// default region. If so, we remove the country calling code, and do some
// checks on the validity of the number before and after.
int default_country_code = default_region_metadata->country_code();
string default_country_code_string(SimpleItoa(default_country_code));
VLOG(4) << "Possible country calling code: " << default_country_code_string;
string potential_national_number;
if (TryStripPrefixString(*national_number,
default_country_code_string,
&potential_national_number)) {
const PhoneNumberDesc& general_num_desc =
default_region_metadata->general_desc();
const RegExp& valid_number_pattern =
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(), ")"));
// 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.
if ((!valid_number_pattern.FullMatch(*national_number) &&
valid_number_pattern.FullMatch(potential_national_number)) ||
TestNumberLengthAgainstPattern(possible_number_pattern,
*national_number) == TOO_LONG) {
national_number->assign(potential_national_number);
if (keep_raw_input) {
phone_number->set_country_code_source(
PhoneNumber::FROM_NUMBER_WITHOUT_PLUS_SIGN);
}
phone_number->set_country_code(default_country_code);
return NO_PARSING_ERROR;
}
}
}
// No country calling code present. Set the country_code to 0.
phone_number->set_country_code(0);
return NO_PARSING_ERROR;
}
PhoneNumberUtil::MatchType PhoneNumberUtil::IsNumberMatch(
const PhoneNumber& first_number_in,
const PhoneNumber& second_number_in) const {
// Make copies of the phone number so that the numbers passed in are not
// edited.
PhoneNumber first_number(first_number_in);
PhoneNumber second_number(second_number_in);
// First clear raw_input and country_code_source and
// preferred_domestic_carrier_code fields and any empty-string extensions so
// that we can use the proto-buffer equality method.
first_number.clear_raw_input();
first_number.clear_country_code_source();
first_number.clear_preferred_domestic_carrier_code();
second_number.clear_raw_input();
second_number.clear_country_code_source();
second_number.clear_preferred_domestic_carrier_code();
if (first_number.extension().empty()) {
first_number.clear_extension();
}
if (second_number.extension().empty()) {
second_number.clear_extension();
}
// Early exit if both had extensions and these are different.
if (first_number.has_extension() && second_number.has_extension() &&
first_number.extension() != second_number.extension()) {
return NO_MATCH;
}
int first_number_country_code = first_number.country_code();
int second_number_country_code = second_number.country_code();
// Both had country calling code specified.
if (first_number_country_code != 0 && second_number_country_code != 0) {
if (ExactlySameAs(first_number, second_number)) {
return EXACT_MATCH;
} else if (first_number_country_code == second_number_country_code &&
IsNationalNumberSuffixOfTheOther(first_number, second_number)) {
// A SHORT_NSN_MATCH occurs if there is a difference because of the
// presence or absence of an 'Italian leading zero', the presence or
// absence of an extension, or one NSN being a shorter variant of the
// other.
return SHORT_NSN_MATCH;
}
// This is not a match.
return NO_MATCH;
}
// Checks cases where one or both country calling codes were not specified. To
// make equality checks easier, we first set the country_code fields to be
// equal.
first_number.set_country_code(second_number_country_code);
// If all else was the same, then this is an NSN_MATCH.
if (ExactlySameAs(first_number, second_number)) {
return NSN_MATCH;
}
if (IsNationalNumberSuffixOfTheOther(first_number, second_number)) {
return SHORT_NSN_MATCH;
}
return NO_MATCH;
}
PhoneNumberUtil::MatchType PhoneNumberUtil::IsNumberMatchWithTwoStrings(
const string& first_number,
const string& second_number) const {
PhoneNumber first_number_as_proto;
ErrorType error_type =
Parse(first_number, RegionCode::GetUnknown(), &first_number_as_proto);
if (error_type == NO_PARSING_ERROR) {
return IsNumberMatchWithOneString(first_number_as_proto, second_number);
}
if (error_type == INVALID_COUNTRY_CODE_ERROR) {
PhoneNumber second_number_as_proto;
ErrorType error_type = Parse(second_number, RegionCode::GetUnknown(),
&second_number_as_proto);
if (error_type == NO_PARSING_ERROR) {
return IsNumberMatchWithOneString(second_number_as_proto, first_number);
}
if (error_type == INVALID_COUNTRY_CODE_ERROR) {
error_type = ParseHelper(first_number, RegionCode::GetUnknown(), false,
false, &first_number_as_proto);
if (error_type == NO_PARSING_ERROR) {
error_type = ParseHelper(second_number, RegionCode::GetUnknown(), false,
false, &second_number_as_proto);
if (error_type == NO_PARSING_ERROR) {
return IsNumberMatch(first_number_as_proto, second_number_as_proto);
}
}
}
}
// One or more of the phone numbers we are trying to match is not a viable
// phone number.
return INVALID_NUMBER;
}
PhoneNumberUtil::MatchType PhoneNumberUtil::IsNumberMatchWithOneString(
const PhoneNumber& first_number,
const string& second_number) const {
// First see if the second number has an implicit country calling code, by
// attempting to parse it.
PhoneNumber second_number_as_proto;
ErrorType error_type =
Parse(second_number, RegionCode::GetUnknown(), &second_number_as_proto);
if (error_type == NO_PARSING_ERROR) {
return IsNumberMatch(first_number, second_number_as_proto);
}
if (error_type == INVALID_COUNTRY_CODE_ERROR) {
// The second number has no country calling code. EXACT_MATCH is no longer
// possible. We parse it as if the region was the same as that for the
// first number, and if EXACT_MATCH is returned, we replace this with
// NSN_MATCH.
string first_number_region;
GetRegionCodeForCountryCode(first_number.country_code(),
&first_number_region);
if (first_number_region != RegionCode::GetUnknown()) {
PhoneNumber second_number_with_first_number_region;
Parse(second_number, first_number_region,
&second_number_with_first_number_region);
MatchType match = IsNumberMatch(first_number,
second_number_with_first_number_region);
if (match == EXACT_MATCH) {
return NSN_MATCH;
}
return match;
} else {
// If the first number didn't have a valid country calling code, then we
// parse the second number without one as well.
error_type = ParseHelper(second_number, RegionCode::GetUnknown(), false,
false, &second_number_as_proto);
if (error_type == NO_PARSING_ERROR) {
return IsNumberMatch(first_number, second_number_as_proto);
}
}
}
// One or more of the phone numbers we are trying to match is not a viable
// phone number.
return INVALID_NUMBER;
}
AsYouTypeFormatter* PhoneNumberUtil::GetAsYouTypeFormatter(
const string& region_code) const {
return new AsYouTypeFormatter(region_code);
}
bool PhoneNumberUtil::CanBeInternationallyDialled(
const PhoneNumber& number) const {
string region_code;
GetRegionCodeForNumber(number, &region_code);
string national_significant_number;
GetNationalSignificantNumber(number, &national_significant_number);
if (!HasValidRegionCode(region_code, number.country_code(),
national_significant_number)) {
return true;
}
const PhoneMetadata* metadata = GetMetadataForRegion(region_code);
return !IsNumberMatchingDesc(
national_significant_number, metadata->no_international_dialling());
}
} // namespace phonenumbers
} // namespace i18n