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