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..2c1c69c76dccb20a15e77780829b8ebcc28f3c99 |
| --- /dev/null |
| +++ b/content/browser/download/download_query.cc |
| @@ -0,0 +1,357 @@ |
| +// 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/values.h" |
| +#include "unicode/regex.h" |
| +#include "content/browser/download/download_item.h" |
| + |
| +namespace { |
| +typedef std::vector<std::string> Strings; |
| +typedef std::vector<DownloadItem*> DownloadItems; |
| + |
| +struct SimpleDownloadItem { |
| + static const char STATE_IN_PROGRESS[]; |
| + static const char STATE_COMPLETE[]; |
| + static const char STATE_INTERRUPTED[]; |
| + static const char DANGER_SAFE[]; |
| + static const char DANGER_FILE[]; |
| + static const char DANGER_URL[]; |
| + |
| + explicit SimpleDownloadItem(DownloadItem* download) |
| + : item(download) { |
| + CHECK(item); |
| + } |
| + |
| + int id() const { return item->id(); } |
| + std::string url() const { return item->original_url().spec(); } |
| + std::string filename() const { |
| + return item->GetFileNameToReportUser().value(); |
| + } |
| + std::string danger() const { return DangerString(item->GetDangerType()); } |
| + bool dangerAccepted() const { |
| + return item->safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED; |
| + } |
| + std::string state() const { return StateString(item->state()); } |
| + bool paused() const { return item->is_paused(); } |
| + std::string mime() const { return item->mime_type(); } |
| + int startTime() const { |
| + return (item->start_time() - base::Time::UnixEpoch()).InMilliseconds(); |
| + } |
| + int endTime() const { return -1; } |
| + int bytesReceived() const { return item->received_bytes(); } |
| + int totalBytes() const { return item->total_bytes(); } |
| + int fileSize() const { return -1; } |
| + int error() const { return 0; } |
| + |
| + base::DictionaryValue* ToJSON() const; |
| + |
| + static std::string DangerString(DownloadItem::DangerType danger) { |
| + switch (danger) { |
| + case DownloadItem::NOT_DANGEROUS: return DANGER_SAFE; |
| + case DownloadItem::DANGEROUS_FILE: return DANGER_FILE; |
| + case DownloadItem::DANGEROUS_URL: return DANGER_URL; |
| + case DownloadItem::DANGEROUS_TYPE_MAX: |
| + default: |
| + NOTREACHED(); |
| + return ""; |
| + } |
| + } |
| + |
| + static std::string StateString(DownloadItem::DownloadState state) { |
| + switch (state) { |
| + case DownloadItem::IN_PROGRESS: return STATE_IN_PROGRESS; |
| + case DownloadItem::COMPLETE: return STATE_COMPLETE; |
| + case DownloadItem::INTERRUPTED: // fall through |
| + case DownloadItem::CANCELLED: return STATE_INTERRUPTED; |
| + case DownloadItem::REMOVING: |
| + case DownloadItem::MAX_DOWNLOAD_STATE: |
| + default: |
| + NOTREACHED(); |
| + return ""; |
| + } |
| + } |
| + |
| + DownloadItem* item; |
| + // Allow copy and assign. |
| +}; |
| + |
| +typedef std::vector<SimpleDownloadItem*> SimpleItems; |
| + |
| +// Functor passed to std::sort to sort SimpleDownloadItems. |
| +class DownloadComparator { |
| + public: |
| + DownloadComparator(std::string* error_msg, Strings* order_terms) |
| + : error_msg_(error_msg), |
| + order_terms_(order_terms) { |
| + } |
| + |
| + bool operator() (const SimpleDownloadItem* left, |
| + const SimpleDownloadItem* right); |
| + private: |
| + static const char kDash = '-'; |
| + |
| + template <typename FieldType> bool CompareField( |
| + std::string term, |
| + std::string field, |
| + FieldType left, |
| + FieldType right, |
| + bool* matched_term, |
| + bool* cmp) const; |
| + |
| + std::string* error_msg_; |
| + Strings* order_terms_; |
| + // std::sort requires this class to be copyable. |
| +}; |
| + |
| +using download_util::DownloadQuery; |
| + |
| +class DownloadSearch : DownloadQuery { |
|
cbentzel
2011/10/10 14:35:30
Would it make more sense to have DownloadSearch ow
benjhayden
2011/10/13 14:07:34
The Field classes allowed me to merge DSearch into
|
| + public: |
| + DownloadSearch(const DownloadQuery& query, DownloadItems* items); |
| + ~DownloadSearch() {} |
| + |
| + bool IsWellFormed(std::string* error_msg); |
| + bool Matches(const SimpleDownloadItem& item); |
| + |
| + SimpleItems::const_iterator begin() const { return simple_items_.begin(); } |
| + SimpleItems::const_iterator end() const { return simple_items_.end(); } |
| + |
| + bool ReachedLimit(int num_results) { |
| + return has_limit_ && (num_results >= limit_); |
| + } |
| + |
| + private: |
| + std::string error_msg_; |
| + Strings order_terms_; |
| + SimpleItems simple_items_; |
| + STLElementDeleter<SimpleItems> delete_simple_items_; |
| + Strings query_terms_; |
| + scoped_ptr<icu::RegexPattern> filename_pattern_; |
| + scoped_ptr<icu::RegexPattern> url_pattern_; |
| + DownloadComparator comparator_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(DownloadSearch); |
| +}; |
| + |
| +base::DictionaryValue* SimpleDownloadItem::ToJSON() const { |
| + base::DictionaryValue* json = new base::DictionaryValue(); |
| +#define FIELD(name, type) \ |
| + json->Set ## type(#name, name()); |
| +#include "content/browser/download/simple_download_item_fields.h" |
| +#undef FIELD |
| + return json; |
| +} |
| + |
| +bool DownloadComparator::operator() ( |
| + const SimpleDownloadItem* left, |
| + const SimpleDownloadItem* right) { |
| + CHECK(left); |
| + CHECK(right); |
| + Strings::iterator order_term, |
| + last_order_term = order_terms_->end(); |
| + for (order_term = order_terms_->begin(); |
| + order_term != last_order_term; ++order_term) { |
| + bool matched_term = false; |
| + bool cmp = false; |
| +#define Integer int |
| +#define Boolean bool |
| +#define String std::string |
| +#define FIELD(name, type) \ |
| + if (CompareField<type>(*order_term, #name, left->name(), right->name(), \ |
| + &matched_term, &cmp)) return cmp; \ |
| + if (matched_term) continue; |
| +#include "content/browser/download/simple_download_item_fields.h" |
| +#undef FIELD |
| +#undef String |
| +#undef Boolean |
| +#undef Integer |
| + *error_msg_ += " " + *order_term; |
| + order_terms_->erase(order_term); |
| + } |
| + CHECK_NE(left->id(), right->id()); |
| + return left->id() < right->id(); |
| +} |
| + |
| +// Return true and set cmp if matched_term and left != right. |
| +template <typename FieldType> |
| +bool DownloadComparator::CompareField( |
| + std::string term, |
| + std::string field, |
| + FieldType left, |
| + FieldType right, |
| + bool* matched_term, |
| + bool* cmp) const { |
| + bool diff = (left != right); |
| + if (term == field) { |
| + *matched_term = true; |
| + if (diff) *cmp = (left < right); |
| + } |
| + if (term == kDash + field) { |
| + *matched_term = true; |
| + if (diff) *cmp = (left > right); |
| + } |
| + return diff && *matched_term; |
| +} |
| + |
| +// NOTE These string constants must match those in |
| +// content/common/extensions/api/extension_api.json |
| +const char SimpleDownloadItem::STATE_IN_PROGRESS[] = "in progress"; |
| +const char SimpleDownloadItem::STATE_COMPLETE[] = "complete"; |
| +const char SimpleDownloadItem::STATE_INTERRUPTED[] = "interrupted"; |
| +const char SimpleDownloadItem::DANGER_SAFE[] = "safe"; |
| +const char SimpleDownloadItem::DANGER_FILE[] = "file"; |
| +const char SimpleDownloadItem::DANGER_URL[] = "url"; |
| + |
| +DownloadSearch::DownloadSearch( |
| + const DownloadQuery& query, DownloadItems* items) |
| + : DownloadQuery(query), |
| + delete_simple_items_(&simple_items_), |
| + comparator_(&error_msg_, &order_terms_) { |
| + if (has_query_) |
| + base::SplitString(query_, ' ', &query_terms_); |
| + // TODO(benjhayden) Split query more intelligently. Quopri? |
| + if (has_filenameRegex_) { |
| + UParseError regex_error; |
| + UErrorCode regex_status = U_ZERO_ERROR; |
| + filename_pattern_.reset(icu::RegexPattern::compile( |
| + icu::UnicodeString::fromUTF8(filenameRegex_), |
| + regex_error, regex_status)); |
| + if (!U_SUCCESS(regex_status)) { |
| + error_msg_ = "bad filenameRegex"; |
| + return; |
| + } |
| + } |
| + if (has_urlRegex_) { |
| + UParseError regex_error; |
| + UErrorCode regex_status = U_ZERO_ERROR; |
| + url_pattern_.reset(icu::RegexPattern::compile( |
| + icu::UnicodeString::fromUTF8(urlRegex_), |
| + regex_error, regex_status)); |
| + if (!U_SUCCESS(regex_status)) { |
| + error_msg_ = "bad urlRegex"; |
| + return; |
| + } |
| + } |
| + for (DownloadItems::const_iterator iter = items->begin(); |
| + iter != items->end(); ++iter) { |
| + if (*iter != NULL) { |
| + if (!filter_func_.is_null() && !filter_func_.Run(**iter)) continue; |
| + simple_items_.push_back(new SimpleDownloadItem(*iter)); |
| + } |
| + } |
| + if (has_orderBy_ && (1 < simple_items_.size())) { |
| + base::SplitString(orderBy_, ' ', &order_terms_); |
| + std::sort(simple_items_.begin(), simple_items_.end(), comparator_); |
| + } |
| +} |
| + |
| +bool DownloadSearch::IsWellFormed(std::string* error_msg) { |
| + if (error_msg != NULL) *error_msg = error_msg_; |
| + return error_msg_.empty(); |
| +} |
| + |
| +bool DownloadSearch::Matches(const SimpleDownloadItem& item) { |
| +#define FIELD(name, unused_type) \ |
| + if (has_ ## name ## _ && (item.name() != name ## _)) return false; |
| +#include "content/browser/download/simple_download_item_fields.h" |
| +#undef FIELD |
| + if (has_state_enum_ && (item.item->state() != state_enum_)) return false; |
| + if (has_danger_enum_ && (item.item->GetDangerType() != danger_enum_)) |
| + return false; |
| + if (has_startedBefore_ && (item.startTime() > startedBefore_)) return false; |
| + if (has_startedAfter_ && (item.startTime() < startedAfter_)) return false; |
| + if (has_endedBefore_ && (item.endTime() > endedBefore_)) return false; |
| + if (has_endedAfter_ && (item.endTime() < endedAfter_)) return false; |
| + if (has_totalBytesGreater_ && (item.totalBytes() < totalBytesGreater_)) |
| + return false; |
| + if (has_totalBytesLess_ && (item.totalBytes() > totalBytesLess_)) |
| + return false; |
| + Strings::const_iterator query_term_iter, |
| + query_term_end = query_terms_.end(); |
| + for (query_term_iter = query_terms_.begin(); |
| + query_term_iter != query_term_end; ++query_term_iter) { |
| + std::string query_term = *query_term_iter; |
| + if ((std::string::npos == item.filename().find(query_term)) && |
| + (std::string::npos == item.url().find(query_term))) { |
| + return false; |
| + } |
| + } |
| + if (filename_pattern_.get() != NULL) { |
| + UErrorCode status = U_ZERO_ERROR; |
| + scoped_ptr<icu::RegexMatcher> filename_matcher(filename_pattern_->matcher( |
| + icu::UnicodeString::fromUTF8(item.filename()), status)); |
| + if (U_FAILURE(status)) { |
| + return false; |
| + } |
| + if (!filename_matcher->find(0, status)) return false; |
| + } |
| + if (url_pattern_.get() != NULL) { |
| + UErrorCode status = U_ZERO_ERROR; |
| + scoped_ptr<icu::RegexMatcher> url_matcher(url_pattern_->matcher( |
| + icu::UnicodeString::fromUTF8(item.url()), status)); |
| + if (U_FAILURE(status)) { |
| + return false; |
| + } |
| + if (!url_matcher->find(0, status)) return false; |
| + } |
| + return true; |
| +} |
| +} // anonymous namespace |
| + |
| +namespace download_util { |
| + |
| +DownloadQuery::DownloadQuery() { |
| + has_state_enum_ = false; |
| + has_danger_enum_ = false; |
| +#define FIELD(name, type) \ |
| + has_ ## name ## _ = false; |
| +#include "content/browser/download/download_query_fields.h" |
| +#undef FIELD |
| +} |
| + |
| +DownloadQuery& DownloadQuery::filter_func(DownloadQuery::FilterType func) { |
| + filter_func_ = func; |
| + return *this; |
| +} |
| + |
| +DownloadQuery& DownloadQuery::state_enum( |
| + DownloadItem::DownloadState stateenum) { |
| + has_state_enum_ = true; |
| + state_enum_ = stateenum; |
| + return *this; |
| +} |
| + |
| +DownloadQuery& DownloadQuery::danger_enum(DownloadItem::DangerType dangerenum) { |
| + has_danger_enum_ = true; |
| + danger_enum_ = dangerenum; |
| + return *this; |
| +} |
| + |
| +bool DownloadQuery::Search( |
| + std::vector<DownloadItem*>* items, |
| + std::string* error_msg, |
| + base::ListValue* results) const { |
| + DownloadSearch search(*this, items); |
| + items->clear(); |
| + if (!search.IsWellFormed(error_msg)) return false; |
| + for (SimpleItems::const_iterator iter = search.begin(); |
| + iter != search.end(); ++iter) { |
| + if (!search.Matches(**iter)) continue; |
| + items->push_back((*iter)->item); |
| + if (results != NULL) results->Append((*iter)->ToJSON()); |
| + if (search.ReachedLimit(items->size())) break; |
| + } |
| + return true; |
| +} |
| +} // namespace download_util |
| + |