Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(74)

Side by Side Diff: chrome/browser/autocomplete/contact_provider_chromeos.cc

Issue 10542076: ABANDONED: chromeos: Download contacts (work in progress). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: ContactProvider cleanup and tests Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/autocomplete/contact_provider_chromeos.h"
6
7 #include <algorithm>
8
9 #include "base/string_split.h"
10 #include "base/utf_string_conversions.h"
11 #include "chrome/browser/autocomplete/autocomplete_input.h"
12 #include "chrome/browser/chromeos/contacts/contact.pb.h"
13 #include "chrome/browser/chromeos/contacts/contact_manager.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "unicode/usearch.h"
16
17 namespace {
18
19 // Searches for the first occurrence of |substring| within |string|. Case and
20 // accent differences are ignored -- see "primary level" at
21 // http://userguide.icu-project.org/collation/concepts.
22 //
23 // false is returned if |substring| is not found or if an error occurs. If
24 // non-NULL, |string_start_index| is set to the 0-based index where the match
25 // begins and |string_match_length| is set to the length of the matched text.
26 bool FindFirstSubstring(const string16& string,
27 const string16& substring,
28 size_t* string_start_index,
29 size_t* string_match_length) {
30 if (string.empty() || substring.empty())
31 return false;
32
33 UErrorCode status = U_ZERO_ERROR;
34 UStringSearch* search = usearch_open(substring.data(), -1,
35 string.data(), -1,
36 uloc_getDefault(),
37 NULL, // breakiter
38 &status);
39 if (!U_SUCCESS(status))
40 return false;
41
42 UCollator* collator = usearch_getCollator(search);
43 ucol_setStrength(collator, UCOL_PRIMARY);
44 usearch_reset(search);
45
46 int32_t start_index = usearch_first(search, &status);
47 int32_t match_length = usearch_getMatchedLength(search);
48 usearch_close(search);
49
50 if (!U_SUCCESS(status) || start_index == USEARCH_DONE)
51 return false;
52
53 DCHECK_GE(start_index, 0);
54 DCHECK_GE(match_length, 0);
55 DCHECK_LE(start_index + match_length, static_cast<int32_t>(string.size()));
56
57 if (string_start_index)
58 *string_start_index = static_cast<size_t>(start_index);
59 if (string_match_length)
60 *string_match_length = static_cast<size_t>(match_length);
61 return true;
62 }
63
64 // Fills |classifications| based on |match_spans|, which consists of sorted
65 // [index, length] pairs for matching text. Overlapping or adjacent spans are
66 // collapsed where possible. Returns false on invalid input.
67 bool FillACMatchClassifications(
68 const ContactProvider::MatchSpans& match_spans,
69 size_t text_length,
70 AutocompleteMatch::ACMatchClassifications* classifications) {
71 DCHECK(classifications);
72 classifications->clear();
73
74 // Index where the previous span started.
75 size_t last_span_start = 0;
76
77 // One beyond the final character in the previous span (e.g. if there's a
78 // span at offset 0 with length 1, this will be set to 1).
79 size_t last_span_end = 0;
80
81 for (ContactProvider::MatchSpans::const_iterator it = match_spans.begin();
82 it != match_spans.end(); ++it) {
83 size_t span_start = it->first;
84 size_t span_length = it->second;
85
86 if (span_start < last_span_start || span_start + span_length > text_length)
87 return false;
88
89 if (span_length == 0)
90 continue;
91
92 if (span_start > last_span_end || last_span_end == 0) {
93 if (span_start != 0) {
94 // If we're not at the beginning of the string, add a preceding unstyled
95 // classification.
96 classifications->push_back(
97 ACMatchClassification(
98 last_span_end,
99 AutocompleteMatch::ACMatchClassification::NONE));
100 }
101 classifications->push_back(
102 ACMatchClassification(
103 span_start, AutocompleteMatch::ACMatchClassification::MATCH));
104 last_span_end = span_start + span_length;
105 } else {
106 // This span starts at or before the point where the last span ended, so
107 // don't bother adding an additional classification.
108 last_span_end = std::max(last_span_end, span_start + span_length);
109 }
110
111 last_span_start = span_start;
112 }
113
114 if (last_span_end < text_length) {
115 classifications->push_back(
116 ACMatchClassification(
117 last_span_end, AutocompleteMatch::ACMatchClassification::NONE));
118 }
119
120 return true;
121 }
122
123 } // namespace
124
125 // static
126 const char ContactProvider::kMatchContactIdKey[] = "contact_id";
127
128 // Cached information about a contact.
129 struct ContactProvider::ContactData {
130 ContactData(const string16& full_name,
131 const string16& given_name,
132 const string16& family_name,
133 const std::string& contact_id)
134 : full_name(full_name),
135 given_name(given_name),
136 family_name(family_name),
137 given_name_index(0),
138 family_name_index(0),
139 given_name_index_valid(false),
140 family_name_index_valid(false),
141 contact_id(contact_id) {
142 given_name_index_valid =
143 FindFirstSubstring(full_name, given_name, &given_name_index, NULL);
144 family_name_index_valid =
145 FindFirstSubstring(full_name, family_name, &family_name_index, NULL);
146 }
147
148 string16 full_name;
149 string16 given_name;
150 string16 family_name;
151
152 // Indices into |full_name| where |given_name| and |family_name| first appear.
153 // Only valid when the corresponding |*_index_valid| field is true.
154 size_t given_name_index;
155 size_t family_name_index;
156
157 // Do |given_name| or |family_name| appear in |full_name|?
158 bool given_name_index_valid;
159 bool family_name_index_valid;
160
161 // Unique ID used to look up additional contact information.
162 std::string contact_id;
163 };
164
165 ContactProvider::TestAPI::TestAPI(ContactProvider* provider)
166 : provider_(provider) {
167 }
168
169 ContactProvider::TestAPI::~TestAPI() {
170 provider_ = NULL;
171 }
172
173 // static
174 AutocompleteMatch::ACMatchClassifications
175 ContactProvider::TestAPI::RunFillACMatchClassifications(
176 const MatchSpans& match_spans,
177 size_t text_length) {
178 ACMatchClassifications classifications;
179 if (FillACMatchClassifications(match_spans, text_length, &classifications))
180 return classifications;
181 else
182 return ACMatchClassifications();
183 }
184
185 ContactProvider::ContactProvider(
186 AutocompleteProviderListener* listener,
187 Profile* profile,
188 base::WeakPtr<contacts::ContactManagerInterface> contact_manager)
189 : AutocompleteProvider(listener, profile, "Contacts"),
190 contact_manager_(contact_manager) {
191 contact_manager_->AddObserver(this, profile);
192 RefreshContacts();
193 }
194
195 void ContactProvider::Start(const AutocompleteInput& input,
196 bool minimal_changes) {
197 matches_.clear();
198
199 if (input.type() != AutocompleteInput::UNKNOWN &&
200 input.type() != AutocompleteInput::QUERY &&
201 input.type() != AutocompleteInput::FORCED_QUERY) {
202 return;
203 }
204
205 std::vector<string16> input_parts;
206 base::SplitStringAlongWhitespace(input.text(), &input_parts);
207
208 for (ContactDataVector::const_iterator it = contacts_.begin();
209 it != contacts_.end(); ++it) {
210 const ContactData& contact = *it;
211
212 // [index, length] spans within |contact.full_name| that are matched by the
213 // input.
214 MatchSpans match_spans;
215
216 bool is_matched = false;
217 size_t match_index = 0, match_length = 0;
218
219 // First, check if the whole input string is a prefix of the full name.
220 if (FindFirstSubstring(
221 contact.full_name, input.text(), &match_index, &match_length) &&
222 match_index == 0) {
223 is_matched = true;
224 match_spans.push_back(std::make_pair(0, match_length));
225 } else {
226 // If not, check whether every search term is a prefix of the given name
227 // or the family name.
228 for (std::vector<string16>::const_iterator it = input_parts.begin();
229 it != input_parts.end(); ++it) {
230 if (FindFirstSubstring(
231 contact.given_name, *it, &match_index, &match_length) &&
232 match_index == 0) {
233 is_matched = true;
234 if (contact.given_name_index_valid) {
235 match_spans.push_back(
236 std::make_pair(contact.given_name_index, match_length));
237 }
238 } else if (
239 FindFirstSubstring(
240 contact.family_name, *it, &match_index, &match_length) &&
241 match_index == 0) {
242 is_matched = true;
243 if (contact.family_name_index_valid) {
244 match_spans.push_back(
245 std::make_pair(contact.family_name_index, match_length));
246 }
247 } else {
248 is_matched = false;
249 break;
250 }
251 }
252 }
253
254 if (is_matched) {
255 std::sort(match_spans.begin(), match_spans.end());
256 matches_.push_back(CreateAutocompleteMatch(input, contact, match_spans));
257 }
258 }
259 }
260
261 void ContactProvider::OnContactsUpdated(Profile* profile) {
262 DCHECK_EQ(profile, profile_);
263 RefreshContacts();
264 }
265
266 ContactProvider::~ContactProvider() {
267 // Like ContactProvider, ContactManager gets destroyed at profile destruction.
268 // Make sure that this class doesn't try to access ContactManager after
269 // ContactManager is gone.
270 if (contact_manager_.get())
271 contact_manager_->RemoveObserver(this, profile_);
272 }
273
274 void ContactProvider::RefreshContacts() {
275 typedef std::vector<const contacts::Contact*> ContactPointers;
276 scoped_ptr<ContactPointers> contacts =
277 contact_manager_->GetAllContacts(profile_);
278
279 contacts_.clear();
280 for (ContactPointers::const_iterator it = contacts->begin();
281 it != contacts->end(); ++it) {
282 const contacts::Contact& contact = **it;
283 string16 full_name =
284 AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.full_name()));
285 string16 given_name =
286 AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.given_name()));
287 string16 family_name =
288 AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.family_name()));
289
290 if (!full_name.empty()) {
291 contacts_.push_back(
292 ContactData(
293 full_name, given_name, family_name, contact.contact_id()));
294 }
295 }
296 }
297
298 AutocompleteMatch ContactProvider::CreateAutocompleteMatch(
299 const AutocompleteInput& input,
300 const ContactData& contact,
301 const MatchSpans& match_spans) {
302 AutocompleteMatch match(this, 0, false, AutocompleteMatch::CONTACT);
303 match.fill_into_edit = input.text();
304 match.inline_autocomplete_offset = string16::npos;
305 match.contents = contact.full_name;
306 match.relevance = 1000; // FIXME: update autocomplete_provider.h
307 match.RecordAdditionalInfo(kMatchContactIdKey, contact.contact_id);
308 FillACMatchClassifications(
309 match_spans,
310 match.contents.length(),
311 &match.contents_class);
312 return match;
313 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698