Rename GetIconID to GetIconId
[chromium-blink-merge.git] / components / autofill / core / browser / phone_number_i18n.cc
blob60cfaddc5d52ecc90c025f640a90e75213c9092c
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/autofill/core/browser/phone_number_i18n.h"
7 #include "base/basictypes.h"
8 #include "base/logging.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "components/autofill/core/browser/autofill_country.h"
13 #include "third_party/libphonenumber/src/phonenumber_api.h"
15 using i18n::phonenumbers::PhoneNumber;
16 using i18n::phonenumbers::PhoneNumberUtil;
18 namespace autofill {
20 namespace {
22 std::string SanitizeRegion(const std::string& region,
23 const std::string& app_locale) {
24 if (region.length() == 2)
25 return region;
27 return AutofillCountry::CountryCodeForLocale(app_locale);
30 // Returns true if |phone_number| is valid.
31 bool IsValidPhoneNumber(const PhoneNumber& phone_number) {
32 PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance();
33 if (!phone_util->IsPossibleNumber(phone_number))
34 return false;
36 // Verify that the number has a valid area code (that in some cases could be
37 // empty) for the parsed country code. Also verify that this is a valid
38 // number (for example, in the US 1234567 is not valid, because numbers do not
39 // start with 1).
40 if (!phone_util->IsValidNumber(phone_number))
41 return false;
43 return true;
46 // Formats the given |number| as a human-readable string, and writes the result
47 // into |formatted_number|. Also, normalizes the formatted number, and writes
48 // that result into |normalized_number|. This function should only be called
49 // with numbers already known to be valid, i.e. validation should be done prior
50 // to calling this function. Note that the |country_code|, which determines
51 // whether to format in the national or in the international format, is passed
52 // in explicitly, as |number| might have an implicit country code set, even
53 // though the original input lacked a country code.
54 void FormatValidatedNumber(const PhoneNumber& number,
55 const base::string16& country_code,
56 base::string16* formatted_number,
57 base::string16* normalized_number) {
58 PhoneNumberUtil::PhoneNumberFormat format =
59 country_code.empty() ?
60 PhoneNumberUtil::NATIONAL :
61 PhoneNumberUtil::INTERNATIONAL;
63 PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance();
64 std::string processed_number;
65 phone_util->Format(number, format, &processed_number);
67 std::string region_code;
68 phone_util->GetRegionCodeForNumber(number, &region_code);
70 // Drop the leading '+' for US numbers as some US sites can't handle the "+",
71 // and in the US dialing "+1..." is the same as dialing "1...".
72 std::string prefix;
73 if (processed_number[0] == '+') {
74 processed_number = processed_number.substr(1);
75 if (region_code != "US")
76 prefix = "+";
79 if (formatted_number)
80 *formatted_number = base::UTF8ToUTF16(prefix + processed_number);
82 if (normalized_number) {
83 phone_util->NormalizeDigitsOnly(&processed_number);
84 *normalized_number = base::UTF8ToUTF16(prefix + processed_number);
88 } // namespace
90 namespace i18n {
92 // Parses the number stored in |value| as it should be interpreted in the given
93 // |default_region|, and stores the results into the remaining arguments.
94 // The |default_region| should be sanitized prior to calling this function.
95 bool ParsePhoneNumber(const base::string16& value,
96 const std::string& default_region,
97 base::string16* country_code,
98 base::string16* city_code,
99 base::string16* number,
100 std::string* inferred_region,
101 PhoneNumber* i18n_number) {
102 country_code->clear();
103 city_code->clear();
104 number->clear();
105 *i18n_number = PhoneNumber();
107 std::string number_text(base::UTF16ToUTF8(value));
109 // Parse phone number based on the region.
110 PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance();
112 // The |default_region| should already be sanitized.
113 DCHECK_EQ(2U, default_region.size());
114 if (phone_util->ParseAndKeepRawInput(
115 number_text, default_region, i18n_number) !=
116 PhoneNumberUtil::NO_PARSING_ERROR) {
117 return false;
120 if (!IsValidPhoneNumber(*i18n_number))
121 return false;
123 std::string national_significant_number;
124 phone_util->GetNationalSignificantNumber(*i18n_number,
125 &national_significant_number);
127 int area_length = phone_util->GetLengthOfGeographicalAreaCode(*i18n_number);
128 int destination_length =
129 phone_util->GetLengthOfNationalDestinationCode(*i18n_number);
130 // Some phones have a destination code in lieu of area code: mobile operators
131 // in Europe, toll and toll-free numbers in USA, etc. From our point of view
132 // these two types of codes are the same.
133 if (destination_length > area_length)
134 area_length = destination_length;
136 std::string area_code;
137 std::string subscriber_number;
138 if (area_length > 0) {
139 area_code = national_significant_number.substr(0, area_length);
140 subscriber_number = national_significant_number.substr(area_length);
141 } else {
142 subscriber_number = national_significant_number;
144 *number = base::UTF8ToUTF16(subscriber_number);
145 *city_code = base::UTF8ToUTF16(area_code);
147 // Check if parsed number has a country code that was not inferred from the
148 // region.
149 if (i18n_number->has_country_code() &&
150 i18n_number->country_code_source() != PhoneNumber::FROM_DEFAULT_COUNTRY) {
151 *country_code = base::UTF8ToUTF16(
152 base::IntToString(i18n_number->country_code()));
155 // The region might be different from what we started with.
156 phone_util->GetRegionCodeForNumber(*i18n_number, inferred_region);
158 return true;
161 base::string16 NormalizePhoneNumber(const base::string16& value,
162 const std::string& region) {
163 DCHECK_EQ(2u, region.size());
164 base::string16 country_code, unused_city_code, unused_number;
165 std::string unused_region;
166 PhoneNumber phone_number;
167 if (!ParsePhoneNumber(value, region, &country_code, &unused_city_code,
168 &unused_number, &unused_region, &phone_number)) {
169 return base::string16(); // Parsing failed - do not store phone.
172 base::string16 normalized_number;
173 FormatValidatedNumber(phone_number, country_code, NULL, &normalized_number);
174 return normalized_number;
177 bool ConstructPhoneNumber(const base::string16& country_code,
178 const base::string16& city_code,
179 const base::string16& number,
180 const std::string& region,
181 base::string16* whole_number) {
182 DCHECK_EQ(2u, region.size());
183 whole_number->clear();
185 base::string16 unused_country_code, unused_city_code, unused_number;
186 std::string unused_region;
187 PhoneNumber phone_number;
188 if (!ParsePhoneNumber(country_code + city_code + number, region,
189 &unused_country_code, &unused_city_code, &unused_number,
190 &unused_region, &phone_number)) {
191 return false;
194 FormatValidatedNumber(phone_number, country_code, whole_number, NULL);
195 return true;
198 bool PhoneNumbersMatch(const base::string16& number_a,
199 const base::string16& number_b,
200 const std::string& raw_region,
201 const std::string& app_locale) {
202 // Sanitize the provided |raw_region| before trying to use it for parsing.
203 const std::string region = SanitizeRegion(raw_region, app_locale);
205 PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance();
207 // Parse phone numbers based on the region
208 PhoneNumber i18n_number1;
209 if (phone_util->Parse(
210 base::UTF16ToUTF8(number_a), region.c_str(), &i18n_number1) !=
211 PhoneNumberUtil::NO_PARSING_ERROR) {
212 return false;
215 PhoneNumber i18n_number2;
216 if (phone_util->Parse(
217 base::UTF16ToUTF8(number_b), region.c_str(), &i18n_number2) !=
218 PhoneNumberUtil::NO_PARSING_ERROR) {
219 return false;
222 switch (phone_util->IsNumberMatch(i18n_number1, i18n_number2)) {
223 case PhoneNumberUtil::INVALID_NUMBER:
224 case PhoneNumberUtil::NO_MATCH:
225 return false;
226 case PhoneNumberUtil::SHORT_NSN_MATCH:
227 return false;
228 case PhoneNumberUtil::NSN_MATCH:
229 case PhoneNumberUtil::EXACT_MATCH:
230 return true;
233 NOTREACHED();
234 return false;
237 PhoneObject::PhoneObject(const base::string16& number,
238 const std::string& region) {
239 DCHECK_EQ(2u, region.size());
240 // TODO(isherman): Autofill profiles should always have a |region| set, but in
241 // some cases it should be marked as implicit. Otherwise, phone numbers
242 // might behave differently when they are synced across computers:
243 // [ http://crbug.com/100845 ]. Once the bug is fixed, add a DCHECK here to
244 // verify.
246 scoped_ptr<PhoneNumber> i18n_number(new PhoneNumber);
247 if (ParsePhoneNumber(number, region, &country_code_, &city_code_, &number_,
248 &region_, i18n_number.get())) {
249 // The phone number was successfully parsed, so store the parsed version.
250 // The formatted and normalized versions will be set on the first call to
251 // the coresponding methods.
252 i18n_number_ = i18n_number.Pass();
253 } else {
254 // Parsing failed. Store passed phone "as is" into |whole_number_|.
255 whole_number_ = number;
259 PhoneObject::PhoneObject(const PhoneObject& other) { *this = other; }
261 PhoneObject::PhoneObject() {}
263 PhoneObject::~PhoneObject() {}
265 const base::string16& PhoneObject::GetFormattedNumber() const {
266 if (i18n_number_ && formatted_number_.empty()) {
267 FormatValidatedNumber(*i18n_number_, country_code_, &formatted_number_,
268 &whole_number_);
271 return formatted_number_;
274 base::string16 PhoneObject::GetNationallyFormattedNumber() const {
275 base::string16 formatted = whole_number_;
276 if (i18n_number_)
277 FormatValidatedNumber(*i18n_number_, base::string16(), &formatted, NULL);
279 return formatted;
282 const base::string16& PhoneObject::GetWholeNumber() const {
283 if (i18n_number_ && whole_number_.empty()) {
284 FormatValidatedNumber(*i18n_number_, country_code_, &formatted_number_,
285 &whole_number_);
288 return whole_number_;
291 PhoneObject& PhoneObject::operator=(const PhoneObject& other) {
292 if (this == &other)
293 return *this;
295 region_ = other.region_;
297 if (other.i18n_number_.get())
298 i18n_number_.reset(new PhoneNumber(*other.i18n_number_));
299 else
300 i18n_number_.reset();
302 country_code_ = other.country_code_;
303 city_code_ = other.city_code_;
304 number_ = other.number_;
306 formatted_number_ = other.formatted_number_;
307 whole_number_ = other.whole_number_;
309 return *this;
312 } // namespace i18n
313 } // namespace autofill