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 <set> | |
| 8 #include <string> | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "base/memory/scoped_ptr.h" | |
| 12 #include "base/stl_util.h" | |
| 13 #include "base/string16.h" | |
| 14 #include "base/string_split.h" | |
| 15 #include "base/utf_string_conversions.h" | |
| 16 #include "unicode/regex.h" | |
| 17 #include "content/browser/download/download_item.h" | |
| 18 | |
| 19 namespace download_util { | |
| 20 | |
| 21 // The FieldInterfaces allow us to keep the templateyness out of the header. | |
| 22 | |
| 23 // Filter fields can be used to filter DownloadItems out of the result set. | |
| 24 class DownloadQuery::FilterFieldInterface { | |
| 25 public: | |
| 26 virtual ~FilterFieldInterface() {} | |
| 27 virtual DownloadQuery::FilterFieldName name() const = 0; | |
| 28 virtual bool is_set() const = 0; | |
| 29 virtual bool Matches(const DownloadItem& item) const = 0; | |
| 30 }; | |
| 31 | |
| 32 // Sort fields are used by DownloadComparator to sort DownloadItems. | |
| 33 class DownloadQuery::SortFieldInterface { | |
| 34 public: | |
| 35 virtual ~SortFieldInterface() {} | |
| 36 virtual DownloadQuery::SortFieldName name() const = 0; | |
| 37 | |
| 38 // Return 0 when left's field is equivalent to right's field, -1 when left < | |
| 39 // right, or 1 when left > right. | |
| 40 virtual int Compare(const DownloadItem& left, | |
| 41 const DownloadItem& right) const = 0; | |
| 42 }; | |
| 43 | |
| 44 namespace { | |
| 45 | |
| 46 // Functor passed to std::sort to sort DownloadItems. | |
| 47 class DownloadComparator { | |
| 48 public: | |
| 49 DownloadComparator(const DownloadQuery::OrderTerms& order_terms, | |
| 50 const DownloadQuery::SortFields& sort_fields) | |
| 51 : order_terms_(order_terms), | |
| 52 sort_fields_(sort_fields) { | |
| 53 } | |
| 54 | |
| 55 // Returns true if |left| sorts before |right|. | |
| 56 bool operator() (const DownloadItem* left, const DownloadItem* right); | |
| 57 | |
| 58 private: | |
| 59 const DownloadQuery::OrderTerms& order_terms_; | |
| 60 const DownloadQuery::SortFields& sort_fields_; | |
| 61 | |
| 62 // std::sort requires this class to be copyable. | |
| 63 }; | |
| 64 | |
| 65 bool DownloadComparator::operator() (const DownloadItem* left, | |
| 66 const DownloadItem* right) { | |
| 67 for (DownloadQuery::OrderTerms::const_iterator order_term = | |
| 68 order_terms_.begin(); order_term != order_terms_.end(); ++order_term) { | |
| 69 DownloadQuery::SortFields::const_iterator sort_field_iter = | |
| 70 sort_fields_.find(order_term->name); | |
| 71 CHECK(sort_field_iter != sort_fields_.end()) << order_term->name; | |
| 72 const DownloadQuery::SortFieldInterface* sort_field = | |
| 73 sort_field_iter->second; | |
| 74 CHECK(sort_field); | |
| 75 CHECK_EQ(sort_field->name(), order_term->name); | |
| 76 int comparison = sort_field->Compare(*left, *right); | |
| 77 if (comparison != 0) | |
| 78 return order_term->ascending == (comparison < 0); | |
| 79 } | |
| 80 if (left->id() != right->id()) | |
| 81 return left->id() < right->id(); | |
| 82 CHECK_NE(left->db_handle(), right->db_handle()); | |
| 83 return left->db_handle() < right->db_handle(); | |
| 84 } | |
| 85 | |
| 86 static int get_start_time(const DownloadItem& item) { | |
|
asanka
2011/10/20 19:29:16
Nit: These aren't trivial accessors.
| |
| 87 return (item.start_time() - base::Time::UnixEpoch()).InMilliseconds(); | |
| 88 } | |
| 89 | |
| 90 static int get_end_time(const DownloadItem& item) { | |
| 91 return 0; // TODO(benjhayden): endTime | |
| 92 } | |
| 93 | |
| 94 static bool get_danger_accepted(const DownloadItem& item) { | |
| 95 return (item.safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED); | |
| 96 } | |
| 97 | |
| 98 static string16 get_filename(const DownloadItem& item) { | |
| 99 return item.full_path().LossyDisplayName(); | |
| 100 } | |
| 101 | |
| 102 template <typename ValueType> | |
| 103 class FilterField : public DownloadQuery::FilterFieldInterface { | |
| 104 public: | |
| 105 explicit FilterField(DownloadQuery::FilterFieldName name) | |
| 106 : name_(name), | |
| 107 is_set_(false) { | |
| 108 } | |
| 109 virtual ~FilterField() {} | |
| 110 | |
| 111 virtual DownloadQuery::FilterFieldName name() const { return name_; } | |
| 112 | |
| 113 virtual bool Set(const ValueType& value) { | |
| 114 value_ = value; | |
| 115 is_set_ = true; | |
| 116 return true; | |
| 117 } | |
| 118 | |
| 119 virtual bool is_set() const { return is_set_; } | |
| 120 | |
| 121 protected: | |
| 122 const ValueType& value() const { return value_; } | |
| 123 | |
| 124 private: | |
| 125 DownloadQuery::FilterFieldName name_; | |
| 126 ValueType value_; | |
| 127 bool is_set_; | |
| 128 }; | |
| 129 | |
| 130 template <typename ValueType> | |
| 131 class SortField : public DownloadQuery::SortFieldInterface { | |
| 132 public: | |
| 133 explicit SortField(DownloadQuery::SortFieldName name) | |
| 134 : name_(name) { | |
| 135 } | |
| 136 virtual ~SortField() {} | |
| 137 | |
| 138 virtual DownloadQuery::SortFieldName name() const { return name_; } | |
| 139 | |
| 140 virtual ValueType GetField(const DownloadItem& item) const = 0; | |
| 141 | |
| 142 virtual int Compare(const DownloadItem& left, | |
| 143 const DownloadItem& right) const { | |
| 144 ValueType left_value = GetField(left), right_value = GetField(right); | |
| 145 if (left_value > right_value) return 1; | |
| 146 if (left_value < right_value) return -1; | |
| 147 if (left_value == right_value) return 0; | |
| 148 NOTREACHED(); | |
| 149 return 0; | |
| 150 } | |
| 151 | |
| 152 private: | |
| 153 DownloadQuery::SortFieldName name_; | |
| 154 }; | |
| 155 | |
| 156 class FilterFieldQuery : public FilterField<string16> { | |
| 157 public: | |
| 158 FilterFieldQuery() | |
| 159 : FilterField<string16>(DownloadQuery::FILTER_FIELD_QUERY) { | |
| 160 } | |
| 161 virtual ~FilterFieldQuery() {} | |
| 162 virtual bool Set(const string16& value) { | |
| 163 base::SplitString(value, L' ', &terms_); | |
| 164 // TODO(benjhayden): Split query more intelligently. Quopri? | |
| 165 return FilterField<string16>::Set(value); | |
| 166 } | |
| 167 | |
| 168 virtual bool Matches(const DownloadItem& item) const { | |
| 169 for (std::vector<string16>::const_iterator term = terms_.begin(); | |
| 170 term != terms_.end(); ++term) { | |
| 171 if (!item.MatchesQuery(*term)) | |
| 172 return false; | |
| 173 } | |
| 174 return true; | |
| 175 } | |
| 176 | |
| 177 private: | |
| 178 std::vector<string16> terms_; | |
| 179 }; | |
| 180 | |
| 181 template <typename StringType> | |
| 182 class FilterFieldRegex : public FilterField<StringType> { | |
| 183 public: | |
| 184 explicit FilterFieldRegex(DownloadQuery::FilterFieldName name) | |
| 185 : FilterField<StringType>(name) { | |
| 186 } | |
| 187 virtual ~FilterFieldRegex() {} | |
| 188 virtual bool Set(const StringType& value) { | |
| 189 UParseError re_err; | |
| 190 UErrorCode re_status = U_ZERO_ERROR; | |
| 191 pattern_.reset(icu::RegexPattern::compile( | |
| 192 icu::UnicodeString::fromUTF8(ToUTF8(value)), | |
| 193 re_err, re_status)); | |
| 194 return U_SUCCESS(re_status) && | |
| 195 FilterField<StringType>::Set(value); | |
| 196 } | |
| 197 | |
| 198 protected: | |
| 199 bool RegexMatches(const StringType& text) const { | |
| 200 UErrorCode status = U_ZERO_ERROR; | |
| 201 scoped_ptr<icu::RegexMatcher> matcher(pattern_->matcher( | |
| 202 icu::UnicodeString::fromUTF8(ToUTF8(text)), status)); | |
| 203 return U_SUCCESS(status) && | |
| 204 matcher->find(0, status); | |
| 205 } | |
| 206 | |
| 207 virtual std::string ToUTF8(const StringType& s) const = 0; | |
| 208 | |
| 209 private: | |
| 210 scoped_ptr<icu::RegexPattern> pattern_; | |
| 211 }; | |
| 212 | |
| 213 class FilterFieldUrlRegex : public FilterFieldRegex<std::string> { | |
| 214 public: | |
| 215 FilterFieldUrlRegex() | |
| 216 : FilterFieldRegex<std::string>(DownloadQuery::FILTER_FIELD_URL_REGEX) { | |
| 217 } | |
| 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() | |
| 230 : FilterFieldRegex<string16>(DownloadQuery::FILTER_FIELD_FILENAME_REGEX) { | |
| 231 } | |
| 232 virtual ~FilterFieldFilenameRegex() {} | |
| 233 virtual bool Matches(const DownloadItem& item) const { | |
| 234 return RegexMatches(get_filename(item)); | |
| 235 } | |
| 236 | |
| 237 protected: | |
| 238 virtual std::string ToUTF8(const string16& s) const { return UTF16ToUTF8(s); } | |
| 239 }; | |
| 240 | |
| 241 } // anonymous namespace | |
| 242 | |
| 243 void DownloadQuery::SetSortField(DownloadQuery::SortFieldName name, | |
| 244 DownloadQuery::SortFieldInterface* field) { | |
| 245 DCHECK(sort_fields_.find(name) == sort_fields_.end()); | |
| 246 sort_fields_[name] = field; | |
| 247 } | |
| 248 | |
| 249 void DownloadQuery::SetFilterField(DownloadQuery::FilterFieldName name, | |
| 250 DownloadQuery::FilterFieldInterface* field) { | |
| 251 DCHECK(filter_fields_.find(name) == filter_fields_.end()); | |
| 252 filter_fields_[name] = field; | |
| 253 } | |
| 254 | |
| 255 #define DEFINE_FIELD(name_enum, class_name, field_type, value_type, \ | |
| 256 method_sig, expr) \ | |
| 257 Set ## field_type ## Field(name_enum, ({ \ | |
| 258 class class_name : public field_type ## Field<value_type> { \ | |
| 259 public: \ | |
| 260 class_name() : field_type ## Field<value_type>(name_enum) {} \ | |
| 261 virtual method_sig(const DownloadItem& item) const { return expr; } \ | |
| 262 }; \ | |
| 263 new class_name(); \ | |
| 264 })) | |
| 265 #define SORT_FIELD(name, type, expr) \ | |
| 266 DEFINE_FIELD(SORT_FIELD_ ## name, SortField ## name, Sort, type, \ | |
| 267 type GetField, expr); \ | |
| 268 DEFINE_FIELD(FILTER_FIELD_ ## name, FilterField ## name, Filter, type, \ | |
| 269 bool Matches, (expr) == value()) | |
| 270 #define FILTER_FIELD(name, type, expr) \ | |
| 271 DEFINE_FIELD(FILTER_FIELD_ ## name, FilterField ## name, Filter, type, \ | |
| 272 bool Matches, expr) | |
| 273 | |
| 274 DownloadQuery::DownloadQuery() | |
| 275 : limit_(kuint32max), | |
| 276 delete_sort_fields_(&sort_fields_), | |
| 277 delete_filter_fields_(&filter_fields_) { | |
| 278 // All sort fields are also filter fields, but not all filter fields are sort | |
| 279 // fields. | |
| 280 // If you define a new sort or filter field, make sure that the type is | |
| 281 // enumerated in INSTANTIATE_SET below. | |
|
asanka
2011/10/20 19:29:16
Have you considered lazy initialization for these,
| |
| 282 SORT_FIELD(START_TIME, int, get_start_time(item)); | |
| 283 SORT_FIELD(URL, std::string, item.original_url().spec()); | |
| 284 SORT_FIELD(FILENAME, string16, get_filename(item)); | |
| 285 SORT_FIELD(DANGER, DownloadItem::DangerType, item.GetDangerType()); | |
| 286 SORT_FIELD(DANGER_ACCEPTED, bool, get_danger_accepted(item)); | |
| 287 SORT_FIELD(STATE, DownloadItem::DownloadState, item.state()); | |
| 288 SORT_FIELD(PAUSED, bool, item.is_paused()); | |
| 289 SORT_FIELD(MIME, std::string, item.mime_type()); | |
| 290 SORT_FIELD(END_TIME, int, get_end_time(item)); | |
| 291 SORT_FIELD(BYTES_RECEIVED, int, item.received_bytes()); | |
| 292 SORT_FIELD(TOTAL_BYTES, int, item.total_bytes()); | |
| 293 SORT_FIELD(FILE_SIZE, int, 0); // TODO | |
| 294 SORT_FIELD(ERROR, int, 0); // TODO | |
| 295 | |
| 296 FILTER_FIELD(STARTED_BEFORE, int, get_start_time(item) < value()); | |
| 297 FILTER_FIELD(STARTED_AFTER, int, get_start_time(item) > value()); | |
| 298 FILTER_FIELD(ENDED_BEFORE, int, get_end_time(item) < value()); | |
| 299 FILTER_FIELD(ENDED_AFTER, int, get_end_time(item) > value()); | |
| 300 FILTER_FIELD(TOTAL_BYTES_GREATER, int, item.total_bytes() > value()); | |
| 301 FILTER_FIELD(TOTAL_BYTES_LESS, int, item.total_bytes() < value()); | |
| 302 FILTER_FIELD(FILE_SIZE_GREATER, int, false); // TODO | |
| 303 FILTER_FIELD(FILE_SIZE_LESS, int, false); // TODO | |
| 304 FILTER_FIELD(FILTER, base::Callback<bool(const DownloadItem& item)>, | |
| 305 value().Run(item)); | |
| 306 | |
| 307 SetFilterField(FILTER_FIELD_QUERY, new FilterFieldQuery()); | |
| 308 SetFilterField(FILTER_FIELD_FILENAME_REGEX, new FilterFieldFilenameRegex()); | |
| 309 SetFilterField(FILTER_FIELD_URL_REGEX, new FilterFieldUrlRegex()); | |
| 310 } | |
| 311 | |
| 312 #undef FILTER_FIELD | |
| 313 #undef SORT_FIELD | |
| 314 | |
| 315 DownloadQuery::~DownloadQuery() { | |
| 316 } | |
| 317 | |
| 318 template <typename ValueType> | |
| 319 bool DownloadQuery::Set(DownloadQuery::FilterFieldName name, | |
| 320 const ValueType& value) { | |
| 321 FilterFields::iterator iter = filter_fields_.find(name); | |
| 322 return (iter != filter_fields_.end()) && | |
| 323 (iter->second != NULL) && | |
| 324 (iter->second->name() == name) && | |
| 325 static_cast<FilterField<ValueType>*>(iter->second)->Set(value); | |
|
asanka
2011/10/20 19:29:16
Would this do the correct thing for something like
| |
| 326 } | |
| 327 | |
| 328 #define INSTANTIATE_SET(type) \ | |
|
cbentzel
2011/10/20 18:18:53
Dumb question: Why do you need to do the instantia
| |
| 329 template bool DownloadQuery::Set( \ | |
| 330 DownloadQuery::FilterFieldName name, const type& value) | |
| 331 INSTANTIATE_SET(int); | |
| 332 INSTANTIATE_SET(bool); | |
| 333 INSTANTIATE_SET(std::string); | |
| 334 INSTANTIATE_SET(string16); | |
| 335 INSTANTIATE_SET(base::Callback<bool(const DownloadItem& item)>); | |
| 336 INSTANTIATE_SET(DownloadItem::DownloadState); | |
| 337 INSTANTIATE_SET(DownloadItem::DangerType); | |
| 338 #undef INSTANTIATE_SET | |
| 339 | |
| 340 bool DownloadQuery::Matches(const DownloadItem& item) const { | |
| 341 for (FilterFields::const_iterator filter_field = filter_fields_.begin(); | |
| 342 filter_field != filter_fields_.end(); ++filter_field) { | |
| 343 if (filter_field->second->is_set() && !filter_field->second->Matches(item)) | |
| 344 return false; | |
| 345 } | |
| 346 return true; | |
| 347 } | |
| 348 | |
| 349 template <typename InputIterator> | |
| 350 void DownloadQuery::Search(InputIterator iter, const InputIterator last, | |
| 351 DownloadQuery::DownloadItems* results) const { | |
| 352 results->clear(); | |
| 353 for (; iter != last; ++iter) { | |
| 354 if (Matches(**iter)) | |
| 355 results->push_back(*iter); | |
| 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 #define INSTANTIATE_SEARCH(type) \ | |
| 365 template void DownloadQuery::Search(type iter, const type last, \ | |
| 366 DownloadQuery::DownloadItems* results) const | |
| 367 INSTANTIATE_SEARCH(std::set<DownloadItem*>::const_iterator); | |
| 368 INSTANTIATE_SEARCH(std::vector<DownloadItem*>::const_iterator); | |
| 369 #undef INSTANTIATE_SEARCH | |
| 370 | |
| 371 } // namespace download_util | |
| 372 | |
| OLD | NEW |