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

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: comments 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..bf6904fa051bbcc9c0ec24abc121c6d4471c3e3c
--- /dev/null
+++ b/content/browser/download/download_query.cc
@@ -0,0 +1,376 @@
+// 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 <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 bool is_set() const = 0;
+ virtual const std::string& name() 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 const std::string& name() const = 0;
+ // Return 0 when left's field is equivalent to right's field, -1 when left >
+ // 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.
+ virtual int Compare(const DownloadItem& left,
+ const DownloadItem& right) const = 0;
+};
+
+namespace {
+
+// Functor passed to std::sort to sort DownloadItems.
+class DownloadComparator {
+ public:
+ static const char kInvert = '-';
+
+ // If |order_term| begins with kInvert, return |order_term| after stripping it
+ // off and set |inverted| = true. Otherwise set |inverted| = false and return
+ // |order_term| as-is.
+ static std::string Revert(const std::string& order_term, bool* inverted) {
+ bool default_inverted = false;
+ if (inverted == NULL)
+ inverted = &default_inverted;
+ *inverted = (order_term[0] == kInvert);
+ return *inverted ? order_term.substr(1) : order_term;
+ }
+
+ DownloadComparator(const DownloadQuery::Strings& order_terms,
+ const DownloadQuery::SortFields& sort_fields)
+ : order_terms_(order_terms),
+ sort_fields_(sort_fields) {
+ }
+
+ bool operator() (const DownloadItem* left, const DownloadItem* right);
+
+ private:
+ const DownloadQuery::Strings& 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::Strings::const_iterator order_term = order_terms_.begin();
+ order_term != order_terms_.end(); ++order_term) {
+ bool inverted = false;
+ std::string order_term_value = Revert(*order_term, &inverted);
+ DownloadQuery::SortFields::const_iterator sort_field_iter =
+ 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
+ CHECK(sort_field_iter != sort_fields_.end()) << order_term_value;
+ const DownloadQuery::SortFieldInterface* sort_field =
+ sort_field_iter->second;
+ CHECK(sort_field);
+ CHECK_EQ(sort_field->name(), order_term_value);
+ int comparison = sort_field->Compare(*left, *right);
+ if (comparison != 0)
+ return inverted == (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) {
+ 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(const char* name)
+ : name_(name),
+ is_set_(false) {
+ }
+ virtual ~FilterField() {}
+
+ virtual const std::string& 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:
+ std::string name_;
+ ValueType value_;
+ bool is_set_;
+};
+
+template <typename ValueType>
+class SortField : public DownloadQuery::SortFieldInterface {
+ public:
+ 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
+ virtual ~SortField() {}
+
+ virtual const std::string& 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);
+ return (left_value < right_value) ? 1 : (right_value < left_value) ? -1 : 0;
+ }
+
+ private:
+ std::string name_;
+};
+
+class FilterFieldQuery : public FilterField<string16> {
+ public:
+ FilterFieldQuery() : FilterField<string16>("query") {}
+ virtual ~FilterFieldQuery() {}
+ virtual bool Set(const string16& value) {
+ 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
+ // 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(const char* 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>("urlRegex") {}
+ 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>("filenameRegex") {}
+ 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(
+ const char* name, DownloadQuery::SortFieldInterface* field) {
+ DCHECK(sort_fields_.find(name) == sort_fields_.end());
+ sort_fields_[name] = field;
+}
+
+void DownloadQuery::SetFilterField(
+ const char* name, DownloadQuery::FilterFieldInterface* field) {
+ DCHECK(filter_fields_.find(name) == filter_fields_.end());
+ filter_fields_[name] = field;
+}
+
+#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
+ SetSortField(#name, ({\
+ class SortField ## name : public SortField<type> { \
+ public: \
+ SortField ## name() : SortField<type>(#name) {} \
+ virtual type GetField(const DownloadItem& item) const { return expr; } \
+ }; \
+ new SortField ## name(); \
+ })); \
+ FILTER_FIELD(name, type, (expr) == value())
+#define FILTER_FIELD(name, type, expr) \
+ SetFilterField(#name, ({ \
+ class FilterField ## name : public FilterField<type> { \
+ public: \
+ FilterField ## name() : FilterField<type>(#name) {} \
+ virtual bool Matches(const DownloadItem& item) const { return expr; } \
+ }; \
+ new FilterField ## name(); \
+ }))
+
+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.
+ SORT_FIELD(startTime, 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(dangerAccepted, 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(endTime, int, get_end_time(item));
+ SORT_FIELD(bytesReceived, int, item.received_bytes());
+ SORT_FIELD(totalBytes, int, item.total_bytes());
+ SORT_FIELD(fileSize, int, 0); // TODO
+ SORT_FIELD(error, int, 0); // TODO
+
+ FILTER_FIELD(startedBefore, int, get_start_time(item) < value());
+ FILTER_FIELD(startedAfter, int, get_start_time(item) > value());
+ FILTER_FIELD(endedBefore, int, get_end_time(item) < value());
+ FILTER_FIELD(endedAfter, int, get_end_time(item) > value());
+ FILTER_FIELD(totalBytesGreater, int, item.total_bytes() > value());
+ FILTER_FIELD(totalBytesLess, int, item.total_bytes() < value());
+ FILTER_FIELD(fileSizeGreater, int, false); // TODO
+ FILTER_FIELD(fileSizeLess, int, false); // TODO
+ FILTER_FIELD(filter, base::Callback<bool(const DownloadItem& item)>,
+ value().Run(item));
+
+ filter_fields_["query"] = new FilterFieldQuery();
+ filter_fields_["filenameRegex"] = new FilterFieldFilenameRegex();
+ filter_fields_["urlRegex"] = new FilterFieldUrlRegex();
+}
+
+#undef FILTER_FIELD
+#undef SORT_FIELD
+
+DownloadQuery::~DownloadQuery() {
+}
+
+template <typename ValueType>
+bool DownloadQuery::Set(const char* 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);
+}
+
+#define INSTANTIATE_SET(type) \
+ template bool DownloadQuery::Set(const char* 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;
+}
+
+bool DownloadQuery::OrderBy(const std::string& order_by) {
+ order_by_fields_.clear();
+ base::SplitString(order_by, ' ', &order_by_fields_);
+ for (Strings::const_iterator field = order_by_fields_.begin();
+ field != order_by_fields_.end(); ++field) {
+ if (sort_fields_.find(DownloadComparator::Revert(*field, NULL)) ==
+ sort_fields_.end()) {
+ order_by_fields_.clear();
+ return false;
+ }
+ }
+ return true;
+}
+
+void DownloadQuery::Search(const DownloadQuery::DownloadItems& all_items,
+ DownloadQuery::DownloadItems* results) const {
+ results->clear();
+ for (DownloadItems::const_iterator item = all_items.begin();
+ item != all_items.end(); ++item) {
+ if (Matches(**item))
+ results->push_back(*item);
+ }
+ if (!order_by_fields_.empty())
+ std::sort(results->begin(), results->end(),
+ 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
+ if (results->size() > limit_)
+ results->resize(limit_);
+}
+
+} // namespace download_util
+

Powered by Google App Engine
This is Rietveld 408576698