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/address.h"
6
7#include <stddef.h>
8
9#include "base/basictypes.h"
10#include "base/logging.h"
11#include "base/string_util.h"
12#include "chrome/browser/autofill/autofill_country.h"
13#include "chrome/browser/autofill/autofill_type.h"
14#include "chrome/browser/autofill/field_types.h"
15
16namespace {
17
18const char16 kAddressSplitChars[] = {'-', ',', '#', '.', ' ', 0};
19
20const AutofillType::FieldTypeSubGroup kAutofillAddressTypes[] = {
21  AutofillType::ADDRESS_LINE1,
22  AutofillType::ADDRESS_LINE2,
23  AutofillType::ADDRESS_CITY,
24  AutofillType::ADDRESS_STATE,
25  AutofillType::ADDRESS_ZIP,
26  AutofillType::ADDRESS_COUNTRY,
27};
28
29const int kAutofillAddressLength = arraysize(kAutofillAddressTypes);
30
31}  // namespace
32
33Address::Address() {}
34
35Address::Address(const Address& address) : FormGroup() {
36  *this = address;
37}
38
39Address::~Address() {}
40
41Address& Address::operator=(const Address& address) {
42  if (this == &address)
43    return *this;
44
45  line1_tokens_ = address.line1_tokens_;
46  line2_tokens_= address.line2_tokens_;
47  line1_ = address.line1_;
48  line2_ = address.line2_;
49  city_ = address.city_;
50  state_ = address.state_;
51  country_code_ = address.country_code_;
52  zip_code_ = address.zip_code_;
53  return *this;
54}
55
56void Address::GetPossibleFieldTypes(const string16& text,
57                                    FieldTypeSet* possible_types) const {
58  DCHECK(possible_types);
59
60  // If the text to match against the field types is empty, then no results will
61  // match.
62  if (text.empty())
63    return;
64
65  if (IsLine1(text))
66    possible_types->insert(ADDRESS_HOME_LINE1);
67
68  if (IsLine2(text))
69    possible_types->insert(ADDRESS_HOME_LINE2);
70
71  if (IsCity(text))
72    possible_types->insert(ADDRESS_HOME_CITY);
73
74  if (IsState(text))
75    possible_types->insert(ADDRESS_HOME_STATE);
76
77  if (IsZipCode(text))
78    possible_types->insert(ADDRESS_HOME_ZIP);
79
80  if (IsCountry(text))
81    possible_types->insert(ADDRESS_HOME_COUNTRY);
82}
83
84void Address::GetAvailableFieldTypes(FieldTypeSet* available_types) const {
85  DCHECK(available_types);
86
87  if (!line1_.empty())
88    available_types->insert(ADDRESS_HOME_LINE1);
89
90  if (!line2_.empty())
91    available_types->insert(ADDRESS_HOME_LINE2);
92
93  if (!city_.empty())
94    available_types->insert(ADDRESS_HOME_CITY);
95
96  if (!state_.empty())
97    available_types->insert(ADDRESS_HOME_STATE);
98
99  if (!zip_code_.empty())
100    available_types->insert(ADDRESS_HOME_ZIP);
101
102  if (!country_code_.empty())
103    available_types->insert(ADDRESS_HOME_COUNTRY);
104}
105
106string16 Address::GetInfo(AutofillFieldType type) const {
107  if (type == ADDRESS_HOME_LINE1)
108    return line1_;
109
110  if (type == ADDRESS_HOME_LINE2)
111    return line2_;
112
113  if (type == ADDRESS_HOME_CITY)
114    return city_;
115
116  if (type == ADDRESS_HOME_STATE)
117    return state_;
118
119  if (type ==  ADDRESS_HOME_ZIP)
120    return zip_code_;
121
122  if (type == ADDRESS_HOME_COUNTRY)
123    return Country();
124
125  return string16();
126}
127
128void Address::SetInfo(AutofillFieldType type, const string16& value) {
129  FieldTypeSubGroup subgroup = AutofillType(type).subgroup();
130  if (subgroup == AutofillType::ADDRESS_LINE1)
131    set_line1(value);
132  else if (subgroup == AutofillType::ADDRESS_LINE2)
133    set_line2(value);
134  else if (subgroup == AutofillType::ADDRESS_CITY)
135    city_ = value;
136  else if (subgroup == AutofillType::ADDRESS_STATE)
137    state_ = value;
138  else if (subgroup == AutofillType::ADDRESS_COUNTRY)
139    SetCountry(value);
140  else if (subgroup == AutofillType::ADDRESS_ZIP)
141    zip_code_ = value;
142  else
143    NOTREACHED();
144}
145
146void Address::Clear() {
147  line1_tokens_.clear();
148  line1_.clear();
149  line2_tokens_.clear();
150  line2_.clear();
151  city_.clear();
152  state_.clear();
153  country_code_.clear();
154  zip_code_.clear();
155}
156
157string16 Address::Country() const {
158  if (country_code().empty())
159    return string16();
160
161  std::string app_locale = AutofillCountry::ApplicationLocale();
162  return AutofillCountry(country_code(), app_locale).name();
163}
164
165void Address::set_line1(const string16& line1) {
166  line1_ = line1;
167  line1_tokens_.clear();
168  Tokenize(line1, kAddressSplitChars, &line1_tokens_);
169  LineTokens::iterator iter;
170  for (iter = line1_tokens_.begin(); iter != line1_tokens_.end(); ++iter)
171    *iter = StringToLowerASCII(*iter);
172}
173
174void Address::set_line2(const string16& line2) {
175  line2_ = line2;
176  line2_tokens_.clear();
177  Tokenize(line2, kAddressSplitChars, &line2_tokens_);
178  LineTokens::iterator iter;
179  for (iter = line2_tokens_.begin(); iter != line2_tokens_.end(); ++iter)
180    *iter = StringToLowerASCII(*iter);
181}
182
183void Address::SetCountry(const string16& country) {
184  std::string app_locale = AutofillCountry::ApplicationLocale();
185  country_code_ = AutofillCountry::GetCountryCode(country, app_locale);
186}
187
188bool Address::IsLine1(const string16& text) const {
189  return IsLineMatch(text, line1_tokens_);
190}
191
192bool Address::IsLine2(const string16& text) const {
193  return IsLineMatch(text, line2_tokens_);
194}
195
196bool Address::IsCity(const string16& text) const {
197  return (StringToLowerASCII(city_) == StringToLowerASCII(text));
198}
199
200bool Address::IsState(const string16& text) const {
201  return (StringToLowerASCII(state_) == StringToLowerASCII(text));
202}
203
204bool Address::IsCountry(const string16& text) const {
205  std::string app_locale = AutofillCountry::ApplicationLocale();
206  std::string country_code = AutofillCountry::GetCountryCode(text, app_locale);
207  return (!country_code.empty() && country_code_ == country_code);
208}
209
210bool Address::IsZipCode(const string16& text) const {
211  return zip_code_ == text;
212}
213
214bool Address::IsLineMatch(const string16& text,
215                          const LineTokens& line_tokens) const {
216  size_t line_tokens_size = line_tokens.size();
217  if (line_tokens_size == 0)
218    return false;
219
220  LineTokens text_tokens;
221  Tokenize(text, kAddressSplitChars, &text_tokens);
222  size_t text_tokens_size = text_tokens.size();
223  if (text_tokens_size == 0)
224    return false;
225
226  if (text_tokens_size > line_tokens_size)
227    return false;
228
229  // If each of the 'words' contained in the text are also present in the line,
230  // then we will consider the text to match the line.
231  LineTokens::iterator iter;
232  for (iter = text_tokens.begin(); iter != text_tokens.end(); ++iter) {
233    if (!IsWordInLine(*iter, line_tokens))
234      return false;
235  }
236
237  return true;
238}
239
240bool Address::IsWordInLine(const string16& word,
241                           const LineTokens& line_tokens) const {
242  LineTokens::const_iterator iter;
243  for (iter = line_tokens.begin(); iter != line_tokens.end(); ++iter) {
244    if (StringToLowerASCII(word) == *iter)
245      return true;
246  }
247
248  return false;
249}
250