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/contact_info.h" 6 7#include <stddef.h> 8#include <ostream> 9#include <string> 10 11#include "base/basictypes.h" 12#include "base/logging.h" 13#include "base/string_util.h" 14#include "base/utf_string_conversions.h" 15#include "chrome/browser/autofill/autofill_type.h" 16#include "chrome/browser/autofill/field_types.h" 17 18static const string16 kNameSplitChars = ASCIIToUTF16("-'. "); 19 20static const AutofillFieldType kAutofillNameInfoTypes[] = { 21 NAME_FIRST, 22 NAME_MIDDLE, 23 NAME_LAST 24}; 25 26static const size_t kAutofillNameInfoLength = 27 arraysize(kAutofillNameInfoTypes); 28 29NameInfo::NameInfo() {} 30 31NameInfo::NameInfo(const NameInfo& info) : FormGroup() { 32 *this = info; 33} 34 35NameInfo::~NameInfo() {} 36 37NameInfo& NameInfo::operator=(const NameInfo& info) { 38 if (this == &info) 39 return *this; 40 41 first_tokens_ = info.first_tokens_; 42 middle_tokens_ = info.middle_tokens_; 43 last_tokens_ = info.last_tokens_; 44 first_ = info.first_; 45 middle_ = info.middle_; 46 last_ = info.last_; 47 return *this; 48} 49 50void NameInfo::GetPossibleFieldTypes(const string16& text, 51 FieldTypeSet* possible_types) const { 52 DCHECK(possible_types); 53 54 if (IsFirstName(text)) 55 possible_types->insert(NAME_FIRST); 56 57 if (IsMiddleName(text)) 58 possible_types->insert(NAME_MIDDLE); 59 60 if (IsLastName(text)) 61 possible_types->insert(NAME_LAST); 62 63 if (IsMiddleInitial(text)) 64 possible_types->insert(NAME_MIDDLE_INITIAL); 65 66 if (IsFullName(text)) 67 possible_types->insert(NAME_FULL); 68} 69 70void NameInfo::GetAvailableFieldTypes(FieldTypeSet* available_types) const { 71 DCHECK(available_types); 72 73 if (!first().empty()) 74 available_types->insert(NAME_FIRST); 75 76 if (!middle().empty()) 77 available_types->insert(NAME_MIDDLE); 78 79 if (!last().empty()) 80 available_types->insert(NAME_LAST); 81 82 if (!MiddleInitial().empty()) 83 available_types->insert(NAME_MIDDLE_INITIAL); 84 85 if (!FullName().empty()) 86 available_types->insert(NAME_FULL); 87} 88 89string16 NameInfo::GetInfo(AutofillFieldType type) const { 90 if (type == NAME_FIRST) 91 return first(); 92 93 if (type == NAME_MIDDLE) 94 return middle(); 95 96 if (type == NAME_LAST) 97 return last(); 98 99 if (type == NAME_MIDDLE_INITIAL) 100 return MiddleInitial(); 101 102 if (type == NAME_FULL) 103 return FullName(); 104 105 return string16(); 106} 107 108void NameInfo::SetInfo(AutofillFieldType type, const string16& value) { 109 DCHECK_EQ(AutofillType::NAME, AutofillType(type).group()); 110 if (type == NAME_FIRST) 111 SetFirst(value); 112 else if (type == NAME_MIDDLE || type == NAME_MIDDLE_INITIAL) 113 SetMiddle(value); 114 else if (type == NAME_LAST) 115 SetLast(value); 116 else if (type == NAME_FULL) 117 SetFullName(value); 118 else 119 NOTREACHED(); 120} 121 122string16 NameInfo::FullName() const { 123 if (first_.empty()) 124 return string16(); 125 126 std::vector<string16> full_name; 127 full_name.push_back(first_); 128 129 if (!middle_.empty()) 130 full_name.push_back(middle_); 131 132 if (!last_.empty()) 133 full_name.push_back(last_); 134 135 return JoinString(full_name, ' '); 136} 137 138string16 NameInfo::MiddleInitial() const { 139 if (middle_.empty()) 140 return string16(); 141 142 string16 middle_name(middle()); 143 string16 initial; 144 initial.push_back(middle_name[0]); 145 return initial; 146} 147 148// If each of the 'words' contained in the text are also present in the first 149// name then we will consider the text to be of type kFirstName. This means 150// that people with multiple first names will be able to enter any one of 151// their first names and have it correctly recognized. 152bool NameInfo::IsFirstName(const string16& text) const { 153 return IsNameMatch(text, first_tokens_); 154} 155 156// If each of the 'words' contained in the text are also present in the middle 157// name then we will consider the text to be of type kMiddleName. 158bool NameInfo::IsMiddleName(const string16& text) const { 159 return IsNameMatch(text, middle_tokens_); 160} 161 162// If each of the 'words' contained in the text are also present in the last 163// name then we will consider the text to be of type kLastName. 164bool NameInfo::IsLastName(const string16& text) const { 165 return IsNameMatch(text, last_tokens_); 166} 167 168bool NameInfo::IsMiddleInitial(const string16& text) const { 169 if (text.length() != 1) 170 return false; 171 172 string16 lower_case = StringToLowerASCII(text); 173 // If the text entered was a single character and it matches the first letter 174 // of any of the given middle names then we consider it to be a middle 175 // initial field. 176 size_t middle_tokens_size = middle_tokens_.size(); 177 for (size_t i = 0; i < middle_tokens_size; ++i) { 178 if (middle_tokens_[i][0] == lower_case[0]) 179 return true; 180 } 181 182 return false; 183} 184 185// A field will be considered to be of type NAME_FULL if: 186// 1) it contains at least one word from the first name. 187// 2) it contains at least one word from the last name. 188// 3) all of the words in the field match a word in either the first, 189// middle, or last name. 190bool NameInfo::IsFullName(const string16& text) const { 191 size_t first_tokens_size = first_tokens_.size(); 192 if (first_tokens_size == 0) 193 return false; 194 195 size_t middle_tokens_size = middle_tokens_.size(); 196 197 size_t last_tokens_size = last_tokens_.size(); 198 if (last_tokens_size == 0) 199 return false; 200 201 std::vector<string16> text_tokens; 202 Tokenize(text, kNameSplitChars, &text_tokens); 203 size_t text_tokens_size = text_tokens.size(); 204 if (text_tokens_size == 0 || text_tokens_size < 2) 205 return false; 206 207 size_t name_tokens_size = 208 first_tokens_size + middle_tokens_size + last_tokens_size; 209 if (text_tokens_size > name_tokens_size) 210 return false; 211 212 bool first_name_match = false; 213 bool last_name_match = false; 214 for (std::vector<string16>::iterator iter = text_tokens.begin(); 215 iter != text_tokens.end(); ++iter) { 216 bool match = false; 217 if (IsWordInName(*iter, first_tokens_)) { 218 match = true; 219 first_name_match = true; 220 } 221 222 if (IsWordInName(*iter, last_tokens_)) { 223 match = true; 224 last_name_match = true; 225 } 226 227 if (IsWordInName(*iter, middle_tokens_)) 228 match = true; 229 230 if (!match) 231 return false; 232 } 233 234 return (first_name_match && last_name_match); 235} 236 237bool NameInfo::IsNameMatch(const string16& text, 238 const std::vector<string16>& name_tokens) const { 239 size_t name_tokens_size = name_tokens.size(); 240 if (name_tokens_size == 0) 241 return false; 242 243 std::vector<string16> text_tokens; 244 Tokenize(text, kNameSplitChars, &text_tokens); 245 size_t text_tokens_size = text_tokens.size(); 246 if (text_tokens_size == 0) 247 return false; 248 249 if (text_tokens_size > name_tokens_size) 250 return false; 251 252 // If each of the 'words' contained in the text are also present in the name, 253 // then we will consider the text to match the name. 254 for (std::vector<string16>::iterator iter = text_tokens.begin(); 255 iter != text_tokens.end(); ++iter) { 256 if (!IsWordInName(*iter, name_tokens)) 257 return false; 258 } 259 260 return true; 261} 262 263bool NameInfo::IsWordInName(const string16& word, 264 const std::vector<string16>& name_tokens) const { 265 for (std::vector<string16>::const_iterator iter = name_tokens.begin(); 266 iter != name_tokens.end(); ++iter) { 267 // |*iter| is already lower-cased. 268 if (StringToLowerASCII(word) == *iter) 269 return true; 270 } 271 272 return false; 273} 274 275void NameInfo::SetFirst(const string16& first) { 276 first_ = first; 277 first_tokens_.clear(); 278 Tokenize(first, kNameSplitChars, &first_tokens_); 279 for (std::vector<string16>::iterator iter = first_tokens_.begin(); 280 iter != first_tokens_.end(); ++iter) { 281 *iter = StringToLowerASCII(*iter); 282 } 283} 284 285void NameInfo::SetMiddle(const string16& middle) { 286 middle_ = middle; 287 middle_tokens_.clear(); 288 Tokenize(middle, kNameSplitChars, &middle_tokens_); 289 for (std::vector<string16>::iterator iter = middle_tokens_.begin(); 290 iter != middle_tokens_.end(); ++iter) { 291 *iter = StringToLowerASCII(*iter); 292 } 293} 294 295void NameInfo::SetLast(const string16& last) { 296 last_ = last; 297 last_tokens_.clear(); 298 Tokenize(last, kNameSplitChars, &last_tokens_); 299 for (std::vector<string16>::iterator iter = last_tokens_.begin(); 300 iter != last_tokens_.end(); ++iter) { 301 *iter = StringToLowerASCII(*iter); 302 } 303} 304 305void NameInfo::SetFullName(const string16& full) { 306 std::vector<string16> full_name_tokens; 307 Tokenize(full, ASCIIToUTF16(" "), &full_name_tokens); 308 // Clear the names. 309 SetFirst(string16()); 310 SetMiddle(string16()); 311 SetLast(string16()); 312 313 // There are four possibilities: empty; first name; first and last names; 314 // first, middle (possibly multiple strings) and then the last name. 315 if (full_name_tokens.size() > 0) { 316 SetFirst(full_name_tokens[0]); 317 if (full_name_tokens.size() > 1) { 318 SetLast(full_name_tokens.back()); 319 if (full_name_tokens.size() > 2) { 320 full_name_tokens.erase(full_name_tokens.begin()); 321 full_name_tokens.pop_back(); 322 SetMiddle(JoinString(full_name_tokens, ' ')); 323 } 324 } 325 } 326} 327 328EmailInfo::EmailInfo() {} 329 330EmailInfo::EmailInfo(const EmailInfo& info) : FormGroup() { 331 *this = info; 332} 333 334EmailInfo::~EmailInfo() {} 335 336EmailInfo& EmailInfo::operator=(const EmailInfo& info) { 337 if (this == &info) 338 return *this; 339 340 email_ = info.email_; 341 return *this; 342} 343 344void EmailInfo::GetPossibleFieldTypes(const string16& text, 345 FieldTypeSet* possible_types) const { 346 DCHECK(possible_types); 347 // TODO(isherman): Investigate case-insensitive comparison. 348 if (email_ == text) 349 possible_types->insert(EMAIL_ADDRESS); 350} 351 352void EmailInfo::GetAvailableFieldTypes(FieldTypeSet* available_types) const { 353 DCHECK(available_types); 354 if (!email_.empty()) 355 available_types->insert(EMAIL_ADDRESS); 356} 357 358string16 EmailInfo::GetInfo(AutofillFieldType type) const { 359 if (type == EMAIL_ADDRESS) 360 return email_; 361 362 return string16(); 363} 364 365void EmailInfo::SetInfo(AutofillFieldType type, const string16& value) { 366 DCHECK_EQ(EMAIL_ADDRESS, type); 367 email_ = value; 368} 369 370CompanyInfo::CompanyInfo() {} 371 372CompanyInfo::CompanyInfo(const CompanyInfo& info) : FormGroup() { 373 *this = info; 374} 375 376CompanyInfo::~CompanyInfo() {} 377 378CompanyInfo& CompanyInfo::operator=(const CompanyInfo& info) { 379 if (this == &info) 380 return *this; 381 382 company_name_ = info.company_name_; 383 return *this; 384} 385 386void CompanyInfo::GetPossibleFieldTypes(const string16& text, 387 FieldTypeSet* possible_types) const { 388 DCHECK(possible_types); 389 390 if (company_name_ == text) 391 possible_types->insert(COMPANY_NAME); 392} 393 394void CompanyInfo::GetAvailableFieldTypes(FieldTypeSet* available_types) const { 395 DCHECK(available_types); 396 397 if (!company_name_.empty()) 398 available_types->insert(COMPANY_NAME); 399} 400 401string16 CompanyInfo::GetInfo(AutofillFieldType type) const { 402 if (type == COMPANY_NAME) 403 return company_name_; 404 405 return string16(); 406} 407 408void CompanyInfo::SetInfo(AutofillFieldType type, const string16& value) { 409 DCHECK_EQ(COMPANY_NAME, type); 410 company_name_ = value; 411} 412