| // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/autofill/phone_number.h" |
| |
| #include "base/basictypes.h" |
| #include "base/string_util.h" |
| #include "chrome/browser/autofill/autofill_profile.h" |
| #include "chrome/browser/autofill/autofill_type.h" |
| #include "chrome/browser/autofill/field_types.h" |
| |
| namespace { |
| |
| const char16 kPhoneNumberSeparators[] = { ' ', '.', '(', ')', '-', 0 }; |
| |
| // The number of digits in a phone number. |
| const size_t kPhoneNumberLength = 7; |
| |
| // The number of digits in an area code. |
| const size_t kPhoneCityCodeLength = 3; |
| |
| const AutoFillType::FieldTypeSubGroup kAutoFillPhoneTypes[] = { |
| AutoFillType::PHONE_NUMBER, |
| AutoFillType::PHONE_CITY_CODE, |
| AutoFillType::PHONE_COUNTRY_CODE, |
| AutoFillType::PHONE_CITY_AND_NUMBER, |
| AutoFillType::PHONE_WHOLE_NUMBER, |
| }; |
| |
| const int kAutoFillPhoneLength = arraysize(kAutoFillPhoneTypes); |
| |
| } // namespace |
| |
| PhoneNumber::PhoneNumber() {} |
| |
| PhoneNumber::~PhoneNumber() {} |
| |
| void PhoneNumber::GetPossibleFieldTypes(const string16& text, |
| FieldTypeSet* possible_types) const { |
| string16 stripped_text(text); |
| StripPunctuation(&stripped_text); |
| if (!Validate(stripped_text)) |
| return; |
| |
| if (IsNumber(stripped_text)) |
| possible_types->insert(GetNumberType()); |
| |
| if (IsCityCode(stripped_text)) |
| possible_types->insert(GetCityCodeType()); |
| |
| if (IsCountryCode(stripped_text)) |
| possible_types->insert(GetCountryCodeType()); |
| |
| if (IsCityAndNumber(stripped_text)) |
| possible_types->insert(GetCityAndNumberType()); |
| |
| if (IsWholeNumber(stripped_text)) |
| possible_types->insert(GetWholeNumberType()); |
| } |
| |
| void PhoneNumber::GetAvailableFieldTypes(FieldTypeSet* available_types) const { |
| DCHECK(available_types); |
| |
| if (!number().empty()) |
| available_types->insert(GetNumberType()); |
| |
| if (!city_code().empty()) |
| available_types->insert(GetCityCodeType()); |
| |
| if (!country_code().empty()) |
| available_types->insert(GetCountryCodeType()); |
| |
| if (!CityAndNumber().empty()) |
| available_types->insert(GetCityAndNumberType()); |
| |
| if (!WholeNumber().empty()) |
| available_types->insert(GetWholeNumberType()); |
| } |
| |
| string16 PhoneNumber::GetFieldText(const AutoFillType& type) const { |
| AutoFillFieldType field_type = type.field_type(); |
| if (field_type == GetNumberType()) |
| return number(); |
| |
| if (field_type == GetCityCodeType()) |
| return city_code(); |
| |
| if (field_type == GetCountryCodeType()) |
| return country_code(); |
| |
| if (field_type == GetCityAndNumberType()) |
| return CityAndNumber(); |
| |
| if (field_type == GetWholeNumberType()) |
| return WholeNumber(); |
| |
| return string16(); |
| } |
| |
| void PhoneNumber::FindInfoMatches(const AutoFillType& type, |
| const string16& info, |
| std::vector<string16>* matched_text) const { |
| if (matched_text == NULL) { |
| DLOG(ERROR) << "NULL matched vector passed in"; |
| return; |
| } |
| |
| string16 number(info); |
| StripPunctuation(&number); |
| if (!Validate(number)) |
| return; |
| |
| string16 match; |
| if (type.field_type() == UNKNOWN_TYPE) { |
| for (int i = 0; i < kAutoFillPhoneLength; ++i) { |
| if (FindInfoMatchesHelper(kAutoFillPhoneTypes[i], info, &match)) |
| matched_text->push_back(match); |
| } |
| } else { |
| if (FindInfoMatchesHelper(type.subgroup(), info, &match)) |
| matched_text->push_back(match); |
| } |
| } |
| |
| void PhoneNumber::SetInfo(const AutoFillType& type, const string16& value) { |
| string16 number(value); |
| StripPunctuation(&number); |
| if (!Validate(number)) |
| return; |
| |
| FieldTypeSubGroup subgroup = type.subgroup(); |
| if (subgroup == AutoFillType::PHONE_NUMBER) |
| set_number(number); |
| else if (subgroup == AutoFillType::PHONE_CITY_CODE) |
| set_city_code(number); |
| else if (subgroup == AutoFillType::PHONE_COUNTRY_CODE) |
| set_country_code(number); |
| else if (subgroup == AutoFillType::PHONE_CITY_AND_NUMBER || |
| subgroup == AutoFillType::PHONE_WHOLE_NUMBER) |
| set_whole_number(number); |
| else |
| NOTREACHED(); |
| } |
| |
| // Static. |
| bool PhoneNumber::ParsePhoneNumber(const string16& value, |
| string16* number, |
| string16* city_code, |
| string16* country_code) { |
| DCHECK(number); |
| DCHECK(city_code); |
| DCHECK(country_code); |
| |
| // Make a working copy of value. |
| string16 working = value; |
| |
| *number = string16(); |
| *city_code = string16(); |
| *country_code = string16(); |
| |
| // First remove any punctuation. |
| StripPunctuation(&working); |
| |
| if (working.size() < kPhoneNumberLength) |
| return false; |
| |
| // Treat the last 7 digits as the number. |
| *number = working.substr(working.size() - kPhoneNumberLength, |
| kPhoneNumberLength); |
| working.resize(working.size() - kPhoneNumberLength); |
| if (working.size() < kPhoneCityCodeLength) |
| return true; |
| |
| // Treat the next three digits as the city code. |
| *city_code = working.substr(working.size() - kPhoneCityCodeLength, |
| kPhoneCityCodeLength); |
| working.resize(working.size() - kPhoneCityCodeLength); |
| if (working.empty()) |
| return true; |
| |
| // Treat any remaining digits as the country code. |
| *country_code = working; |
| return true; |
| } |
| |
| string16 PhoneNumber::WholeNumber() const { |
| string16 whole_number; |
| if (!country_code_.empty()) |
| whole_number.append(country_code_); |
| |
| if (!city_code_.empty()) |
| whole_number.append(city_code_); |
| |
| if (!number_.empty()) |
| whole_number.append(number_); |
| |
| return whole_number; |
| } |
| |
| void PhoneNumber::set_number(const string16& number) { |
| string16 digits(number); |
| StripPunctuation(&digits); |
| number_ = digits; |
| } |
| |
| void PhoneNumber::set_whole_number(const string16& whole_number) { |
| string16 number, city_code, country_code; |
| ParsePhoneNumber(whole_number, &number, &city_code, &country_code); |
| set_number(number); |
| set_city_code(city_code); |
| set_country_code(country_code); |
| } |
| |
| PhoneNumber::PhoneNumber(const PhoneNumber& phone_number) |
| : FormGroup(), |
| country_code_(phone_number.country_code_), |
| city_code_(phone_number.city_code_), |
| number_(phone_number.number_), |
| extension_(phone_number.extension_) { |
| } |
| |
| bool PhoneNumber::FindInfoMatchesHelper(const FieldTypeSubGroup& subgroup, |
| const string16& info, |
| string16* match) const { |
| if (match == NULL) { |
| DLOG(ERROR) << "NULL match string passed in"; |
| return false; |
| } |
| |
| match->clear(); |
| if (subgroup == AutoFillType::PHONE_NUMBER && |
| StartsWith(number(), info, true)) { |
| *match = number(); |
| } else if (subgroup == AutoFillType::PHONE_CITY_CODE && |
| StartsWith(city_code(), info, true)) { |
| *match = city_code(); |
| } else if (subgroup == AutoFillType::PHONE_COUNTRY_CODE && |
| StartsWith(country_code(), info, true)) { |
| *match = country_code(); |
| } else if (subgroup == AutoFillType::PHONE_CITY_AND_NUMBER && |
| StartsWith(CityAndNumber(), info, true)) { |
| *match = CityAndNumber(); |
| } else if (subgroup == AutoFillType::PHONE_WHOLE_NUMBER && |
| StartsWith(WholeNumber(), info, true)) { |
| *match = WholeNumber(); |
| } |
| |
| return !match->empty(); |
| } |
| |
| bool PhoneNumber::IsNumber(const string16& text) const { |
| if (text.length() > number_.length()) |
| return false; |
| |
| return StartsWith(number_, text, false); |
| } |
| |
| bool PhoneNumber::IsCityCode(const string16& text) const { |
| if (text.length() > city_code_.length()) |
| return false; |
| |
| return StartsWith(city_code_, text, false); |
| } |
| |
| bool PhoneNumber::IsCountryCode(const string16& text) const { |
| if (text.length() > country_code_.length()) |
| return false; |
| |
| return StartsWith(country_code_, text, false); |
| } |
| |
| bool PhoneNumber::IsCityAndNumber(const string16& text) const { |
| string16 city_and_number(CityAndNumber()); |
| if (text.length() > city_and_number.length()) |
| return false; |
| |
| return StartsWith(city_and_number, text, false); |
| } |
| |
| bool PhoneNumber::IsWholeNumber(const string16& text) const { |
| string16 whole_number(WholeNumber()); |
| if (text.length() > whole_number.length()) |
| return false; |
| |
| return StartsWith(whole_number, text, false); |
| } |
| |
| bool PhoneNumber::Validate(const string16& number) const { |
| for (size_t i = 0; i < number.length(); ++i) { |
| if (!IsAsciiDigit(number[i])) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Static. |
| void PhoneNumber::StripPunctuation(string16* number) { |
| RemoveChars(*number, kPhoneNumberSeparators, number); |
| } |