Chromium Code Reviews| Index: content/browser/download/download_query.cc |
| diff --git a/content/browser/download/download_query.cc b/content/browser/download/download_query.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c7e4e29566982d834abdb33d7029d6dde73fcb38 |
| --- /dev/null |
| +++ b/content/browser/download/download_query.cc |
| @@ -0,0 +1,372 @@ |
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "content/browser/download/download_query.h" |
| + |
| +#include <set> |
| +#include <string> |
| +#include <vector> |
| + |
| +#include "base/memory/scoped_ptr.h" |
| +#include "base/stl_util.h" |
| +#include "base/string16.h" |
| +#include "base/string_split.h" |
| +#include "base/utf_string_conversions.h" |
| +#include "unicode/regex.h" |
| +#include "content/browser/download/download_item.h" |
| + |
| +namespace download_util { |
| + |
| +// The FieldInterfaces allow us to keep the templateyness out of the header. |
| + |
| +// Filter fields can be used to filter DownloadItems out of the result set. |
| +class DownloadQuery::FilterFieldInterface { |
| + public: |
| + virtual ~FilterFieldInterface() {} |
| + virtual DownloadQuery::FilterFieldName name() const = 0; |
| + virtual bool is_set() const = 0; |
| + virtual bool Matches(const DownloadItem& item) const = 0; |
| +}; |
| + |
| +// Sort fields are used by DownloadComparator to sort DownloadItems. |
| +class DownloadQuery::SortFieldInterface { |
| + public: |
| + virtual ~SortFieldInterface() {} |
| + virtual DownloadQuery::SortFieldName name() const = 0; |
| + |
| + // Return 0 when left's field is equivalent to right's field, -1 when left < |
| + // right, or 1 when left > right. |
| + virtual int Compare(const DownloadItem& left, |
| + const DownloadItem& right) const = 0; |
| +}; |
| + |
| +namespace { |
| + |
| +// Functor passed to std::sort to sort DownloadItems. |
| +class DownloadComparator { |
| + public: |
| + DownloadComparator(const DownloadQuery::OrderTerms& order_terms, |
| + const DownloadQuery::SortFields& sort_fields) |
| + : order_terms_(order_terms), |
| + sort_fields_(sort_fields) { |
| + } |
| + |
| + // Returns true if |left| sorts before |right|. |
| + bool operator() (const DownloadItem* left, const DownloadItem* right); |
| + |
| + private: |
| + const DownloadQuery::OrderTerms& order_terms_; |
| + const DownloadQuery::SortFields& sort_fields_; |
| + |
| + // std::sort requires this class to be copyable. |
| +}; |
| + |
| +bool DownloadComparator::operator() (const DownloadItem* left, |
| + const DownloadItem* right) { |
| + for (DownloadQuery::OrderTerms::const_iterator order_term = |
| + order_terms_.begin(); order_term != order_terms_.end(); ++order_term) { |
| + DownloadQuery::SortFields::const_iterator sort_field_iter = |
| + sort_fields_.find(order_term->name); |
| + CHECK(sort_field_iter != sort_fields_.end()) << order_term->name; |
| + const DownloadQuery::SortFieldInterface* sort_field = |
| + sort_field_iter->second; |
| + CHECK(sort_field); |
| + CHECK_EQ(sort_field->name(), order_term->name); |
| + int comparison = sort_field->Compare(*left, *right); |
| + if (comparison != 0) |
| + return order_term->ascending == (comparison < 0); |
| + } |
| + if (left->id() != right->id()) |
| + return left->id() < right->id(); |
| + CHECK_NE(left->db_handle(), right->db_handle()); |
| + return left->db_handle() < right->db_handle(); |
| +} |
| + |
| +static int get_start_time(const DownloadItem& item) { |
|
asanka
2011/10/20 19:29:16
Nit: These aren't trivial accessors.
|
| + return (item.start_time() - base::Time::UnixEpoch()).InMilliseconds(); |
| +} |
| + |
| +static int get_end_time(const DownloadItem& item) { |
| + return 0; // TODO(benjhayden): endTime |
| +} |
| + |
| +static bool get_danger_accepted(const DownloadItem& item) { |
| + return (item.safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED); |
| +} |
| + |
| +static string16 get_filename(const DownloadItem& item) { |
| + return item.full_path().LossyDisplayName(); |
| +} |
| + |
| +template <typename ValueType> |
| +class FilterField : public DownloadQuery::FilterFieldInterface { |
| + public: |
| + explicit FilterField(DownloadQuery::FilterFieldName name) |
| + : name_(name), |
| + is_set_(false) { |
| + } |
| + virtual ~FilterField() {} |
| + |
| + virtual DownloadQuery::FilterFieldName name() const { return name_; } |
| + |
| + virtual bool Set(const ValueType& value) { |
| + value_ = value; |
| + is_set_ = true; |
| + return true; |
| + } |
| + |
| + virtual bool is_set() const { return is_set_; } |
| + |
| + protected: |
| + const ValueType& value() const { return value_; } |
| + |
| + private: |
| + DownloadQuery::FilterFieldName name_; |
| + ValueType value_; |
| + bool is_set_; |
| +}; |
| + |
| +template <typename ValueType> |
| +class SortField : public DownloadQuery::SortFieldInterface { |
| + public: |
| + explicit SortField(DownloadQuery::SortFieldName name) |
| + : name_(name) { |
| + } |
| + virtual ~SortField() {} |
| + |
| + virtual DownloadQuery::SortFieldName name() const { return name_; } |
| + |
| + virtual ValueType GetField(const DownloadItem& item) const = 0; |
| + |
| + virtual int Compare(const DownloadItem& left, |
| + const DownloadItem& right) const { |
| + ValueType left_value = GetField(left), right_value = GetField(right); |
| + if (left_value > right_value) return 1; |
| + if (left_value < right_value) return -1; |
| + if (left_value == right_value) return 0; |
| + NOTREACHED(); |
| + return 0; |
| + } |
| + |
| + private: |
| + DownloadQuery::SortFieldName name_; |
| +}; |
| + |
| +class FilterFieldQuery : public FilterField<string16> { |
| + public: |
| + FilterFieldQuery() |
| + : FilterField<string16>(DownloadQuery::FILTER_FIELD_QUERY) { |
| + } |
| + virtual ~FilterFieldQuery() {} |
| + virtual bool Set(const string16& value) { |
| + base::SplitString(value, L' ', &terms_); |
| + // TODO(benjhayden): Split query more intelligently. Quopri? |
| + return FilterField<string16>::Set(value); |
| + } |
| + |
| + virtual bool Matches(const DownloadItem& item) const { |
| + for (std::vector<string16>::const_iterator term = terms_.begin(); |
| + term != terms_.end(); ++term) { |
| + if (!item.MatchesQuery(*term)) |
| + return false; |
| + } |
| + return true; |
| + } |
| + |
| + private: |
| + std::vector<string16> terms_; |
| +}; |
| + |
| +template <typename StringType> |
| +class FilterFieldRegex : public FilterField<StringType> { |
| + public: |
| + explicit FilterFieldRegex(DownloadQuery::FilterFieldName name) |
| + : FilterField<StringType>(name) { |
| + } |
| + virtual ~FilterFieldRegex() {} |
| + virtual bool Set(const StringType& value) { |
| + UParseError re_err; |
| + UErrorCode re_status = U_ZERO_ERROR; |
| + pattern_.reset(icu::RegexPattern::compile( |
| + icu::UnicodeString::fromUTF8(ToUTF8(value)), |
| + re_err, re_status)); |
| + return U_SUCCESS(re_status) && |
| + FilterField<StringType>::Set(value); |
| + } |
| + |
| + protected: |
| + bool RegexMatches(const StringType& text) const { |
| + UErrorCode status = U_ZERO_ERROR; |
| + scoped_ptr<icu::RegexMatcher> matcher(pattern_->matcher( |
| + icu::UnicodeString::fromUTF8(ToUTF8(text)), status)); |
| + return U_SUCCESS(status) && |
| + matcher->find(0, status); |
| + } |
| + |
| + virtual std::string ToUTF8(const StringType& s) const = 0; |
| + |
| + private: |
| + scoped_ptr<icu::RegexPattern> pattern_; |
| +}; |
| + |
| +class FilterFieldUrlRegex : public FilterFieldRegex<std::string> { |
| + public: |
| + FilterFieldUrlRegex() |
| + : FilterFieldRegex<std::string>(DownloadQuery::FILTER_FIELD_URL_REGEX) { |
| + } |
| + virtual ~FilterFieldUrlRegex() {} |
| + virtual bool Matches(const DownloadItem& item) const { |
| + return RegexMatches(item.original_url().spec()); |
| + } |
| + |
| + protected: |
| + virtual std::string ToUTF8(const std::string& s) const { return s; } |
| +}; |
| + |
| +class FilterFieldFilenameRegex : public FilterFieldRegex<string16> { |
| + public: |
| + FilterFieldFilenameRegex() |
| + : FilterFieldRegex<string16>(DownloadQuery::FILTER_FIELD_FILENAME_REGEX) { |
| + } |
| + virtual ~FilterFieldFilenameRegex() {} |
| + virtual bool Matches(const DownloadItem& item) const { |
| + return RegexMatches(get_filename(item)); |
| + } |
| + |
| + protected: |
| + virtual std::string ToUTF8(const string16& s) const { return UTF16ToUTF8(s); } |
| +}; |
| + |
| +} // anonymous namespace |
| + |
| +void DownloadQuery::SetSortField(DownloadQuery::SortFieldName name, |
| + DownloadQuery::SortFieldInterface* field) { |
| + DCHECK(sort_fields_.find(name) == sort_fields_.end()); |
| + sort_fields_[name] = field; |
| +} |
| + |
| +void DownloadQuery::SetFilterField(DownloadQuery::FilterFieldName name, |
| + DownloadQuery::FilterFieldInterface* field) { |
| + DCHECK(filter_fields_.find(name) == filter_fields_.end()); |
| + filter_fields_[name] = field; |
| +} |
| + |
| +#define DEFINE_FIELD(name_enum, class_name, field_type, value_type, \ |
| + method_sig, expr) \ |
| + Set ## field_type ## Field(name_enum, ({ \ |
| + class class_name : public field_type ## Field<value_type> { \ |
| + public: \ |
| + class_name() : field_type ## Field<value_type>(name_enum) {} \ |
| + virtual method_sig(const DownloadItem& item) const { return expr; } \ |
| + }; \ |
| + new class_name(); \ |
| + })) |
| +#define SORT_FIELD(name, type, expr) \ |
| + DEFINE_FIELD(SORT_FIELD_ ## name, SortField ## name, Sort, type, \ |
| + type GetField, expr); \ |
| + DEFINE_FIELD(FILTER_FIELD_ ## name, FilterField ## name, Filter, type, \ |
| + bool Matches, (expr) == value()) |
| +#define FILTER_FIELD(name, type, expr) \ |
| + DEFINE_FIELD(FILTER_FIELD_ ## name, FilterField ## name, Filter, type, \ |
| + bool Matches, expr) |
| + |
| +DownloadQuery::DownloadQuery() |
| + : limit_(kuint32max), |
| + delete_sort_fields_(&sort_fields_), |
| + delete_filter_fields_(&filter_fields_) { |
| + // All sort fields are also filter fields, but not all filter fields are sort |
| + // fields. |
| + // If you define a new sort or filter field, make sure that the type is |
| + // enumerated in INSTANTIATE_SET below. |
|
asanka
2011/10/20 19:29:16
Have you considered lazy initialization for these,
|
| + SORT_FIELD(START_TIME, int, get_start_time(item)); |
| + SORT_FIELD(URL, std::string, item.original_url().spec()); |
| + SORT_FIELD(FILENAME, string16, get_filename(item)); |
| + SORT_FIELD(DANGER, DownloadItem::DangerType, item.GetDangerType()); |
| + SORT_FIELD(DANGER_ACCEPTED, bool, get_danger_accepted(item)); |
| + SORT_FIELD(STATE, DownloadItem::DownloadState, item.state()); |
| + SORT_FIELD(PAUSED, bool, item.is_paused()); |
| + SORT_FIELD(MIME, std::string, item.mime_type()); |
| + SORT_FIELD(END_TIME, int, get_end_time(item)); |
| + SORT_FIELD(BYTES_RECEIVED, int, item.received_bytes()); |
| + SORT_FIELD(TOTAL_BYTES, int, item.total_bytes()); |
| + SORT_FIELD(FILE_SIZE, int, 0); // TODO |
| + SORT_FIELD(ERROR, int, 0); // TODO |
| + |
| + FILTER_FIELD(STARTED_BEFORE, int, get_start_time(item) < value()); |
| + FILTER_FIELD(STARTED_AFTER, int, get_start_time(item) > value()); |
| + FILTER_FIELD(ENDED_BEFORE, int, get_end_time(item) < value()); |
| + FILTER_FIELD(ENDED_AFTER, int, get_end_time(item) > value()); |
| + FILTER_FIELD(TOTAL_BYTES_GREATER, int, item.total_bytes() > value()); |
| + FILTER_FIELD(TOTAL_BYTES_LESS, int, item.total_bytes() < value()); |
| + FILTER_FIELD(FILE_SIZE_GREATER, int, false); // TODO |
| + FILTER_FIELD(FILE_SIZE_LESS, int, false); // TODO |
| + FILTER_FIELD(FILTER, base::Callback<bool(const DownloadItem& item)>, |
| + value().Run(item)); |
| + |
| + SetFilterField(FILTER_FIELD_QUERY, new FilterFieldQuery()); |
| + SetFilterField(FILTER_FIELD_FILENAME_REGEX, new FilterFieldFilenameRegex()); |
| + SetFilterField(FILTER_FIELD_URL_REGEX, new FilterFieldUrlRegex()); |
| +} |
| + |
| +#undef FILTER_FIELD |
| +#undef SORT_FIELD |
| + |
| +DownloadQuery::~DownloadQuery() { |
| +} |
| + |
| +template <typename ValueType> |
| +bool DownloadQuery::Set(DownloadQuery::FilterFieldName name, |
| + const ValueType& value) { |
| + FilterFields::iterator iter = filter_fields_.find(name); |
| + return (iter != filter_fields_.end()) && |
| + (iter->second != NULL) && |
| + (iter->second->name() == name) && |
| + static_cast<FilterField<ValueType>*>(iter->second)->Set(value); |
|
asanka
2011/10/20 19:29:16
Would this do the correct thing for something like
|
| +} |
| + |
| +#define INSTANTIATE_SET(type) \ |
|
cbentzel
2011/10/20 18:18:53
Dumb question: Why do you need to do the instantia
|
| + template bool DownloadQuery::Set( \ |
| + DownloadQuery::FilterFieldName name, const type& value) |
| +INSTANTIATE_SET(int); |
| +INSTANTIATE_SET(bool); |
| +INSTANTIATE_SET(std::string); |
| +INSTANTIATE_SET(string16); |
| +INSTANTIATE_SET(base::Callback<bool(const DownloadItem& item)>); |
| +INSTANTIATE_SET(DownloadItem::DownloadState); |
| +INSTANTIATE_SET(DownloadItem::DangerType); |
| +#undef INSTANTIATE_SET |
| + |
| +bool DownloadQuery::Matches(const DownloadItem& item) const { |
| + for (FilterFields::const_iterator filter_field = filter_fields_.begin(); |
| + filter_field != filter_fields_.end(); ++filter_field) { |
| + if (filter_field->second->is_set() && !filter_field->second->Matches(item)) |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +template <typename InputIterator> |
| +void DownloadQuery::Search(InputIterator iter, const InputIterator last, |
| + DownloadQuery::DownloadItems* results) const { |
| + results->clear(); |
| + for (; iter != last; ++iter) { |
| + if (Matches(**iter)) |
| + results->push_back(*iter); |
| + } |
| + if (!order_by_fields_.empty()) |
| + std::sort(results->begin(), results->end(), |
| + DownloadComparator(order_by_fields_, sort_fields_)); |
| + if (results->size() > limit_) |
| + results->resize(limit_); |
| +} |
| + |
| +#define INSTANTIATE_SEARCH(type) \ |
| + template void DownloadQuery::Search(type iter, const type last, \ |
| + DownloadQuery::DownloadItems* results) const |
| +INSTANTIATE_SEARCH(std::set<DownloadItem*>::const_iterator); |
| +INSTANTIATE_SEARCH(std::vector<DownloadItem*>::const_iterator); |
| +#undef INSTANTIATE_SEARCH |
| + |
| +} // namespace download_util |
| + |