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