OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/autofill/phone_number_i18n.h" | 5 #include "chrome/browser/autofill/phone_number_i18n.h" |
6 | 6 |
7 #include <map> | |
8 | |
7 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
8 #include "base/logging.h" | 10 #include "base/logging.h" |
9 #include "base/stringprintf.h" | 11 #include "base/stringprintf.h" |
10 #include "base/string_number_conversions.h" | 12 #include "base/string_number_conversions.h" |
11 #include "base/utf_string_conversions.h" | 13 #include "base/utf_string_conversions.h" |
12 #include "chrome/browser/autofill/autofill_country.h" | 14 #include "chrome/browser/autofill/autofill_country.h" |
13 #include "third_party/libphonenumber/cpp/src/phonenumberutil.h" | 15 #include "third_party/libphonenumber/cpp/src/phonenumberutil.h" |
14 | 16 |
15 namespace { | 17 namespace { |
16 | 18 |
(...skipping 14 matching lines...) Expand all Loading... | |
31 case autofill_i18n::NATIONAL: | 33 case autofill_i18n::NATIONAL: |
32 return i18n::phonenumbers::PhoneNumberUtil::NATIONAL; | 34 return i18n::phonenumbers::PhoneNumberUtil::NATIONAL; |
33 case autofill_i18n::RFC3966: | 35 case autofill_i18n::RFC3966: |
34 return i18n::phonenumbers::PhoneNumberUtil::RFC3966; | 36 return i18n::phonenumbers::PhoneNumberUtil::RFC3966; |
35 default: | 37 default: |
36 NOTREACHED(); | 38 NOTREACHED(); |
37 } | 39 } |
38 return i18n::phonenumbers::PhoneNumberUtil::NATIONAL; | 40 return i18n::phonenumbers::PhoneNumberUtil::NATIONAL; |
39 } | 41 } |
40 | 42 |
41 } // namespace | 43 bool ParsePhoneNumberInternal(const string16& value, |
42 | 44 const std::string& locale, |
43 namespace autofill_i18n { | 45 string16* country_code, |
44 | 46 string16* city_code, |
45 string16 NormalizePhoneNumber(const string16& value, | 47 string16* number, |
46 std::string const& locale) { | 48 i18n::phonenumbers::PhoneNumber* i18n_number) { |
47 string16 number; | |
48 string16 city_code; | |
49 string16 country_code; | |
50 string16 result; | |
51 // Full number - parse it, split it and re-combine into canonical form. | |
52 if (!ParsePhoneNumber(value, locale, &country_code, &city_code, &number)) | |
53 return string16(); // Parsing failed - do not store phone. | |
54 if (!autofill_i18n::ConstructPhoneNumber( | |
55 country_code, city_code, number, | |
56 locale, | |
57 (country_code.empty() ? | |
58 autofill_i18n::NATIONAL : autofill_i18n::INTERNATIONAL), | |
59 &result)) { | |
60 // Reconstruction failed - do not store phone. | |
61 return string16(); | |
62 } | |
63 std::string result_utf8(UTF16ToUTF8(result)); | |
64 i18n::phonenumbers::PhoneNumberUtil::NormalizeDigitsOnly(&result_utf8); | |
65 return UTF8ToUTF16(result_utf8); | |
66 } | |
67 | |
68 bool ParsePhoneNumber(const string16& value, | |
69 const std::string& locale, | |
70 string16* country_code, | |
71 string16* city_code, | |
72 string16* number) { | |
73 DCHECK(number); | 49 DCHECK(number); |
74 DCHECK(city_code); | 50 DCHECK(city_code); |
75 DCHECK(country_code); | 51 DCHECK(country_code); |
52 DCHECK(i18n_number); | |
76 | 53 |
77 number->clear(); | 54 number->clear(); |
78 city_code->clear(); | 55 city_code->clear(); |
79 country_code->clear(); | 56 country_code->clear(); |
80 | 57 |
81 std::string number_text(UTF16ToUTF8(value)); | 58 std::string number_text(UTF16ToUTF8(value)); |
82 | 59 |
83 // Parse phone number based on the locale. | 60 // Parse phone number based on the locale. |
84 i18n::phonenumbers::PhoneNumber i18n_number; | |
85 i18n::phonenumbers::PhoneNumberUtil* phone_util = | 61 i18n::phonenumbers::PhoneNumberUtil* phone_util = |
86 i18n::phonenumbers::PhoneNumberUtil::GetInstance(); | 62 i18n::phonenumbers::PhoneNumberUtil::GetInstance(); |
87 DCHECK(phone_util); | 63 DCHECK(phone_util); |
88 | 64 |
89 if (phone_util->Parse(number_text, SanitizeLocaleCode(locale).c_str(), | 65 if (phone_util->Parse(number_text, SanitizeLocaleCode(locale).c_str(), |
90 &i18n_number) != | 66 i18n_number) != |
91 i18n::phonenumbers::PhoneNumberUtil::NO_PARSING_ERROR) { | 67 i18n::phonenumbers::PhoneNumberUtil::NO_PARSING_ERROR) { |
92 return false; | 68 return false; |
93 } | 69 } |
94 | 70 |
95 i18n::phonenumbers::PhoneNumberUtil::ValidationResult validation = | 71 i18n::phonenumbers::PhoneNumberUtil::ValidationResult validation = |
96 phone_util->IsPossibleNumberWithReason(i18n_number); | 72 phone_util->IsPossibleNumberWithReason(*i18n_number); |
97 if (validation != i18n::phonenumbers::PhoneNumberUtil::IS_POSSIBLE) | 73 if (validation != i18n::phonenumbers::PhoneNumberUtil::IS_POSSIBLE) |
98 return false; | 74 return false; |
99 | 75 |
100 // This verifies that number has a valid area code (that in some cases could | 76 // This verifies that number has a valid area code (that in some cases could |
101 // be empty) for parsed country code. Also verifies that this is a valid | 77 // be empty) for parsed country code. Also verifies that this is a valid |
102 // number (in US 1234567 is not valid, because numbers do not start with 1). | 78 // number (in US 1234567 is not valid, because numbers do not start with 1). |
103 if (!phone_util->IsValidNumber(i18n_number)) | 79 if (!phone_util->IsValidNumber(*i18n_number)) |
104 return false; | 80 return false; |
105 | 81 |
106 std::string national_significant_number; | 82 std::string national_significant_number; |
107 phone_util->GetNationalSignificantNumber(i18n_number, | 83 phone_util->GetNationalSignificantNumber(*i18n_number, |
108 &national_significant_number); | 84 &national_significant_number); |
109 | 85 |
110 std::string area_code; | 86 std::string area_code; |
111 std::string subscriber_number; | 87 std::string subscriber_number; |
112 | 88 |
113 int area_length = phone_util->GetLengthOfGeographicalAreaCode(i18n_number); | 89 int area_length = phone_util->GetLengthOfGeographicalAreaCode(*i18n_number); |
114 int destination_length = | 90 int destination_length = |
115 phone_util->GetLengthOfNationalDestinationCode(i18n_number); | 91 phone_util->GetLengthOfNationalDestinationCode(*i18n_number); |
116 // Some phones have a destination code in lieu of area code: mobile operators | 92 // Some phones have a destination code in lieu of area code: mobile operators |
117 // in Europe, toll and toll-free numbers in USA, etc. From our point of view | 93 // in Europe, toll and toll-free numbers in USA, etc. From our point of view |
118 // these two types of codes are the same. | 94 // these two types of codes are the same. |
119 if (destination_length > area_length) | 95 if (destination_length > area_length) |
120 area_length = destination_length; | 96 area_length = destination_length; |
121 if (area_length > 0) { | 97 if (area_length > 0) { |
122 area_code = national_significant_number.substr(0, area_length); | 98 area_code = national_significant_number.substr(0, area_length); |
123 subscriber_number = national_significant_number.substr(area_length); | 99 subscriber_number = national_significant_number.substr(area_length); |
124 } else { | 100 } else { |
125 subscriber_number = national_significant_number; | 101 subscriber_number = national_significant_number; |
126 } | 102 } |
127 *number = UTF8ToUTF16(subscriber_number); | 103 *number = UTF8ToUTF16(subscriber_number); |
128 *city_code = UTF8ToUTF16(area_code); | 104 *city_code = UTF8ToUTF16(area_code); |
129 *country_code = string16(); | 105 *country_code = string16(); |
130 | 106 |
131 i18n::phonenumbers::PhoneNumberUtil::NormalizeDigitsOnly(&number_text); | 107 i18n::phonenumbers::PhoneNumberUtil::NormalizeDigitsOnly(&number_text); |
132 string16 normalized_number(UTF8ToUTF16(number_text)); | 108 string16 normalized_number(UTF8ToUTF16(number_text)); |
133 // Check if parsed number has country code and it was not inferred from the | 109 // Check if parsed number has country code and it was not inferred from the |
134 // locale. | 110 // locale. |
135 if (i18n_number.has_country_code()) { | 111 if (i18n_number->has_country_code()) { |
136 *country_code = UTF8ToUTF16(base::StringPrintf("%d", | 112 *country_code = UTF8ToUTF16( |
137 i18n_number.country_code())); | 113 base::StringPrintf("%d", i18n_number->country_code())); |
138 if (normalized_number.length() <= national_significant_number.length() && | 114 if (normalized_number.length() <= national_significant_number.length() && |
139 (normalized_number.length() < country_code->length() || | 115 (normalized_number.length() < country_code->length() || |
140 normalized_number.compare(0, country_code->length(), *country_code))) { | 116 normalized_number.compare(0, country_code->length(), *country_code))) { |
141 country_code->clear(); | 117 country_code->clear(); |
142 } | 118 } |
143 } | 119 } |
144 | 120 |
145 return true; | 121 return true; |
146 } | 122 } |
147 | 123 |
124 } // namespace | |
125 | |
126 namespace autofill_i18n { | |
127 | |
128 string16 NormalizePhoneNumber(const string16& value, | |
129 std::string const& locale) { | |
130 string16 number; | |
131 string16 city_code; | |
132 string16 country_code; | |
133 string16 result; | |
134 // Full number - parse it, split it and re-combine into canonical form. | |
135 if (!ParsePhoneNumber(value, locale, &country_code, &city_code, &number)) | |
136 return string16(); // Parsing failed - do not store phone. | |
137 if (!autofill_i18n::ConstructPhoneNumber( | |
138 country_code, city_code, number, | |
139 locale, | |
140 (country_code.empty() ? | |
141 autofill_i18n::NATIONAL : autofill_i18n::INTERNATIONAL), | |
142 &result)) { | |
143 // Reconstruction failed - do not store phone. | |
144 return string16(); | |
145 } | |
146 std::string result_utf8(UTF16ToUTF8(result)); | |
147 i18n::phonenumbers::PhoneNumberUtil::NormalizeDigitsOnly(&result_utf8); | |
148 return UTF8ToUTF16(result_utf8); | |
149 } | |
150 | |
151 bool ParsePhoneNumber(const string16& value, | |
152 const std::string& locale, | |
153 string16* country_code, | |
154 string16* city_code, | |
155 string16* number) { | |
156 i18n::phonenumbers::PhoneNumber i18n_number; | |
157 return ParsePhoneNumberInternal(value, locale, country_code, city_code, | |
158 number, &i18n_number); | |
159 } | |
160 | |
148 bool ConstructPhoneNumber(const string16& country_code, | 161 bool ConstructPhoneNumber(const string16& country_code, |
149 const string16& city_code, | 162 const string16& city_code, |
150 const string16& number, | 163 const string16& number, |
151 const std::string& locale, | 164 const std::string& locale, |
152 FullPhoneFormat phone_format, | 165 FullPhoneFormat phone_format, |
153 string16* whole_number) { | 166 string16* whole_number) { |
154 DCHECK(whole_number); | 167 DCHECK(whole_number); |
155 | 168 |
156 whole_number->clear(); | 169 whole_number->clear(); |
157 | 170 |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
258 } | 271 } |
259 return PHONES_NOT_EQUAL; | 272 return PHONES_NOT_EQUAL; |
260 } | 273 } |
261 | 274 |
262 bool PhoneNumbersMatch(const string16& number_a, | 275 bool PhoneNumbersMatch(const string16& number_a, |
263 const string16& number_b, | 276 const string16& number_b, |
264 const std::string& country_code) { | 277 const std::string& country_code) { |
265 return ComparePhones(number_a, number_b, country_code) == PHONES_EQUAL; | 278 return ComparePhones(number_a, number_b, country_code) == PHONES_EQUAL; |
266 } | 279 } |
267 | 280 |
281 PhoneObject::PhoneObject(const string16& number, const std::string& locale) | |
282 : locale_(SanitizeLocaleCode(locale)), | |
283 i18n_number_(NULL) { | |
284 scoped_ptr<i18n::phonenumbers::PhoneNumber> | |
285 i18n_number(new i18n::phonenumbers::PhoneNumber); | |
286 if (ParsePhoneNumberInternal(number, locale_, &country_code_, &city_code_, | |
287 &number_, i18n_number.get())) { | |
288 i18n_number_ = i18n_number.release(); | |
289 } else { | |
290 whole_number_ = number; | |
291 } | |
Ilya Sherman
2011/06/10 07:17:33
nit: The else stmt here makes the code a little bi
GeorgeY
2011/06/10 22:36:35
Commented.
To move logic into GetWholeNumber() I w
| |
292 } | |
293 | |
294 PhoneObject::PhoneObject(const PhoneObject& other) | |
295 : i18n_number_(NULL) { | |
296 *this = other; | |
297 } | |
298 | |
299 PhoneObject::PhoneObject() | |
300 : i18n_number_(NULL) { | |
301 } | |
302 | |
303 PhoneObject::~PhoneObject() { | |
304 Clear(); | |
305 } | |
306 | |
307 string16 PhoneObject::GetCountryCode() const { | |
308 return country_code_; | |
309 } | |
310 | |
311 string16 PhoneObject::GetCityCode() const { | |
312 return city_code_; | |
313 } | |
314 | |
315 string16 PhoneObject::GetNumber() const { | |
316 return number_; | |
317 } | |
318 | |
319 string16 PhoneObject::GetWholeNumber() const { | |
320 if (i18n_number_ && whole_number_.empty()) { | |
321 i18n::phonenumbers::PhoneNumberUtil::PhoneNumberFormat format = | |
322 i18n::phonenumbers::PhoneNumberUtil::INTERNATIONAL; | |
323 if (country_code_.empty()) | |
324 format = i18n::phonenumbers::PhoneNumberUtil::NATIONAL; | |
325 | |
326 std::string formatted_number; | |
327 i18n::phonenumbers::PhoneNumberUtil* phone_util = | |
328 i18n::phonenumbers::PhoneNumberUtil::GetInstance(); | |
329 phone_util->Format(*i18n_number_, format, &formatted_number); | |
330 i18n::phonenumbers::PhoneNumberUtil::NormalizeDigitsOnly(&formatted_number); | |
331 whole_number_ = UTF8ToUTF16(formatted_number); | |
332 } | |
333 return whole_number_; | |
334 } | |
335 | |
336 PhoneMatch PhoneObject::ComparePhones(const string16& phone) const { | |
337 const size_t kMaxCacheSize = 1024; | |
338 static std::map<string16, PhoneObject> cached_objects; | |
339 // Remove last element from cache to prevent unlimited grow. | |
340 if (cached_objects.size() > kMaxCacheSize) { | |
341 std::map<string16, PhoneObject>::iterator it = cached_objects.end(); | |
342 --it; | |
343 cached_objects.erase(it); | |
344 } | |
345 string16 key(ASCIIToUTF16(locale_)); | |
346 key.append(phone); | |
347 std::map<string16, PhoneObject>::const_iterator it = cached_objects.find(key); | |
348 if (it == cached_objects.end()) { | |
349 // Add it to the cache. | |
350 it = cached_objects.insert( | |
351 std::pair<string16, PhoneObject>(key, | |
352 PhoneObject(phone, locale_))).first; | |
353 DCHECK(it != cached_objects.end()); | |
354 } | |
355 return ComparePhones(it->second); | |
356 } | |
357 | |
358 PhoneMatch PhoneObject::ComparePhones(const PhoneObject& phone) const { | |
359 if (!i18n_number_ || !phone.i18n_number_) { | |
360 if (GetWholeNumber().empty()) | |
361 return PHONES_NOT_EQUAL; | |
362 return (GetWholeNumber() == phone.GetWholeNumber()) ? PHONES_EQUAL : | |
363 PHONES_NOT_EQUAL; | |
364 } | |
365 | |
366 i18n::phonenumbers::PhoneNumberUtil* phone_util = | |
367 i18n::phonenumbers::PhoneNumberUtil::GetInstance(); | |
368 switch (phone_util->IsNumberMatch(*i18n_number_, *(phone.i18n_number_))) { | |
369 case i18n::phonenumbers::PhoneNumberUtil::INVALID_NUMBER: | |
370 case i18n::phonenumbers::PhoneNumberUtil::NO_MATCH: | |
371 return PHONES_NOT_EQUAL; | |
372 case i18n::phonenumbers::PhoneNumberUtil::SHORT_NSN_MATCH: | |
373 return PHONES_SUBMATCH; | |
374 case i18n::phonenumbers::PhoneNumberUtil::NSN_MATCH: | |
375 case i18n::phonenumbers::PhoneNumberUtil::EXACT_MATCH: | |
376 return PHONES_EQUAL; | |
377 default: | |
378 NOTREACHED(); | |
379 } | |
380 return PHONES_NOT_EQUAL; | |
381 } | |
382 | |
383 PhoneObject& PhoneObject::operator=(const PhoneObject& other) { | |
Ilya Sherman
2011/06/10 07:17:33
This is not currently a no-op when this == &other,
GeorgeY
2011/06/10 22:36:35
Sure, but most compilers are smart enough to auto
| |
384 country_code_ = other.country_code_; | |
385 city_code_ = other.city_code_; | |
386 number_ = other.number_; | |
387 locale_ = other.locale_; | |
388 Clear(); | |
389 if (other.i18n_number_) | |
390 i18n_number_ = new i18n::phonenumbers::PhoneNumber(*other.i18n_number_); | |
391 return *this; | |
392 } | |
393 | |
394 void PhoneObject::Clear() { | |
395 if (i18n_number_) | |
396 delete i18n_number_; | |
Ilya Sherman
2011/06/10 07:17:33
How about making i18n_number_ be a scoped_ptr<> in
GeorgeY
2011/06/10 22:36:35
As I explained in the first review, there are some
| |
397 i18n_number_ = NULL; | |
398 } | |
399 | |
268 } // namespace autofill_i18n | 400 } // namespace autofill_i18n |
269 | 401 |
OLD | NEW |