Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ui / app_list / search / history_data.cc
blob636d60f70167ca3faabd71e885b8dbe956722216
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/app_list/search/history_data.h"
7 #include <algorithm>
8 #include <vector>
10 #include "base/bind.h"
11 #include "ui/app_list/search/history_data_observer.h"
12 #include "ui/app_list/search/history_data_store.h"
14 namespace app_list {
16 namespace {
18 // A struct used to sort query entries by time.
19 struct EntrySortData {
20 EntrySortData() : query(NULL), update_time(NULL) {}
21 EntrySortData(const std::string* query, const base::Time* update_time)
22 : query(query), update_time(update_time) {}
24 const std::string* query;
25 const base::Time* update_time;
28 bool EntrySortByTimeAscending(const EntrySortData& entry1,
29 const EntrySortData& entry2) {
30 return *entry1.update_time < *entry2.update_time;
33 } // namespace
35 HistoryData::Data::Data() {
37 HistoryData::Data::~Data() {
40 HistoryData::HistoryData(HistoryDataStore* store,
41 size_t max_primary,
42 size_t max_secondary)
43 : store_(store), max_primary_(max_primary), max_secondary_(max_secondary) {
44 store_->Load(base::Bind(&HistoryData::OnStoreLoaded, AsWeakPtr()));
47 HistoryData::~HistoryData() {
50 void HistoryData::Add(const std::string& query, const std::string& result_id) {
51 Associations::iterator assoc_it = associations_.find(query);
53 // Creates a primary association if query is seen for the first time.
54 if (assoc_it == associations_.end()) {
55 Data& data = associations_[query];
56 data.primary = result_id;
57 data.update_time = base::Time::Now();
59 store_->SetPrimary(query, result_id);
60 store_->SetUpdateTime(query, data.update_time);
62 TrimEntries();
63 return;
66 Data& data = assoc_it->second;
67 data.update_time = base::Time::Now();
68 store_->SetUpdateTime(query, data.update_time);
70 SecondaryDeque& secondary = data.secondary;
71 if (!secondary.empty() && secondary.back() == result_id) {
72 // Nothing to do if the last secondary is the current primary.
73 if (data.primary == result_id)
74 return;
76 // If |result_id| is the last (the most recent) secondary and it's not the
77 // current primary, promote it and demote the primary.
78 secondary.pop_back();
79 secondary.push_back(data.primary);
80 data.primary = result_id;
82 store_->SetPrimary(query, result_id);
83 store_->SetSecondary(query, secondary);
84 return;
87 // Otherwise, append to secondary list.
88 SecondaryDeque::iterator secondary_it =
89 std::find(secondary.begin(), secondary.end(), result_id);
90 if (secondary_it != secondary.end())
91 secondary.erase(secondary_it);
93 secondary.push_back(result_id);
94 if (secondary.size() > max_secondary_)
95 secondary.pop_front();
96 store_->SetSecondary(query, secondary);
99 scoped_ptr<KnownResults> HistoryData::GetKnownResults(
100 const std::string& query) const {
101 scoped_ptr<KnownResults> results(new KnownResults);
102 for (Associations::const_iterator assoc_it = associations_.lower_bound(query);
103 assoc_it != associations_.end();
104 ++assoc_it) {
105 // Break out of the loop if |query| is no longer a prefix.
106 if (assoc_it->first.size() < query.size() ||
107 strncmp(assoc_it->first.c_str(), query.c_str(), query.length()) != 0) {
108 break;
111 const bool perfect = assoc_it->first == query;
112 // Primary match should always be returned.
113 (*results)[assoc_it->second.primary] =
114 perfect ? PERFECT_PRIMARY : PREFIX_PRIMARY;
116 const KnownResultType secondary_type =
117 perfect ? PERFECT_SECONDARY : PREFIX_SECONDARY;
118 const HistoryData::SecondaryDeque& secondary = assoc_it->second.secondary;
119 for (HistoryData::SecondaryDeque::const_iterator secondary_it =
120 secondary.begin();
121 secondary_it != secondary.end();
122 ++secondary_it) {
123 const std::string& secondary_result_id = (*secondary_it);
125 // Secondary match only gets added if there there is no primary match.
126 if (results->find(secondary_result_id) != results->end())
127 continue;
128 (*results)[secondary_result_id] = secondary_type;
132 return results.Pass();
135 void HistoryData::AddObserver(HistoryDataObserver* observer) {
136 observers_.AddObserver(observer);
139 void HistoryData::RemoveObserver(HistoryDataObserver* observer) {
140 observers_.RemoveObserver(observer);
143 void HistoryData::OnStoreLoaded(scoped_ptr<Associations> loaded_data) {
144 if (loaded_data)
145 loaded_data->swap(associations_);
147 FOR_EACH_OBSERVER(
148 HistoryDataObserver, observers_, OnHistoryDataLoadedFromStore());
151 void HistoryData::TrimEntries() {
152 if (associations_.size() <= max_primary_)
153 return;
155 std::vector<EntrySortData> entries;
156 for (Associations::const_iterator it = associations_.begin();
157 it != associations_.end();
158 ++it) {
159 entries.push_back(EntrySortData(&it->first, &it->second.update_time));
162 const size_t entries_to_remove = associations_.size() - max_primary_;
163 std::partial_sort(entries.begin(),
164 entries.begin() + entries_to_remove,
165 entries.end(),
166 &EntrySortByTimeAscending);
168 for (size_t i = 0; i < entries_to_remove; ++i) {
169 const std::string& query = *entries[i].query;
170 store_->Delete(query);
171 associations_.erase(query);
175 } // namespace app_list