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 13 namespace { 14 15 const char16 kPhoneNumberSeparators[] = { ' ', '.', '(', ')', '-', 0 }; 16 17 // The number of digits in a phone number. 18 const size_t kPhoneNumberLength = 7; 19 20 // The number of digits in an area code. 21 const size_t kPhoneCityCodeLength = 3; 22 23 const 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 31 const int kAutofillPhoneLength = arraysize(kAutofillPhoneTypes); 32 33 } // namespace 34 35 PhoneNumber::PhoneNumber() {} 36 37 PhoneNumber::PhoneNumber(const PhoneNumber& number) : FormGroup() { 38 *this = number; 39 } 40 41 PhoneNumber::~PhoneNumber() {} 42 43 PhoneNumber& 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 53 void 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 76 void 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 95 string16 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 114 void 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. 135 bool 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 175 string16 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 189 void PhoneNumber::set_number(const string16& number) { 190 string16 digits(number); 191 StripPunctuation(&digits); 192 number_ = digits; 193 } 194 195 void 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 203 bool 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 216 bool PhoneNumber::IsCityCode(const string16& text) const { 217 return text == city_code_; 218 } 219 220 bool PhoneNumber::IsCountryCode(const string16& text) const { 221 return text == country_code_; 222 } 223 224 bool PhoneNumber::IsCityAndNumber(const string16& text) const { 225 return text == CityAndNumber(); 226 } 227 228 bool PhoneNumber::IsWholeNumber(const string16& text) const { 229 return text == WholeNumber(); 230 } 231 232 bool 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. 242 void PhoneNumber::StripPunctuation(string16* number) { 243 RemoveChars(*number, kPhoneNumberSeparators, number); 244 } 245