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

Unified 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: merge 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 side-by-side diff with in-line comments
Download patch
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
+

Powered by Google App Engine
This is Rietveld 408576698