blob: 2c541b416fb2b5a7be19466dfcaad91138a7d3bd [file] [log] [blame]
[email protected]3db3ff62010-01-07 00:35:391// 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"
[email protected]9b435be92010-03-04 21:20:159#include "chrome/browser/autofill/autofill_profile.h"
[email protected]3db3ff62010-01-07 00:35:3910#include "chrome/browser/autofill/autofill_type.h"
11#include "chrome/browser/autofill/field_types.h"
12
[email protected]9b435be92010-03-04 21:20:1513namespace {
[email protected]3db3ff62010-01-07 00:35:3914
[email protected]9b435be92010-03-04 21:20:1515const 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[] = {
[email protected]3db3ff62010-01-07 00:35:3924 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
[email protected]9b435be92010-03-04 21:20:1531const int kAutoFillPhoneLength = arraysize(kAutoFillPhoneTypes);
32
33} // namespace
[email protected]3db3ff62010-01-07 00:35:3934
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
58string16 PhoneNumber::GetFieldText(const AutoFillType& type) const {
59 AutoFillFieldType field_type = type.field_type();
60 if (field_type == GetNumberType())
61 return number();
62
63 if (field_type == GetCityCodeType())
64 return city_code();
65
66 if (field_type == GetCountryCodeType())
67 return country_code();
68
69 if (field_type == GetCityAndNumberType())
70 return CityAndNumber();
71
72 if (field_type == GetWholeNumberType())
73 return WholeNumber();
74
75 return EmptyString16();
76}
77
78void PhoneNumber::FindInfoMatches(const AutoFillType& type,
79 const string16& info,
80 std::vector<string16>* matched_text) const {
81 if (matched_text == NULL) {
82 DLOG(ERROR) << "NULL matched vector passed in";
83 return;
84 }
85
86 string16 number(info);
87 StripPunctuation(&number);
88 if (!Validate(number))
89 return;
90
91 string16 match;
92 if (type.field_type() == UNKNOWN_TYPE) {
93 for (int i = 0; i < kAutoFillPhoneLength; ++i) {
94 if (FindInfoMatchesHelper(kAutoFillPhoneTypes[i], info, &match))
95 matched_text->push_back(match);
96 }
97 } else {
98 if (FindInfoMatchesHelper(type.subgroup(), info, &match))
99 matched_text->push_back(match);
100 }
101}
102
103void PhoneNumber::SetInfo(const AutoFillType& type, const string16& value) {
104 string16 number(value);
105 StripPunctuation(&number);
106 if (!Validate(number))
107 return;
108
109 FieldTypeSubGroup subgroup = type.subgroup();
110 if (subgroup == AutoFillType::PHONE_NUMBER)
111 set_number(number);
112 else if (subgroup == AutoFillType::PHONE_CITY_CODE)
113 set_city_code(number);
114 else if (subgroup == AutoFillType::PHONE_COUNTRY_CODE)
115 set_country_code(number);
[email protected]afbd3542010-03-17 20:31:07116 else if (subgroup == AutoFillType::PHONE_WHOLE_NUMBER)
117 set_whole_number(number);
[email protected]3db3ff62010-01-07 00:35:39118 else
119 NOTREACHED();
120 // TODO(jhawkins): Add extension support.
121 // else if (subgroup == AutoFillType::PHONE_EXTENSION)
122 // set_extension(number);
123}
124
[email protected]9b435be92010-03-04 21:20:15125// Static.
[email protected]f8e7b62d2010-06-03 18:12:54126bool PhoneNumber::ParsePhoneNumber(const string16& value,
[email protected]9b435be92010-03-04 21:20:15127 string16* number,
128 string16* city_code,
129 string16* country_code) {
130 DCHECK(number);
131 DCHECK(city_code);
132 DCHECK(country_code);
133
134 // Make a working copy of value.
135 string16 working = value;
136
137 *number = string16();
138 *city_code = string16();
139 *country_code = string16();
140
141 // First remove any punctuation.
142 StripPunctuation(&working);
143
144 if (working.size() < kPhoneNumberLength)
[email protected]f8e7b62d2010-06-03 18:12:54145 return false;
[email protected]9b435be92010-03-04 21:20:15146
147 // Treat the last 7 digits as the number.
148 *number = working.substr(working.size() - kPhoneNumberLength,
149 kPhoneNumberLength);
150 working.resize(working.size() - kPhoneNumberLength);
151 if (working.size() < kPhoneCityCodeLength)
[email protected]f8e7b62d2010-06-03 18:12:54152 return true;
[email protected]9b435be92010-03-04 21:20:15153
154 // Treat the next three digits as the city code.
155 *city_code = working.substr(working.size() - kPhoneCityCodeLength,
156 kPhoneCityCodeLength);
157 working.resize(working.size() - kPhoneCityCodeLength);
158 if (working.empty())
[email protected]f8e7b62d2010-06-03 18:12:54159 return true;
[email protected]9b435be92010-03-04 21:20:15160
161 // Treat any remaining digits as the country code.
162 *country_code = working;
[email protected]f8e7b62d2010-06-03 18:12:54163 return true;
[email protected]9b435be92010-03-04 21:20:15164}
165
[email protected]3db3ff62010-01-07 00:35:39166string16 PhoneNumber::WholeNumber() const {
167 string16 whole_number;
168 if (!country_code_.empty())
169 whole_number.append(country_code_);
170
171 if (!city_code_.empty())
172 whole_number.append(city_code_);
173
174 if (!number_.empty())
175 whole_number.append(number_);
176
177 return whole_number;
178}
179
180void PhoneNumber::set_number(const string16& number) {
181 string16 digits(number);
182 StripPunctuation(&digits);
183 number_ = digits;
184}
185
[email protected]afbd3542010-03-17 20:31:07186void PhoneNumber::set_whole_number(const string16& whole_number) {
187 string16 number, city_code, country_code;
188 ParsePhoneNumber(whole_number, &number, &city_code, &country_code);
189 set_number(number);
190 set_city_code(city_code);
191 set_country_code(country_code);
192}
193
[email protected]3db3ff62010-01-07 00:35:39194PhoneNumber::PhoneNumber(const PhoneNumber& phone_number)
[email protected]225c8f52010-02-05 22:23:20195 : FormGroup(),
196 country_code_(phone_number.country_code_),
[email protected]3db3ff62010-01-07 00:35:39197 city_code_(phone_number.city_code_),
198 number_(phone_number.number_),
199 extension_(phone_number.extension_) {
200}
201
202bool PhoneNumber::FindInfoMatchesHelper(const FieldTypeSubGroup& subgroup,
203 const string16& info,
204 string16* match) const {
205 if (match == NULL) {
206 DLOG(ERROR) << "NULL match string passed in";
207 return false;
208 }
209
210 match->clear();
211 if (subgroup == AutoFillType::PHONE_NUMBER &&
212 StartsWith(number(), info, true)) {
213 *match = number();
214 } else if (subgroup == AutoFillType::PHONE_CITY_CODE &&
215 StartsWith(city_code(), info, true)) {
216 *match = city_code();
217 } else if (subgroup == AutoFillType::PHONE_COUNTRY_CODE &&
218 StartsWith(country_code(), info, true)) {
219 *match = country_code();
220 } else if (subgroup == AutoFillType::PHONE_CITY_AND_NUMBER &&
221 StartsWith(CityAndNumber(), info, true)) {
222 *match = CityAndNumber();
223 } else if (subgroup == AutoFillType::PHONE_WHOLE_NUMBER &&
224 StartsWith(WholeNumber(), info, true)) {
225 *match = WholeNumber();
226 }
227
228 return !match->empty();
229}
230
231bool PhoneNumber::IsNumber(const string16& text) const {
[email protected]48db6fb2010-05-25 16:48:01232 if (text.length() > number_.length())
[email protected]3db3ff62010-01-07 00:35:39233 return false;
234
235 return StartsWith(number_, text, false);
236}
237
238bool PhoneNumber::IsCityCode(const string16& text) const {
[email protected]48db6fb2010-05-25 16:48:01239 if (text.length() > city_code_.length())
[email protected]3db3ff62010-01-07 00:35:39240 return false;
241
242 return StartsWith(city_code_, text, false);
243}
244
245bool PhoneNumber::IsCountryCode(const string16& text) const {
[email protected]48db6fb2010-05-25 16:48:01246 if (text.length() > country_code_.length())
[email protected]3db3ff62010-01-07 00:35:39247 return false;
248
249 return StartsWith(country_code_, text, false);
250}
251
252bool PhoneNumber::IsCityAndNumber(const string16& text) const {
253 string16 city_and_number(CityAndNumber());
254 if (text.length() > city_and_number.length())
255 return false;
256
257 return StartsWith(city_and_number, text, false);
258}
259
260bool PhoneNumber::IsWholeNumber(const string16& text) const {
261 string16 whole_number(WholeNumber());
262 if (text.length() > whole_number.length())
263 return false;
264
265 return StartsWith(whole_number, text, false);
266}
267
268bool PhoneNumber::Validate(const string16& number) const {
269 for (size_t i = 0; i < number.length(); ++i) {
270 if (!IsAsciiDigit(number[i]))
271 return false;
272 }
273
274 return true;
275}
276
[email protected]9b435be92010-03-04 21:20:15277// Static.
278void PhoneNumber::StripPunctuation(string16* number) {
279 RemoveChars(*number, kPhoneNumberSeparators, number);
[email protected]3db3ff62010-01-07 00:35:39280}