OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/autocomplete/contact_provider_chromeos.h" | 5 #include "chrome/browser/autocomplete/contact_provider_chromeos.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 | 9 |
10 #include "base/i18n/break_iterator.h" | 10 #include "base/i18n/break_iterator.h" |
(...skipping 15 matching lines...) Expand all Loading... |
26 | 26 |
27 // Base match relevance assigned to a contact with an affinity of 0.0. | 27 // Base match relevance assigned to a contact with an affinity of 0.0. |
28 int kBaseRelevance = 1300; | 28 int kBaseRelevance = 1300; |
29 | 29 |
30 // Maximum boost to relevance for a contact with an affinity of 1.0. | 30 // Maximum boost to relevance for a contact with an affinity of 1.0. |
31 int kAffinityRelevanceBoost = 200; | 31 int kAffinityRelevanceBoost = 200; |
32 | 32 |
33 // Returns true if |word_to_find| is a prefix of |name_to_search| and marks the | 33 // Returns true if |word_to_find| is a prefix of |name_to_search| and marks the |
34 // matching text in |classifications| (which corresponds to the contact's full | 34 // matching text in |classifications| (which corresponds to the contact's full |
35 // name). |name_index_in_full_name| contains |name_to_search|'s index within | 35 // name). |name_index_in_full_name| contains |name_to_search|'s index within |
36 // the full name or string16::npos if it doesn't appear in it. | 36 // the full name or base::string16::npos if it doesn't appear in it. |
37 bool WordIsNamePrefix(const string16& word_to_find, | 37 bool WordIsNamePrefix(const base::string16& word_to_find, |
38 const string16& name_to_search, | 38 const base::string16& name_to_search, |
39 size_t name_index_in_full_name, | 39 size_t name_index_in_full_name, |
40 size_t full_name_length, | 40 size_t full_name_length, |
41 ACMatchClassifications* classifications) { | 41 ACMatchClassifications* classifications) { |
42 DCHECK(classifications); | 42 DCHECK(classifications); |
43 | 43 |
44 size_t match_index = 0; | 44 size_t match_index = 0; |
45 size_t match_length = 0; | 45 size_t match_length = 0; |
46 if (!base::i18n::StringSearchIgnoringCaseAndAccents(word_to_find, | 46 if (!base::i18n::StringSearchIgnoringCaseAndAccents(word_to_find, |
47 name_to_search, &match_index, &match_length) || (match_index != 0)) | 47 name_to_search, &match_index, &match_length) || (match_index != 0)) |
48 return false; | 48 return false; |
49 | 49 |
50 if (name_index_in_full_name != string16::npos) { | 50 if (name_index_in_full_name != base::string16::npos) { |
51 AutocompleteMatch::ACMatchClassifications new_class; | 51 AutocompleteMatch::ACMatchClassifications new_class; |
52 AutocompleteMatch::ClassifyLocationInString(name_index_in_full_name, | 52 AutocompleteMatch::ClassifyLocationInString(name_index_in_full_name, |
53 match_length, full_name_length, 0, &new_class); | 53 match_length, full_name_length, 0, &new_class); |
54 *classifications = AutocompleteMatch::MergeClassifications( | 54 *classifications = AutocompleteMatch::MergeClassifications( |
55 *classifications, new_class); | 55 *classifications, new_class); |
56 } | 56 } |
57 | 57 |
58 return true; | 58 return true; |
59 } | 59 } |
60 | 60 |
61 } // namespace | 61 } // namespace |
62 | 62 |
63 // static | 63 // static |
64 const char ContactProvider::kMatchContactIdKey[] = "contact_id"; | 64 const char ContactProvider::kMatchContactIdKey[] = "contact_id"; |
65 | 65 |
66 // Cached information about a contact. | 66 // Cached information about a contact. |
67 struct ContactProvider::ContactData { | 67 struct ContactProvider::ContactData { |
68 ContactData(const string16& full_name, | 68 ContactData(const base::string16& full_name, |
69 const string16& given_name, | 69 const base::string16& given_name, |
70 const string16& family_name, | 70 const base::string16& family_name, |
71 const std::string& contact_id, | 71 const std::string& contact_id, |
72 float affinity) | 72 float affinity) |
73 : full_name(full_name), | 73 : full_name(full_name), |
74 given_name(given_name), | 74 given_name(given_name), |
75 family_name(family_name), | 75 family_name(family_name), |
76 given_name_index(string16::npos), | 76 given_name_index(base::string16::npos), |
77 family_name_index(string16::npos), | 77 family_name_index(base::string16::npos), |
78 contact_id(contact_id), | 78 contact_id(contact_id), |
79 affinity(affinity) { | 79 affinity(affinity) { |
80 base::i18n::StringSearchIgnoringCaseAndAccents( | 80 base::i18n::StringSearchIgnoringCaseAndAccents( |
81 given_name, full_name, &given_name_index, NULL); | 81 given_name, full_name, &given_name_index, NULL); |
82 base::i18n::StringSearchIgnoringCaseAndAccents( | 82 base::i18n::StringSearchIgnoringCaseAndAccents( |
83 family_name, full_name, &family_name_index, NULL); | 83 family_name, full_name, &family_name_index, NULL); |
84 } | 84 } |
85 | 85 |
86 string16 full_name; | 86 base::string16 full_name; |
87 string16 given_name; | 87 base::string16 given_name; |
88 string16 family_name; | 88 base::string16 family_name; |
89 | 89 |
90 // Indices into |full_name| where |given_name| and |family_name| first appear, | 90 // Indices into |full_name| where |given_name| and |family_name| first appear, |
91 // or string16::npos if they don't appear in it. | 91 // or base::string16::npos if they don't appear in it. |
92 size_t given_name_index; | 92 size_t given_name_index; |
93 size_t family_name_index; | 93 size_t family_name_index; |
94 | 94 |
95 // Unique ID used to look up additional contact information. | 95 // Unique ID used to look up additional contact information. |
96 std::string contact_id; | 96 std::string contact_id; |
97 | 97 |
98 // Affinity between the user and this contact, in the range [0.0, 1.0]. | 98 // Affinity between the user and this contact, in the range [0.0, 1.0]. |
99 float affinity; | 99 float affinity; |
100 }; | 100 }; |
101 | 101 |
(...skipping 12 matching lines...) Expand all Loading... |
114 if (minimal_changes) | 114 if (minimal_changes) |
115 return; | 115 return; |
116 | 116 |
117 matches_.clear(); | 117 matches_.clear(); |
118 | 118 |
119 if (input.type() != AutocompleteInput::UNKNOWN && | 119 if (input.type() != AutocompleteInput::UNKNOWN && |
120 input.type() != AutocompleteInput::QUERY && | 120 input.type() != AutocompleteInput::QUERY && |
121 input.type() != AutocompleteInput::FORCED_QUERY) | 121 input.type() != AutocompleteInput::FORCED_QUERY) |
122 return; | 122 return; |
123 | 123 |
124 std::vector<string16> input_words; | 124 std::vector<base::string16> input_words; |
125 base::i18n::BreakIterator break_iterator( | 125 base::i18n::BreakIterator break_iterator( |
126 input.text(), | 126 input.text(), |
127 base::i18n::BreakIterator::BREAK_WORD); | 127 base::i18n::BreakIterator::BREAK_WORD); |
128 if (break_iterator.Init()) { | 128 if (break_iterator.Init()) { |
129 while (break_iterator.Advance()) { | 129 while (break_iterator.Advance()) { |
130 if (break_iterator.IsWord()) | 130 if (break_iterator.IsWord()) |
131 input_words.push_back(break_iterator.GetString()); | 131 input_words.push_back(break_iterator.GetString()); |
132 } | 132 } |
133 } | 133 } |
134 | 134 |
(...skipping 29 matching lines...) Expand all Loading... |
164 return; | 164 return; |
165 | 165 |
166 scoped_ptr<contacts::ContactPointers> contacts = | 166 scoped_ptr<contacts::ContactPointers> contacts = |
167 contact_manager_->GetAllContacts(profile_); | 167 contact_manager_->GetAllContacts(profile_); |
168 | 168 |
169 contacts_.clear(); | 169 contacts_.clear(); |
170 contacts_.reserve(contacts->size()); | 170 contacts_.reserve(contacts->size()); |
171 for (contacts::ContactPointers::const_iterator it = contacts->begin(); | 171 for (contacts::ContactPointers::const_iterator it = contacts->begin(); |
172 it != contacts->end(); ++it) { | 172 it != contacts->end(); ++it) { |
173 const contacts::Contact& contact = **it; | 173 const contacts::Contact& contact = **it; |
174 string16 full_name = | 174 base::string16 full_name = |
175 AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.full_name())); | 175 AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.full_name())); |
176 string16 given_name = | 176 base::string16 given_name = |
177 AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.given_name())); | 177 AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.given_name())); |
178 string16 family_name = | 178 base::string16 family_name = |
179 AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.family_name())); | 179 AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.family_name())); |
180 float affinity = | 180 float affinity = |
181 contact.has_affinity() ? contact.affinity() : kDefaultAffinity; | 181 contact.has_affinity() ? contact.affinity() : kDefaultAffinity; |
182 | 182 |
183 if (!full_name.empty()) { | 183 if (!full_name.empty()) { |
184 contacts_.push_back( | 184 contacts_.push_back( |
185 ContactData(full_name, given_name, family_name, contact.contact_id(), | 185 ContactData(full_name, given_name, family_name, contact.contact_id(), |
186 affinity)); | 186 affinity)); |
187 } | 187 } |
188 } | 188 } |
189 std::sort(contacts_.begin(), contacts_.end(), CompareAffinity); | 189 std::sort(contacts_.begin(), contacts_.end(), CompareAffinity); |
190 } | 190 } |
191 | 191 |
192 void ContactProvider::AddContactIfMatched( | 192 void ContactProvider::AddContactIfMatched( |
193 const AutocompleteInput& input, | 193 const AutocompleteInput& input, |
194 const std::vector<string16>& input_words, | 194 const std::vector<base::string16>& input_words, |
195 const ContactData& contact) { | 195 const ContactData& contact) { |
196 // First, check if the whole input string is a prefix of the full name. | 196 // First, check if the whole input string is a prefix of the full name. |
197 // TODO(derat): Consider additionally segmenting the full name so we can match | 197 // TODO(derat): Consider additionally segmenting the full name so we can match |
198 // e.g. middle names or initials even when they aren't typed as a prefix of | 198 // e.g. middle names or initials even when they aren't typed as a prefix of |
199 // the full name. | 199 // the full name. |
200 ACMatchClassifications classifications; | 200 ACMatchClassifications classifications; |
201 if (!WordIsNamePrefix(input.text(), contact.full_name, 0, | 201 if (!WordIsNamePrefix(input.text(), contact.full_name, 0, |
202 contact.full_name.size(), &classifications)) { | 202 contact.full_name.size(), &classifications)) { |
203 // If not, check whether every search term is a prefix of the given name | 203 // If not, check whether every search term is a prefix of the given name |
204 // or the family name. | 204 // or the family name. |
205 if (input_words.empty()) | 205 if (input_words.empty()) |
206 return; | 206 return; |
207 | 207 |
208 // TODO(derat): Check new matches against previous ones to make sure they | 208 // TODO(derat): Check new matches against previous ones to make sure they |
209 // don't overlap (e.g. the query "bob b" against a contact with full name | 209 // don't overlap (e.g. the query "bob b" against a contact with full name |
210 // "Bob G. Bryson", given name "Bob", and family name "Bryson" should result | 210 // "Bob G. Bryson", given name "Bob", and family name "Bryson" should result |
211 // in classifications "_Bob_ G. _B_ryson" rather than "_Bob_ G. Bryson". | 211 // in classifications "_Bob_ G. _B_ryson" rather than "_Bob_ G. Bryson". |
212 for (std::vector<string16>::const_iterator it = input_words.begin(); | 212 for (std::vector<base::string16>::const_iterator it = input_words.begin(); |
213 it != input_words.end(); ++it) { | 213 it != input_words.end(); ++it) { |
214 if (!WordIsNamePrefix(*it, contact.given_name, contact.given_name_index, | 214 if (!WordIsNamePrefix(*it, contact.given_name, contact.given_name_index, |
215 contact.full_name.size(), &classifications) && | 215 contact.full_name.size(), &classifications) && |
216 !WordIsNamePrefix(*it, contact.family_name, contact.family_name_index, | 216 !WordIsNamePrefix(*it, contact.family_name, contact.family_name_index, |
217 contact.full_name.size(), &classifications)) | 217 contact.full_name.size(), &classifications)) |
218 return; | 218 return; |
219 } | 219 } |
220 } | 220 } |
221 | 221 |
222 matches_.push_back(CreateAutocompleteMatch(input, contact)); | 222 matches_.push_back(CreateAutocompleteMatch(input, contact)); |
223 matches_.back().contents_class = classifications; | 223 matches_.back().contents_class = classifications; |
224 } | 224 } |
225 | 225 |
226 AutocompleteMatch ContactProvider::CreateAutocompleteMatch( | 226 AutocompleteMatch ContactProvider::CreateAutocompleteMatch( |
227 const AutocompleteInput& input, | 227 const AutocompleteInput& input, |
228 const ContactData& contact) { | 228 const ContactData& contact) { |
229 AutocompleteMatch match(this, 0, false, AutocompleteMatchType::CONTACT); | 229 AutocompleteMatch match(this, 0, false, AutocompleteMatchType::CONTACT); |
230 match.contents = contact.full_name; | 230 match.contents = contact.full_name; |
231 match.fill_into_edit = match.contents; | 231 match.fill_into_edit = match.contents; |
232 match.relevance = kBaseRelevance + | 232 match.relevance = kBaseRelevance + |
233 static_cast<int>(roundf(kAffinityRelevanceBoost * contact.affinity)); | 233 static_cast<int>(roundf(kAffinityRelevanceBoost * contact.affinity)); |
234 match.RecordAdditionalInfo(kMatchContactIdKey, contact.contact_id); | 234 match.RecordAdditionalInfo(kMatchContactIdKey, contact.contact_id); |
235 return match; | 235 return match; |
236 } | 236 } |
OLD | NEW |