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