Merge branch 'master' into refactor-regex-consts-cpp
diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt
index 96def5c..623ba21 100644
--- a/cpp/CMakeLists.txt
+++ b/cpp/CMakeLists.txt
@@ -272,6 +272,7 @@
"src/phonenumbers/phonenumber.pb.cc" # Generated by Protocol Buffers.
"src/phonenumbers/phonenumberutil.cc"
"src/phonenumbers/regex_based_matcher.cc"
+ "src/phonenumbers/regexpsandmappings.cc"
"src/phonenumbers/regexp_cache.cc"
"src/phonenumbers/shortnumberinfo.cc"
"src/phonenumbers/string_byte_sink.cc"
diff --git a/cpp/src/phonenumbers/constants.h b/cpp/src/phonenumbers/constants.h
new file mode 100644
index 0000000..867968a
--- /dev/null
+++ b/cpp/src/phonenumbers/constants.h
@@ -0,0 +1,83 @@
+// Copyright (C) 2025 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.
+
+#include <stddef.h>
+
+#ifndef I18N_PHONENUMBERS_CONSTANTS_H_
+#define I18N_PHONENUMBERS_CONSTANTS_H_
+
+namespace i18n {
+namespace phonenumbers {
+
+class Constants {
+ friend class PhoneNumberMatcherRegExps;
+ friend class PhoneNumberRegExpsAndMappings;
+ friend class PhoneNumberUtil;
+
+ private:
+ // The kPlusSign signifies the international prefix.
+ static constexpr char kPlusSign[] = "+";
+
+ static constexpr char kStarSign[] = "*";
+
+ static constexpr char kRfc3966ExtnPrefix[] = ";ext=";
+ static constexpr char kRfc3966VisualSeparator[] = "[\\-\\.\\(\\)]?";
+
+ static constexpr char kDigits[] = "\\p{Nd}";
+
+ // We accept alpha characters in phone numbers, ASCII only. We store
+ // lower-case here only since our regular expressions are case-insensitive.
+ static constexpr char kValidAlpha[] = "a-z";
+ static constexpr char kValidAlphaInclUppercase[] = "A-Za-z";
+
+ static constexpr char kPossibleSeparatorsBetweenNumberAndExtLabel[] =
+ "[ \xC2\xA0\\t,]*";
+
+ // Optional full stop (.) or colon, followed by zero or more
+ // spaces/tabs/commas.
+ static constexpr char kPossibleCharsAfterExtLabel[] =
+ "[:\\.\xEF\xBC\x8E]?[ \xC2\xA0\\t,-]*";
+
+ static constexpr char kOptionalExtSuffix[] = "#?";
+
+ // The minimum and maximum length of the national significant number.
+ static constexpr size_t kMinLengthForNsn = 2;
+
+ static constexpr char kPlusChars[] = "+\xEF\xBC\x8B"; /* "++" */
+
+ // Regular expression of acceptable punctuation found in phone numbers, used
+ // to find numbers in text and to decide what is a viable phone number. This
+ // excludes diallable characters.
+ // This consists of dash characters, white space characters, full stops,
+ // slashes, square brackets, parentheses and tildes. It also includes the
+ // letter 'x' as that is found as a placeholder for carrier information in
+ // some phone numbers. Full-width variants are also present. 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 constexpr char 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\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 constexpr char kCaptureUpToSecondNumberStart[] = "(.*)[\\\\/] *x";
+};
+
+} // namespace phonenumbers
+} // namespace i18n
+
+#endif // I18N_PHONENUMBERS_CONSTANTS_H_
\ No newline at end of file
diff --git a/cpp/src/phonenumbers/phonenumbermatcher.cc b/cpp/src/phonenumbers/phonenumbermatcher.cc
index 2d60ceb..0edd29b 100644
--- a/cpp/src/phonenumbers/phonenumbermatcher.cc
+++ b/cpp/src/phonenumbers/phonenumbermatcher.cc
@@ -27,19 +27,21 @@
#include <ctype.h>
#include <stddef.h>
+#include <unicode/uchar.h>
+
#include <limits>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
-#include <unicode/uchar.h>
#include "phonenumbers/alternate_format.h"
#include "phonenumbers/base/logging.h"
#include "phonenumbers/base/memory/scoped_ptr.h"
#include "phonenumbers/base/memory/singleton.h"
#include "phonenumbers/callback.h"
+#include "phonenumbers/constants.h"
#include "phonenumbers/default_logger.h"
#include "phonenumbers/encoding_utils.h"
#include "phonenumbers/normalize_utf8.h"
@@ -283,10 +285,10 @@
digit_block_limit_(PhoneNumberUtil::kMaxLengthForNsn +
PhoneNumberUtil::kMaxLengthCountryCode),
block_limit_(Limit(0, digit_block_limit_)),
- punctuation_(StrCat("[", PhoneNumberUtil::kValidPunctuation, "]",
+ punctuation_(StrCat("[", Constants::kValidPunctuation, "]",
punctuation_limit_)),
digit_sequence_(StrCat("\\p{Nd}", Limit(1, digit_block_limit_))),
- lead_class_chars_(StrCat(opening_parens_, PhoneNumberUtil::kPlusChars)),
+ lead_class_chars_(StrCat(opening_parens_, Constants::kPlusChars)),
lead_class_(StrCat("[", lead_class_chars_, "]")),
regexp_factory_for_pattern_(new ICURegExpFactory()),
#ifdef I18N_PHONENUMBERS_USE_RE2
@@ -314,7 +316,7 @@
inner_matches_(new std::vector<const RegExp*>()),
capture_up_to_second_number_start_pattern_(
regexp_factory_->CreateRegExp(
- PhoneNumberUtil::kCaptureUpToSecondNumberStart)),
+ Constants::kCaptureUpToSecondNumberStart)),
capturing_ascii_digits_pattern_(
regexp_factory_->CreateRegExp("(\\d+)")),
lead_class_pattern_(regexp_factory_->CreateRegExp(lead_class_)),
diff --git a/cpp/src/phonenumbers/phonenumberutil.cc b/cpp/src/phonenumbers/phonenumberutil.cc
index c0c9d09..92b8c95 100644
--- a/cpp/src/phonenumbers/phonenumberutil.cc
+++ b/cpp/src/phonenumbers/phonenumberutil.cc
@@ -14,6 +14,9 @@
#include "phonenumbers/phonenumberutil.h"
+#include <unicode/uchar.h>
+#include <unicode/utf8.h>
+
#include <algorithm>
#include <cctype>
#include <cstring>
@@ -22,15 +25,12 @@
#include <utility>
#include <vector>
-#include <unicode/uchar.h>
-#include <unicode/utf8.h>
-
#include "phonenumbers/asyoutypeformatter.h"
#include "phonenumbers/base/basictypes.h"
#include "phonenumbers/base/logging.h"
#include "phonenumbers/base/memory/singleton.h"
+#include "phonenumbers/constants.h"
#include "phonenumbers/default_logger.h"
-#include "phonenumbers/encoding_utils.h"
#include "phonenumbers/matcher_api.h"
#include "phonenumbers/metadata.h"
#include "phonenumbers/normalize_utf8.h"
@@ -41,6 +41,7 @@
#include "phonenumbers/regexp_adapter.h"
#include "phonenumbers/regexp_cache.h"
#include "phonenumbers/regexp_factory.h"
+#include "phonenumbers/regexpsandmappings.h"
#include "phonenumbers/region_code.h"
#include "phonenumbers/stl_util.h"
#include "phonenumbers/stringutil.h"
@@ -54,56 +55,18 @@
using gtl::OrderByFirst;
// 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"; /* "++" */
-// Regular expression of acceptable punctuation found in phone numbers, used to
-// find numbers in text and to decide what is a viable phone number. This
-// excludes diallable characters.
-// This consists of dash characters, white space characters, full stops,
-// slashes, square brackets, parentheses and tildes. It also includes the letter
-// 'x' as that is found as a placeholder for carrier information in some phone
-// numbers. Full-width variants are also present.
-// 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\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";
-
-// static
const char PhoneNumberUtil::kRegionCodeForNonGeoEntity[] = "001";
namespace {
-// The kPlusSign signifies the international prefix.
-const char kPlusSign[] = "+";
-
-const char kStarSign[] = "*";
-
-const char kRfc3966ExtnPrefix[] = ";ext=";
const char kRfc3966Prefix[] = "tel:";
const char kRfc3966PhoneContext[] = ";phone-context=";
const char kRfc3966IsdnSubaddress[] = ";isub=";
-const char kRfc3966VisualSeparator[] = "[\\-\\.\\(\\)]?";
-
-const char kDigits[] = "\\p{Nd}";
-// 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";
-const char kValidAlphaInclUppercase[] = "A-Za-z";
// 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
@@ -112,15 +75,6 @@
// prefix. This can be overridden by region-specific preferences.
const char kDefaultExtnPrefix[] = " ext. ";
-const char kPossibleSeparatorsBetweenNumberAndExtLabel[] =
- "[ \xC2\xA0\\t,]*";
-
-// Optional full stop (.) or colon, followed by zero or more
-// spaces/tabs/commas.
-const char kPossibleCharsAfterExtLabel[] =
- "[:\\.\xEF\xBC\x8E]?[ \xC2\xA0\\t,-]*";
-const char kOptionalExtSuffix[] = "#?";
-
bool LoadCompiledInMetadata(PhoneMetadataCollection* metadata) {
if (!metadata->ParseFromArray(metadata_get(), metadata_size())) {
LOG(ERROR) << "Could not parse binary data.";
@@ -161,29 +115,6 @@
}
}
-// A helper function that is used by Format and FormatByPattern.
-void PrefixNumberWithCountryCallingCode(
- int country_calling_code,
- PhoneNumberUtil::PhoneNumberFormat number_format,
- string* formatted_number) {
- switch (number_format) {
- case PhoneNumberUtil::E164:
- formatted_number->insert(0, StrCat(kPlusSign, country_calling_code));
- return;
- case PhoneNumberUtil::INTERNATIONAL:
- formatted_number->insert(0, StrCat(kPlusSign, country_calling_code, " "));
- return;
- case PhoneNumberUtil::RFC3966:
- formatted_number->insert(0, StrCat(kRfc3966Prefix, kPlusSign,
- country_calling_code, "-"));
- return;
- case PhoneNumberUtil::NATIONAL:
- default:
- // Do nothing.
- return;
- }
-}
-
// Returns true when one national number is the suffix of the other or both are
// the same.
bool IsNationalNumberSuffixOfTheOther(const PhoneNumber& first_number,
@@ -199,117 +130,6 @@
first_number_national_number);
}
-char32 ToUnicodeCodepoint(const char* unicode_char) {
- char32 codepoint;
- EncodingUtils::DecodeUTF8Char(unicode_char, &codepoint);
- return codepoint;
-}
-
-// Helper method for constructing regular expressions for parsing. Creates an
-// expression that captures up to max_length digits.
-std::string ExtnDigits(int max_length) {
- return StrCat("([", kDigits, "]{1,", max_length, "})");
-}
-
-// Helper initialiser method to create the regular-expression pattern to match
-// extensions. Note that:
-// - There are currently six capturing groups for the extension itself. If this
-// number is changed, MaybeStripExtension needs to be updated.
-// - The only capturing groups should be around the digits that you want to
-// capture as part of the extension, or else parsing will fail!
-std::string CreateExtnPattern(bool for_parsing) {
- // We cap the maximum length of an extension based on the ambiguity of the
- // way the extension is prefixed. As per ITU, the officially allowed
- // length for extensions is actually 40, but we don't support this since we
- // haven't seen real examples and this introduces many false interpretations
- // as the extension labels are not standardized.
- int ext_limit_after_explicit_label = 20;
- int ext_limit_after_likely_label = 15;
- int ext_limit_after_ambiguous_char = 9;
- int ext_limit_when_not_sure = 6;
-
- // Canonical-equivalence doesn't seem to be an option with RE2, so we allow
- // two options for representing any non-ASCII character like ó - the character
- // itself, and one in the unicode decomposed form with the combining acute
- // accent.
-
- // Here the extension is called out in a more explicit way, i.e mentioning it
- // obvious patterns like "ext.".
- string explicit_ext_labels =
- "(?:e?xt(?:ensi(?:o\xCC\x81?|\xC3\xB3))?n?|(?:\xEF\xBD\x85)?"
- "\xEF\xBD\x98\xEF\xBD\x94(?:\xEF\xBD\x8E)?|\xD0\xB4\xD0\xBE\xD0\xB1|"
- "anexo)";
- // One-character symbols that can be used to indicate an extension, and less
- // commonly used or more ambiguous extension labels.
- string ambiguous_ext_labels =
- "(?:[x\xEF\xBD\x98#\xEF\xBC\x83~\xEF\xBD\x9E]|int|"
- "\xEF\xBD\x89\xEF\xBD\x8E\xEF\xBD\x94)";
- // When extension is not separated clearly.
- string ambiguous_separator = "[- ]+";
-
- string rfc_extn = StrCat(kRfc3966ExtnPrefix,
- ExtnDigits(ext_limit_after_explicit_label));
- string explicit_extn = StrCat(
- kPossibleSeparatorsBetweenNumberAndExtLabel,
- explicit_ext_labels, kPossibleCharsAfterExtLabel,
- ExtnDigits(ext_limit_after_explicit_label),
- kOptionalExtSuffix);
- string ambiguous_extn = StrCat(
- kPossibleSeparatorsBetweenNumberAndExtLabel,
- ambiguous_ext_labels, kPossibleCharsAfterExtLabel,
- ExtnDigits(ext_limit_after_ambiguous_char),
- kOptionalExtSuffix);
- string american_style_extn_with_suffix = StrCat(
- ambiguous_separator, ExtnDigits(ext_limit_when_not_sure), "#");
-
- // The first regular expression covers RFC 3966 format, where the extension is
- // added using ";ext=". The second more generic where extension is mentioned
- // with explicit labels like "ext:". In both the above cases we allow more
- // numbers in extension than any other extension labels. The third one
- // captures when single character extension labels or less commonly used
- // labels are present. In such cases we capture fewer extension digits in
- // order to reduce the chance of falsely interpreting two numbers beside each
- // other as a number + extension. The fourth one covers the special case of
- // American numbers where the extension is written with a hash at the end,
- // such as "- 503#".
- string extension_pattern = StrCat(
- rfc_extn, "|",
- explicit_extn, "|",
- ambiguous_extn, "|",
- american_style_extn_with_suffix);
- // Additional pattern that is supported when parsing extensions, not when
- // matching.
- if (for_parsing) {
- // ",," is commonly used for auto dialling the extension when connected.
- // Semi-colon works in Iphone and also in Android to pop up a button with
- // the extension number following.
- string auto_dialling_and_ext_labels_found = "(?:,{2}|;)";
- // This is same as kPossibleSeparatorsBetweenNumberAndExtLabel, but not
- // matching comma as extension label may have it.
- string possible_separators_number_extLabel_no_comma = "[ \xC2\xA0\\t]*";
-
- string auto_dialling_extn = StrCat(
- possible_separators_number_extLabel_no_comma,
- auto_dialling_and_ext_labels_found, kPossibleCharsAfterExtLabel,
- ExtnDigits(ext_limit_after_likely_label),
- kOptionalExtSuffix);
- string only_commas_extn = StrCat(
- possible_separators_number_extLabel_no_comma,
- "(?:,)+", kPossibleCharsAfterExtLabel,
- ExtnDigits(ext_limit_after_ambiguous_char),
- kOptionalExtSuffix);
- // Here the first pattern is exclusive for extension autodialling formats
- // which are used when dialling and in this case we accept longer
- // extensions. However, the second pattern is more liberal on number of
- // commas that acts as extension labels, so we have strict cap on number of
- // digits in such extensions.
- return StrCat(extension_pattern, "|",
- auto_dialling_extn, "|",
- only_commas_extn);
- }
- return extension_pattern;
-}
-
// 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.
@@ -516,344 +336,6 @@
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('*', '*'));
- 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'));
- std::map<char32, char> lower_case_mappings;
- std::map<char32, char> alpha_letters;
- for (std::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(54, '9'));
- countries_without_national_prefix_with_area_codes_.insert(52); // Mexico
- geo_mobile_countries_without_mobile_area_codes_.insert(86); // China
- geo_mobile_countries_.insert(52); // Mexico
- geo_mobile_countries_.insert(54); // Argentina
- geo_mobile_countries_.insert(55); // Brazil
- // Indonesia: some prefixes only (fixed CMDA wireless)
- geo_mobile_countries_.insert(62);
- geo_mobile_countries_.insert(
- geo_mobile_countries_without_mobile_area_codes_.begin(),
- geo_mobile_countries_without_mobile_area_codes_.end());
- }
-
- // 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 "comma" and "semicolon" as possible extension
- // indicators. When matching, these are hardly ever used to indicate this.
- const string extn_patterns_for_parsing_;
-
- // Regular expressions of different parts of the phone-context parameter,
- // following the syntax defined in RFC3966.
- const std::string rfc3966_phone_digit_;
- const std::string alphanum_;
- const std::string rfc3966_domainlabel_;
- const std::string rfc3966_toplabel_;
-
- 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.
- std::map<char32, char> diallable_char_mappings_;
- // These mappings map a character (key) to a specific digit that should
- // replace it for normalization purposes.
- std::map<char32, char> alpha_mappings_;
- // For performance reasons, store a map of combining alpha_mappings with ASCII
- // digits.
- std::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 " ".
- std::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.
- std::map<int, char> mobile_token_mappings_;
-
- // Set of country codes that doesn't have national prefix, but it has area
- // codes.
- std::set<int> countries_without_national_prefix_with_area_codes_;
-
- // Set of country codes that have geographically assigned mobile numbers (see
- // geo_mobile_countries_ below) which are not based on *area codes*. For
- // example, in China mobile numbers start with a carrier indicator, and beyond
- // that are geographically assigned: this carrier indicator is not considered
- // to be an area code.
- std::set<int> geo_mobile_countries_without_mobile_area_codes_;
-
- // Set of country calling codes that have geographically assigned mobile
- // numbers. This may not be complete; we add calling codes case by case, as we
- // find geographical mobile numbers or hear from user reports.
- std::set<int> geo_mobile_countries_;
-
- // Pattern that makes it easy to distinguish whether a region has a single
- // international dialing prefix or not. If a region has a single international
- // prefix (e.g. 011 in USA), it will be represented as a string that contains
- // a sequence of ASCII digits, and possibly a tilde, which signals waiting for
- // the tone. If there are multiple available international prefixes in a
- // region, they will be represented as a regex string that always contains one
- // or more characters that are not ASCII digits or a tilde.
- scoped_ptr<const RegExp> single_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_;
-
- // Regular expression of valid global-number-digits for the phone-context
- // parameter, following the syntax defined in RFC3966.
- std::unique_ptr<const RegExp> rfc3966_global_number_digits_pattern_;
-
- // Regular expression of valid domainname for the phone-context parameter,
- // following the syntax defined in RFC3966.
- std::unique_ptr<const RegExp> rfc3966_domainname_pattern_;
-
- PhoneNumberRegExpsAndMappings()
- : valid_phone_number_(
- StrCat(kDigits, "{", PhoneNumberUtil::kMinLengthForNsn, "}|[",
- PhoneNumberUtil::kPlusChars, "]*(?:[",
- PhoneNumberUtil::kValidPunctuation, kStarSign, "]*",
- kDigits, "){3,}[", PhoneNumberUtil::kValidPunctuation,
- kStarSign, kValidAlpha, kDigits, "]*")),
- extn_patterns_for_parsing_(CreateExtnPattern(/* for_parsing= */ true)),
- rfc3966_phone_digit_(
- StrCat("(", kDigits, "|", kRfc3966VisualSeparator, ")")),
- alphanum_(StrCat(kValidAlphaInclUppercase, kDigits)),
- rfc3966_domainlabel_(
- StrCat("[", alphanum_, "]+((\\-)*[", alphanum_, "])*")),
- rfc3966_toplabel_(StrCat("[", kValidAlphaInclUppercase,
- "]+((\\-)*[", alphanum_, "])*")),
- 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_(),
- countries_without_national_prefix_with_area_codes_(),
- geo_mobile_countries_without_mobile_area_codes_(),
- geo_mobile_countries_(),
- single_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(/* for_parsing= */ false)),
- 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, "]+"))),
- rfc3966_global_number_digits_pattern_(regexp_factory_->CreateRegExp(
- StrCat("^\\", kPlusSign, rfc3966_phone_digit_, "*", kDigits,
- rfc3966_phone_digit_, "*$"))),
- rfc3966_domainname_pattern_(regexp_factory_->CreateRegExp(StrCat(
- "^(", rfc3966_domainlabel_, "\\.)*", rfc3966_toplabel_, "\\.?$"))) {
- InitializeMapsAndSets();
- }
-
- // This type is neither copyable nor movable.
- PhoneNumberRegExpsAndMappings(const PhoneNumberRegExpsAndMappings&) = delete;
- PhoneNumberRegExpsAndMappings& operator=(
- const PhoneNumberRegExpsAndMappings&) = delete;
-};
-
// Private constructor. Also takes care of initialisation.
PhoneNumberUtil::PhoneNumberUtil()
: logger_(Logger::set_logger_impl(new NullLogger())),
@@ -927,6 +409,29 @@
country_calling_code_to_region_code_map_->end());
}
+void PhoneNumberUtil::PrefixNumberWithCountryCallingCode(
+ int country_calling_code, PhoneNumberFormat number_format,
+ string* formatted_number) {
+ switch (number_format) {
+ case E164:
+ formatted_number->insert(
+ 0, StrCat(Constants::kPlusSign, country_calling_code));
+ return;
+ case INTERNATIONAL:
+ formatted_number->insert(
+ 0, StrCat(Constants::kPlusSign, country_calling_code, " "));
+ return;
+ case RFC3966:
+ formatted_number->insert(0, StrCat(kRfc3966Prefix, Constants::kPlusSign,
+ country_calling_code, "-"));
+ return;
+ case NATIONAL:
+ default:
+ // Do nothing.
+ return;
+ }
+}
+
void PhoneNumberUtil::GetSupportedRegions(std::set<string>* regions)
const {
DCHECK(regions);
@@ -1041,9 +546,9 @@
// group is present in the output pattern to ensure no data is lost while
// formatting; when we format as you type, this should always be the case.
const RegExp& eligible_format_pattern = reg_exps_->regexp_cache_->GetRegExp(
- StrCat("[", kValidPunctuation, "]*", "\\$1",
- "[", kValidPunctuation, "]*", "(\\$\\d",
- "[", kValidPunctuation, "]*)*"));
+ StrCat("[", Constants::kValidPunctuation, "]*", "\\$1",
+ "[", Constants::kValidPunctuation, "]*", "(\\$\\d",
+ "[", Constants::kValidPunctuation, "]*)*"));
return eligible_format_pattern.FullMatch(format);
}
@@ -1857,7 +1362,7 @@
DCHECK(formatted_number);
if (number.has_extension() && number.extension().length() > 0) {
if (number_format == RFC3966) {
- StrAppend(formatted_number, kRfc3966ExtnPrefix, number.extension());
+ StrAppend(formatted_number, Constants::kRfc3966ExtnPrefix, number.extension());
} else {
if (metadata.has_preferred_extn_prefix()) {
StrAppend(formatted_number, metadata.preferred_extn_prefix(),
@@ -2014,7 +1519,7 @@
// numbers (and it gives us a variety of different lengths for the resulting
// phone numbers - otherwise they would all be kMinLengthForNsn digits long.)
for (size_t phone_number_length = example_number.length() - 1;
- phone_number_length >= kMinLengthForNsn;
+ phone_number_length >= Constants::kMinLengthForNsn;
phone_number_length--) {
string number_to_try = example_number.substr(0, phone_number_length);
PhoneNumber possibly_valid_number;
@@ -2081,7 +1586,7 @@
GetMetadataForNonGeographicalRegion(country_calling_code);
const PhoneNumberDesc* desc = GetNumberDescByType(*metadata, type);
if (desc->has_example_number()) {
- ErrorType success = Parse(StrCat(kPlusSign,
+ ErrorType success = Parse(StrCat(Constants::kPlusSign,
country_calling_code,
desc->example_number()),
RegionCode::GetUnknown(), number);
@@ -2115,7 +1620,7 @@
metadata->premium_rate()};
for (int i = 0; i < kNumberTypes; ++i) {
if (types[i].has_example_number()) {
- ErrorType success = Parse(StrCat(kPlusSign,
+ ErrorType success = Parse(StrCat(Constants::kPlusSign,
SimpleItoa(country_calling_code),
types[i].example_number()),
RegionCode::GetUnknown(), number);
@@ -2233,7 +1738,7 @@
if (phone_context.has_value()) {
// If the phone context contains a phone number prefix, we need to capture
// it, whereas domains will be ignored.
- if (phone_context.value().at(0) == kPlusSign[0]) {
+ if (phone_context.value().at(0) == Constants::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.
@@ -2352,7 +1857,7 @@
// MaybeExtractCountryCode.
country_code = country_metadata->country_code();
}
- if (normalized_national_number.length() < kMinLengthForNsn) {
+ if (normalized_national_number.length() < Constants::kMinLengthForNsn) {
VLOG(2) << "The string supplied is too short to be a phone number.";
return TOO_SHORT_NSN;
}
@@ -2379,7 +1884,7 @@
}
size_t normalized_national_number_length =
normalized_national_number.length();
- if (normalized_national_number_length < kMinLengthForNsn) {
+ if (normalized_national_number_length < Constants::kMinLengthForNsn) {
VLOG(2) << "The string supplied is too short to be a phone number.";
return TOO_SHORT_NSN;
}
@@ -2782,7 +2287,7 @@
void PhoneNumberUtil::NormalizeDigitsOnly(string* number) const {
DCHECK(number);
const RegExp& non_digits_pattern = reg_exps_->regexp_cache_->GetRegExp(
- StrCat("[^", kDigits, "]"));
+ StrCat("[^", Constants::kDigits, "]"));
// Delete everything that isn't valid digits.
non_digits_pattern.GlobalReplace(number, "");
// Normalize all decimal digits to ASCII digits.
@@ -2839,7 +2344,7 @@
// 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) {
+ if (number.length() < Constants::kMinLengthForNsn) {
return false;
}
return reg_exps_->valid_phone_number_pattern_->FullMatch(number);
@@ -3099,7 +2604,7 @@
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() <= Constants::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;
diff --git a/cpp/src/phonenumbers/phonenumberutil.h b/cpp/src/phonenumbers/phonenumberutil.h
index d22f997..8f35b19 100644
--- a/cpp/src/phonenumbers/phonenumberutil.h
+++ b/cpp/src/phonenumbers/phonenumberutil.h
@@ -17,7 +17,6 @@
#ifndef I18N_PHONENUMBERS_PHONENUMBERUTIL_H_
#define I18N_PHONENUMBERS_PHONENUMBERUTIL_H_
-#include <stddef.h>
#include <list>
#include <map>
#include <set>
@@ -25,13 +24,13 @@
#include <utility>
#include <vector>
+#include "absl/container/node_hash_map.h"
+#include "absl/container/node_hash_set.h"
#include "phonenumbers/base/basictypes.h"
#include "phonenumbers/base/memory/scoped_ptr.h"
#include "phonenumbers/base/memory/singleton.h"
#include "phonenumbers/phonenumber.pb.h"
-
-#include "absl/container/node_hash_set.h"
-#include "absl/container/node_hash_map.h"
+#include "phonenumbers/regexpsandmappings.h"
class TelephoneNumber;
@@ -47,7 +46,6 @@
class NumberFormat;
class PhoneMetadata;
class PhoneNumberDesc;
-class PhoneNumberRegExpsAndMappings;
class RegExp;
// NOTE: A lot of methods in this class require Region Code strings. These must
@@ -183,6 +181,11 @@
void GetSupportedRegions(
std::set<string>* regions) const;
+ // A helper function that is used by Format and FormatByPattern.
+ static void PrefixNumberWithCountryCallingCode(
+ int country_calling_code, PhoneNumberFormat number_format,
+ string* formatted_number);
+
// Returns all global network calling codes the library has metadata for.
// @returns an unordered set of the country calling codes for every
// non-geographical entity the library supports
@@ -783,23 +786,12 @@
typedef std::pair<int, std::list<string>*> IntRegionsPair;
- // The minimum and maximum length of the national significant number.
- static const size_t kMinLengthForNsn = 2;
// The ITU says the maximum length should be 15, but we have found longer
// numbers in Germany.
static const size_t kMaxLengthForNsn = 17;
// The maximum length of the country calling code.
static const size_t kMaxLengthCountryCode = 3;
- static const char kPlusChars[];
- // Regular expression of acceptable punctuation found in phone numbers. This
- // excludes punctuation found as a leading character only. This consists of
- // dash characters, white space characters, full stops, slashes, square
- // brackets, parentheses and tildes. It also includes the letter 'x' as that
- // is found as a placeholder for carrier information in some phone numbers.
- // Full-width variants are also present.
- static const char kValidPunctuation[];
-
// Regular expression of characters typically used to start a second phone
// number for the purposes of parsing. This allows us to strip off parts of
// the number that are actually the start of another number, such as for:
diff --git a/cpp/src/phonenumbers/regexpsandmappings.cc b/cpp/src/phonenumbers/regexpsandmappings.cc
new file mode 100644
index 0000000..11d350b
--- /dev/null
+++ b/cpp/src/phonenumbers/regexpsandmappings.cc
@@ -0,0 +1,303 @@
+// Copyright (C) 2025 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.
+
+#include "phonenumbers/regexpsandmappings.h"
+
+#include "phonenumbers/constants.h"
+#include "phonenumbers/encoding_utils.h"
+
+namespace i18n {
+namespace phonenumbers {
+
+char32 ToUnicodeCodepoint(const char* unicode_char) {
+ char32 codepoint;
+ EncodingUtils::DecodeUTF8Char(unicode_char, &codepoint);
+ return codepoint;
+}
+
+std::string PhoneNumberRegExpsAndMappings::CreateExtnPattern(bool for_parsing) {
+ // We cap the maximum length of an extension based on the ambiguity of the
+ // way the extension is prefixed. As per ITU, the officially allowed
+ // length for extensions is actually 40, but we don't support this since we
+ // haven't seen real examples and this introduces many false interpretations
+ // as the extension labels are not standardized.
+ int ext_limit_after_explicit_label = 20;
+ int ext_limit_after_likely_label = 15;
+ int ext_limit_after_ambiguous_char = 9;
+ int ext_limit_when_not_sure = 6;
+
+ // Canonical-equivalence doesn't seem to be an option with RE2, so we allow
+ // two options for representing any non-ASCII character like ó - the character
+ // itself, and one in the unicode decomposed form with the combining acute
+ // accent.
+
+ // Here the extension is called out in a more explicit way, i.e mentioning it
+ // obvious patterns like "ext.".
+ string explicit_ext_labels =
+ "(?:e?xt(?:ensi(?:o\xCC\x81?|\xC3\xB3))?n?|(?:\xEF\xBD\x85)?"
+ "\xEF\xBD\x98\xEF\xBD\x94(?:\xEF\xBD\x8E)?|\xD0\xB4\xD0\xBE\xD0\xB1|"
+ "anexo)";
+ // One-character symbols that can be used to indicate an extension, and less
+ // commonly used or more ambiguous extension labels.
+ string ambiguous_ext_labels =
+ "(?:[x\xEF\xBD\x98#\xEF\xBC\x83~\xEF\xBD\x9E]|int|"
+ "\xEF\xBD\x89\xEF\xBD\x8E\xEF\xBD\x94)";
+ // When extension is not separated clearly.
+ string ambiguous_separator = "[- ]+";
+
+ string rfc_extn = StrCat(Constants::kRfc3966ExtnPrefix,
+ ExtnDigits(ext_limit_after_explicit_label));
+ string explicit_extn =
+ StrCat(Constants::kPossibleSeparatorsBetweenNumberAndExtLabel,
+ explicit_ext_labels, Constants::kPossibleCharsAfterExtLabel,
+ ExtnDigits(ext_limit_after_explicit_label),
+ Constants::kOptionalExtSuffix);
+ string ambiguous_extn =
+ StrCat(Constants::kPossibleSeparatorsBetweenNumberAndExtLabel,
+ ambiguous_ext_labels, Constants::kPossibleCharsAfterExtLabel,
+ ExtnDigits(ext_limit_after_ambiguous_char),
+ Constants::kOptionalExtSuffix);
+ string american_style_extn_with_suffix =
+ StrCat(ambiguous_separator, ExtnDigits(ext_limit_when_not_sure), "#");
+
+ // The first regular expression covers RFC 3966 format, where the extension is
+ // added using ";ext=". The second more generic where extension is mentioned
+ // with explicit labels like "ext:". In both the above cases we allow more
+ // numbers in extension than any other extension labels. The third one
+ // captures when single character extension labels or less commonly used
+ // labels are present. In such cases we capture fewer extension digits in
+ // order to reduce the chance of falsely interpreting two numbers beside each
+ // other as a number + extension. The fourth one covers the special case of
+ // American numbers where the extension is written with a hash at the end,
+ // such as "- 503#".
+ string extension_pattern =
+ StrCat(rfc_extn, "|", explicit_extn, "|", ambiguous_extn, "|",
+ american_style_extn_with_suffix);
+ // Additional pattern that is supported when parsing extensions, not when
+ // matching.
+ if (for_parsing) {
+ // ",," is commonly used for auto dialling the extension when connected.
+ // Semi-colon works in Iphone and also in Android to pop up a button with
+ // the extension number following.
+ string auto_dialling_and_ext_labels_found = "(?:,{2}|;)";
+ // This is same as kPossibleSeparatorsBetweenNumberAndExtLabel, but not
+ // matching comma as extension label may have it.
+ string possible_separators_number_extLabel_no_comma = "[ \xC2\xA0\\t]*";
+
+ string auto_dialling_extn =
+ StrCat(possible_separators_number_extLabel_no_comma,
+ auto_dialling_and_ext_labels_found,
+ Constants::kPossibleCharsAfterExtLabel,
+ ExtnDigits(ext_limit_after_likely_label),
+ Constants::kOptionalExtSuffix);
+ string only_commas_extn =
+ StrCat(possible_separators_number_extLabel_no_comma, "(?:,)+",
+ Constants::kPossibleCharsAfterExtLabel,
+ ExtnDigits(ext_limit_after_ambiguous_char),
+ Constants::kOptionalExtSuffix);
+ // Here the first pattern is exclusive for extension autodialling formats
+ // which are used when dialling and in this case we accept longer
+ // extensions. However, the second pattern is more liberal on number of
+ // commas that acts as extension labels, so we have strict cap on number of
+ // digits in such extensions.
+ return StrCat(extension_pattern, "|", auto_dialling_extn, "|",
+ only_commas_extn);
+ }
+ return extension_pattern;
+}
+
+std::string PhoneNumberRegExpsAndMappings::ExtnDigits(int max_length) {
+ return StrCat("([", Constants::kDigits, "]{1,", max_length, "})");
+}
+
+PhoneNumberRegExpsAndMappings::PhoneNumberRegExpsAndMappings()
+ : valid_phone_number_(
+ StrCat(Constants::kDigits, "{", Constants::kMinLengthForNsn, "}|[",
+ Constants::kPlusChars, "]*(?:[", Constants::kValidPunctuation,
+ Constants::kStarSign, "]*", Constants::kDigits, "){3,}[",
+ Constants::kValidPunctuation, Constants::kStarSign,
+ Constants::kValidAlpha, Constants::kDigits, "]*")),
+ extn_patterns_for_parsing_(CreateExtnPattern(/* for_parsing= */ true)),
+ rfc3966_phone_digit_(StrCat("(", Constants::kDigits, "|",
+ Constants::kRfc3966VisualSeparator, ")")),
+ alphanum_(
+ StrCat(Constants::kValidAlphaInclUppercase, Constants::kDigits)),
+ rfc3966_domainlabel_(
+ StrCat("[", alphanum_, "]+((\\-)*[", alphanum_, "])*")),
+ rfc3966_toplabel_(StrCat("[", Constants::kValidAlphaInclUppercase,
+ "]+((\\-)*[", alphanum_, "])*")),
+ 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_(),
+ countries_without_national_prefix_with_area_codes_(),
+ geo_mobile_countries_without_mobile_area_codes_(),
+ geo_mobile_countries_(),
+ single_international_prefix_(regexp_factory_->CreateRegExp(
+ /* "[\\d]+(?:[~⁓∼~][\\d]+)?" */
+ "[\\d]+(?:[~\xE2\x81\x93\xE2\x88\xBC\xEF\xBD\x9E][\\d]+)?")),
+ digits_pattern_(
+ regexp_factory_->CreateRegExp(StrCat("[", Constants::kDigits, "]*"))),
+ capturing_digit_pattern_(regexp_factory_->CreateRegExp(
+ StrCat("([", Constants::kDigits, "])"))),
+ capturing_ascii_digits_pattern_(regexp_factory_->CreateRegExp("(\\d+)")),
+ valid_start_char_pattern_(regexp_factory_->CreateRegExp(
+ StrCat("[", Constants::kPlusChars, Constants::kDigits, "]"))),
+ capture_up_to_second_number_start_pattern_(regexp_factory_->CreateRegExp(
+ Constants::kCaptureUpToSecondNumberStart)),
+ unwanted_end_char_pattern_(
+ regexp_factory_->CreateRegExp("[^\\p{N}\\p{L}#]")),
+ separator_pattern_(regexp_factory_->CreateRegExp(
+ StrCat("[", Constants::kValidPunctuation, "]+"))),
+ extn_patterns_for_matching_(CreateExtnPattern(/* for_parsing= */ false)),
+ 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)(?:.*?[", Constants::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("[", Constants::kPlusChars, "]+"))),
+ rfc3966_global_number_digits_pattern_(regexp_factory_->CreateRegExp(
+ StrCat("^\\", Constants::kPlusSign, rfc3966_phone_digit_, "*",
+ Constants::kDigits, rfc3966_phone_digit_, "*$"))),
+ rfc3966_domainname_pattern_(regexp_factory_->CreateRegExp(StrCat(
+ "^(", rfc3966_domainlabel_, "\\.)*", rfc3966_toplabel_, "\\.?$"))) {
+ InitializeMapsAndSets();
+}
+
+void PhoneNumberRegExpsAndMappings::InitializeMapsAndSets() {
+ diallable_char_mappings_.insert(std::make_pair('+', '+'));
+ 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'));
+ std::map<char32, char> lower_case_mappings;
+ std::map<char32, char> alpha_letters;
+ for (std::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(54, '9'));
+ countries_without_national_prefix_with_area_codes_.insert(52); // Mexico
+ geo_mobile_countries_without_mobile_area_codes_.insert(86); // China
+ geo_mobile_countries_.insert(52); // Mexico
+ geo_mobile_countries_.insert(54); // Argentina
+ geo_mobile_countries_.insert(55); // Brazil
+ // Indonesia: some prefixes only (fixed CMDA wireless)
+ geo_mobile_countries_.insert(62);
+ geo_mobile_countries_.insert(
+ geo_mobile_countries_without_mobile_area_codes_.begin(),
+ geo_mobile_countries_without_mobile_area_codes_.end());
+}
+
+} // namespace phonenumbers
+} // namespace i18n
diff --git a/cpp/src/phonenumbers/regexpsandmappings.h b/cpp/src/phonenumbers/regexpsandmappings.h
new file mode 100644
index 0000000..ccefc5b
--- /dev/null
+++ b/cpp/src/phonenumbers/regexpsandmappings.h
@@ -0,0 +1,207 @@
+// Copyright (C) 2025 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.
+
+#include <bits/stl_pair.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "phonenumbers/regexp_cache.h"
+#include "phonenumbers/regexp_factory.h"
+#include "phonenumbers/stringutil.h"
+
+#ifndef I18N_PHONENUMBERS_REGEXPSANDMAPPINGS_H_
+#define I18N_PHONENUMBERS_REGEXPSANDMAPPINGS_H_
+
+namespace i18n {
+namespace phonenumbers {
+
+class PhoneNumberRegExpsAndMappings {
+ friend class PhoneNumberUtil;
+
+ private:
+ void InitializeMapsAndSets();
+
+ // Helper initialiser method to create the regular-expression pattern to match
+ // extensions. Note that:
+ // - There are currently six capturing groups for the extension itself. If this
+ // number is changed, MaybeStripExtension needs to be updated.
+ // - The only capturing groups should be around the digits that you want to
+ // capture as part of the extension, or else parsing will fail!
+ static std::string CreateExtnPattern(bool for_parsing);
+
+ // Helper method for constructing regular expressions for parsing. Creates an
+ // expression that captures up to max_length digits.
+ static std::string ExtnDigits(int max_length);
+
+ // 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 "comma" and "semicolon" as possible extension
+ // indicators. When matching, these are hardly ever used to indicate this.
+ const string extn_patterns_for_parsing_;
+
+ // Regular expressions of different parts of the phone-context parameter,
+ // following the syntax defined in RFC3966.
+ const std::string rfc3966_phone_digit_;
+ const std::string alphanum_;
+ const std::string rfc3966_domainlabel_;
+ const std::string rfc3966_toplabel_;
+
+ 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.
+ std::map<char32, char> diallable_char_mappings_;
+ // These mappings map a character (key) to a specific digit that should
+ // replace it for normalization purposes.
+ std::map<char32, char> alpha_mappings_;
+ // For performance reasons, store a map of combining alpha_mappings with ASCII
+ // digits.
+ std::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 " ".
+ std::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.
+ std::map<int, char> mobile_token_mappings_;
+
+ // Set of country codes that doesn't have national prefix, but it has area
+ // codes.
+ std::set<int> countries_without_national_prefix_with_area_codes_;
+
+ // Set of country codes that have geographically assigned mobile numbers (see
+ // geo_mobile_countries_ below) which are not based on *area codes*. For
+ // example, in China mobile numbers start with a carrier indicator, and beyond
+ // that are geographically assigned: this carrier indicator is not considered
+ // to be an area code.
+ std::set<int> geo_mobile_countries_without_mobile_area_codes_;
+
+ // Set of country calling codes that have geographically assigned mobile
+ // numbers. This may not be complete; we add calling codes case by case, as we
+ // find geographical mobile numbers or hear from user reports.
+ std::set<int> geo_mobile_countries_;
+
+ // Pattern that makes it easy to distinguish whether a region has a single
+ // international dialing prefix or not. If a region has a single international
+ // prefix (e.g. 011 in USA), it will be represented as a string that contains
+ // a sequence of ASCII digits, and possibly a tilde, which signals waiting for
+ // the tone. If there are multiple available international prefixes in a
+ // region, they will be represented as a regex string that always contains one
+ // or more characters that are not ASCII digits or a tilde.
+ scoped_ptr<const RegExp> single_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_;
+
+ // Regular expression of valid global-number-digits for the phone-context
+ // parameter, following the syntax defined in RFC3966.
+ std::unique_ptr<const RegExp> rfc3966_global_number_digits_pattern_;
+
+ // Regular expression of valid domainname for the phone-context parameter,
+ // following the syntax defined in RFC3966.
+ std::unique_ptr<const RegExp> rfc3966_domainname_pattern_;
+
+ PhoneNumberRegExpsAndMappings();
+
+ // This type is neither copyable nor movable.
+ PhoneNumberRegExpsAndMappings(const PhoneNumberRegExpsAndMappings&) = delete;
+ PhoneNumberRegExpsAndMappings& operator=(
+ const PhoneNumberRegExpsAndMappings&) = delete;
+};
+
+} // namespace phonenumbers
+} // namespace i18n
+
+#endif // I18N_PHONENUMBERS_REGEXPSANDMAPPINGS_H_
diff --git a/cpp/test/phonenumbers/phonenumberutil_test.cc b/cpp/test/phonenumbers/phonenumberutil_test.cc
index 97cf1f0..f63f00d 100644
--- a/cpp/test/phonenumbers/phonenumberutil_test.cc
+++ b/cpp/test/phonenumbers/phonenumberutil_test.cc
@@ -453,6 +453,14 @@
RegionCode::CS(), PhoneNumberUtil::MOBILE, &test_number));
EXPECT_EQ(PhoneNumber::default_instance(), test_number);
+ // Make sure we can get an example number for a type that only exists for a
+ // non-geographical entity.
+ test_number.Clear();
+ EXPECT_TRUE(phone_util_.GetExampleNumberForType(PhoneNumberUtil::VOICEMAIL,
+ &test_number));
+ EXPECT_EQ(882, test_number.country_code());
+ EXPECT_EQ(34851234567, test_number.national_number());
+
// RegionCode 001 is reserved for supporting non-geographical country calling
// code. We don't support getting an example number for it with this method.
EXPECT_FALSE(phone_util_.GetExampleNumber(RegionCode::UN001(), &test_number));
diff --git a/resources/PhoneNumberMetadataForTesting.xml b/resources/PhoneNumberMetadataForTesting.xml
index 14fdad2..8a882aa 100644
--- a/resources/PhoneNumberMetadataForTesting.xml
+++ b/resources/PhoneNumberMetadataForTesting.xml
@@ -1227,6 +1227,11 @@
<possibleLengths national="9"/>
<exampleNumber>123456789</exampleNumber>
</mobile>
+ <voicemail>
+ <possibleLengths national="11"/>
+ <exampleNumber>34851234567</exampleNumber>
+ <nationalNumberPattern>348[57]\d{7}</nationalNumberPattern>
+ </voicemail>
</territory>
<!-- Universal International Preium Rate Number -->