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

Side by Side Diff: content/browser/download/download_query.cc

Issue 8601012: DownloadQuery filters and sorts DownloadItems. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Callbacks Created 9 years 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) 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 "content/browser/download/download_query.h"
6
7 #include <algorithm>
8 #include <set>
9 #include <string>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/stl_util.h"
17 #include "base/string16.h"
18 #include "base/string_split.h"
19 #include "base/time.h"
20 #include "base/utf_string_conversions.h"
21 #include "content/browser/download/download_item.h"
22 #include "googleurl/src/gurl.h"
23 #include "unicode/regex.h"
24
25 namespace {
26
27 enum ComparisonType {LT, EQ, GT};
28
29 static bool MatchesQuery(const string16& value, const DownloadItem& item) {
Randy Smith (Not in Mondays) 2011/11/30 19:26:17 nit, suggestion: Comment before this set of functi
benjhayden 2011/11/30 22:13:49 Done.
30 return item.MatchesQuery(value);
31 }
32
33 static int GetStartTime(const DownloadItem& item) {
34 return (item.GetStartTime() - base::Time::UnixEpoch()).InMilliseconds();
35 }
36
37 static int GetEndTime(const DownloadItem& item) {
38 return 0; // TODO
39 }
40
41 static bool GetDangerAccepted(const DownloadItem& item) {
42 return (item.GetSafetyState() == DownloadItem::DANGEROUS_BUT_VALIDATED);
43 }
44
45 static string16 GetFilename(const DownloadItem& item) {
46 // This filename will be compared with strings that could be passed in by the
47 // user, who only sees LossyDisplayNames.
48 return item.GetFullPath().LossyDisplayName();
49 }
50
51 static std::string GetFilenameUTF8(const DownloadItem& item) {
52 return UTF16ToUTF8(GetFilename(item));
53 }
54
55 static std::string GetUrl(const DownloadItem& item) {
56 return item.GetOriginalUrl().spec();
57 }
58
59 static DownloadItem::DownloadState GetState(const DownloadItem& item) {
60 return item.GetState();
61 }
62
63 static DownloadStateInfo::DangerType GetDangerType(const DownloadItem& item) {
64 return item.GetDangerType();
65 }
66
67 static int GetReceivedBytes(const DownloadItem& item) {
68 return item.GetReceivedBytes();
69 }
70
71 static int GetTotalBytes(const DownloadItem& item) {
72 return item.GetTotalBytes();
73 }
74
75 static std::string GetMimeType(const DownloadItem& item) {
76 return item.GetMimeType();
77 }
78
79 static bool IsPaused(const DownloadItem& item) {
80 return item.IsPaused();
81 }
82
83 // Wrap Callback to work around a bug in base::Bind/Callback where the inner
84 // callback is nullified when the outer callback is Run.
Randy Smith (Not in Mondays) 2011/11/30 19:26:17 nit: If there's an issue number, it should be in t
benjhayden 2011/11/30 22:13:49 There isn't a bug, it's just one CL: http://codere
85 template<typename ValueType>
86 class InnerCallback {
87 public:
88 typedef base::Callback<ValueType(const DownloadItem&)> CallbackType;
89
90 explicit InnerCallback(const CallbackType& inner) : inner_(inner) {}
91 ~InnerCallback() {}
92
93 // Mimic Callback's interface to facilitate removing InnerCallback when the
94 // bug is fixed.
95 bool is_null() const { return inner_.is_null(); }
96 ValueType Run(const DownloadItem& item) const { return inner_.Run(item); }
97
98 private:
99 CallbackType inner_;
100 };
101
102 // Returns true if |item| matches the filter specified by |value|, |signum|, and
Randy Smith (Not in Mondays) 2011/11/30 19:26:17 nit: I think this would be more easily comprehensi
benjhayden 2011/11/30 22:13:49 Done.
103 // |accessor|. |accessor| is conceptually a function that takes a DownloadItem
104 // and returns one of its fields, which is then compared to |value|.
105 template<typename ValueType>
106 static bool FieldMatches(
107 const ValueType& value,
108 ComparisonType signum,
109 const InnerCallback<ValueType>& accessor,
110 const DownloadItem& item) {
111 switch (signum) {
112 case LT: return accessor.Run(item) < value;
113 case EQ: return accessor.Run(item) == value;
114 case GT: return accessor.Run(item) > value;
115 }
116 return false;
117 }
118
119 static bool ValidateRegex(const std::string& regex_str) {
120 UParseError re_err;
121 UErrorCode re_status = U_ZERO_ERROR;
122 scoped_ptr<icu::RegexPattern> pattern(icu::RegexPattern::compile(
123 icu::UnicodeString::fromUTF8(regex_str), re_err, re_status));
124 return U_SUCCESS(re_status);
125 }
126
127 // Returns true if |accessor.Run(item)| matches the regex pattern in
128 // |regex_str|.
129 static bool FindRegex(
130 const std::string& regex_str,
131 const InnerCallback<std::string>& accessor,
132 const DownloadItem& item) {
133 CHECK(!accessor.is_null());
134 icu::UnicodeString pattern(regex_str.c_str());
135 icu::UnicodeString input(accessor.Run(item).c_str());
136 UErrorCode status = U_ZERO_ERROR;
137 icu::RegexMatcher match(pattern, input, 0, status);
Randy Smith (Not in Mondays) 2011/11/30 19:26:17 This looks like we're going to recompile the patte
benjhayden 2011/11/30 22:13:49 Done.
138 return match.find();
139 }
140
141 // Returns a ComparisonType to indicate whether a field in |left| is less than,
142 // greater than or equal to the same field in |right|.
143 template<typename ValueType>
144 static ComparisonType Compare(
145 const InnerCallback<ValueType>& accessor,
146 const DownloadItem& left, const DownloadItem& right) {
147 CHECK(!accessor.is_null());
148 ValueType left_value = accessor.Run(left);
149 ValueType right_value = accessor.Run(right);
150 if (left_value > right_value) return GT;
151 if (left_value < right_value) return LT;
152 DCHECK_EQ(left_value, right_value);
153 return EQ;
154 }
155
156 } // anonymous namespace
157
158 DownloadQuery::DownloadQuery()
159 : limit_(kuint32max) {
160 }
161
162 DownloadQuery::~DownloadQuery() {
163 }
164
165 // Filter() pushes a new FilterType to filter_fields_. Most FilterTypes are
166 // Callbacks to FieldMatches<>(). Search() iterates over given DownloadItems,
167 // discarding items for which any filter returns false. A DownloadQuery may have
168 // zero or more FilterTypes.
169
170 void DownloadQuery::Filter(const DownloadQuery::FilterType& value) {
171 filter_fields_.push_back(value);
172 }
173
174 void DownloadQuery::Filter(DownloadItem::DownloadState state) {
175 filter_fields_.push_back(base::Bind(
176 &FieldMatches<DownloadItem::DownloadState>,
177 state,
178 EQ,
179 InnerCallback<DownloadItem::DownloadState>(base::Bind(&GetState))));
180 }
181
182 void DownloadQuery::Filter(DownloadStateInfo::DangerType danger) {
183 filter_fields_.push_back(base::Bind(
184 &FieldMatches<DownloadStateInfo::DangerType>,
185 danger,
186 EQ,
187 InnerCallback<DownloadStateInfo::DangerType>(base::Bind(
188 &GetDangerType))));
189 }
190
191 bool DownloadQuery::Filter(DownloadQuery::FilterFieldName name, int value) {
192 switch (static_cast<int>(name)) {
193 case FILTER_FIELD_START_TIME:
194 Filter(base::Bind(&FieldMatches<int>, value, EQ,
195 InnerCallback<int>(base::Bind(&GetStartTime))));
196 return true;
197 case FILTER_FIELD_STARTED_BEFORE:
198 Filter(base::Bind(&FieldMatches<int>, value, GT,
199 InnerCallback<int>(base::Bind(&GetStartTime))));
200 return true;
201 case FILTER_FIELD_STARTED_AFTER:
202 Filter(base::Bind(&FieldMatches<int>, value, LT,
203 InnerCallback<int>(base::Bind(&GetStartTime))));
204 return true;
205 case FILTER_FIELD_END_TIME:
206 Filter(base::Bind(&FieldMatches<int>, value, EQ,
207 InnerCallback<int>(base::Bind(&GetEndTime))));
208 return true;
209 case FILTER_FIELD_ENDED_BEFORE:
210 Filter(base::Bind(&FieldMatches<int>, value, GT,
211 InnerCallback<int>(base::Bind(&GetEndTime))));
212 return true;
213 case FILTER_FIELD_ENDED_AFTER:
214 Filter(base::Bind(&FieldMatches<int>, value, LT,
215 InnerCallback<int>(base::Bind(&GetEndTime))));
216 return true;
217 case FILTER_FIELD_BYTES_RECEIVED:
218 Filter(base::Bind(&FieldMatches<int>, value, EQ,
219 InnerCallback<int>(base::Bind(&GetReceivedBytes))));
220 return true;
221 case FILTER_FIELD_TOTAL_BYTES:
222 Filter(base::Bind(&FieldMatches<int>, value, EQ,
223 InnerCallback<int>(base::Bind(&GetTotalBytes))));
224 return true;
225 case FILTER_FIELD_TOTAL_BYTES_GREATER:
226 Filter(base::Bind(&FieldMatches<int>, value, LT,
227 InnerCallback<int>(base::Bind(&GetTotalBytes))));
228 return true;
229 case FILTER_FIELD_TOTAL_BYTES_LESS:
230 Filter(base::Bind(&FieldMatches<int>, value, GT,
231 InnerCallback<int>(base::Bind(&GetTotalBytes))));
232 return true;
233 case FILTER_FIELD_FILE_SIZE: return false; // TODO
234 case FILTER_FIELD_FILE_SIZE_GREATER: return false; // TODO
235 case FILTER_FIELD_FILE_SIZE_LESS: return false; // TODO
236 case FILTER_FIELD_ERROR: return false; // TODO
237 }
238 return false;
239 }
240
241 bool DownloadQuery::Filter(DownloadQuery::FilterFieldName name, bool value) {
242 switch (static_cast<int>(name)) {
243 case FILTER_FIELD_DANGER_ACCEPTED:
244 Filter(base::Bind(&FieldMatches<bool>, value, EQ,
245 InnerCallback<bool>(base::Bind(&GetDangerAccepted))));
246 return true;
247 case FILTER_FIELD_PAUSED:
248 Filter(base::Bind(&FieldMatches<bool>, value, EQ,
249 InnerCallback<bool>(base::Bind(&IsPaused))));
250 return true;
251 }
252 return false;
253 }
254
255 bool DownloadQuery::Filter(
256 DownloadQuery::FilterFieldName name, const std::string& value) {
257 switch (static_cast<int>(name)) {
258 case FILTER_FIELD_MIME:
259 Filter(base::Bind(&FieldMatches<std::string>, value, EQ,
260 InnerCallback<std::string>(base::Bind(&GetMimeType))));
261 return true;
262 case FILTER_FIELD_URL:
263 Filter(base::Bind(&FieldMatches<std::string>, value, EQ,
264 InnerCallback<std::string>(base::Bind(&GetUrl))));
265 return true;
266 case FILTER_FIELD_URL_REGEX:
267 if (!ValidateRegex(value)) return false;
268 Filter(base::Bind(&FindRegex, value,
269 InnerCallback<std::string>(base::Bind(&GetUrl))));
270 return true;
271 }
272 return false;
273 }
274
275 bool DownloadQuery::Filter(
276 DownloadQuery::FilterFieldName name, const string16& value) {
277 switch (static_cast<int>(name)) {
278 case FILTER_FIELD_QUERY:
279 Filter(base::Bind(&MatchesQuery, value));
280 return true;
281 case FILTER_FIELD_FILENAME:
282 Filter(base::Bind(&FieldMatches<string16>, value, EQ,
283 InnerCallback<string16>(base::Bind(&GetFilename))));
284 return true;
285 case FILTER_FIELD_FILENAME_REGEX:
286 if (!ValidateRegex(UTF16ToUTF8(value))) return false;
287 Filter(base::Bind(&FindRegex, UTF16ToUTF8(value),
288 InnerCallback<std::string>(base::Bind(&GetFilenameUTF8))));
289 return true;
290 }
291 return false;
292 }
293
294 bool DownloadQuery::Matches(const DownloadItem& item) const {
295 for (FilterFieldVector::const_iterator filter = filter_fields_.begin();
296 filter != filter_fields_.end(); ++filter) {
297 CHECK(!filter->is_null());
298 if (!filter->Run(item))
299 return false;
300 }
301 return true;
302 }
303
304 // Sort() creates a SortField and pushes it onto sort_fields_. A SortField is a
305 // direction and a Callback to Compare<>(). After filtering, Search() makes a
306 // DownloadComparator functor from the sort_fields_ and passes the
307 // DownloadComparator to std::partial_sort. std::partial_sort calls the
308 // DownloadComparator with different pairs of DownloadItems. DownloadComparator
309 // iterates over the sort fields until a callback returns ComparisonType LT or
310 // GT. DownloadComparator returns true or false depending on that
311 // ComparisonType and the sort field's direction in order to indicate to
312 // std::partial_sort whether the left item is after or before the right item. If
313 // all sort fields return EQ, then DownloadComparator compares GetId or
314 // GetDbHandle. A DownloadQuery may have zero or more SortFields, but there is
315 // one DownloadComparator per call to Search().
316
317 struct DownloadQuery::SortField {
318 typedef base::Callback<ComparisonType(
319 const DownloadItem&, const DownloadItem&)> SortType;
320
321 SortField(DownloadQuery::SortFieldDirection adirection,
322 const SortType& asorter)
323 : direction(adirection),
324 sorter(asorter) {
325 }
326 ~SortField() {}
327
328 DownloadQuery::SortFieldDirection direction;
329 SortType sorter;
330 };
331
332 class DownloadQuery::DownloadComparator {
333 public:
334 DownloadComparator(const DownloadQuery::SortFieldVector& terms)
335 : terms_(terms) {
336 }
337
338 // Returns true if |left| sorts before |right|.
339 bool operator() (const DownloadItem* left, const DownloadItem* right);
340
341 private:
342 const DownloadQuery::SortFieldVector& terms_;
343
344 // std::sort requires this class to be copyable.
345 };
346
347 bool DownloadQuery::DownloadComparator::operator() (
348 const DownloadItem* left, const DownloadItem* right) {
349 for (DownloadQuery::SortFieldVector::const_iterator term = terms_.begin();
350 term != terms_.end(); ++term) {
351 CHECK(!(*term).sorter.is_null());
352 switch ((*term).sorter.Run(*left, *right)) {
353 case LT: return ((*term).direction == DownloadQuery::ASCENDING);
354 case GT: return ((*term).direction == DownloadQuery::DESCENDING);
355 case EQ: continue;
356 }
357 }
358 if (left->GetId() != right->GetId())
359 return left->GetId() < right->GetId();
360 CHECK_NE(left->GetDbHandle(), right->GetDbHandle());
361 return left->GetDbHandle() < right->GetDbHandle();
362 }
363
364 void DownloadQuery::Sort(DownloadQuery::SortFieldName name,
365 DownloadQuery::SortFieldDirection direction) {
366 switch (name) {
367 case SORT_FIELD_START_TIME:
368 sort_fields_.push_back(SortField(direction, base::Bind(
369 &Compare<int>, InnerCallback<int>(base::Bind(&GetStartTime)))));
370 break;
371 case SORT_FIELD_URL:
372 sort_fields_.push_back(SortField(direction, base::Bind(
373 &Compare<std::string>,
374 InnerCallback<std::string>(base::Bind(&GetUrl)))));
375 break;
376 case SORT_FIELD_FILENAME:
377 sort_fields_.push_back(SortField(direction, base::Bind(
378 &Compare<string16>,
379 InnerCallback<string16>(base::Bind(&GetFilename)))));
380 break;
381 case SORT_FIELD_DANGER:
382 sort_fields_.push_back(SortField(direction, base::Bind(
383 &Compare<DownloadStateInfo::DangerType>,
384 InnerCallback<DownloadStateInfo::DangerType>(base::Bind(
385 &GetDangerType)))));
386 break;
387 case SORT_FIELD_DANGER_ACCEPTED:
388 sort_fields_.push_back(SortField(direction, base::Bind(
389 &Compare<bool>,
390 InnerCallback<bool>(base::Bind(&GetDangerAccepted)))));
391 break;
392 case SORT_FIELD_STATE:
393 sort_fields_.push_back(SortField(direction, base::Bind(
394 &Compare<DownloadItem::DownloadState>,
395 InnerCallback<DownloadItem::DownloadState>(base::Bind(&GetState)))));
396 break;
397 case SORT_FIELD_PAUSED:
398 sort_fields_.push_back(SortField(direction, base::Bind(
399 &Compare<bool>, InnerCallback<bool>(base::Bind(&IsPaused)))));
400 break;
401 case SORT_FIELD_MIME:
402 sort_fields_.push_back(SortField(direction, base::Bind(
403 &Compare<std::string>,
404 InnerCallback<std::string>(base::Bind(&GetMimeType)))));
405 break;
406 case SORT_FIELD_END_TIME:
407 sort_fields_.push_back(SortField(direction, base::Bind(
408 &Compare<int>, InnerCallback<int>(base::Bind(&GetEndTime)))));
409 break;
410 case SORT_FIELD_BYTES_RECEIVED:
411 sort_fields_.push_back(SortField(direction, base::Bind(
412 &Compare<int>, InnerCallback<int>(base::Bind(&GetReceivedBytes)))));
413 break;
414 case SORT_FIELD_TOTAL_BYTES:
415 sort_fields_.push_back(SortField(direction, base::Bind(
416 &Compare<int>, InnerCallback<int>(base::Bind(&GetTotalBytes)))));
417 break;
418 case SORT_FIELD_FILE_SIZE: break; // TODO
419 case SORT_FIELD_ERROR: break; // TODO
Randy Smith (Not in Mondays) 2011/11/30 19:26:17 Why are these no-ops? I think of it as friendlier
benjhayden 2011/11/30 22:13:49 Sort() doesn't return anything, so I'll make it NO
420 default: NOTREACHED();
421 }
422 }
423
424 template <typename InputIterator>
425 void DownloadQuery::Search(InputIterator iter, const InputIterator last,
426 DownloadQuery::DownloadVector* results) const {
427 results->clear();
428 for (; iter != last; ++iter) {
429 if (Matches(**iter))
430 results->push_back(*iter);
431 }
432 if (!sort_fields_.empty())
433 std::partial_sort(results->begin(),
434 results->begin() + std::min(limit_, results->size()),
435 results->end(),
436 DownloadComparator(sort_fields_));
437 if (results->size() > limit_)
438 results->resize(limit_);
439 }
440
441 template void DownloadQuery::Search(
442 std::set<DownloadItem*>::const_iterator iter,
443 const std::set<DownloadItem*>::const_iterator last,
444 DownloadQuery::DownloadVector* results) const;
445 template void DownloadQuery::Search(
446 std::vector<DownloadItem*>::const_iterator iter,
447 const std::vector<DownloadItem*>::const_iterator last,
448 DownloadQuery::DownloadVector* results) const;
449 template void DownloadQuery::Search(
450 std::vector<DownloadItem*>::iterator iter,
451 const std::vector<DownloadItem*>::iterator last,
452 DownloadQuery::DownloadVector* results) const;
453
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698