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 3// found in the LICENSE file. 4 5#include "chrome/browser/autofill/phone_number.h" 6 7#include "base/basictypes.h" 8#include "base/string_util.h" 9#include "chrome/browser/autofill/autofill_profile.h" 10#include "chrome/browser/autofill/autofill_type.h" 11#include "chrome/browser/autofill/field_types.h" 12 13namespace { 14 15const char16 kPhoneNumberSeparators[] = { ' ', '.', '(', ')', '-', 0 }; 16 17// The number of digits in a phone number. 18const size_t kPhoneNumberLength = 7; 19 20// The number of digits in an area code. 21const size_t kPhoneCityCodeLength = 3; 22 23const AutofillType::FieldTypeSubGroup kAutofillPhoneTypes[] = { 24 AutofillType::PHONE_NUMBER, 25 AutofillType::PHONE_CITY_CODE, 26 AutofillType::PHONE_COUNTRY_CODE, 27 AutofillType::PHONE_CITY_AND_NUMBER, 28 AutofillType::PHONE_WHOLE_NUMBER, 29}; 30 31const int kAutofillPhoneLength = arraysize(kAutofillPhoneTypes); 32 33} // namespace 34 35PhoneNumber::PhoneNumber() {} 36 37PhoneNumber::PhoneNumber(const PhoneNumber& number) : FormGroup() { 38 *this = number; 39} 40 41PhoneNumber::~PhoneNumber() {} 42 43PhoneNumber& PhoneNumber::operator=(const PhoneNumber& number) { 44 if (this == &number) 45 return *this; 46 country_code_ = number.country_code_; 47 city_code_ = number.city_code_; 48 number_ = number.number_; 49 extension_ = number.extension_; 50 return *this; 51} 52 53void PhoneNumber::GetPossibleFieldTypes(const string16& text, 54 FieldTypeSet* possible_types) const { 55 string16 stripped_text(text); 56 StripPunctuation(&stripped_text); 57 if (!Validate(stripped_text)) 58 return; 59 60 if (IsNumber(stripped_text)) 61 possible_types->insert(GetNumberType()); 62 63 if (IsCityCode(stripped_text)) 64 possible_types->insert(GetCityCodeType()); 65 66 if (IsCountryCode(stripped_text)) 67 possible_types->insert(GetCountryCodeType()); 68 69 if (IsCityAndNumber(stripped_text)) 70 possible_types->insert(GetCityAndNumberType()); 71 72 if (IsWholeNumber(stripped_text)) 73 possible_types->insert(GetWholeNumberType()); 74} 75 76void PhoneNumber::GetAvailableFieldTypes(FieldTypeSet* available_types) const { 77 DCHECK(available_types); 78 79 if (!number().empty()) 80 available_types->insert(GetNumberType()); 81 82 if (!city_code().empty()) 83 available_types->insert(GetCityCodeType()); 84 85 if (!country_code().empty()) 86 available_types->insert(GetCountryCodeType()); 87 88 if (!CityAndNumber().empty()) 89 available_types->insert(GetCityAndNumberType()); 90 91 if (!WholeNumber().empty()) 92 available_types->insert(GetWholeNumberType()); 93} 94 95string16 PhoneNumber::GetInfo(AutofillFieldType type) const { 96 if (type == GetNumberType()) 97 return number(); 98 99 if (type == GetCityCodeType()) 100 return city_code(); 101 102 if (type == GetCountryCodeType()) 103 return country_code(); 104 105 if (type == GetCityAndNumberType()) 106 return CityAndNumber(); 107 108 if (type == GetWholeNumberType()) 109 return WholeNumber(); 110 111 return string16(); 112} 113 114void PhoneNumber::SetInfo(AutofillFieldType type, const string16& value) { 115 string16 number(value); 116 StripPunctuation(&number); 117 if (!Validate(number)) 118 return; 119 120 FieldTypeSubGroup subgroup = AutofillType(type).subgroup(); 121 if (subgroup == AutofillType::PHONE_NUMBER) 122 set_number(number); 123 else if (subgroup == AutofillType::PHONE_CITY_CODE) 124 set_city_code(number); 125 else if (subgroup == AutofillType::PHONE_COUNTRY_CODE) 126 set_country_code(number); 127 else if (subgroup == AutofillType::PHONE_CITY_AND_NUMBER || 128 subgroup == AutofillType::PHONE_WHOLE_NUMBER) 129 set_whole_number(number); 130 else 131 NOTREACHED(); 132} 133 134// Static. 135bool PhoneNumber::ParsePhoneNumber(const string16& value, 136 string16* number, 137 string16* city_code, 138 string16* country_code) { 139 DCHECK(number); 140 DCHECK(city_code); 141 DCHECK(country_code); 142 143 // Make a working copy of value. 144 string16 working = value; 145 146 *number = string16(); 147 *city_code = string16(); 148 *country_code = string16(); 149 150 // First remove any punctuation. 151 StripPunctuation(&working); 152 153 if (working.size() < kPhoneNumberLength) 154 return false; 155 156 // Treat the last 7 digits as the number. 157 *number = working.substr(working.size() - kPhoneNumberLength, 158 kPhoneNumberLength); 159 working.resize(working.size() - kPhoneNumberLength); 160 if (working.size() < kPhoneCityCodeLength) 161 return true; 162 163 // Treat the next three digits as the city code. 164 *city_code = working.substr(working.size() - kPhoneCityCodeLength, 165 kPhoneCityCodeLength); 166 working.resize(working.size() - kPhoneCityCodeLength); 167 if (working.empty()) 168 return true; 169 170 // Treat any remaining digits as the country code. 171 *country_code = working; 172 return true; 173} 174 175string16 PhoneNumber::WholeNumber() const { 176 string16 whole_number; 177 if (!country_code_.empty()) 178 whole_number.append(country_code_); 179 180 if (!city_code_.empty()) 181 whole_number.append(city_code_); 182 183 if (!number_.empty()) 184 whole_number.append(number_); 185 186 return whole_number; 187} 188 189void PhoneNumber::set_number(const string16& number) { 190 string16 digits(number); 191 StripPunctuation(&digits); 192 number_ = digits; 193} 194 195void PhoneNumber::set_whole_number(const string16& whole_number) { 196 string16 number, city_code, country_code; 197 ParsePhoneNumber(whole_number, &number, &city_code, &country_code); 198 set_number(number); 199 set_city_code(city_code); 200 set_country_code(country_code); 201} 202 203bool PhoneNumber::IsNumber(const string16& text) const { 204 // TODO(isherman): This will need to be updated once we add support for 205 // international phone numbers. 206 const size_t kPhoneNumberPrefixLength = 3; 207 const size_t kPhoneNumberSuffixLength = 4; 208 return 209 (text == number_) || 210 (text.length() == kPhoneNumberPrefixLength && 211 StartsWith(number_, text, true)) || 212 (text.length() == kPhoneNumberSuffixLength && 213 EndsWith(number_, text, true)); 214} 215 216bool PhoneNumber::IsCityCode(const string16& text) const { 217 return text == city_code_; 218} 219 220bool PhoneNumber::IsCountryCode(const string16& text) const { 221 return text == country_code_; 222} 223 224bool PhoneNumber::IsCityAndNumber(const string16& text) const { 225 return text == CityAndNumber(); 226} 227 228bool PhoneNumber::IsWholeNumber(const string16& text) const { 229 return text == WholeNumber(); 230} 231 232bool PhoneNumber::Validate(const string16& number) const { 233 for (size_t i = 0; i < number.length(); ++i) { 234 if (!IsAsciiDigit(number[i])) 235 return false; 236 } 237 238 return true; 239} 240 241// Static. 242void PhoneNumber::StripPunctuation(string16* number) { 243 RemoveChars(*number, kPhoneNumberSeparators, number); 244} 245