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..89bb19ef9ab54c9231c953b89ba4429e8fe50654 |
| --- /dev/null |
| +++ b/content/browser/download/download_query.cc |
| @@ -0,0 +1,358 @@ |
| +// 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 <algorithm> |
| +#include <set> |
| +#include <string> |
| +#include <vector> |
| + |
| +#include "base/logging.h" |
| +#include "base/memory/scoped_ptr.h" |
| +#include "base/stl_util.h" |
| +#include "base/string16.h" |
| +#include "base/string_split.h" |
| +#include "base/time.h" |
| +#include "base/utf_string_conversions.h" |
| +#include "content/browser/download/download_item.h" |
| +#include "googleurl/src/gurl.h" |
| +#include "unicode/regex.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 Valid() = 0; |
| + virtual bool Matches(const DownloadItem& item) const = 0; |
| +}; |
| + |
| +// Sort fields are used by DownloadComparator to sort DownloadItems. |
| +class SortFieldInterface { |
| + public: |
| + virtual ~SortFieldInterface() {} |
| + |
| + virtual DownloadQuery::SortFieldDirection direction() 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 { |
| + |
| +typedef std::vector<SortFieldInterface*> SortFieldVector; |
| + |
| +// Functor passed to std::sort to sort DownloadItems. |
| +class DownloadComparator { |
| + public: |
| + DownloadComparator(const SortFieldVector& terms) |
| + : terms_(terms) { |
| + } |
| + |
| + // Returns true if |left| sorts before |right|. |
| + bool operator() (const DownloadItem* left, const DownloadItem* right); |
| + |
| + private: |
| + const SortFieldVector& terms_; |
| + |
| + // std::sort requires this class to be copyable. |
| +}; |
| + |
| +bool DownloadComparator::operator() (const DownloadItem* left, |
| + const DownloadItem* right) { |
| + for (SortFieldVector::const_iterator term = terms_.begin(); |
| + term != terms_.end(); ++term) { |
| + int comparison = (*term)->Compare(*left, *right); |
| + if (comparison != 0) |
| + return ((*term)->direction() == DownloadQuery::ASCENDING) == |
| + (comparison < 0); |
| + } |
| + if (left->GetId() != right->GetId()) |
| + return left->GetId() < right->GetId(); |
| + CHECK_NE(left->GetDbHandle(), right->GetDbHandle()); |
| + return left->GetDbHandle() < right->GetDbHandle(); |
| +} |
| + |
| +static int get_start_time(const DownloadItem& item) { |
| + return (item.GetStartTime() - 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.GetSafetyState() == DownloadItem::DANGEROUS_BUT_VALIDATED); |
| +} |
| + |
| +static string16 get_filename(const DownloadItem& item) { |
| + return item.GetFullPath().LossyDisplayName(); |
|
Randy Smith (Not in Mondays)
2011/11/23 21:07:34
A comment here as to why we're using the LossyDisp
benjhayden
2011/11/30 16:21:47
Done.
|
| +} |
| + |
| +template <typename ValueType_> |
|
Randy Smith (Not in Mondays)
2011/11/23 21:07:34
nit: I think it would be useful to have comments i
benjhayden
2011/11/30 16:21:47
Done.
|
| +class FilterField : public DownloadQuery::FilterFieldInterface { |
| + public: |
| + typedef ValueType_ ValueType; |
|
Randy Smith (Not in Mondays)
2011/11/23 21:07:34
Just for my education, what's the value in type tr
benjhayden
2011/11/30 16:21:47
Short: Done.
Long:
If any of FilterField's methods
|
| + explicit FilterField(const ValueType& value) : value_(value) {} |
| + virtual ~FilterField() {} |
| + virtual bool Valid() OVERRIDE { return true; } |
| + |
| + protected: |
| + const ValueType& value() const { return value_; } |
| + |
| + private: |
| + ValueType value_; |
| +}; |
| + |
| +template <typename StringType> |
| +std::string ToUTF8(const StringType& s); |
| + |
| +template<> std::string ToUTF8(const std::string& s) { |
| + return s; |
| +} |
| + |
| +template<> std::string ToUTF8(const string16& s) { |
| + return UTF16ToUTF8(s); |
| +} |
| + |
| +template <typename StringType> |
| +class FilterFieldRegex : public FilterField<StringType> { |
| + public: |
| + explicit FilterFieldRegex(const StringType& value) |
| + : FilterField<StringType>(value) { |
| + } |
| + virtual ~FilterFieldRegex() {} |
| + using FilterField<StringType>::value; |
| + virtual bool Valid() OVERRIDE { |
| + UParseError re_err; |
| + UErrorCode re_status = U_ZERO_ERROR; |
| + scoped_ptr<icu::RegexPattern> pattern(icu::RegexPattern::compile( |
| + icu::UnicodeString::fromUTF8(ToUTF8(value())), |
| + re_err, re_status)); |
| + return U_SUCCESS(re_status); |
| + } |
| + |
| + protected: |
| + bool RegexMatches(const StringType& text) const { |
| + icu::UnicodeString pattern(ToUTF8(value()).c_str()); |
| + icu::UnicodeString input(ToUTF8(text).c_str()); |
| + UErrorCode status = U_ZERO_ERROR; |
| + icu::RegexMatcher match(pattern, input, 0, status); |
| + return match.find(); |
| + } |
| +}; |
| + |
| +template <typename ValueType> |
| +class SortField : public SortFieldInterface { |
| + public: |
| + explicit SortField(DownloadQuery::SortFieldDirection direction) |
| + : direction_(direction) { |
| + } |
| + virtual ~SortField() {} |
| + |
| + virtual DownloadQuery::SortFieldDirection direction() const { |
| + return direction_; |
| + } |
| + |
| + virtual ValueType GetField(const DownloadItem& item) const = 0; |
| + |
| + virtual int Compare(const DownloadItem& left, |
| + const DownloadItem& right) const OVERRIDE { |
| + 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::SortFieldDirection direction_; |
| +}; |
| + |
| +#define LAMBDA(__name, __base, __method, __expr, __ctor_arg_type, __ctor_arg) ({ \ |
|
Randy Smith (Not in Mondays)
2011/11/23 21:07:34
nit: This line looks long--I suspect > 80 columns?
Randy Smith (Not in Mondays)
2011/11/23 21:07:34
nit/suggestion (up to you): I personally prefer li
Randy Smith (Not in Mondays)
2011/11/23 21:07:34
A comment describing the context for this macro (w
benjhayden
2011/11/30 16:21:47
Done.
benjhayden
2011/11/30 16:21:47
Done.
benjhayden
2011/11/30 16:21:47
Done.
|
| + class __name : public __base { \ |
| + public: \ |
| + explicit __name(__ctor_arg_type x) : __base(x) {} \ |
| + virtual __method(const DownloadItem& item) const OVERRIDE { \ |
| + return __expr; \ |
| + } \ |
| + }; \ |
| + new __name(__ctor_arg); \ |
| + }) |
| + |
| +SortFieldInterface* GetSortField(DownloadQuery::SortFieldName name, |
| + DownloadQuery::SortFieldDirection direction) { |
| + switch (name) { |
| +#define SORT_FIELD(__name, __type, __expr) \ |
| + case DownloadQuery::SORT_FIELD_ ## __name: return LAMBDA( \ |
| + SortField ## __name, SortField<__type>, __type GetField, __expr, \ |
| + DownloadQuery::SortFieldDirection, direction) |
| + SORT_FIELD(START_TIME, int, get_start_time(item)); |
| + SORT_FIELD(URL, std::string, item.GetOriginalUrl().spec()); |
| + SORT_FIELD(FILENAME, string16, get_filename(item)); |
| + SORT_FIELD(DANGER, DownloadStateInfo::DangerType, item.GetDangerType()); |
| + SORT_FIELD(DANGER_ACCEPTED, bool, get_danger_accepted(item)); |
| + SORT_FIELD(STATE, DownloadItem::DownloadState, item.GetState()); |
| + SORT_FIELD(PAUSED, bool, item.IsPaused()); |
| + SORT_FIELD(MIME, std::string, item.GetMimeType()); |
| + SORT_FIELD(END_TIME, int, get_end_time(item)); |
| + SORT_FIELD(BYTES_RECEIVED, int, item.GetReceivedBytes()); |
| + SORT_FIELD(TOTAL_BYTES, int, item.GetTotalBytes()); |
| + SORT_FIELD(FILE_SIZE, int, 0); // TODO |
| + SORT_FIELD(ERROR, int, 0); // TODO |
| +#undef SORT_FIELD |
| + default: return NULL; |
| + } |
| +} |
| + |
| +} // anonymous namespace |
| + |
| +DownloadQuery::DownloadQuery() |
| + : limit_(kuint32max) { |
| +} |
| + |
| +DownloadQuery::~DownloadQuery() { |
| + STLDeleteElements(&filter_fields_); |
| +} |
| + |
| +bool DownloadQuery::AddFilter(FilterFieldInterface* field) { |
| + if (!field->Valid()) { |
| + delete field; |
| + return false; |
| + } |
| + filter_fields_.push_back(field); |
| + return true; |
| +} |
| + |
| +#define START_FILTER_FIELDS(__type) \ |
| + template<> bool DownloadQuery::Filter(DownloadQuery::FilterFieldName name, \ |
| + const __type& _value) { \ |
| + typedef __type ValueType; \ |
| + switch (static_cast<int>(name)) { |
| +#define FILTER_FIELD(__name, __expr) \ |
| + case FILTER_FIELD_ ## __name: return AddFilter( \ |
| + LAMBDA(FilterField ## __name, FilterField<ValueType>, bool Matches, \ |
| + __expr, const ValueType&, _value)); |
| +#define FILTER_FIELD_REGEX(__name, __expr) \ |
| + case FILTER_FIELD_ ## __name ## _REGEX: return AddFilter(LAMBDA( \ |
| + FilterField ## __name ## _REGEX, FilterFieldRegex<ValueType>, \ |
| + bool Matches, RegexMatches(__expr), const ValueType&, _value)); |
| +#define END_FILTER_FIELDS \ |
| + } \ |
| + return false; \ |
| + } |
| + |
| +START_FILTER_FIELDS(int) |
| + FILTER_FIELD(START_TIME, get_start_time(item) == value()) |
| + FILTER_FIELD(END_TIME, get_end_time(item) == value()) |
| + FILTER_FIELD(BYTES_RECEIVED, item.GetReceivedBytes() == value()) |
| + FILTER_FIELD(TOTAL_BYTES, item.GetTotalBytes() == value()) |
| + FILTER_FIELD(STARTED_BEFORE, get_start_time(item) < value()) |
| + FILTER_FIELD(STARTED_AFTER, get_start_time(item) > value()) |
| + FILTER_FIELD(ENDED_BEFORE, get_end_time(item) < value()) |
| + FILTER_FIELD(ENDED_AFTER, get_end_time(item) > value()) |
| + FILTER_FIELD(TOTAL_BYTES_GREATER, item.GetTotalBytes() > value()) |
| + FILTER_FIELD(TOTAL_BYTES_LESS, item.GetTotalBytes() < value()) |
| + FILTER_FIELD(FILE_SIZE_GREATER, false) // TODO |
| + FILTER_FIELD(FILE_SIZE_LESS, false) // TODO |
| + FILTER_FIELD(FILE_SIZE, false) // TODO |
| + FILTER_FIELD(ERROR, false) // TODO |
| +END_FILTER_FIELDS |
| + |
| +START_FILTER_FIELDS(bool) |
| + FILTER_FIELD(DANGER_ACCEPTED, get_danger_accepted(item) == value()) |
| + FILTER_FIELD(PAUSED, item.IsPaused() == value()) |
| +END_FILTER_FIELDS |
| + |
| +START_FILTER_FIELDS(std::string) |
| + FILTER_FIELD(URL, item.GetOriginalUrl().spec() == value()) |
| + FILTER_FIELD_REGEX(URL, item.GetOriginalUrl().spec()) |
| + FILTER_FIELD(MIME, item.GetMimeType() == value()) |
| +END_FILTER_FIELDS |
| + |
| +START_FILTER_FIELDS(string16) |
| + FILTER_FIELD(QUERY, item.MatchesQuery(value())) |
| + FILTER_FIELD(FILENAME, get_filename(item) == value()) |
| + FILTER_FIELD_REGEX(FILENAME, get_filename(item)) |
| +END_FILTER_FIELDS |
| + |
| +START_FILTER_FIELDS(base::Callback<bool(const DownloadItem& item)>) |
| + FILTER_FIELD(FILTER, value().Run(item)) |
| +END_FILTER_FIELDS |
| + |
| +START_FILTER_FIELDS(DownloadStateInfo::DangerType) |
| + FILTER_FIELD(DANGER, item.GetDangerType() == value()) |
| +END_FILTER_FIELDS |
| + |
| +START_FILTER_FIELDS(DownloadItem::DownloadState) |
| + FILTER_FIELD(STATE, item.GetState() == value()) |
| +END_FILTER_FIELDS |
| + |
| +#undef START_FILTER_FIELDS |
| +#undef FILTER_FIELD |
| +#undef END_FILTER_FIELDS |
| + |
| +void DownloadQuery::Sort(DownloadQuery::SortFieldName name, |
| + DownloadQuery::SortFieldDirection direction) { |
| + order_by_fields_.push_back(OrderTerm(name, direction)); |
| +} |
| + |
| +bool DownloadQuery::Matches(const DownloadItem& item) const { |
| + for (FilterFieldVector::const_iterator filter_field = filter_fields_.begin(); |
| + filter_field != filter_fields_.end(); ++filter_field) { |
| + if (!(*filter_field)->Matches(item)) |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +template <typename InputIterator> |
| +void DownloadQuery::Search(InputIterator iter, const InputIterator last, |
| + DownloadQuery::DownloadVector* results) const { |
| + results->clear(); |
| + for (; iter != last; ++iter) { |
| + if (Matches(**iter)) |
| + results->push_back(*iter); |
| + } |
| + if (!order_by_fields_.empty()) { |
| + SortFieldVector order_terms; |
| + for (OrderTermVector::const_iterator iter = order_by_fields_.begin(); |
| + iter != order_by_fields_.end(); ++iter) { |
| + SortFieldInterface* sort_field = GetSortField( |
| + iter->name, iter->direction); |
|
Randy Smith (Not in Mondays)
2011/11/23 21:07:34
Any reason not to do this as the Sort calls are be
benjhayden
2011/11/30 16:21:47
I meant to do that and forgot. It required moving
|
| + if (sort_field) |
| + order_terms.push_back(sort_field); |
| + } |
| + std::partial_sort(results->begin(), |
| + results->begin() + std::min(limit_, results->size()), |
| + results->end(), |
| + DownloadComparator(order_terms)); |
| + STLDeleteElements(&order_terms); |
| + } |
| + if (results->size() > limit_) |
| + results->resize(limit_); |
| +} |
| + |
| +template void DownloadQuery::Search( |
| + std::set<DownloadItem*>::const_iterator iter, |
| + const std::set<DownloadItem*>::const_iterator last, |
| + DownloadQuery::DownloadVector* results) const; |
| +template void DownloadQuery::Search( |
| + std::vector<DownloadItem*>::const_iterator iter, |
| + const std::vector<DownloadItem*>::const_iterator last, |
| + DownloadQuery::DownloadVector* results) const; |
| +template void DownloadQuery::Search( |
| + std::vector<DownloadItem*>::iterator iter, |
| + const std::vector<DownloadItem*>::iterator last, |
| + DownloadQuery::DownloadVector* results) const; |
| + |
| +} // namespace download_util |
| + |