Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 | |
| OLD | NEW |