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
13 namespace {
14
15 const char16 kPhoneNumberSeparators[] = { ' ', '.', '(', ')', '-', 0 };
16
17 // The number of digits in a phone number.
18 const size_t kPhoneNumberLength = 7;
19
20 // The number of digits in an area code.
21 const size_t kPhoneCityCodeLength = 3;
22
23 const 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
31 const int kAutofillPhoneLength = arraysize(kAutofillPhoneTypes);
32
33 } // namespace
34
PhoneNumber()35 PhoneNumber::PhoneNumber() {}
36
PhoneNumber(const PhoneNumber & number)37 PhoneNumber::PhoneNumber(const PhoneNumber& number) : FormGroup() {
38 *this = number;
39 }
40
~PhoneNumber()41 PhoneNumber::~PhoneNumber() {}
42
operator =(const PhoneNumber & number)43 PhoneNumber& 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
GetPossibleFieldTypes(const string16 & text,FieldTypeSet * possible_types) const53 void 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
GetAvailableFieldTypes(FieldTypeSet * available_types) const76 void 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
GetInfo(AutofillFieldType type) const95 string16 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
SetInfo(AutofillFieldType type,const string16 & value)114 void 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.
ParsePhoneNumber(const string16 & value,string16 * number,string16 * city_code,string16 * country_code)135 bool 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
WholeNumber() const175 string16 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
set_number(const string16 & number)189 void PhoneNumber::set_number(const string16& number) {
190 string16 digits(number);
191 StripPunctuation(&digits);
192 number_ = digits;
193 }
194
set_whole_number(const string16 & whole_number)195 void 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
IsNumber(const string16 & text) const203 bool 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
IsCityCode(const string16 & text) const216 bool PhoneNumber::IsCityCode(const string16& text) const {
217 return text == city_code_;
218 }
219
IsCountryCode(const string16 & text) const220 bool PhoneNumber::IsCountryCode(const string16& text) const {
221 return text == country_code_;
222 }
223
IsCityAndNumber(const string16 & text) const224 bool PhoneNumber::IsCityAndNumber(const string16& text) const {
225 return text == CityAndNumber();
226 }
227
IsWholeNumber(const string16 & text) const228 bool PhoneNumber::IsWholeNumber(const string16& text) const {
229 return text == WholeNumber();
230 }
231
Validate(const string16 & number) const232 bool 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.
StripPunctuation(string16 * number)242 void PhoneNumber::StripPunctuation(string16* number) {
243 RemoveChars(*number, kPhoneNumberSeparators, number);
244 }
245