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

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: Templates<>! 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.
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);
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) {}
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_);
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 #define SORT_FIELD(name, type, expr) \
cbentzel 2011/10/13 15:22:24 I'd add a helper function called SetSortField(name
benjhayden 2011/10/13 17:19:47 Done.
242 sort_fields_[#name] = ({\
243 class SortField ## name : public SortField<type> { \
244 public: \
245 SortField ## name() : SortField<type>(#name) {} \
246 virtual type GetField(const DownloadItem& item) const { return expr; } \
247 }; \
248 new SortField ## name(); \
249 }); \
250 FILTER_FIELD(name, type, (expr) == value())
251 #define FILTER_FIELD(name, type, expr) \
252 filter_fields_[#name] = ({ \
253 class FilterField ## name : public FilterField<type> { \
254 public: \
255 FilterField ## name() : FilterField<type>(#name) {} \
256 virtual bool Matches(const DownloadItem& item) const { return expr; } \
257 }; \
258 new FilterField ## name(); \
259 })
260
261 DownloadQuery::DownloadQuery()
262 : limit_(kuint32max),
263 delete_sort_fields_(&sort_fields_),
264 delete_filter_fields_(&filter_fields_) {
265 // All sort fields are also filter fields, but not all filter fields are sort
266 // fields.
267 // If you define a new sort or filter field, make sure that the type is
268 // enumerated in INSTANTIATE_SET below.
269 SORT_FIELD(startTime, int, get_start_time(item));
270 SORT_FIELD(url, std::string, item.original_url().spec());
271 SORT_FIELD(filename, string16, get_filename(item));
272 SORT_FIELD(danger, DownloadItem::DangerType, item.GetDangerType());
273 SORT_FIELD(dangerAccepted, bool, get_danger_accepted(item));
274 SORT_FIELD(state, DownloadItem::DownloadState, item.state());
275 SORT_FIELD(paused, bool, item.is_paused());
276 SORT_FIELD(mime, std::string, item.mime_type());
277 SORT_FIELD(endTime, int, get_end_time(item));
278 SORT_FIELD(bytesReceived, int, item.received_bytes());
279 SORT_FIELD(totalBytes, int, item.total_bytes());
280 SORT_FIELD(fileSize, int, 0); // TODO
281 SORT_FIELD(error, int, 0); // TODO
282
283 FILTER_FIELD(startedBefore, int, get_start_time(item) < value());
284 FILTER_FIELD(startedAfter, int, get_start_time(item) > value());
285 FILTER_FIELD(endedBefore, int, get_end_time(item) < value());
286 FILTER_FIELD(endedAfter, int, get_end_time(item) > value());
287 FILTER_FIELD(totalBytesGreater, int, item.total_bytes() > value());
288 FILTER_FIELD(totalBytesLess, int, item.total_bytes() < value());
289 FILTER_FIELD(fileSizeGreater, int, false); // TODO
290 FILTER_FIELD(fileSizeLess, int, false); // TODO
291 FILTER_FIELD(filter, base::Callback<bool(const DownloadItem& item)>,
292 value().Run(item));
293
294 filter_fields_["query"] = new FilterFieldQuery();
295 filter_fields_["filenameRegex"] = new FilterFieldFilenameRegex();
296 filter_fields_["urlRegex"] = new FilterFieldUrlRegex();
297 }
298
299 #undef FILTER_FIELD
300 #undef SORT_FIELD
301
302 DownloadQuery::~DownloadQuery() {
303 }
304
305 template <typename ValueType>
306 bool DownloadQuery::Set(const char* name, const ValueType& value) {
307 FilterFieldInterface* field_interface = filter_fields_[name];
cbentzel 2011/10/13 15:22:24 Use find here - this will add name to the map if i
benjhayden 2011/10/13 17:19:47 Done.
308 if ((field_interface == NULL) || (field_interface->name() != name))
309 return false;
310 FilterField<ValueType>* field = reinterpret_cast<FilterField<ValueType>*>(
311 field_interface);
312 return (field != NULL) && field->Set(value);
313 }
314
315 #define INSTANTIATE_SET(type) \
316 template bool DownloadQuery::Set(const char* name, const type& value)
317 INSTANTIATE_SET(int);
318 INSTANTIATE_SET(bool);
319 INSTANTIATE_SET(std::string);
320 INSTANTIATE_SET(string16);
321 INSTANTIATE_SET(base::Callback<bool(const DownloadItem& item)>);
322 INSTANTIATE_SET(DownloadItem::DownloadState);
323 INSTANTIATE_SET(DownloadItem::DangerType);
324 #undef INSTANTIATE_SET
325
326 bool DownloadQuery::Matches(const DownloadItem& item) const {
327 for (FilterFields::const_iterator filter_field = filter_fields_.begin();
328 filter_field != filter_fields_.end(); ++filter_field) {
329 if (filter_field->second->is_set() && !filter_field->second->Matches(item))
330 return false;
331 }
332 return true;
333 }
334
335 bool DownloadQuery::OrderBy(const std::string& order_by) {
336 order_by_fields_.clear();
337 base::SplitString(order_by, ' ', &order_by_fields_);
338 for (Strings::const_iterator field = order_by_fields_.begin();
339 field != order_by_fields_.end(); ++field) {
340 if (sort_fields_.find(DownloadComparator::Revert(*field, NULL)) ==
341 sort_fields_.end()) {
342 order_by_fields_.clear();
343 return false;
344 }
345 }
346 return true;
347 }
348
349 void DownloadQuery::Search(const DownloadQuery::DownloadItems& all_items,
350 DownloadQuery::DownloadItems* results) const {
351 results->clear();
352 for (DownloadItems::const_iterator item = all_items.begin();
353 item != all_items.end(); ++item) {
354 if (Matches(**item))
355 results->push_back(*item);
356 }
357 if (!order_by_fields_.empty())
358 std::sort(results->begin(), results->end(),
359 DownloadComparator(order_by_fields_, sort_fields_));
360 if (results->size() > limit_)
361 results->resize(limit_);
362 }
363
364 } // namespace download_util
365
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698