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

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: include 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 <string>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/stl_util.h"
16 #include "base/string16.h"
17 #include "base/string_split.h"
18 #include "base/time.h"
19 #include "base/utf_string_conversions.h"
20 #include "base/values.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 // Templatized base::Value::GetAs*().
28 template <typename T> bool GetAs(const base::Value& in, T* out);
29 template<> bool GetAs(const base::Value& in, bool* out) {
30 return in.GetAsBoolean(out);
31 }
32 template<> bool GetAs(const base::Value& in, int* out) {
33 return in.GetAsInteger(out);
34 }
35 template<> bool GetAs(const base::Value& in, std::string* out) {
36 return in.GetAsString(out);
37 }
38 template<> bool GetAs(const base::Value& in, string16* out) {
39 return in.GetAsString(out);
40 }
41
42 // The next several functions are helpers for making Callbacks that access
43 // DownloadItem fields.
44
45 static bool MatchesQuery(const string16& value, const DownloadItem& item) {
46 return item.MatchesQuery(value);
47 }
48
49 static int GetStartTime(const DownloadItem& item) {
50 return (item.GetStartTime() - base::Time::UnixEpoch()).InMilliseconds();
51 }
52
53 static bool GetDangerAccepted(const DownloadItem& item) {
54 return (item.GetSafetyState() == DownloadItem::DANGEROUS_BUT_VALIDATED);
55 }
56
57 static string16 GetFilename(const DownloadItem& item) {
58 // This filename will be compared with strings that could be passed in by the
59 // user, who only sees LossyDisplayNames.
60 return item.GetFullPath().LossyDisplayName();
61 }
62
63 static std::string GetFilenameUTF8(const DownloadItem& item) {
64 return UTF16ToUTF8(GetFilename(item));
65 }
66
67 static std::string GetUrl(const DownloadItem& item) {
68 return item.GetOriginalUrl().spec();
69 }
70
71 static DownloadItem::DownloadState GetState(const DownloadItem& item) {
72 return item.GetState();
73 }
74
75 static DownloadStateInfo::DangerType GetDangerType(const DownloadItem& item) {
76 return item.GetDangerType();
77 }
78
79 static int GetReceivedBytes(const DownloadItem& item) {
80 return item.GetReceivedBytes();
81 }
82
83 static int GetTotalBytes(const DownloadItem& item) {
84 return item.GetTotalBytes();
85 }
86
87 static std::string GetMimeType(const DownloadItem& item) {
88 return item.GetMimeType();
89 }
90
91 static bool IsPaused(const DownloadItem& item) {
92 return item.IsPaused();
93 }
94
95 // Wrap Callback to work around a bug in base::Bind/Callback where the inner
96 // callback is nullified when the outer callback is Run.
97 template<typename ValueType>
98 class InnerCallback {
99 public:
100 typedef base::Callback<ValueType(const DownloadItem&)> CallbackType;
101
102 explicit InnerCallback(const CallbackType& inner) : inner_(inner) {}
103 ~InnerCallback() {}
104
105 // Mimic Callback's interface to facilitate removing InnerCallback when the
106 // bug is fixed.
107 ValueType Run(const DownloadItem& item) const { return inner_.Run(item); }
108
109 private:
110 CallbackType inner_;
111 };
112
113 enum ComparisonType {LT, EQ, GT};
114
115 // Returns true if |item| matches the filter specified by |value|, |cmptype|,
116 // and |accessor|. |accessor| is conceptually a function that takes a
117 // DownloadItem and returns one of its fields, which is then compared to
118 // |value|.
119 template<typename ValueType>
120 static bool FieldMatches(
121 const ValueType& value,
122 ComparisonType cmptype,
123 const InnerCallback<ValueType>& accessor,
124 const DownloadItem& item) {
125 switch (cmptype) {
126 case LT: return accessor.Run(item) < value;
127 case EQ: return accessor.Run(item) == value;
128 case GT: return accessor.Run(item) > value;
129 }
130 NOTREACHED();
131 return false;
132 }
133
134 // Helper for building a Callback to FieldMatches<>().
135 template <typename ValueType> DownloadQuery::FilterCallback BuildFilter(
136 const base::Value& value, ComparisonType cmptype,
137 ValueType (*accessor)(const DownloadItem&)) {
138 ValueType cpp_value;
139 if (!GetAs(value, &cpp_value)) return DownloadQuery::FilterCallback();
140 return base::Bind(&FieldMatches<ValueType>, cpp_value, cmptype,
141 InnerCallback<ValueType>(base::Bind(accessor)));
142 }
143
144 // Returns true if |accessor.Run(item)| matches |pattern|.
145 static bool FindRegex(
146 icu::RegexPattern* pattern,
147 const InnerCallback<std::string>& accessor,
148 const DownloadItem& item) {
149 icu::UnicodeString input(accessor.Run(item).c_str());
150 UErrorCode status = U_ZERO_ERROR;
151 scoped_ptr<icu::RegexMatcher> matcher(pattern->matcher(input, status));
152 return matcher->find() == TRUE; // Ugh, VS complains bool != UBool.
153 }
154
155 // Helper for building a Callback to FindRegex().
156 DownloadQuery::FilterCallback BuildRegexFilter(
157 const base::Value& regex_value,
158 std::string (*accessor)(const DownloadItem&)) {
159 std::string regex_str;
160 if (!GetAs(regex_value, &regex_str)) return DownloadQuery::FilterCallback();
161 UParseError re_err;
162 UErrorCode re_status = U_ZERO_ERROR;
163 scoped_ptr<icu::RegexPattern> pattern(icu::RegexPattern::compile(
164 icu::UnicodeString::fromUTF8(regex_str), re_err, re_status));
165 if (!U_SUCCESS(re_status)) return DownloadQuery::FilterCallback();
166 return base::Bind(&FindRegex, base::Owned(pattern.release()),
167 InnerCallback<std::string>(base::Bind(accessor)));
168 }
169
170 // Returns a ComparisonType to indicate whether a field in |left| is less than,
171 // greater than or equal to the same field in |right|.
172 template<typename ValueType>
173 static ComparisonType Compare(
174 const InnerCallback<ValueType>& accessor,
175 const DownloadItem& left, const DownloadItem& right) {
176 ValueType left_value = accessor.Run(left);
177 ValueType right_value = accessor.Run(right);
178 if (left_value > right_value) return GT;
179 if (left_value < right_value) return LT;
180 DCHECK_EQ(left_value, right_value);
181 return EQ;
182 }
183
184 } // anonymous namespace
185
186 DownloadQuery::DownloadQuery()
187 : limit_(kuint32max) {
188 }
189
190 DownloadQuery::~DownloadQuery() {
191 }
192
193 // AddFilter() pushes a new FilterCallback to filters_. Most FilterCallbacks are
194 // Callbacks to FieldMatches<>(). Search() iterates over given DownloadItems,
195 // discarding items for which any filter returns false. A DownloadQuery may have
196 // zero or more FilterCallbacks.
197
198 bool DownloadQuery::AddFilter(const DownloadQuery::FilterCallback& value) {
199 if (value.is_null()) return false;
200 filters_.push_back(value);
201 return true;
202 }
203
204 void DownloadQuery::AddFilter(DownloadItem::DownloadState state) {
205 AddFilter(base::Bind(&FieldMatches<DownloadItem::DownloadState>, state, EQ,
206 InnerCallback<DownloadItem::DownloadState>(base::Bind(&GetState))));
207 }
208
209 void DownloadQuery::AddFilter(DownloadStateInfo::DangerType danger) {
210 AddFilter(base::Bind(&FieldMatches<DownloadStateInfo::DangerType>, danger, EQ,
211 InnerCallback<DownloadStateInfo::DangerType>(base::Bind(
212 &GetDangerType))));
213 }
214
215 bool DownloadQuery::AddFilter(DownloadQuery::FilterType type,
216 const base::Value& value) {
217 switch (type) {
218 case FILTER_BYTES_RECEIVED:
219 return AddFilter(BuildFilter<int>(value, EQ, &GetReceivedBytes));
220 case FILTER_DANGER_ACCEPTED:
221 return AddFilter(BuildFilter<bool>(value, EQ, &GetDangerAccepted));
222 case FILTER_FILENAME:
223 return AddFilter(BuildFilter<string16>(value, EQ, &GetFilename));
224 case FILTER_FILENAME_REGEX:
225 return AddFilter(BuildRegexFilter(value, &GetFilenameUTF8));
226 case FILTER_MIME:
227 return AddFilter(BuildFilter<std::string>(value, EQ, &GetMimeType));
228 case FILTER_PAUSED:
229 return AddFilter(BuildFilter<bool>(value, EQ, &IsPaused));
230 case FILTER_QUERY: {
231 string16 query;
232 return GetAs(value, &query) &&
233 AddFilter(base::Bind(&MatchesQuery, query));
234 }
235 case FILTER_STARTED_AFTER:
236 return AddFilter(BuildFilter<int>(value, GT, &GetStartTime));
237 case FILTER_STARTED_BEFORE:
238 return AddFilter(BuildFilter<int>(value, LT, &GetStartTime));
239 case FILTER_START_TIME:
240 return AddFilter(BuildFilter<int>(value, EQ, &GetStartTime));
241 case FILTER_TOTAL_BYTES:
242 return AddFilter(BuildFilter<int>(value, EQ, &GetTotalBytes));
243 case FILTER_TOTAL_BYTES_GREATER:
244 return AddFilter(BuildFilter<int>(value, GT, &GetTotalBytes));
245 case FILTER_TOTAL_BYTES_LESS:
246 return AddFilter(BuildFilter<int>(value, LT, &GetTotalBytes));
247 case FILTER_URL:
248 return AddFilter(BuildFilter<std::string>(value, EQ, &GetUrl));
249 case FILTER_URL_REGEX:
250 return AddFilter(BuildRegexFilter(value, &GetUrl));
251 }
252 return false;
253 }
254
255 bool DownloadQuery::Matches(const DownloadItem& item) const {
256 for (FilterCallbackVector::const_iterator filter = filters_.begin();
257 filter != filters_.end(); ++filter) {
258 if (!filter->Run(item))
259 return false;
260 }
261 return true;
262 }
263
264 // AddSorter() creates a Sorter and pushes it onto sorters_. A Sorter is a
265 // direction and a Callback to Compare<>(). After filtering, Search() makes a
266 // DownloadComparator functor from the sorters_ and passes the
267 // DownloadComparator to std::partial_sort. std::partial_sort calls the
268 // DownloadComparator with different pairs of DownloadItems. DownloadComparator
269 // iterates over the sorters until a callback returns ComparisonType LT or GT.
270 // DownloadComparator returns true or false depending on that ComparisonType and
271 // the sorter's direction in order to indicate to std::partial_sort whether the
272 // left item is after or before the right item. If all sorters return EQ, then
273 // DownloadComparator compares GetId. A DownloadQuery may have zero or more
274 // Sorters, but there is one DownloadComparator per call to Search().
275
276 struct DownloadQuery::Sorter {
277 typedef base::Callback<ComparisonType(
278 const DownloadItem&, const DownloadItem&)> SortType;
279
280 template<typename ValueType>
281 static Sorter Build(DownloadQuery::SortDirection adirection,
282 ValueType (*accessor)(const DownloadItem&)) {
283 return Sorter(adirection, base::Bind(&Compare<ValueType>,
284 InnerCallback<ValueType>(base::Bind(accessor))));
285 }
286
287 Sorter(DownloadQuery::SortDirection adirection,
288 const SortType& asorter)
289 : direction(adirection),
290 sorter(asorter) {
291 }
292 ~Sorter() {}
293
294 DownloadQuery::SortDirection direction;
295 SortType sorter;
296 };
297
298 class DownloadQuery::DownloadComparator {
299 public:
300 DownloadComparator(const DownloadQuery::SorterVector& terms)
301 : terms_(terms) {
302 }
303
304 // Returns true if |left| sorts before |right|.
305 bool operator() (const DownloadItem* left, const DownloadItem* right);
306
307 private:
308 const DownloadQuery::SorterVector& terms_;
309
310 // std::sort requires this class to be copyable.
311 };
312
313 bool DownloadQuery::DownloadComparator::operator() (
314 const DownloadItem* left, const DownloadItem* right) {
315 for (DownloadQuery::SorterVector::const_iterator term = terms_.begin();
316 term != terms_.end(); ++term) {
317 switch (term->sorter.Run(*left, *right)) {
318 case LT: return term->direction == DownloadQuery::ASCENDING;
319 case GT: return term->direction == DownloadQuery::DESCENDING;
320 case EQ: break; // break the switch but not the loop
321 }
322 }
323 CHECK_NE(left->GetId(), right->GetId());
324 return left->GetId() < right->GetId();
325 }
326
327 void DownloadQuery::AddSorter(DownloadQuery::SortType type,
328 DownloadQuery::SortDirection direction) {
329 switch (type) {
330 case SORT_START_TIME:
331 sorters_.push_back(Sorter::Build<int>(direction, &GetStartTime));
332 break;
333 case SORT_URL:
334 sorters_.push_back(Sorter::Build<std::string>(direction, &GetUrl));
335 break;
336 case SORT_FILENAME:
337 sorters_.push_back(Sorter::Build<string16>(direction, &GetFilename));
338 break;
339 case SORT_DANGER:
340 sorters_.push_back(Sorter::Build<DownloadStateInfo::DangerType>(
341 direction, &GetDangerType));
342 break;
343 case SORT_DANGER_ACCEPTED:
344 sorters_.push_back(Sorter::Build<bool>(direction, &GetDangerAccepted));
345 break;
346 case SORT_STATE:
347 sorters_.push_back(Sorter::Build<DownloadItem::DownloadState>(
348 direction, &GetState));
349 break;
350 case SORT_PAUSED:
351 sorters_.push_back(Sorter::Build<bool>(direction, &IsPaused));
352 break;
353 case SORT_MIME:
354 sorters_.push_back(Sorter::Build<std::string>(direction, &GetMimeType));
355 break;
356 case SORT_BYTES_RECEIVED:
357 sorters_.push_back(Sorter::Build<int>(direction, &GetReceivedBytes));
358 break;
359 case SORT_TOTAL_BYTES:
360 sorters_.push_back(Sorter::Build<int>(direction, &GetTotalBytes));
361 break;
362 }
363 }
364
365 void DownloadQuery::FinishSearch(DownloadQuery::DownloadVector* results) const {
366 if (!sorters_.empty())
367 std::partial_sort(results->begin(),
368 results->begin() + std::min(limit_, results->size()),
369 results->end(),
370 DownloadComparator(sorters_));
371 if (results->size() > limit_)
372 results->resize(limit_);
373 }
OLDNEW
« no previous file with comments | « content/browser/download/download_query.h ('k') | content/browser/download/download_query_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698