phone_number.cc revision dc0f95d653279beabeb9817299e2902918ba123e
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() {} 38 39void PhoneNumber::GetPossibleFieldTypes(const string16& text, 40 FieldTypeSet* possible_types) const { 41 string16 stripped_text(text); 42 StripPunctuation(&stripped_text); 43 if (!Validate(stripped_text)) 44 return; 45 46 if (IsNumber(stripped_text)) 47 possible_types->insert(GetNumberType()); 48 49 if (IsCityCode(stripped_text)) 50 possible_types->insert(GetCityCodeType()); 51 52 if (IsCountryCode(stripped_text)) 53 possible_types->insert(GetCountryCodeType()); 54 55 if (IsCityAndNumber(stripped_text)) 56 possible_types->insert(GetCityAndNumberType()); 57 58 if (IsWholeNumber(stripped_text)) 59 possible_types->insert(GetWholeNumberType()); 60} 61 62void PhoneNumber::GetAvailableFieldTypes(FieldTypeSet* available_types) const { 63 DCHECK(available_types); 64 65 if (!number().empty()) 66 available_types->insert(GetNumberType()); 67 68 if (!city_code().empty()) 69 available_types->insert(GetCityCodeType()); 70 71 if (!country_code().empty()) 72 available_types->insert(GetCountryCodeType()); 73 74 if (!CityAndNumber().empty()) 75 available_types->insert(GetCityAndNumberType()); 76 77 if (!WholeNumber().empty()) 78 available_types->insert(GetWholeNumberType()); 79} 80 81string16 PhoneNumber::GetFieldText(const AutofillType& type) const { 82 AutofillFieldType field_type = type.field_type(); 83 if (field_type == GetNumberType()) 84 return number(); 85 86 if (field_type == GetCityCodeType()) 87 return city_code(); 88 89 if (field_type == GetCountryCodeType()) 90 return country_code(); 91 92 if (field_type == GetCityAndNumberType()) 93 return CityAndNumber(); 94 95 if (field_type == GetWholeNumberType()) 96 return WholeNumber(); 97 98 return string16(); 99} 100 101void PhoneNumber::FindInfoMatches(const AutofillType& type, 102 const string16& info, 103 std::vector<string16>* matched_text) const { 104 if (matched_text == NULL) { 105 DLOG(ERROR) << "NULL matched vector passed in"; 106 return; 107 } 108 109 string16 number(info); 110 StripPunctuation(&number); 111 if (!Validate(number)) 112 return; 113 114 string16 match; 115 if (type.field_type() == UNKNOWN_TYPE) { 116 for (int i = 0; i < kAutoFillPhoneLength; ++i) { 117 if (FindInfoMatchesHelper(kAutoFillPhoneTypes[i], info, &match)) 118 matched_text->push_back(match); 119 } 120 } else { 121 if (FindInfoMatchesHelper(type.subgroup(), info, &match)) 122 matched_text->push_back(match); 123 } 124} 125 126void PhoneNumber::SetInfo(const AutofillType& type, const string16& value) { 127 string16 number(value); 128 StripPunctuation(&number); 129 if (!Validate(number)) 130 return; 131 132 FieldTypeSubGroup subgroup = type.subgroup(); 133 if (subgroup == AutofillType::PHONE_NUMBER) 134 set_number(number); 135 else if (subgroup == AutofillType::PHONE_CITY_CODE) 136 set_city_code(number); 137 else if (subgroup == AutofillType::PHONE_COUNTRY_CODE) 138 set_country_code(number); 139 else if (subgroup == AutofillType::PHONE_CITY_AND_NUMBER || 140 subgroup == AutofillType::PHONE_WHOLE_NUMBER) 141 set_whole_number(number); 142 else 143 NOTREACHED(); 144} 145 146// Static. 147bool PhoneNumber::ParsePhoneNumber(const string16& value, 148 string16* number, 149 string16* city_code, 150 string16* country_code) { 151 DCHECK(number); 152 DCHECK(city_code); 153 DCHECK(country_code); 154 155 // Make a working copy of value. 156 string16 working = value; 157 158 *number = string16(); 159 *city_code = string16(); 160 *country_code = string16(); 161 162 // First remove any punctuation. 163 StripPunctuation(&working); 164 165 if (working.size() < kPhoneNumberLength) 166 return false; 167 168 // Treat the last 7 digits as the number. 169 *number = working.substr(working.size() - kPhoneNumberLength, 170 kPhoneNumberLength); 171 working.resize(working.size() - kPhoneNumberLength); 172 if (working.size() < kPhoneCityCodeLength) 173 return true; 174 175 // Treat the next three digits as the city code. 176 *city_code = working.substr(working.size() - kPhoneCityCodeLength, 177 kPhoneCityCodeLength); 178 working.resize(working.size() - kPhoneCityCodeLength); 179 if (working.empty()) 180 return true; 181 182 // Treat any remaining digits as the country code. 183 *country_code = working; 184 return true; 185} 186 187string16 PhoneNumber::WholeNumber() const { 188 string16 whole_number; 189 if (!country_code_.empty()) 190 whole_number.append(country_code_); 191 192 if (!city_code_.empty()) 193 whole_number.append(city_code_); 194 195 if (!number_.empty()) 196 whole_number.append(number_); 197 198 return whole_number; 199} 200 201void PhoneNumber::set_number(const string16& number) { 202 string16 digits(number); 203 StripPunctuation(&digits); 204 number_ = digits; 205} 206 207void PhoneNumber::set_whole_number(const string16& whole_number) { 208 string16 number, city_code, country_code; 209 ParsePhoneNumber(whole_number, &number, &city_code, &country_code); 210 set_number(number); 211 set_city_code(city_code); 212 set_country_code(country_code); 213} 214 215PhoneNumber::PhoneNumber(const PhoneNumber& phone_number) 216 : FormGroup(), 217 country_code_(phone_number.country_code_), 218 city_code_(phone_number.city_code_), 219 number_(phone_number.number_), 220 extension_(phone_number.extension_) { 221} 222 223bool PhoneNumber::FindInfoMatchesHelper(const FieldTypeSubGroup& subgroup, 224 const string16& info, 225 string16* match) const { 226 if (match == NULL) { 227 DLOG(ERROR) << "NULL match string passed in"; 228 return false; 229 } 230 231 match->clear(); 232 if (subgroup == AutofillType::PHONE_NUMBER && 233 StartsWith(number(), info, true)) { 234 *match = number(); 235 } else if (subgroup == AutofillType::PHONE_CITY_CODE && 236 StartsWith(city_code(), info, true)) { 237 *match = city_code(); 238 } else if (subgroup == AutofillType::PHONE_COUNTRY_CODE && 239 StartsWith(country_code(), info, true)) { 240 *match = country_code(); 241 } else if (subgroup == AutofillType::PHONE_CITY_AND_NUMBER && 242 StartsWith(CityAndNumber(), info, true)) { 243 *match = CityAndNumber(); 244 } else if (subgroup == AutofillType::PHONE_WHOLE_NUMBER && 245 StartsWith(WholeNumber(), info, true)) { 246 *match = WholeNumber(); 247 } 248 249 return !match->empty(); 250} 251 252bool PhoneNumber::IsNumber(const string16& text) const { 253 if (text.length() > number_.length()) 254 return false; 255 256 return StartsWith(number_, text, false); 257} 258 259bool PhoneNumber::IsCityCode(const string16& text) const { 260 if (text.length() > city_code_.length()) 261 return false; 262 263 return StartsWith(city_code_, text, false); 264} 265 266bool PhoneNumber::IsCountryCode(const string16& text) const { 267 if (text.length() > country_code_.length()) 268 return false; 269 270 return StartsWith(country_code_, text, false); 271} 272 273bool PhoneNumber::IsCityAndNumber(const string16& text) const { 274 string16 city_and_number(CityAndNumber()); 275 if (text.length() > city_and_number.length()) 276 return false; 277 278 return StartsWith(city_and_number, text, false); 279} 280 281bool PhoneNumber::IsWholeNumber(const string16& text) const { 282 string16 whole_number(WholeNumber()); 283 if (text.length() > whole_number.length()) 284 return false; 285 286 return StartsWith(whole_number, text, false); 287} 288 289bool PhoneNumber::Validate(const string16& number) const { 290 for (size_t i = 0; i < number.length(); ++i) { 291 if (!IsAsciiDigit(number[i])) 292 return false; 293 } 294 295 return true; 296} 297 298// Static. 299void PhoneNumber::StripPunctuation(string16* number) { 300 RemoveChars(*number, kPhoneNumberSeparators, number); 301} 302