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(const PhoneNumber& number) : FormGroup() {
38  *this = number;
39}
40
41PhoneNumber::~PhoneNumber() {}
42
43PhoneNumber& PhoneNumber::operator=(const PhoneNumber& number) {
44  if (this == &number)
45    return *this;
46  country_code_ = number.country_code_;
47  city_code_ = number.city_code_;
48  number_ = number.number_;
49  extension_ = number.extension_;
50  return *this;
51}
52
53void PhoneNumber::GetPossibleFieldTypes(const string16& text,
54                                        FieldTypeSet* possible_types) const {
55  string16 stripped_text(text);
56  StripPunctuation(&stripped_text);
57  if (!Validate(stripped_text))
58    return;
59
60  if (IsNumber(stripped_text))
61    possible_types->insert(GetNumberType());
62
63  if (IsCityCode(stripped_text))
64    possible_types->insert(GetCityCodeType());
65
66  if (IsCountryCode(stripped_text))
67    possible_types->insert(GetCountryCodeType());
68
69  if (IsCityAndNumber(stripped_text))
70    possible_types->insert(GetCityAndNumberType());
71
72  if (IsWholeNumber(stripped_text))
73    possible_types->insert(GetWholeNumberType());
74}
75
76void PhoneNumber::GetAvailableFieldTypes(FieldTypeSet* available_types) const {
77  DCHECK(available_types);
78
79  if (!number().empty())
80    available_types->insert(GetNumberType());
81
82  if (!city_code().empty())
83    available_types->insert(GetCityCodeType());
84
85  if (!country_code().empty())
86    available_types->insert(GetCountryCodeType());
87
88  if (!CityAndNumber().empty())
89    available_types->insert(GetCityAndNumberType());
90
91  if (!WholeNumber().empty())
92    available_types->insert(GetWholeNumberType());
93}
94
95string16 PhoneNumber::GetInfo(AutofillFieldType type) const {
96  if (type == GetNumberType())
97    return number();
98
99  if (type == GetCityCodeType())
100    return city_code();
101
102  if (type == GetCountryCodeType())
103    return country_code();
104
105  if (type == GetCityAndNumberType())
106    return CityAndNumber();
107
108  if (type == GetWholeNumberType())
109    return WholeNumber();
110
111  return string16();
112}
113
114void PhoneNumber::SetInfo(AutofillFieldType type, const string16& value) {
115  string16 number(value);
116  StripPunctuation(&number);
117  if (!Validate(number))
118    return;
119
120  FieldTypeSubGroup subgroup = AutofillType(type).subgroup();
121  if (subgroup == AutofillType::PHONE_NUMBER)
122    set_number(number);
123  else if (subgroup == AutofillType::PHONE_CITY_CODE)
124    set_city_code(number);
125  else if (subgroup == AutofillType::PHONE_COUNTRY_CODE)
126    set_country_code(number);
127  else if (subgroup == AutofillType::PHONE_CITY_AND_NUMBER ||
128           subgroup == AutofillType::PHONE_WHOLE_NUMBER)
129    set_whole_number(number);
130  else
131    NOTREACHED();
132}
133
134// Static.
135bool PhoneNumber::ParsePhoneNumber(const string16& value,
136                                   string16* number,
137                                   string16* city_code,
138                                   string16* country_code) {
139  DCHECK(number);
140  DCHECK(city_code);
141  DCHECK(country_code);
142
143  // Make a working copy of value.
144  string16 working = value;
145
146  *number = string16();
147  *city_code = string16();
148  *country_code = string16();
149
150  // First remove any punctuation.
151  StripPunctuation(&working);
152
153  if (working.size() < kPhoneNumberLength)
154    return false;
155
156  // Treat the last 7 digits as the number.
157  *number = working.substr(working.size() - kPhoneNumberLength,
158                           kPhoneNumberLength);
159  working.resize(working.size() - kPhoneNumberLength);
160  if (working.size() < kPhoneCityCodeLength)
161    return true;
162
163  // Treat the next three digits as the city code.
164  *city_code = working.substr(working.size() - kPhoneCityCodeLength,
165                              kPhoneCityCodeLength);
166  working.resize(working.size() - kPhoneCityCodeLength);
167  if (working.empty())
168    return true;
169
170  // Treat any remaining digits as the country code.
171  *country_code = working;
172  return true;
173}
174
175string16 PhoneNumber::WholeNumber() const {
176  string16 whole_number;
177  if (!country_code_.empty())
178    whole_number.append(country_code_);
179
180  if (!city_code_.empty())
181    whole_number.append(city_code_);
182
183  if (!number_.empty())
184    whole_number.append(number_);
185
186  return whole_number;
187}
188
189void PhoneNumber::set_number(const string16& number) {
190  string16 digits(number);
191  StripPunctuation(&digits);
192  number_ = digits;
193}
194
195void PhoneNumber::set_whole_number(const string16& whole_number) {
196  string16 number, city_code, country_code;
197  ParsePhoneNumber(whole_number, &number, &city_code, &country_code);
198  set_number(number);
199  set_city_code(city_code);
200  set_country_code(country_code);
201}
202
203bool PhoneNumber::IsNumber(const string16& text) const {
204  // TODO(isherman): This will need to be updated once we add support for
205  // international phone numbers.
206  const size_t kPhoneNumberPrefixLength = 3;
207  const size_t kPhoneNumberSuffixLength = 4;
208  return
209      (text == number_) ||
210      (text.length() == kPhoneNumberPrefixLength &&
211       StartsWith(number_, text, true)) ||
212      (text.length() == kPhoneNumberSuffixLength &&
213       EndsWith(number_, text, true));
214}
215
216bool PhoneNumber::IsCityCode(const string16& text) const {
217  return text == city_code_;
218}
219
220bool PhoneNumber::IsCountryCode(const string16& text) const {
221  return text == country_code_;
222}
223
224bool PhoneNumber::IsCityAndNumber(const string16& text) const {
225  return text == CityAndNumber();
226}
227
228bool PhoneNumber::IsWholeNumber(const string16& text) const {
229  return text == WholeNumber();
230}
231
232bool PhoneNumber::Validate(const string16& number) const {
233  for (size_t i = 0; i < number.length(); ++i) {
234    if (!IsAsciiDigit(number[i]))
235      return false;
236  }
237
238  return true;
239}
240
241// Static.
242void PhoneNumber::StripPunctuation(string16* number) {
243  RemoveChars(*number, kPhoneNumberSeparators, number);
244}
245