From 7f1324b0c3b3f233b301fc30315a03b46b192ece Mon Sep 17 00:00:00 2001 From: "philip.liard@gmail.com" Date: Wed, 14 Sep 2011 10:42:59 +0000 Subject: [PATCH] CPP: Add AsYouTypeFormatter (port from Java). git-svn-id: http://libphonenumber.googlecode.com/svn/trunk@352 ee073f10-1060-11df-b6a4-87a95322a99c --- cpp/CMakeLists.txt | 12 + cpp/src/base/logging.h | 21 +- cpp/src/phonenumbers/asyoutypeformatter.cc | 629 +++++++++++++++++++ cpp/src/phonenumbers/asyoutypeformatter.h | 197 ++++++ cpp/src/phonenumbers/phonenumberutil.cc | 24 + cpp/src/phonenumbers/phonenumberutil.h | 15 + cpp/src/phonenumbers/unicodestring.cc | 114 ++++ cpp/src/phonenumbers/unicodestring.h | 133 ++++ cpp/test/phonenumbers/asyoutypeformatter_test.cc | 759 +++++++++++++++++++++++ cpp/test/phonenumbers/unicodestring_test.cc | 236 +++++++ tools/script/continuous-integration.sh | 10 +- 11 files changed, 2138 insertions(+), 12 deletions(-) create mode 100644 cpp/src/phonenumbers/asyoutypeformatter.cc create mode 100644 cpp/src/phonenumbers/asyoutypeformatter.h create mode 100644 cpp/src/phonenumbers/unicodestring.cc create mode 100644 cpp/src/phonenumbers/unicodestring.h create mode 100644 cpp/test/phonenumbers/asyoutypeformatter_test.cc create mode 100644 cpp/test/phonenumbers/unicodestring_test.cc diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 0be3d68..f152613 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -153,6 +153,7 @@ add_custom_target ( set ( SOURCES "src/base/string_piece.cc" + "src/phonenumbers/asyoutypeformatter.cc" "src/phonenumbers/default_logger.cc" "src/phonenumbers/logger.cc" "src/phonenumbers/metadata.h" # Generated by build tools. @@ -162,6 +163,7 @@ set ( "src/phonenumbers/phonenumberutil.cc" "src/phonenumbers/regexp_cache.cc" "src/phonenumbers/stringutil.cc" + "src/phonenumbers/unicodestring.cc" "src/phonenumbers/utf/rune.c" "src/phonenumbers/utf/unicodetext.cc" "src/phonenumbers/utf/unilib.cc" @@ -291,12 +293,14 @@ target_link_libraries (phonenumber_testing ${LIBRARY_DEPS}) add_dependencies (phonenumber_testing generate-sources ${TEST_METADATA_TARGET}) set (TEST_SOURCES + "test/phonenumbers/asyoutypeformatter_test.cc" "test/phonenumbers/logger_test.cc" "test/phonenumbers/phonenumberutil_test.cc" "test/phonenumbers/regexp_adapter_test.cc" "test/phonenumbers/regexp_cache_test.cc" "test/phonenumbers/run_tests.cc" "test/phonenumbers/stringutil_test.cc" + "test/phonenumbers/unicodestring_test.cc" "test/phonenumbers/utf/unicodetext_test.cc" ) @@ -311,12 +315,20 @@ add_custom_target(test COMMAND libphonenumber_test DEPENDS libphonenumber_test) # Install rules. install (FILES + "src/phonenumbers/asyoutypeformatter.h" "src/phonenumbers/logger.h" "src/phonenumbers/phonenumber.pb.h" + "src/phonenumbers/phonemetadata.pb.h" "src/phonenumbers/phonenumberutil.h" + "src/phonenumbers/regexp_adapter.h" + "src/phonenumbers/regexp_cache.h" + "src/phonenumbers/unicodestring.h" DESTINATION include/phonenumbers/ ) +install (FILES "src/phonenumbers/utf/unicodetext.h" + DESTINATION include/phonenumbers/utf/) + install (FILES src/base/basictypes.h DESTINATION include/base/) diff --git a/cpp/src/base/logging.h b/cpp/src/base/logging.h index aeb9f14..ce31e34 100644 --- a/cpp/src/base/logging.h +++ b/cpp/src/base/logging.h @@ -14,20 +14,21 @@ // Author: Philippe Liard -// This file does not come from Chromium. -// It provides a minimalist implementation of common macros. +// This file provides a minimalist implementation of common macros. #ifndef BASE_LOGGING_H_ -# define BASE_LOGGING_H_ +#define BASE_LOGGING_H_ -# include -# include +#include -# define CHECK_EQ(X, Y) assert((X) == (Y)) +#define CHECK_EQ(X, Y) assert((X) == (Y)) -# define DCHECK(X) assert(X) -# define DCHECK_EQ(X, Y) CHECK_EQ((X), (Y)) +#define DCHECK(X) assert(X) +#define DCHECK_EQ(X, Y) CHECK_EQ((X), (Y)) -# define NOTREACHED() std::cerr +template T* CHECK_NOTNULL(T* ptr) { + assert(ptr); + return ptr; +} -#endif +#endif // BASE_LOGGING_H_ diff --git a/cpp/src/phonenumbers/asyoutypeformatter.cc b/cpp/src/phonenumbers/asyoutypeformatter.cc new file mode 100644 index 0000000..057fc7e --- /dev/null +++ b/cpp/src/phonenumbers/asyoutypeformatter.cc @@ -0,0 +1,629 @@ +// Copyright (C) 2011 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/asyoutypeformatter.h" + +#include +#include +#include + +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "phonenumbers/phonemetadata.pb.h" +#include "phonenumbers/phonenumberutil.h" +#include "phonenumbers/regexp_cache.h" +#include "phonenumbers/regexp_factory.h" +#include "phonenumbers/stringutil.h" +#include "phonenumbers/unicodestring.h" + +namespace i18n { +namespace phonenumbers { + +using google::protobuf::RepeatedPtrField; + +namespace { + +const char kPlusSign = '+'; + +// A pattern that is used to match character classes in regular expressions. +// An example of a character class is [1-4]. +const char kCharacterClassPattern[] = "\\[([^\\[\\]])*\\]"; + +// This is the minimum length of national number accrued that is required to +// trigger the formatter. The first element of the leading_digits_pattern of +// each number_format contains a regular expression that matches up to this +// number of digits. +const size_t kMinLeadingDigitsLength = 3; + +// The digits that have not been entered yet will be represented by a \u2008, +// the punctuation space. +const char kDigitPlaceholder[] = "\xE2\x80\x88"; /* " " */ + +// Replaces any standalone digit in the pattern (not any inside a {} grouping) +// with \d. This function replaces the standalone digit regex used in the Java +// version which is currently not supported by RE2 because it uses a special +// construct (?=). +void ReplacePatternDigits(string* pattern) { + DCHECK(pattern); + string new_pattern; + + for (string::const_iterator it = pattern->begin(); it != pattern->end(); + ++it) { + const char current_char = *it; + + if (isdigit(current_char)) { + if (it + 1 != pattern->end()) { + const char next_char = it[1]; + + if (next_char != ',' && next_char != '}') { + new_pattern += "\\d"; + } else { + new_pattern += current_char; + } + } else { + new_pattern += "\\d"; + } + } else { + new_pattern += current_char; + } + } + pattern->assign(new_pattern); +} + +// Matches all the groups contained in 'input' against 'pattern'. +void MatchAllGroups(const string& pattern, + const string& input, + const AbstractRegExpFactory& regexp_factory, + RegExpCache* cache, + string* group) { + DCHECK(cache); + DCHECK(group); + string new_pattern(pattern); + + // Transforms pattern "(...)(...)(...)" to "(.........)". + strrmm(&new_pattern, "()"); + new_pattern = StrCat("(", new_pattern, ")"); + + const scoped_ptr consume_input( + regexp_factory.CreateInput(input)); + bool status = + cache->GetRegExp(new_pattern).Consume(consume_input.get(), group); + DCHECK(status); +} + +PhoneMetadata CreateEmptyMetadata() { + PhoneMetadata metadata; + metadata.set_international_prefix("NA"); + return metadata; +} + +} // namespace + +AsYouTypeFormatter::AsYouTypeFormatter(const string& region_code) + : regexp_factory_(new RegExpFactory()), + regexp_cache_(*regexp_factory_.get(), 64), + current_output_(), + formatting_template_(), + current_formatting_pattern_(), + accrued_input_(), + accrued_input_without_formatting_(), + able_to_format_(true), + is_international_formatting_(false), + is_expecting_country_code_(false), + phone_util_(*PhoneNumberUtil::GetInstance()), + default_country_(region_code), + empty_metadata_(CreateEmptyMetadata()), + default_metadata_(GetMetadataForRegion(region_code)), + current_metadata_(default_metadata_), + last_match_position_(0), + original_position_(0), + position_to_remember_(0), + prefix_before_national_number_(), + national_number_(), + possible_formats_() { +} + +// The metadata needed by this class is the same for all regions sharing the +// same country calling code. Therefore, we return the metadata for "main" +// region for this country calling code. +const PhoneMetadata* AsYouTypeFormatter::GetMetadataForRegion( + const string& region_code) const { + int country_calling_code = phone_util_.GetCountryCodeForRegion(region_code); + string main_country; + phone_util_.GetRegionCodeForCountryCode(country_calling_code, &main_country); + const PhoneMetadata* const metadata = + phone_util_.GetMetadataForRegion(main_country); + if (metadata) { + return metadata; + } + // Set to a default instance of the metadata. This allows us to function with + // an incorrect region code, even if formatting only works for numbers + // specified with "+". + return &empty_metadata_; +} + +bool AsYouTypeFormatter::MaybeCreateNewTemplate() { + // When there are multiple available formats, the formatter uses the first + // format where a formatting template could be created. + for (list::const_iterator it = possible_formats_.begin(); + it != possible_formats_.end(); ++it) { + DCHECK(*it); + const NumberFormat& number_format = **it; + const string& pattern = number_format.pattern(); + if (current_formatting_pattern_ == pattern) { + return false; + } + if (CreateFormattingTemplate(number_format)) { + current_formatting_pattern_ = pattern; + return true; + } + } + able_to_format_ = false; + return false; +} + +void AsYouTypeFormatter::GetAvailableFormats( + const string& leading_three_digits) { + const RepeatedPtrField& format_list = + (is_international_formatting_ && + current_metadata_->intl_number_format().size() > 0) + ? current_metadata_->intl_number_format() + : current_metadata_->number_format(); + + for (RepeatedPtrField::const_iterator it = format_list.begin(); + it != format_list.end(); ++it) { + if (phone_util_.IsFormatEligibleForAsYouTypeFormatter(it->format())) { + possible_formats_.push_back(&*it); + } + } + NarrowDownPossibleFormats(leading_three_digits); +} + +void AsYouTypeFormatter::NarrowDownPossibleFormats( + const string& leading_digits) { + const int index_of_leading_digits_pattern = + leading_digits.length() - kMinLeadingDigitsLength; + + for (list::iterator it = possible_formats_.begin(); + it != possible_formats_.end(); ) { + DCHECK(*it); + const NumberFormat& format = **it; + + if (format.leading_digits_pattern_size() > + index_of_leading_digits_pattern) { + const scoped_ptr input( + regexp_factory_->CreateInput(leading_digits)); + if (!regexp_cache_.GetRegExp(format.leading_digits_pattern().Get( + index_of_leading_digits_pattern)).Consume(input.get())) { + it = possible_formats_.erase(it); + continue; + } + } // else the particular format has no more specific leadingDigitsPattern, + // and it should be retained. + ++it; + } +} + +bool AsYouTypeFormatter::CreateFormattingTemplate(const NumberFormat& format) { + string number_pattern = format.pattern(); + + // The formatter doesn't format numbers when numberPattern contains "|", e.g. + // (20|3)\d{4}. In those cases we quickly return. + if (number_pattern.find('|') != string::npos) { + return false; + } + // Replace anything in the form of [..] with \d. + static const scoped_ptr character_class_pattern( + regexp_factory_->CreateRegExp(kCharacterClassPattern)); + character_class_pattern->GlobalReplace(&number_pattern, "\\\\d"); + + // Replace any standalone digit (not the one in d{}) with \d. + ReplacePatternDigits(&number_pattern); + + string number_format = format.format(); + formatting_template_.remove(); + UnicodeString temp_template; + GetFormattingTemplate(number_pattern, number_format, &temp_template); + + if (temp_template.length() > 0) { + formatting_template_.append(temp_template); + return true; + } + return false; +} + +void AsYouTypeFormatter::GetFormattingTemplate( + const string& number_pattern, + const string& number_format, + UnicodeString* formatting_template) { + DCHECK(formatting_template); + + // Creates a phone number consisting only of the digit 9 that matches the + // number_pattern by applying the pattern to the longest_phone_number string. + static const char longest_phone_number[] = "999999999999999"; + string a_phone_number; + + MatchAllGroups(number_pattern, longest_phone_number, *regexp_factory_, + ®exp_cache_, &a_phone_number); + // No formatting template can be created if the number of digits entered so + // far is longer than the maximum the current formatting rule can accommodate. + if (a_phone_number.length() < national_number_.length()) { + formatting_template->remove(); + return; + } + // Formats the number according to number_format. + regexp_cache_.GetRegExp(number_pattern).GlobalReplace( + &a_phone_number, number_format); + // Replaces each digit with character kDigitPlaceholder. + GlobalReplaceSubstring("9", kDigitPlaceholder, &a_phone_number); + formatting_template->setTo(a_phone_number.c_str(), a_phone_number.size()); +} + +void AsYouTypeFormatter::Clear() { + current_output_.clear(); + accrued_input_.remove(); + accrued_input_without_formatting_.remove(); + formatting_template_.remove(); + last_match_position_ = 0; + current_formatting_pattern_.clear(); + prefix_before_national_number_.clear(); + national_number_.clear(); + able_to_format_ = true; + position_to_remember_ = 0; + original_position_ = 0; + is_international_formatting_ = false; + is_expecting_country_code_ = false; + possible_formats_.clear(); + + if (current_metadata_ != default_metadata_) { + current_metadata_ = GetMetadataForRegion(default_country_); + } +} + +const string& AsYouTypeFormatter::InputDigit(char32 next_char, string* result) { + DCHECK(result); + + InputDigitWithOptionToRememberPosition(next_char, false, ¤t_output_); + result->assign(current_output_); + return *result; +} + +const string& AsYouTypeFormatter::InputDigitAndRememberPosition( + char32 next_char, + string* result) { + DCHECK(result); + + InputDigitWithOptionToRememberPosition(next_char, true, ¤t_output_); + result->assign(current_output_); + return *result; +} + +void AsYouTypeFormatter::InputDigitWithOptionToRememberPosition( + char32 next_char, + bool remember_position, + string* phone_number) { + DCHECK(phone_number); + + accrued_input_.append(next_char); + if (remember_position) { + original_position_ = accrued_input_.length(); + } + // We do formatting on-the-fly only when each character entered is either a + // plus sign or a digit. + string next_char_string; + UnicodeString(next_char).toUTF8String(next_char_string); + + if (next_char != kPlusSign && + !phone_util_.ContainsOnlyValidDigits(next_char_string)) { + able_to_format_ = false; + } + if (!able_to_format_) { + phone_number->clear(); + accrued_input_.toUTF8String(*phone_number); + return; + } + char normalized_next_char = + NormalizeAndAccrueDigitsAndPlusSign(next_char, remember_position); + // We start to attempt to format only when at least kMinLeadingDigitsLength + // digits (the plus sign is counted as a digit as well for this purpose) have + // been entered. + switch (accrued_input_without_formatting_.length()) { + case 0: + case 1: + case 2: + phone_number->clear(); + accrued_input_.toUTF8String(*phone_number); + return; + case 3: + if (AttemptToExtractIdd()) { + is_expecting_country_code_ = true; + } else { + // No IDD or plus sign is found, must be entering in national format. + RemoveNationalPrefixFromNationalNumber(); + AttemptToChooseFormattingPattern(phone_number); + return; + } + case 4: + case 5: + if (is_expecting_country_code_) { + if (AttemptToExtractCountryCode()) { + is_expecting_country_code_ = false; + } + phone_number->assign(prefix_before_national_number_); + phone_number->append(national_number_); + return; + } + // We make a last attempt to extract a country code at the 6th digit because + // the maximum length of IDD and country code are both 3. + case 6: + if (is_expecting_country_code_ && !AttemptToExtractCountryCode()) { + able_to_format_ = false; + phone_number->clear(); + accrued_input_.toUTF8String(*phone_number); + return; + } + default: + if (possible_formats_.size() > 0) { + // The formatting pattern is already chosen. + string temp_national_number; + InputDigitHelper(normalized_next_char, &temp_national_number); + // See if accrued digits can be formatted properly already. If not, use + // the results from InputDigitHelper, which does formatting based on the + // formatting pattern chosen. + string formatted_number; + AttemptToFormatAccruedDigits(&formatted_number); + if (formatted_number.length() > 0) { + phone_number->assign(formatted_number); + return; + } + NarrowDownPossibleFormats(national_number_); + if (MaybeCreateNewTemplate()) { + InputAccruedNationalNumber(phone_number); + return; + } + phone_number->assign(able_to_format_ + ? prefix_before_national_number_ + temp_national_number + : temp_national_number); + } else { + AttemptToChooseFormattingPattern(phone_number); + } + } +} + +void AsYouTypeFormatter::AttemptToFormatAccruedDigits( + string* formatted_number) { + DCHECK(formatted_number); + + for (list::const_iterator it = possible_formats_.begin(); + it != possible_formats_.end(); ++it) { + DCHECK(*it); + const NumberFormat& num_format = **it; + string pattern = num_format.pattern(); + + if (regexp_cache_.GetRegExp(pattern).FullMatch(national_number_)) { + formatted_number->assign(national_number_); + string new_formatted_number(*formatted_number); + string format = num_format.format(); + + bool status = regexp_cache_.GetRegExp(pattern).GlobalReplace( + &new_formatted_number, format); + DCHECK(status); + + formatted_number->assign(prefix_before_national_number_); + formatted_number->append(new_formatted_number); + return; + } + } + formatted_number->clear(); +} + +int AsYouTypeFormatter::GetRememberedPosition() const { + UnicodeString current_output(current_output_.c_str()); + if (!able_to_format_) { + return ConvertUnicodeStringPosition(current_output, original_position_); + } + int accrued_input_index = 0; + int current_output_index = 0; + + while (accrued_input_index < position_to_remember_ && + current_output_index < current_output.length()) { + if (accrued_input_without_formatting_[accrued_input_index] == + current_output[current_output_index]) { + ++accrued_input_index; + } + ++current_output_index; + } + return ConvertUnicodeStringPosition(current_output, current_output_index); +} + +void AsYouTypeFormatter::AttemptToChooseFormattingPattern( + string* formatted_number) { + DCHECK(formatted_number); + + if (national_number_.length() >= kMinLeadingDigitsLength) { + const string leading_digits = + national_number_.substr(0, kMinLeadingDigitsLength); + + GetAvailableFormats(leading_digits); + MaybeCreateNewTemplate(); + InputAccruedNationalNumber(formatted_number); + } else { + formatted_number->assign(prefix_before_national_number_ + national_number_); + } +} + +void AsYouTypeFormatter::InputAccruedNationalNumber(string* number) { + DCHECK(number); + int length_of_national_number = national_number_.length(); + + if (length_of_national_number > 0) { + string temp_national_number; + + for (int i = 0; i < length_of_national_number; ++i) { + temp_national_number.clear(); + InputDigitHelper(national_number_[i], &temp_national_number); + } + number->assign(able_to_format_ + ? prefix_before_national_number_ + temp_national_number + : temp_national_number); + } else { + number->assign(prefix_before_national_number_); + } +} + +void AsYouTypeFormatter::RemoveNationalPrefixFromNationalNumber() { + int start_of_national_number = 0; + + if (current_metadata_->country_code() == 1 && national_number_[0] == '1') { + start_of_national_number = 1; + prefix_before_national_number_.append("1 "); + is_international_formatting_ = true; + } else if (current_metadata_->has_national_prefix()) { + const scoped_ptr consumed_input( + regexp_factory_->CreateInput(national_number_)); + const RegExp& pattern = regexp_cache_.GetRegExp( + current_metadata_->national_prefix_for_parsing()); + + if (pattern.Consume(consumed_input.get())) { + // When the national prefix is detected, we use international formatting + // rules instead of national ones, because national formatting rules could + // countain local formatting rules for numbers entered without area code. + is_international_formatting_ = true; + start_of_national_number = + national_number_.length() - consumed_input->ToString().length(); + prefix_before_national_number_.append( + national_number_.substr(0, start_of_national_number)); + } + } + national_number_.erase(0, start_of_national_number); +} + +bool AsYouTypeFormatter::AttemptToExtractIdd() { + string accrued_input_without_formatting_stdstring; + accrued_input_without_formatting_ + .toUTF8String(accrued_input_without_formatting_stdstring); + const scoped_ptr consumed_input( + regexp_factory_->CreateInput(accrued_input_without_formatting_stdstring)); + const RegExp& international_prefix = regexp_cache_.GetRegExp( + StrCat("\\", string(&kPlusSign, 1), "|", + current_metadata_->international_prefix())); + + if (international_prefix.Consume(consumed_input.get())) { + is_international_formatting_ = true; + const int start_of_country_code = + accrued_input_without_formatting_.length() - + consumed_input->ToString().length(); + + national_number_.clear(); + accrued_input_without_formatting_.tempSubString(start_of_country_code) + .toUTF8String(national_number_); + + string before_country_code; + accrued_input_without_formatting_.tempSubString(0, start_of_country_code) + .toUTF8String(before_country_code); + prefix_before_national_number_.append(before_country_code); + + if (accrued_input_without_formatting_[0] != kPlusSign) { + prefix_before_national_number_.append(" "); + } + return true; + } + return false; +} + +bool AsYouTypeFormatter::AttemptToExtractCountryCode() { + if (national_number_.length() == 0) { + return false; + } + string number_without_country_code(national_number_); + int country_code = + phone_util_.ExtractCountryCode(&number_without_country_code); + if (country_code == 0) { + return false; + } + national_number_.assign(number_without_country_code); + string new_region_code; + phone_util_.GetRegionCodeForCountryCode(country_code, &new_region_code); + + if (new_region_code != default_country_) { + current_metadata_ = GetMetadataForRegion(new_region_code); + } + StrAppend(&prefix_before_national_number_, country_code, " "); + + return true; +} + +char AsYouTypeFormatter::NormalizeAndAccrueDigitsAndPlusSign( + char32 next_char, + bool remember_position) { + char normalized_char = next_char; + + if (next_char == kPlusSign) { + accrued_input_without_formatting_.append(next_char); + } else { + string number; + UnicodeString(next_char).toUTF8String(number); + phone_util_.NormalizeDigitsOnly(&number); + accrued_input_without_formatting_.append(next_char); + national_number_.append(number); + normalized_char = number[0]; + } + if (remember_position) { + position_to_remember_ = accrued_input_without_formatting_.length(); + } + return normalized_char; +} + +void AsYouTypeFormatter::InputDigitHelper(char next_char, string* number) { + DCHECK(number); + number->clear(); + const char32 placeholder_codepoint = UnicodeString(kDigitPlaceholder)[0]; + int placeholder_pos = formatting_template_ + .tempSubString(last_match_position_).indexOf(placeholder_codepoint); + if (placeholder_pos != -1) { + UnicodeString temp_template = formatting_template_; + placeholder_pos = temp_template.indexOf(placeholder_codepoint); + temp_template.setCharAt(placeholder_pos, UnicodeString(next_char)[0]); + last_match_position_ = placeholder_pos; + formatting_template_.replace(0, temp_template.length(), temp_template); + formatting_template_.tempSubString(0, last_match_position_ + 1) + .toUTF8String(*number); + } else { + if (possible_formats_.size() == 1) { + // More digits are entered than we could handle, and there are no other + // valid patterns to try. + able_to_format_ = false; + } // else, we just reset the formatting pattern. + current_formatting_pattern_.clear(); + accrued_input_.toUTF8String(*number); + } +} + +// Returns the number of bytes contained in the given UnicodeString up to the +// specified position. +// static +int AsYouTypeFormatter::ConvertUnicodeStringPosition(const UnicodeString& s, + int pos) { + if (pos > s.length()) { + return -1; + } + string substring; + s.tempSubString(0, pos).toUTF8String(substring); + return substring.length(); +} + +} // namespace phonenumbers +} // namespace i18n diff --git a/cpp/src/phonenumbers/asyoutypeformatter.h b/cpp/src/phonenumbers/asyoutypeformatter.h new file mode 100644 index 0000000..2ae3aa3 --- /dev/null +++ b/cpp/src/phonenumbers/asyoutypeformatter.h @@ -0,0 +1,197 @@ +// Copyright (C) 2011 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. + +// A formatter which formats phone numbers as they are entered. +// +// An AsYouTypeFormatter can be created by invoking the GetAsYouTypeFormatter +// method of the PhoneNumberUtil. After that digits can be added by invoking the +// InputDigit method on the formatter instance, and the partially formatted +// phone number will be returned each time a digit is added. The Clear method +// can be invoked before a new number needs to be formatted. +// +// See AYTF_US, AYTF_GBFixedLine and AYTF_DE test functions in +// asyoutypeformatter_test.cc for more details on how the formatter is to be +// used. +// +// This is a direct port from AsYouTypeFormatter.java. Changes to this class +// should also happen to the Java version, whenever it makes sense. +// +// This class is NOT THREAD SAFE. + +#ifndef I18N_PHONENUMBERS_ASYOUTYPEFORMATTER_H_ +#define I18N_PHONENUMBERS_ASYOUTYPEFORMATTER_H_ + +#include +#include + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "phonenumbers/regexp_adapter.h" +#include "phonenumbers/regexp_cache.h" +#include "phonenumbers/phonemetadata.pb.h" +#include "phonenumbers/unicodestring.h" + +namespace i18n { +namespace phonenumbers { + +using std::list; +using std::string; + +class PhoneNumberUtil; + +class AsYouTypeFormatter { + public: + ~AsYouTypeFormatter() {} + + // Formats a phone number on-the-fly as each digit is entered. + // next_char is the most recently entered digit of a phone number. Formatting + // characters are allowed, but as soon as they are encountered this method + // formats the number as entered and not "as you type" anymore. Full width + // digits and Arabic-indic digits are allowed, and will be shown as they are. + // Returns the partially formatted phone number (which is a reference to the + // given string parameter for convenience). + const string& InputDigit(char32 next_char, string* result); + + // Same as InputDigit, but remembers the position where next_char is inserted, + // so that it could be retrieved later by using GetRememberedPosition(). The + // remembered position will be automatically adjusted if additional formatting + // characters are later inserted/removed in front of next_char. + // Returns the partially formatted phone number (which is a reference to the + // given string parameter for convenience). + const string& InputDigitAndRememberPosition(char32 next_char, string* result); + + // Returns the current position in the partially formatted phone number of the + // character which was previously passed in as the parameter of + // InputDigitAndRememberPosition(). + int GetRememberedPosition() const; + + // Clears the internal state of the formatter, so it could be reused. + void Clear(); + + private: + // Constructs an as-you-type formatter. Should be obtained from + // PhoneNumberUtil::GetAsYouTypeFormatter(). + explicit AsYouTypeFormatter(const string& region_code); + + // Returns the metadata corresponding to the given region code or empty + // metadata if it is unsupported. + const PhoneMetadata* GetMetadataForRegion(const string& region_code) const; + + // Returns true if a new template is created as opposed to reusing the + // existing template. + bool MaybeCreateNewTemplate(); + + void GetAvailableFormats(const string& leading_three_digits); + + void NarrowDownPossibleFormats(const string& leading_digits); + + bool CreateFormattingTemplate(const NumberFormat& format); + + // Gets a formatting template which could be used to efficiently format a + // partial number where digits are added one by one. + void GetFormattingTemplate(const string& number_pattern, + const string& number_format, + UnicodeString* formatting_template); + + void InputDigitWithOptionToRememberPosition(char32 next_char, + bool remember_position, + string* phone_number); + + void AttemptToFormatAccruedDigits(string* formatted_number); + + // Attempts to set the formatting template and assigns the passed-in string + // parameter to the formatted version of the digits entered so far. + void AttemptToChooseFormattingPattern(string* formatted_number); + + // Invokes InputDigitHelper on each digit of the national number accrued, and + // assigns the passed-in string parameter to a formatted string in the end. + void InputAccruedNationalNumber(string* number); + + void RemoveNationalPrefixFromNationalNumber(); + + // Extracts IDD and plus sign to prefix_before_national_number_ when they are + // available, and places the remaining input into national_number_. + bool AttemptToExtractIdd(); + + // Extracts country code from the begining of national_number_ to + // prefix_before_national_number_ when they are available, and places the + // remaining input into national_number_. + // Returns true when a valid country code can be found. + bool AttemptToExtractCountryCode(); + + // Accrues digits and the plus sign to accrued_input_without_formatting for + // later use. If next_char contains a digit in non-ASCII format (e.g the + // full-width version of digits), it is first normalized to the ASCII + // version. The return value is next_char itself, or its normalized version, + // if next_char is a digit in non-ASCII format. + char NormalizeAndAccrueDigitsAndPlusSign(char32 next_char, + bool remember_position); + + void InputDigitHelper(char next_char, string* number); + + // Converts UnicodeString position to std::string position. + static int ConvertUnicodeStringPosition(const UnicodeString& s, int pos); + + // Class attributes. + const scoped_ptr regexp_factory_; + RegExpCache regexp_cache_; + + string current_output_; + + UnicodeString formatting_template_; + string current_formatting_pattern_; + + UnicodeString accrued_input_; + UnicodeString accrued_input_without_formatting_; + + bool able_to_format_; + bool is_international_formatting_; + bool is_expecting_country_code_; + + const PhoneNumberUtil& phone_util_; + + const string default_country_; + + const PhoneMetadata empty_metadata_; + const PhoneMetadata* const default_metadata_; + const PhoneMetadata* current_metadata_; + + int last_match_position_; + + // The position of a digit upon which InputDigitAndRememberPosition is most + // recently invoked, as found in the original sequence of characters the user + // entered. + int original_position_; + + // The position of a digit upon which InputDigitAndRememberPosition is most + // recently invoked, as found in AccruedInputWithoutFormatting. + int position_to_remember_; + + string prefix_before_national_number_; + string national_number_; + + list possible_formats_; + + friend class PhoneNumberUtil; + friend class AsYouTypeFormatterTest; + + // Disallow copy and assign since this class uses RegExpCache which can't be + // copied. + DISALLOW_COPY_AND_ASSIGN(AsYouTypeFormatter); +}; + +} // namespace phonenumbers +} // namespace i18n + +#endif // I18N_PHONENUMBERS_ASYOUTYPEFORMATTER_H_ diff --git a/cpp/src/phonenumbers/phonenumberutil.cc b/cpp/src/phonenumbers/phonenumberutil.cc index d81a80c..3c24fdc 100644 --- a/cpp/src/phonenumbers/phonenumberutil.cc +++ b/cpp/src/phonenumbers/phonenumberutil.cc @@ -34,6 +34,7 @@ #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" @@ -748,6 +749,10 @@ 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; @@ -767,6 +772,20 @@ void PhoneNumberUtil::TrimUnwantedEndChars(string* number) const { 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* regions) const { DCHECK(regions); for (map::const_iterator it = @@ -2207,5 +2226,10 @@ PhoneNumberUtil::MatchType PhoneNumberUtil::IsNumberMatchWithOneString( return INVALID_NUMBER; } +AsYouTypeFormatter* PhoneNumberUtil::GetAsYouTypeFormatter( + const string& region_code) const { + return new AsYouTypeFormatter(region_code); +} + } // namespace phonenumbers } // namespace i18n diff --git a/cpp/src/phonenumbers/phonenumberutil.h b/cpp/src/phonenumbers/phonenumberutil.h index 8115fbb..c35de85 100644 --- a/cpp/src/phonenumbers/phonenumberutil.h +++ b/cpp/src/phonenumbers/phonenumberutil.h @@ -46,6 +46,7 @@ using std::vector; using google::protobuf::RepeatedPtrField; +class AsYouTypeFormatter; class Logger; class NumberFormat; class PhoneMetadata; @@ -65,6 +66,7 @@ class PhoneNumberUtil { class PhoneNumberUtil : public Singleton { friend class Singleton; #endif + friend class AsYouTypeFormatter; friend class PhoneNumberUtilTest; public: ~PhoneNumberUtil(); @@ -499,6 +501,13 @@ class PhoneNumberUtil : public Singleton { // handled by this class (i.e don't delete it). static void SetLogger(Logger* logger); + // Gets an AsYouTypeFormatter for the specific region. + // Returns an AsYouTypeFormatter object, which could be used to format phone + // numbers in the specific region "as you type". + // The deletion of the returned instance is under the responsibility of the + // caller. + AsYouTypeFormatter* GetAsYouTypeFormatter(const string& region_code) const; + friend bool ConvertFromTelephoneNumberProto( const TelephoneNumber& proto_to_convert, PhoneNumber* new_proto); @@ -560,6 +569,12 @@ class PhoneNumberUtil : public Singleton { // in a number, for use when matching. const string& GetExtnPatternsForMatching() const; + // Checks whether a string contains only valid digits. + bool ContainsOnlyValidDigits(const string& s) const; + + // Checks if a format is eligible to be used by the AsYouTypeFormatter. + bool IsFormatEligibleForAsYouTypeFormatter(const string& format) const; + // Trims unwanted end characters from a phone number string. void TrimUnwantedEndChars(string* number) const; diff --git a/cpp/src/phonenumbers/unicodestring.cc b/cpp/src/phonenumbers/unicodestring.cc new file mode 100644 index 0000000..627f683 --- /dev/null +++ b/cpp/src/phonenumbers/unicodestring.cc @@ -0,0 +1,114 @@ +// Copyright (C) 2011 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: Philippe Liard + +#include "phonenumbers/unicodestring.h" + +#include +#include +#include + +using std::advance; +using std::equal; + +namespace i18n { +namespace phonenumbers { + +UnicodeString& UnicodeString::operator=(const UnicodeString& src) { + if (&src != this) { + invalidateCachedIndex(); + text_ = src.text_; + } + return *this; +} + +bool UnicodeString::operator==(const UnicodeString& rhs) const { + return equal(text_.begin(), text_.end(), rhs.text_.begin()); +} + +void UnicodeString::append(const UnicodeString& unicode_string) { + invalidateCachedIndex(); + for (UnicodeString::const_iterator it = unicode_string.begin(); + it != unicode_string.end(); ++it) { + append(*it); + } +} + +int UnicodeString::indexOf(char32 codepoint) const { + int pos = 0; + for (UnicodeText::const_iterator it = text_.begin(); it != text_.end(); + ++it, ++pos) { + if (*it == codepoint) { + return pos; + } + } + return -1; +} + +void UnicodeString::replace(int start, int length, const UnicodeString& src) { + assert(length >= 0 && length <= this->length()); + invalidateCachedIndex(); + UnicodeText::const_iterator start_it = text_.begin(); + advance(start_it, start); + UnicodeText unicode_text; + unicode_text.append(text_.begin(), start_it); + unicode_text.append(src.text_); + advance(start_it, length); + unicode_text.append(start_it, text_.end()); + text_ = unicode_text; +} + +void UnicodeString::setCharAt(int pos, char32 c) { + assert(pos < length()); + invalidateCachedIndex(); + UnicodeText::const_iterator pos_it = text_.begin(); + advance(pos_it, pos); + UnicodeText unicode_text; + unicode_text.append(text_.begin(), pos_it); + unicode_text.push_back(c); + ++pos_it; + unicode_text.append(pos_it, text_.end()); + text_ = unicode_text; +} + +UnicodeString UnicodeString::tempSubString(int start, int length) const { + const int unicodestring_length = this->length(); + if (length == std::numeric_limits::max()) { + length = unicodestring_length - start; + } + if (start > unicodestring_length || length > unicodestring_length) { + return UnicodeString(""); + } + UnicodeText::const_iterator start_it = text_.begin(); + advance(start_it, start); + UnicodeText::const_iterator end_it = start_it; + advance(end_it, length); + UnicodeString substring; + substring.text_.PointTo(start_it, end_it); + return substring; +} + +char32 UnicodeString::operator[](int index) const { + assert(index < length()); + if (cached_index_ == -1 || cached_index_ > index) { + cached_it_ = text_.begin(); + cached_index_ = 0; + } + for (; cached_index_ < index; ++cached_index_, ++cached_it_) {} + return *cached_it_; +} + +} // namespace phonenumbers +} // namespace i18n diff --git a/cpp/src/phonenumbers/unicodestring.h b/cpp/src/phonenumbers/unicodestring.h new file mode 100644 index 0000000..47a151f --- /dev/null +++ b/cpp/src/phonenumbers/unicodestring.h @@ -0,0 +1,133 @@ +// Copyright (C) 2011 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: Philippe Liard + +#ifndef I18N_PHONENUMBERS_UNICODESTRING_H_ +#define I18N_PHONENUMBERS_UNICODESTRING_H_ + +#include "phonenumbers/utf/unicodetext.h" + +#include +#include + +namespace i18n { +namespace phonenumbers { + +// This class supports the minimal subset of icu::UnicodeString needed by +// AsYouTypeFormatter in order to let the libphonenumber not depend on ICU +// which is not available by default on some systems, such as iOS. +class UnicodeString { + public: + UnicodeString() : cached_index_(-1) {} + + // Constructs a new unicode string copying the provided C string. + explicit UnicodeString(const char* utf8) + : text_(UTF8ToUnicodeText(utf8, std::strlen(utf8))), + cached_index_(-1) {} + + // Constructs a new unicode string containing the provided codepoint. + explicit UnicodeString(char32 codepoint) : cached_index_(-1) { + append(codepoint); + } + + UnicodeString(const UnicodeString& src) + : text_(src.text_), cached_index_(-1) {} + + UnicodeString& operator=(const UnicodeString& src); + + bool operator==(const UnicodeString& rhs) const; + + void append(const UnicodeString& unicode_string); + + inline void append(char32 codepoint) { + invalidateCachedIndex(); + text_.push_back(codepoint); + } + + typedef UnicodeText::const_iterator const_iterator; + + inline const_iterator begin() const { + return text_.begin(); + } + + inline const_iterator end() const { + return text_.end(); + } + + // Returns the index of the provided codepoint or -1 if not found. + int indexOf(char32 codepoint) const; + + // Returns the number of codepoints contained in the unicode string. + inline int length() const { + return text_.size(); + } + + // Clears the unicode string. + inline void remove() { + invalidateCachedIndex(); + text_.clear(); + } + + // Replaces the substring located at [ start, start + length - 1 ] with the + // provided unicode string. + void replace(int start, int length, const UnicodeString& src); + + void setCharAt(int pos, char32 c); + + // Copies the provided C string. + inline void setTo(const char* s, size_t len) { + invalidateCachedIndex(); + text_.CopyUTF8(s, len); + } + + // Returns the substring located at [ start, start + length - 1 ] without + // copying the underlying C string. If one of the provided parameters is out + // of range, the function returns an empty unicode string. + UnicodeString tempSubString( + int start, + int length = std::numeric_limits::max()) const; + + inline void toUTF8String(string& out) const { + out = UnicodeTextToUTF8(text_); + } + + char32 operator[](int index) const; + + private: + UnicodeText text_; + + // As UnicodeText doesn't provide random access, an operator[] implementation + // would naively iterate from the beginning of the string to the supplied + // index which would be inefficient. + // As operator[] is very likely to be called in a loop with consecutive + // indexes, we save the corresponding iterator so we can reuse it the next + // time it is called. + + // The following function which invalidates the cached index corresponding to + // the iterator position must be called every time the unicode string is + // modified (i.e. in all the non-const methods). + inline void invalidateCachedIndex() { + cached_index_ = -1; + } + + // Iterator corresponding to the cached index below, used by operator[]. + mutable UnicodeText::const_iterator cached_it_; + mutable int cached_index_; +}; + +} // namespace phonenumbers +} // namespace i18n + +#endif // I18N_PHONENUMBERS_UNICODESTRING_H_ diff --git a/cpp/test/phonenumbers/asyoutypeformatter_test.cc b/cpp/test/phonenumbers/asyoutypeformatter_test.cc new file mode 100644 index 0000000..782193d --- /dev/null +++ b/cpp/test/phonenumbers/asyoutypeformatter_test.cc @@ -0,0 +1,759 @@ +// Copyright (C) 2011 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. + +// Unit tests for asyoutypeformatter.cc, ported from AsYouTypeFormatterTest.java + +#include "phonenumbers/asyoutypeformatter.h" + +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "phonenumbers/phonenumberutil.h" +#include "phonenumbers/region_code.h" + +namespace i18n { +namespace phonenumbers { + +class PhoneMetadata; + +class AsYouTypeFormatterTest : public testing::Test { + protected: + AsYouTypeFormatterTest() : phone_util_(*PhoneNumberUtil::GetInstance()) { + } + + const PhoneMetadata* GetCurrentMetadata() const { + return CHECK_NOTNULL(formatter_.get())->current_metadata_; + } + + int ConvertUnicodeStringPosition(const UnicodeString& s, int pos) const { + return AsYouTypeFormatter::ConvertUnicodeStringPosition(s, pos); + } + + const PhoneNumberUtil& phone_util_; + scoped_ptr formatter_; + string result_; + + private: + DISALLOW_COPY_AND_ASSIGN(AsYouTypeFormatterTest); +}; + +TEST_F(AsYouTypeFormatterTest, ConvertUnicodeStringPosition) { + EXPECT_EQ(-1, ConvertUnicodeStringPosition(UnicodeString("12345"), 10)); + EXPECT_EQ(3, ConvertUnicodeStringPosition(UnicodeString("12345"), 3)); + EXPECT_EQ(0, ConvertUnicodeStringPosition( + UnicodeString("\xEF\xBC\x95" /* "5" */), 0)); + EXPECT_EQ(4, ConvertUnicodeStringPosition( + UnicodeString("0\xEF\xBC\x95""3" /* "053" */), 2)); + EXPECT_EQ(5, ConvertUnicodeStringPosition( + UnicodeString("0\xEF\xBC\x95""3" /* "053" */), 3)); +} + +TEST_F(AsYouTypeFormatterTest, Constructor) { + formatter_.reset(phone_util_.GetAsYouTypeFormatter("US")); + + EXPECT_TRUE(GetCurrentMetadata() != NULL); +} + +TEST_F(AsYouTypeFormatterTest, TooLongNumberMatchingMultipleLeadingDigits) { + // See http://code.google.com/p/libphonenumber/issues/detail?id=36 + // The bug occurred last time for countries which have two formatting rules + // with exactly the same leading digits pattern but differ in length. + formatter_.reset(phone_util_.GetAsYouTypeFormatter(RegionCode::GetUnknown())); + + EXPECT_EQ("+", formatter_->InputDigit('+', &result_)); + EXPECT_EQ("+8", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("+81 ", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+81 9", formatter_->InputDigit('9', &result_)); + EXPECT_EQ("+81 90", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("+81 90 1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+81 90 12", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+81 90 123", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+81 90 1234", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("+81 90 1234 5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+81 90 1234 56", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("+81 90 1234 567", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("+81 90 1234 5678", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("+81 90 12 345 6789", formatter_->InputDigit('9', &result_)); + EXPECT_EQ("+81901234567890", formatter_->InputDigit('0', &result_)); +} + +TEST_F(AsYouTypeFormatterTest, AYTF_US) { + formatter_.reset(phone_util_.GetAsYouTypeFormatter("US")); + + EXPECT_EQ("6", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("65", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("650", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("650 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("650 25", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("650 253", formatter_->InputDigit('3', &result_)); + + // Note this is how a US local number (without area code) should be formatted. + EXPECT_EQ("650 2532", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("650 253 22", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("650 253 222", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("650 253 2222", formatter_->InputDigit('2', &result_)); + + formatter_->Clear(); + EXPECT_EQ("1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("16", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("1 65", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("1 650", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("1 650 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("1 650 25", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("1 650 253", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("1 650 253 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("1 650 253 22", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("1 650 253 222", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("1 650 253 2222", formatter_->InputDigit('2', &result_)); + + formatter_->Clear(); + EXPECT_EQ("0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("01", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 ", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 4", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("011 44 ", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("011 44 6", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("011 44 61", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 44 6 12", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("011 44 6 123", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("011 44 6 123 1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 44 6 123 12", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("011 44 6 123 123", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("011 44 6 123 123 1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 44 6 123 123 12", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("011 44 6 123 123 123", formatter_->InputDigit('3', &result_)); + + formatter_->Clear(); + EXPECT_EQ("0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("01", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 ", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("011 54 ", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("011 54 9", formatter_->InputDigit('9', &result_)); + EXPECT_EQ("011 54 91", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 54 9 11", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 54 9 11 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("011 54 9 11 23", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("011 54 9 11 231", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 54 9 11 2312", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("011 54 9 11 2312 1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 54 9 11 2312 12", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("011 54 9 11 2312 123", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("011 54 9 11 2312 1234", formatter_->InputDigit('4', &result_)); + + formatter_->Clear(); + EXPECT_EQ("0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("01", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 ", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("011 24", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("011 244 ", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("011 244 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("011 244 28", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("011 244 280", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("011 244 280 0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("011 244 280 00", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("011 244 280 000", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("011 244 280 000 0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("011 244 280 000 00", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("011 244 280 000 000", formatter_->InputDigit('0', &result_)); + + formatter_->Clear(); + EXPECT_EQ("+", formatter_->InputDigit('+', &result_)); + EXPECT_EQ("+4", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("+48 ", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("+48 8", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("+48 88", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("+48 88 1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+48 88 12", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+48 88 123", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+48 88 123 1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+48 88 123 12", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+48 88 123 12 1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+48 88 123 12 12", formatter_->InputDigit('2', &result_)); +} + +TEST_F(AsYouTypeFormatterTest, AYTF_USFullWidthCharacters) { + formatter_.reset(phone_util_.GetAsYouTypeFormatter("US")); + + EXPECT_EQ("\xEF\xBC\x96" /* "6" */, + formatter_->InputDigit(UnicodeString("\xEF\xBC\x96" /* "6" */)[0], + &result_)); + EXPECT_EQ("\xEF\xBC\x96\xEF\xBC\x95" /* "65" */, + formatter_->InputDigit(UnicodeString("\xEF\xBC\x95" /* "5" */)[0], + &result_)); + EXPECT_EQ("650", + formatter_->InputDigit(UnicodeString("\xEF\xBC\x90" /* "0" */)[0], + &result_)); + EXPECT_EQ("650 2", + formatter_->InputDigit(UnicodeString("\xEF\xBC\x92" /* "2" */)[0], + &result_)); + EXPECT_EQ("650 25", + formatter_->InputDigit(UnicodeString("\xEF\xBC\x95" /* "5" */)[0], + &result_)); + EXPECT_EQ("650 253", + formatter_->InputDigit(UnicodeString("\xEF\xBC\x93" /* "3" */)[0], + &result_)); + EXPECT_EQ("650 2532", + formatter_->InputDigit(UnicodeString("\xEF\xBC\x92" /* "2" */)[0], + &result_)); + EXPECT_EQ("650 253 22", + formatter_->InputDigit(UnicodeString("\xEF\xBC\x92" /* "2" */)[0], + &result_)); + EXPECT_EQ("650 253 222", + formatter_->InputDigit(UnicodeString("\xEF\xBC\x92" /* "2" */)[0], + &result_)); + EXPECT_EQ("650 253 2222", + formatter_->InputDigit(UnicodeString("\xEF\xBC\x92" /* "2" */)[0], + &result_)); +} + +TEST_F(AsYouTypeFormatterTest, AYTF_USMobileShortCode) { + formatter_.reset(phone_util_.GetAsYouTypeFormatter("US")); + + EXPECT_EQ("*", formatter_->InputDigit('*', &result_)); + EXPECT_EQ("*1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("*12", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("*121", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("*121#", formatter_->InputDigit('#', &result_)); +} + +TEST_F(AsYouTypeFormatterTest, AYTF_USVanityNumber) { + formatter_.reset(phone_util_.GetAsYouTypeFormatter("US")); + + EXPECT_EQ("8", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("80", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("800", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("800 ", formatter_->InputDigit(' ', &result_)); + EXPECT_EQ("800 M", formatter_->InputDigit('M', &result_)); + EXPECT_EQ("800 MY", formatter_->InputDigit('Y', &result_)); + EXPECT_EQ("800 MY ", formatter_->InputDigit(' ', &result_)); + EXPECT_EQ("800 MY A", formatter_->InputDigit('A', &result_)); + EXPECT_EQ("800 MY AP", formatter_->InputDigit('P', &result_)); + EXPECT_EQ("800 MY APP", formatter_->InputDigit('P', &result_)); + EXPECT_EQ("800 MY APPL", formatter_->InputDigit('L', &result_)); + EXPECT_EQ("800 MY APPLE", formatter_->InputDigit('E', &result_)); +} + +TEST_F(AsYouTypeFormatterTest, AYTFAndRememberPositionUS) { + formatter_.reset(phone_util_.GetAsYouTypeFormatter("US")); + + EXPECT_EQ("1", formatter_->InputDigitAndRememberPosition('1', &result_)); + EXPECT_EQ(1, formatter_->GetRememberedPosition()); + + EXPECT_EQ("16", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("1 65", formatter_->InputDigit('5', &result_)); + EXPECT_EQ(1, formatter_->GetRememberedPosition()); + EXPECT_EQ("1 650", formatter_->InputDigitAndRememberPosition('0', &result_)); + EXPECT_EQ(5, formatter_->GetRememberedPosition()); + EXPECT_EQ("1 650 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("1 650 25", formatter_->InputDigit('5', &result_)); + + // Note the remembered position for digit "0" changes from 4 to 5, because a + // space is now inserted in the front. + EXPECT_EQ(5, formatter_->GetRememberedPosition()); + EXPECT_EQ("1 650 253", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("1 650 253 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("1 650 253 22", formatter_->InputDigit('2', &result_)); + EXPECT_EQ(5, formatter_->GetRememberedPosition()); + EXPECT_EQ("1 650 253 222", formatter_->InputDigitAndRememberPosition('2', + &result_)); + EXPECT_EQ(13, formatter_->GetRememberedPosition()); + EXPECT_EQ("1 650 253 2222", formatter_->InputDigit('2', &result_)); + EXPECT_EQ(13, formatter_->GetRememberedPosition()); + EXPECT_EQ("165025322222", formatter_->InputDigit('2', &result_)); + EXPECT_EQ(10, formatter_->GetRememberedPosition()); + EXPECT_EQ("1650253222222", formatter_->InputDigit('2', &result_)); + EXPECT_EQ(10, formatter_->GetRememberedPosition()); + + formatter_->Clear(); + EXPECT_EQ("1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("16", formatter_->InputDigitAndRememberPosition('6', &result_)); + EXPECT_EQ(2, formatter_->GetRememberedPosition()); + EXPECT_EQ("1 65", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("1 650", formatter_->InputDigit('0', &result_)); + EXPECT_EQ(3, formatter_->GetRememberedPosition()); + EXPECT_EQ("1 650 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("1 650 25", formatter_->InputDigit('5', &result_)); + EXPECT_EQ(3, formatter_->GetRememberedPosition()); + EXPECT_EQ("1 650 253", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("1 650 253 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("1 650 253 22", formatter_->InputDigit('2', &result_)); + EXPECT_EQ(3, formatter_->GetRememberedPosition()); + EXPECT_EQ("1 650 253 222", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("1 650 253 2222", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("165025322222", formatter_->InputDigit('2', &result_)); + EXPECT_EQ(2, formatter_->GetRememberedPosition()); + EXPECT_EQ("1650253222222", formatter_->InputDigit('2', &result_)); + EXPECT_EQ(2, formatter_->GetRememberedPosition()); + + formatter_->Clear(); + EXPECT_EQ("6", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("65", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("650", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("650 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("650 25", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("650 253", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("650 2532", + formatter_->InputDigitAndRememberPosition('2', &result_)); + EXPECT_EQ(8, formatter_->GetRememberedPosition()); + EXPECT_EQ("650 253 22", formatter_->InputDigit('2', &result_)); + EXPECT_EQ(9, formatter_->GetRememberedPosition()); + EXPECT_EQ("650 253 222", formatter_->InputDigit('2', &result_)); + // No more formatting when semicolon is entered. + EXPECT_EQ("650253222;", formatter_->InputDigit(';', &result_)); + EXPECT_EQ(7, formatter_->GetRememberedPosition()); + EXPECT_EQ("650253222;2", formatter_->InputDigit('2', &result_)); + + formatter_->Clear(); + EXPECT_EQ("6", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("65", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("650", formatter_->InputDigit('0', &result_)); + // No more formatting when users choose to do their own formatting. + EXPECT_EQ("650-", formatter_->InputDigit('-', &result_)); + EXPECT_EQ("650-2", formatter_->InputDigitAndRememberPosition('2', &result_)); + EXPECT_EQ(5, formatter_->GetRememberedPosition()); + EXPECT_EQ("650-25", formatter_->InputDigit('5', &result_)); + EXPECT_EQ(5, formatter_->GetRememberedPosition()); + EXPECT_EQ("650-253", formatter_->InputDigit('3', &result_)); + EXPECT_EQ(5, formatter_->GetRememberedPosition()); + EXPECT_EQ("650-253-", formatter_->InputDigit('-', &result_)); + EXPECT_EQ("650-253-2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("650-253-22", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("650-253-222", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("650-253-2222", formatter_->InputDigit('2', &result_)); + + formatter_->Clear(); + EXPECT_EQ("0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("01", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 ", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 4", formatter_->InputDigitAndRememberPosition('4', &result_)); + EXPECT_EQ("011 48 ", formatter_->InputDigit('8', &result_)); + EXPECT_EQ(5, formatter_->GetRememberedPosition()); + EXPECT_EQ("011 48 8", formatter_->InputDigit('8', &result_)); + EXPECT_EQ(5, formatter_->GetRememberedPosition()); + EXPECT_EQ("011 48 88", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("011 48 88 1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 48 88 12", formatter_->InputDigit('2', &result_)); + EXPECT_EQ(5, formatter_->GetRememberedPosition()); + EXPECT_EQ("011 48 88 123", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("011 48 88 123 1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 48 88 123 12", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("011 48 88 123 12 1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 48 88 123 12 12", formatter_->InputDigit('2', &result_)); + + formatter_->Clear(); + EXPECT_EQ("+", formatter_->InputDigit('+', &result_)); + EXPECT_EQ("+1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+1 6", formatter_->InputDigitAndRememberPosition('6', &result_)); + EXPECT_EQ("+1 65", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+1 650", formatter_->InputDigit('0', &result_)); + EXPECT_EQ(4, formatter_->GetRememberedPosition()); + EXPECT_EQ("+1 650 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ(4, formatter_->GetRememberedPosition()); + EXPECT_EQ("+1 650 25", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+1 650 253", + formatter_->InputDigitAndRememberPosition('3', &result_)); + EXPECT_EQ("+1 650 253 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+1 650 253 22", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+1 650 253 222", formatter_->InputDigit('2', &result_)); + EXPECT_EQ(10, formatter_->GetRememberedPosition()); + + formatter_->Clear(); + EXPECT_EQ("+", formatter_->InputDigit('+', &result_)); + EXPECT_EQ("+1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+1 6", formatter_->InputDigitAndRememberPosition('6', &result_)); + EXPECT_EQ("+1 65", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+1 650", formatter_->InputDigit('0', &result_)); + EXPECT_EQ(4, formatter_->GetRememberedPosition()); + EXPECT_EQ("+1 650 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ(4, formatter_->GetRememberedPosition()); + EXPECT_EQ("+1 650 25", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+1 650 253", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+1 650 253 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+1 650 253 22", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+1 650 253 222", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+1650253222;", formatter_->InputDigit(';', &result_)); + EXPECT_EQ(3, formatter_->GetRememberedPosition()); +} + +TEST_F(AsYouTypeFormatterTest, AYTF_GBFixedLine) { + formatter_.reset(phone_util_.GetAsYouTypeFormatter("GB")); + + EXPECT_EQ("0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("02", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("020", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("020 7", formatter_->InputDigitAndRememberPosition('7', &result_)); + EXPECT_EQ(5, formatter_->GetRememberedPosition()); + EXPECT_EQ("020 70", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("020 703", formatter_->InputDigit('3', &result_)); + EXPECT_EQ(5, formatter_->GetRememberedPosition()); + EXPECT_EQ("020 7031", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("020 7031 3", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("020 7031 30", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("020 7031 300", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("020 7031 3000", formatter_->InputDigit('0', &result_)); +} + +TEST_F(AsYouTypeFormatterTest, AYTF_GBTollFree) { + formatter_.reset(phone_util_.GetAsYouTypeFormatter("GB")); + + EXPECT_EQ("0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("08", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("080", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("080 7", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("080 70", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("080 703", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("080 7031", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("080 7031 3", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("080 7031 30", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("080 7031 300", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("080 7031 3000", formatter_->InputDigit('0', &result_)); +} + +TEST_F(AsYouTypeFormatterTest, AYTF_GBPremiumRate) { + formatter_.reset(phone_util_.GetAsYouTypeFormatter("GB")); + + EXPECT_EQ("0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("09", formatter_->InputDigit('9', &result_)); + EXPECT_EQ("090", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("090 7", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("090 70", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("090 703", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("090 7031", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("090 7031 3", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("090 7031 30", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("090 7031 300", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("090 7031 3000", formatter_->InputDigit('0', &result_)); +} + +TEST_F(AsYouTypeFormatterTest, AYTF_NZMobile) { + formatter_.reset(phone_util_.GetAsYouTypeFormatter("NZ")); + + EXPECT_EQ("0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("02", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("021", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("02-11", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("02-112", formatter_->InputDigit('2', &result_)); + // Note the unittest is using fake metadata which might produce non-ideal + // results. + EXPECT_EQ("02-112 3", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("02-112 34", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("02-112 345", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("02-112 3456", formatter_->InputDigit('6', &result_)); +} + +TEST_F(AsYouTypeFormatterTest, AYTF_DE) { + formatter_.reset(phone_util_.GetAsYouTypeFormatter("DE")); + + EXPECT_EQ("0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("03", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("030", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("030/1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("030/12", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("030/123", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("030/1234", formatter_->InputDigit('4', &result_)); + + // 08021 2345 + formatter_->Clear(); + EXPECT_EQ("0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("08", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("080", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("080 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("080 21", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("08021 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("08021 23", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("08021 234", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("08021 2345", formatter_->InputDigit('5', &result_)); + + // 00 1 650 253 2250 + formatter_->Clear(); + EXPECT_EQ("0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("00", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("00 1 ", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("00 1 6", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("00 1 65", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("00 1 650", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("00 1 650 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("00 1 650 25", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("00 1 650 253", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("00 1 650 253 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("00 1 650 253 22", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("00 1 650 253 222", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("00 1 650 253 2222", formatter_->InputDigit('2', &result_)); +} + +TEST_F(AsYouTypeFormatterTest, AYTF_AR) { + formatter_.reset(phone_util_.GetAsYouTypeFormatter("AR")); + + EXPECT_EQ("0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("01", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 7", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("011 70", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("011 703", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("011 7031", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011 7031-3", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("011 7031-30", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("011 7031-300", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("011 7031-3000", formatter_->InputDigit('0', &result_)); +} + +TEST_F(AsYouTypeFormatterTest, AYTF_ARMobile) { + formatter_.reset(phone_util_.GetAsYouTypeFormatter("AR")); + + EXPECT_EQ("+", formatter_->InputDigit('+', &result_)); + EXPECT_EQ("+5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+54 ", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("+54 9", formatter_->InputDigit('9', &result_)); + EXPECT_EQ("+54 91", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+54 9 11", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+54 9 11 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+54 9 11 23", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+54 9 11 231", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+54 9 11 2312", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+54 9 11 2312 1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+54 9 11 2312 12", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+54 9 11 2312 123", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+54 9 11 2312 1234", formatter_->InputDigit('4', &result_)); +} + +TEST_F(AsYouTypeFormatterTest, AYTF_KR) { + formatter_.reset(phone_util_.GetAsYouTypeFormatter("KR")); + + // +82 51 234 5678 + EXPECT_EQ("+", formatter_->InputDigit('+', &result_)); + EXPECT_EQ("+8", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("+82 ", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+82 5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+82 51", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+82 51-2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+82 51-23", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+82 51-234", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("+82 51-234-5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+82 51-234-56", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("+82 51-234-567", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("+82 51-234-5678", formatter_->InputDigit('8', &result_)); + + // +82 2 531 5678 + formatter_->Clear(); + EXPECT_EQ("+", formatter_->InputDigit('+', &result_)); + EXPECT_EQ("+8", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("+82 ", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+82 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+82 25", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+82 2-53", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+82 2-531", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+82 2-531-5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+82 2-531-56", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("+82 2-531-567", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("+82 2-531-5678", formatter_->InputDigit('8', &result_)); + + // +82 2 3665 5678 + formatter_->Clear(); + EXPECT_EQ("+", formatter_->InputDigit('+', &result_)); + EXPECT_EQ("+8", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("+82 ", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+82 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+82 23", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+82 2-36", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("+82 2-366", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("+82 2-3665", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+82 2-3665-5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+82 2-3665-56", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("+82 2-3665-567", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("+82 2-3665-5678", formatter_->InputDigit('8', &result_)); + + // 02-114 + formatter_->Clear(); + EXPECT_EQ("0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("02", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("021", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("02-11", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("02-114", formatter_->InputDigit('4', &result_)); + + // 02-1300 + formatter_->Clear(); + EXPECT_EQ("0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("02", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("021", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("02-13", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("02-130", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("02-1300", formatter_->InputDigit('0', &result_)); + + // 011-456-7890 + formatter_->Clear(); + EXPECT_EQ("0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("01", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011-4", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("011-45", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("011-456", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("011-456-7", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("011-456-78", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("011-456-789", formatter_->InputDigit('9', &result_)); + EXPECT_EQ("011-456-7890", formatter_->InputDigit('0', &result_)); + + // 011-9876-7890 + formatter_->Clear(); + EXPECT_EQ("0", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("01", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("011-9", formatter_->InputDigit('9', &result_)); + EXPECT_EQ("011-98", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("011-987", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("011-9876", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("011-9876-7", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("011-9876-78", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("011-9876-789", formatter_->InputDigit('9', &result_)); + EXPECT_EQ("011-9876-7890", formatter_->InputDigit('0', &result_)); +} + +TEST_F(AsYouTypeFormatterTest, AYTF_MX) { + formatter_.reset(phone_util_.GetAsYouTypeFormatter("MX")); + + // +52 800 123 4567 + EXPECT_EQ("+", formatter_->InputDigit('+', &result_)); + EXPECT_EQ("+5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+52 ", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+52 8", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("+52 80", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("+52 800", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("+52 800 1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+52 800 12", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+52 800 123", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+52 800 123 4", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("+52 800 123 45", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+52 800 123 456", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("+52 800 123 4567", formatter_->InputDigit('7', &result_)); + + // +52 55 1234 5678 + formatter_->Clear(); + EXPECT_EQ("+", formatter_->InputDigit('+', &result_)); + EXPECT_EQ("+5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+52 ", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+52 5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+52 55", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+52 55 1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+52 55 12", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+52 55 123", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+52 55 1234", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("+52 55 1234 5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+52 55 1234 56", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("+52 55 1234 567", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("+52 55 1234 5678", formatter_->InputDigit('8', &result_)); + + // +52 212 345 6789 + formatter_->Clear(); + EXPECT_EQ("+", formatter_->InputDigit('+', &result_)); + EXPECT_EQ("+5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+52 ", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+52 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+52 21", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+52 212", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+52 212 3", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+52 212 34", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("+52 212 345", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+52 212 345 6", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("+52 212 345 67", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("+52 212 345 678", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("+52 212 345 6789", formatter_->InputDigit('9', &result_)); + + // +52 1 55 1234 5678 + formatter_->Clear(); + EXPECT_EQ("+", formatter_->InputDigit('+', &result_)); + EXPECT_EQ("+5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+52 ", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+52 1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+52 15", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+52 1 55", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+52 1 55 1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+52 1 55 12", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+52 1 55 123", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+52 1 55 1234", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("+52 1 55 1234 5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+52 1 55 1234 56", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("+52 1 55 1234 567", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("+52 1 55 1234 5678", formatter_->InputDigit('8', &result_)); + + // +52 1 541 234 5678 + formatter_->Clear(); + EXPECT_EQ("+", formatter_->InputDigit('+', &result_)); + EXPECT_EQ("+5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+52 ", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+52 1", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+52 15", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+52 1 54", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("+52 1 541", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+52 1 541 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+52 1 541 23", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+52 1 541 234", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("+52 1 541 234 5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+52 1 541 234 56", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("+52 1 541 234 567", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("+52 1 541 234 5678", formatter_->InputDigit('8', &result_)); +} + +TEST_F(AsYouTypeFormatterTest, AYTF_MultipleLeadingDigitPatterns) { + formatter_.reset(phone_util_.GetAsYouTypeFormatter("JP")); + + // +81 50 2345 6789 + EXPECT_EQ("+", formatter_->InputDigit('+', &result_)); + EXPECT_EQ("+8", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("+81 ", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+81 5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+81 50", formatter_->InputDigit('0', &result_)); + EXPECT_EQ("+81 50 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+81 50 23", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+81 50 234", formatter_->InputDigit('4', &result_)); + EXPECT_EQ("+81 50 2345", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+81 50 2345 6", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("+81 50 2345 67", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("+81 50 2345 678", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("+81 50 2345 6789", formatter_->InputDigit('9', &result_)); + + // +81 222 12 5678 + formatter_->Clear(); + EXPECT_EQ("+", formatter_->InputDigit('+', &result_)); + EXPECT_EQ("+8", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("+81 ", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+81 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+81 22", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+81 22 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+81 22 21", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+81 2221 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+81 222 12 5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+81 222 12 56", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("+81 222 12 567", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("+81 222 12 5678", formatter_->InputDigit('8', &result_)); + + // +81 3332 2 5678 + formatter_->Clear(); + EXPECT_EQ("+", formatter_->InputDigit('+', &result_)); + EXPECT_EQ("+8", formatter_->InputDigit('8', &result_)); + EXPECT_EQ("+81 ", formatter_->InputDigit('1', &result_)); + EXPECT_EQ("+81 3", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+81 33", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+81 33 3", formatter_->InputDigit('3', &result_)); + EXPECT_EQ("+81 3332", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+81 3332 2", formatter_->InputDigit('2', &result_)); + EXPECT_EQ("+81 3332 2 5", formatter_->InputDigit('5', &result_)); + EXPECT_EQ("+81 3332 2 56", formatter_->InputDigit('6', &result_)); + EXPECT_EQ("+81 3332 2 567", formatter_->InputDigit('7', &result_)); + EXPECT_EQ("+81 3332 2 5678", formatter_->InputDigit('8', &result_)); +} + +} // namespace phonenumbers +} // namespace i18n diff --git a/cpp/test/phonenumbers/unicodestring_test.cc b/cpp/test/phonenumbers/unicodestring_test.cc new file mode 100644 index 0000000..12b3d4c --- /dev/null +++ b/cpp/test/phonenumbers/unicodestring_test.cc @@ -0,0 +1,236 @@ +// Copyright (C) 2011 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: Philippe Liard + +#include + +#include + +#include "phonenumbers/unicodestring.h" + +using std::ostream; + +// Used by GTest to print the expected and actual results in case of failure. +ostream& operator<<(ostream& out, const i18n::phonenumbers::UnicodeString& s) { + string utf8; + s.toUTF8String(utf8); + out << utf8; + return out; +} + +namespace i18n { +namespace phonenumbers { + +TEST(UnicodeString, ToUTF8StringWithEmptyString) { + UnicodeString s; + string utf8; + s.toUTF8String(utf8); + EXPECT_EQ("", utf8); +} + +TEST(UnicodeString, ToUTF8String) { + UnicodeString s("hello"); + string utf8; + s.toUTF8String(utf8); + EXPECT_EQ("hello", utf8); +} + +TEST(UnicodeString, ToUTF8StringWithNonAscii) { + UnicodeString s("\xEF\xBC\x95\xEF\xBC\x93" /* "53" */); + string utf8; + s.toUTF8String(utf8); + EXPECT_EQ("\xEF\xBC\x95\xEF\xBC\x93", utf8); +} + +TEST(UnicodeString, AppendCodepoint) { + UnicodeString s; + s.append('h'); + ASSERT_EQ(UnicodeString("h"), s); + s.append('e'); + EXPECT_EQ(UnicodeString("he"), s); +} + +TEST(UnicodeString, AppendCodepointWithNonAscii) { + UnicodeString s; + s.append(0xFF15 /* 5 */); + ASSERT_EQ(UnicodeString("\xEF\xBC\x95" /* 5 */), s); + s.append(0xFF13 /* 3 */); + EXPECT_EQ(UnicodeString("\xEF\xBC\x95\xEF\xBC\x93" /* 53 */), s); +} + +TEST(UnicodeString, AppendUnicodeString) { + UnicodeString s; + s.append(UnicodeString("he")); + ASSERT_EQ(UnicodeString("he"), s); + s.append(UnicodeString("llo")); + EXPECT_EQ(UnicodeString("hello"), s); +} + +TEST(UnicodeString, AppendUnicodeStringWithNonAscii) { + UnicodeString s; + s.append(UnicodeString("\xEF\xBC\x95" /* 5 */)); + ASSERT_EQ(UnicodeString("\xEF\xBC\x95"), s); + s.append(UnicodeString("\xEF\xBC\x93" /* 3 */)); + EXPECT_EQ(UnicodeString("\xEF\xBC\x95\xEF\xBC\x93" /* 53 */), s); +} + +TEST(UnicodeString, IndexOf) { + UnicodeString s("hello"); + EXPECT_EQ(0, s.indexOf('h')); + EXPECT_EQ(2, s.indexOf('l')); + EXPECT_EQ(4, s.indexOf('o')); +} + +TEST(UnicodeString, IndexOfWithNonAscii) { + UnicodeString s("\xEF\xBC\x95\xEF\xBC\x93" /* 53 */); + EXPECT_EQ(1, s.indexOf(0xFF13 /* 3 */)); +} + +TEST(UnicodeString, ReplaceWithEmptyInputs) { + UnicodeString s; + s.replace(0, 0, UnicodeString("")); + EXPECT_EQ(UnicodeString(""), s); +} + +TEST(UnicodeString, ReplaceWithEmptyReplacement) { + UnicodeString s("hello"); + s.replace(0, 5, UnicodeString("")); + EXPECT_EQ(UnicodeString(""), s); +} + +TEST(UnicodeString, ReplaceBegining) { + UnicodeString s("hello world"); + s.replace(0, 5, UnicodeString("HELLO")); + EXPECT_EQ(UnicodeString("HELLO world"), s); +} + +TEST(UnicodeString, ReplaceMiddle) { + UnicodeString s("hello world"); + s.replace(5, 1, UnicodeString("AB")); + EXPECT_EQ(UnicodeString("helloABworld"), s); +} + +TEST(UnicodeString, ReplaceEnd) { + UnicodeString s("hello world"); + s.replace(10, 1, UnicodeString("AB")); + EXPECT_EQ(UnicodeString("hello worlAB"), s); +} + +TEST(UnicodeString, ReplaceWithNonAscii) { + UnicodeString s("hello world"); + s.replace(3, 2, UnicodeString("\xEF\xBC\x91\xEF\xBC\x90" /* 10 */)); + EXPECT_EQ(UnicodeString("hel\xEF\xBC\x91\xEF\xBC\x90 world"), s); +} + +TEST(UnicodeString, SetCharBegining) { + UnicodeString s("hello"); + s.setCharAt(0, 'H'); + EXPECT_EQ(UnicodeString("Hello"), s); +} + +TEST(UnicodeString, SetCharMiddle) { + UnicodeString s("hello"); + s.setCharAt(2, 'L'); + EXPECT_EQ(UnicodeString("heLlo"), s); +} + +TEST(UnicodeString, SetCharEnd) { + UnicodeString s("hello"); + s.setCharAt(4, 'O'); + EXPECT_EQ(UnicodeString("hellO"), s); +} + +TEST(UnicodeString, SetCharWithNonAscii) { + UnicodeString s("hello"); + s.setCharAt(4, 0xFF10 /* 0 */); + EXPECT_EQ(UnicodeString("hell\xEF\xBC\x90" /* 0 */), s); +} + +TEST(UnicodeString, TempSubStringWithEmptyString) { + EXPECT_EQ(UnicodeString(""), UnicodeString().tempSubString(0, 0)); +} + +TEST(UnicodeString, TempSubStringWithInvalidInputs) { + UnicodeString s("hello"); + // tempSubString() returns an empty unicode string if one of the provided + // paramaters is out of range. + EXPECT_EQ(UnicodeString(""), s.tempSubString(6)); + EXPECT_EQ(UnicodeString(""), s.tempSubString(2, 6)); +} + +TEST(UnicodeString, TempSubString) { + UnicodeString s("hello"); + EXPECT_EQ(UnicodeString(""), s.tempSubString(0, 0)); + EXPECT_EQ(UnicodeString("h"), s.tempSubString(0, 1)); + EXPECT_EQ(UnicodeString("hello"), s.tempSubString(0, 5)); + EXPECT_EQ(UnicodeString("llo"), s.tempSubString(2, 3)); +} + +TEST(UnicodeString, TempSubStringWithNoLength) { + UnicodeString s("hello"); + EXPECT_EQ(UnicodeString("hello"), s.tempSubString(0)); + EXPECT_EQ(UnicodeString("llo"), s.tempSubString(2)); +} + +TEST(UnicodeString, TempSubStringWithNonAscii) { + UnicodeString s("hel\xEF\xBC\x91\xEF\xBC\x90" /* 10 */); + EXPECT_EQ(UnicodeString("\xEF\xBC\x91" /* 1 */), s.tempSubString(3, 1)); +} + +TEST(UnicodeString, OperatorEqual) { + UnicodeString s("hello"); + s = UnicodeString("Hello"); + EXPECT_EQ(UnicodeString("Hello"), s); +} + +TEST(UnicodeString, OperatorEqualWithNonAscii) { + UnicodeString s("hello"); + s = UnicodeString("hel\xEF\xBC\x91\xEF\xBC\x90" /* 10 */); + EXPECT_EQ(UnicodeString("hel\xEF\xBC\x91\xEF\xBC\x90"), s); +} + +TEST(UnicodeString, OperatorBracket) { + UnicodeString s("hello"); + EXPECT_EQ('h', s[0]); + EXPECT_EQ('e', s[1]); + EXPECT_EQ('l', s[2]); + EXPECT_EQ('l', s[3]); + EXPECT_EQ('o', s[4]); +} + +TEST(UnicodeString, OperatorBracketWithNonAscii) { + UnicodeString s("hel\xEF\xBC\x91\xEF\xBC\x90" /* 10 */); + EXPECT_EQ('h', s[0]); + EXPECT_EQ('e', s[1]); + EXPECT_EQ('l', s[2]); + EXPECT_EQ(0xFF11 /* 1 */, s[3]); + EXPECT_EQ(0xFF10 /* 0 */, s[4]); +} + +TEST(UnicodeString, OperatorBracketWithIteratorCacheInvalidation) { + UnicodeString s("hello"); + EXPECT_EQ('h', s[0]); + EXPECT_EQ('e', s[1]); + // Modify the string which should invalidate the iterator cache. + s.setCharAt(1, 'E'); + EXPECT_EQ(UnicodeString("hEllo"), s); + EXPECT_EQ('E', s[1]); + // Get the previous character which should invalidate the iterator cache. + EXPECT_EQ('h', s[0]); + EXPECT_EQ('o', s[4]); +} + +} // namespace phonenumbers +} // namespace i18n diff --git a/tools/script/continuous-integration.sh b/tools/script/continuous-integration.sh index 535e26d..6b2d04c 100755 --- a/tools/script/continuous-integration.sh +++ b/tools/script/continuous-integration.sh @@ -33,12 +33,18 @@ test_cpp_version() { # Write the program that tests the installation of the library to a temporary # source file. > $CC_TEST_FILE echo ' - #include + #include + #include #include + + using i18n::phonenumbers::AsYouTypeFormatter; using i18n::phonenumbers::PhoneNumberUtil; + int main() { PhoneNumberUtil* const phone_util = PhoneNumberUtil::GetInstance(); - return phone_util == NULL; + const scoped_ptr asytf( + phone_util->GetAsYouTypeFormatter("US")); + return !(phone_util != NULL && asytf != NULL); }' # Run the build and tests. ( -- 2.7.4