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