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

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

Issue 7825035: Implement chrome.experimental.downloads.search() (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: comments Created 9 years, 2 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) 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 <string>
8 #include <vector>
9
10 #include "base/memory/scoped_ptr.h"
11 #include "base/stl_util.h"
12 #include "base/string16.h"
13 #include "base/string_split.h"
14 #include "base/utf_string_conversions.h"
15 #include "unicode/regex.h"
16 #include "content/browser/download/download_item.h"
17
18 namespace download_util {
19
20 // The FieldInterfaces allow us to keep the templateyness out of the header.
21
22 // Filter fields can be used to filter DownloadItems out of the result set.
23 class DownloadQuery::FilterFieldInterface {
24 public:
25 virtual ~FilterFieldInterface() {}
26 virtual bool is_set() const = 0;
27 virtual const std::string& name() const = 0;
28 virtual bool Matches(const DownloadItem& item) const = 0;
29 };
30
31 // Sort fields are used by DownloadComparator to sort DownloadItems.
32 class DownloadQuery::SortFieldInterface {
33 public:
34 virtual ~SortFieldInterface() {}
35 virtual const std::string& name() const = 0;
36 // Return 0 when left's field is equivalent to right's field, -1 when left >
37 // right, or 1 when left < right.
Randy Smith (Not in Mondays) 2011/10/13 23:23:11 That seems backward from the usual custom (e.g. qs
benjhayden 2011/10/14 19:31:02 Ah, I meant to check the convention. Done.
38 virtual int Compare(const DownloadItem& left,
39 const DownloadItem& right) const = 0;
40 };
41
42 namespace {
43
44 // Functor passed to std::sort to sort DownloadItems.
45 class DownloadComparator {
46 public:
47 static const char kInvert = '-';
48
49 // If |order_term| begins with kInvert, return |order_term| after stripping it
50 // off and set |inverted| = true. Otherwise set |inverted| = false and return
51 // |order_term| as-is.
52 static std::string Revert(const std::string& order_term, bool* inverted) {
53 bool default_inverted = false;
54 if (inverted == NULL)
55 inverted = &default_inverted;
56 *inverted = (order_term[0] == kInvert);
57 return *inverted ? order_term.substr(1) : order_term;
58 }
59
60 DownloadComparator(const DownloadQuery::Strings& order_terms,
61 const DownloadQuery::SortFields& sort_fields)
62 : order_terms_(order_terms),
63 sort_fields_(sort_fields) {
64 }
65
66 bool operator() (const DownloadItem* left, const DownloadItem* right);
67
68 private:
69 const DownloadQuery::Strings& order_terms_;
70 const DownloadQuery::SortFields& sort_fields_;
71
72 // std::sort requires this class to be copyable.
73 };
74
75 bool DownloadComparator::operator() (const DownloadItem* left,
76 const DownloadItem* right) {
77 for (DownloadQuery::Strings::const_iterator order_term = order_terms_.begin();
78 order_term != order_terms_.end(); ++order_term) {
79 bool inverted = false;
80 std::string order_term_value = Revert(*order_term, &inverted);
81 DownloadQuery::SortFields::const_iterator sort_field_iter =
82 sort_fields_.find(order_term_value);
Randy Smith (Not in Mondays) 2011/10/18 23:58:46 I wince when I see this. Performance isn't vital
83 CHECK(sort_field_iter != sort_fields_.end()) << order_term_value;
84 const DownloadQuery::SortFieldInterface* sort_field =
85 sort_field_iter->second;
86 CHECK(sort_field);
87 CHECK_EQ(sort_field->name(), order_term_value);
88 int comparison = sort_field->Compare(*left, *right);
89 if (comparison != 0)
90 return inverted == (comparison < 0);
91 }
92 if (left->id() != right->id())
93 return left->id() < right->id();
94 CHECK_NE(left->db_handle(), right->db_handle());
95 return left->db_handle() < right->db_handle();
96 }
97
98 static int get_start_time(const DownloadItem& item) {
99 return (item.start_time() - base::Time::UnixEpoch()).InMilliseconds();
100 }
101
102 static int get_end_time(const DownloadItem& item) {
103 return 0; // TODO(benjhayden): endTime
104 }
105
106 static bool get_danger_accepted(const DownloadItem& item) {
107 return (item.safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED);
108 }
109
110 static string16 get_filename(const DownloadItem& item) {
111 return item.full_path().LossyDisplayName();
112 }
113
114 template <typename ValueType>
115 class FilterField : public DownloadQuery::FilterFieldInterface {
116 public:
117 explicit FilterField(const char* name)
118 : name_(name),
119 is_set_(false) {
120 }
121 virtual ~FilterField() {}
122
123 virtual const std::string& name() const { return name_; }
124
125 virtual bool Set(const ValueType& value) {
126 value_ = value;
127 is_set_ = true;
128 return true;
129 }
130
131 virtual bool is_set() const { return is_set_; }
132
133 protected:
134 const ValueType& value() const { return value_; }
135
136 private:
137 std::string name_;
138 ValueType value_;
139 bool is_set_;
140 };
141
142 template <typename ValueType>
143 class SortField : public DownloadQuery::SortFieldInterface {
144 public:
145 explicit SortField(const char* name) : name_(name) {}
Randy Smith (Not in Mondays) 2011/10/18 23:58:46 What's the purpose for having the name field on th
146 virtual ~SortField() {}
147
148 virtual const std::string& name() const { return name_; }
149
150 virtual ValueType GetField(const DownloadItem& item) const = 0;
151
152 virtual int Compare(const DownloadItem& left,
153 const DownloadItem& right) const {
154 ValueType left_value = GetField(left), right_value = GetField(right);
155 return (left_value < right_value) ? 1 : (right_value < left_value) ? -1 : 0;
156 }
157
158 private:
159 std::string name_;
160 };
161
162 class FilterFieldQuery : public FilterField<string16> {
163 public:
164 FilterFieldQuery() : FilterField<string16>("query") {}
165 virtual ~FilterFieldQuery() {}
166 virtual bool Set(const string16& value) {
167 base::SplitString(value, L' ', &terms_);
Randy Smith (Not in Mondays) 2011/10/18 23:58:46 This feels like more smarts than I'd expect at thi
168 // TODO(benjhayden): Split query more intelligently. Quopri?
169 return FilterField<string16>::Set(value);
170 }
171
172 virtual bool Matches(const DownloadItem& item) const {
173 for (std::vector<string16>::const_iterator term = terms_.begin();
174 term != terms_.end(); ++term) {
175 if (!item.MatchesQuery(*term))
176 return false;
177 }
178 return true;
179 }
180
181 private:
182 std::vector<string16> terms_;
183 };
184
185 template <typename StringType>
186 class FilterFieldRegex : public FilterField<StringType> {
187 public:
188 explicit FilterFieldRegex(const char* name) : FilterField<StringType>(name) {}
189 virtual ~FilterFieldRegex() {}
190 virtual bool Set(const StringType& value) {
191 UParseError re_err;
192 UErrorCode re_status = U_ZERO_ERROR;
193 pattern_.reset(icu::RegexPattern::compile(
194 icu::UnicodeString::fromUTF8(ToUTF8(value)),
195 re_err, re_status));
196 return U_SUCCESS(re_status) &&
197 FilterField<StringType>::Set(value);
198 }
199
200 protected:
201 bool RegexMatches(const StringType& text) const {
202 UErrorCode status = U_ZERO_ERROR;
203 scoped_ptr<icu::RegexMatcher> matcher(pattern_->matcher(
204 icu::UnicodeString::fromUTF8(ToUTF8(text)), status));
205 return U_SUCCESS(status) &&
206 matcher->find(0, status);
207 }
208
209 virtual std::string ToUTF8(const StringType& s) const = 0;
210
211 private:
212 scoped_ptr<icu::RegexPattern> pattern_;
213 };
214
215 class FilterFieldUrlRegex : public FilterFieldRegex<std::string> {
216 public:
217 FilterFieldUrlRegex() : FilterFieldRegex<std::string>("urlRegex") {}
218 virtual ~FilterFieldUrlRegex() {}
219 virtual bool Matches(const DownloadItem& item) const {
220 return RegexMatches(item.original_url().spec());
221 }
222
223 protected:
224 virtual std::string ToUTF8(const std::string& s) const { return s; }
225 };
226
227 class FilterFieldFilenameRegex : public FilterFieldRegex<string16> {
228 public:
229 FilterFieldFilenameRegex() : FilterFieldRegex<string16>("filenameRegex") {}
230 virtual ~FilterFieldFilenameRegex() {}
231 virtual bool Matches(const DownloadItem& item) const {
232 return RegexMatches(get_filename(item));
233 }
234
235 protected:
236 virtual std::string ToUTF8(const string16& s) const { return UTF16ToUTF8(s); }
237 };
238
239 } // anonymous namespace
240
241 void DownloadQuery::SetSortField(
242 const char* name, DownloadQuery::SortFieldInterface* field) {
243 DCHECK(sort_fields_.find(name) == sort_fields_.end());
244 sort_fields_[name] = field;
245 }
246
247 void DownloadQuery::SetFilterField(
248 const char* name, DownloadQuery::FilterFieldInterface* field) {
249 DCHECK(filter_fields_.find(name) == filter_fields_.end());
250 filter_fields_[name] = field;
251 }
252
253 #define SORT_FIELD(name, type, expr) \
Randy Smith (Not in Mondays) 2011/10/18 23:58:46 It took me a couple of read throughs through these
254 SetSortField(#name, ({\
255 class SortField ## name : public SortField<type> { \
256 public: \
257 SortField ## name() : SortField<type>(#name) {} \
258 virtual type GetField(const DownloadItem& item) const { return expr; } \
259 }; \
260 new SortField ## name(); \
261 })); \
262 FILTER_FIELD(name, type, (expr) == value())
263 #define FILTER_FIELD(name, type, expr) \
264 SetFilterField(#name, ({ \
265 class FilterField ## name : public FilterField<type> { \
266 public: \
267 FilterField ## name() : FilterField<type>(#name) {} \
268 virtual bool Matches(const DownloadItem& item) const { return expr; } \
269 }; \
270 new FilterField ## name(); \
271 }))
272
273 DownloadQuery::DownloadQuery()
274 : limit_(kuint32max),
275 delete_sort_fields_(&sort_fields_),
276 delete_filter_fields_(&filter_fields_) {
277 // All sort fields are also filter fields, but not all filter fields are sort
278 // fields.
279 // If you define a new sort or filter field, make sure that the type is
280 // enumerated in INSTANTIATE_SET below.
281 SORT_FIELD(startTime, int, get_start_time(item));
282 SORT_FIELD(url, std::string, item.original_url().spec());
283 SORT_FIELD(filename, string16, get_filename(item));
284 SORT_FIELD(danger, DownloadItem::DangerType, item.GetDangerType());
285 SORT_FIELD(dangerAccepted, bool, get_danger_accepted(item));
286 SORT_FIELD(state, DownloadItem::DownloadState, item.state());
287 SORT_FIELD(paused, bool, item.is_paused());
288 SORT_FIELD(mime, std::string, item.mime_type());
289 SORT_FIELD(endTime, int, get_end_time(item));
290 SORT_FIELD(bytesReceived, int, item.received_bytes());
291 SORT_FIELD(totalBytes, int, item.total_bytes());
292 SORT_FIELD(fileSize, int, 0); // TODO
293 SORT_FIELD(error, int, 0); // TODO
294
295 FILTER_FIELD(startedBefore, int, get_start_time(item) < value());
296 FILTER_FIELD(startedAfter, int, get_start_time(item) > value());
297 FILTER_FIELD(endedBefore, int, get_end_time(item) < value());
298 FILTER_FIELD(endedAfter, int, get_end_time(item) > value());
299 FILTER_FIELD(totalBytesGreater, int, item.total_bytes() > value());
300 FILTER_FIELD(totalBytesLess, int, item.total_bytes() < value());
301 FILTER_FIELD(fileSizeGreater, int, false); // TODO
302 FILTER_FIELD(fileSizeLess, int, false); // TODO
303 FILTER_FIELD(filter, base::Callback<bool(const DownloadItem& item)>,
304 value().Run(item));
305
306 filter_fields_["query"] = new FilterFieldQuery();
307 filter_fields_["filenameRegex"] = new FilterFieldFilenameRegex();
308 filter_fields_["urlRegex"] = new FilterFieldUrlRegex();
309 }
310
311 #undef FILTER_FIELD
312 #undef SORT_FIELD
313
314 DownloadQuery::~DownloadQuery() {
315 }
316
317 template <typename ValueType>
318 bool DownloadQuery::Set(const char* name, const ValueType& value) {
319 FilterFields::iterator iter = filter_fields_.find(name);
320 return (iter != filter_fields_.end()) &&
321 (iter->second != NULL) &&
322 (iter->second->name() == name) &&
323 static_cast<FilterField<ValueType>*>(iter->second)->Set(value);
324 }
325
326 #define INSTANTIATE_SET(type) \
327 template bool DownloadQuery::Set(const char* name, const type& value)
328 INSTANTIATE_SET(int);
329 INSTANTIATE_SET(bool);
330 INSTANTIATE_SET(std::string);
331 INSTANTIATE_SET(string16);
332 INSTANTIATE_SET(base::Callback<bool(const DownloadItem& item)>);
333 INSTANTIATE_SET(DownloadItem::DownloadState);
334 INSTANTIATE_SET(DownloadItem::DangerType);
335 #undef INSTANTIATE_SET
336
337 bool DownloadQuery::Matches(const DownloadItem& item) const {
338 for (FilterFields::const_iterator filter_field = filter_fields_.begin();
339 filter_field != filter_fields_.end(); ++filter_field) {
340 if (filter_field->second->is_set() && !filter_field->second->Matches(item))
341 return false;
342 }
343 return true;
344 }
345
346 bool DownloadQuery::OrderBy(const std::string& order_by) {
347 order_by_fields_.clear();
348 base::SplitString(order_by, ' ', &order_by_fields_);
349 for (Strings::const_iterator field = order_by_fields_.begin();
350 field != order_by_fields_.end(); ++field) {
351 if (sort_fields_.find(DownloadComparator::Revert(*field, NULL)) ==
352 sort_fields_.end()) {
353 order_by_fields_.clear();
354 return false;
355 }
356 }
357 return true;
358 }
359
360 void DownloadQuery::Search(const DownloadQuery::DownloadItems& all_items,
361 DownloadQuery::DownloadItems* results) const {
362 results->clear();
363 for (DownloadItems::const_iterator item = all_items.begin();
364 item != all_items.end(); ++item) {
365 if (Matches(**item))
366 results->push_back(*item);
367 }
368 if (!order_by_fields_.empty())
369 std::sort(results->begin(), results->end(),
370 DownloadComparator(order_by_fields_, sort_fields_));
Randy Smith (Not in Mondays) 2011/10/18 23:58:46 partial sort, so we don't take quite as big a hit
371 if (results->size() > limit_)
372 results->resize(limit_);
373 }
374
375 } // namespace download_util
376
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698