Upstream version 11.39.266.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / app_list / search / history_data.cc
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.
4
5 #include "chrome/browser/ui/app_list/search/history_data.h"
6
7 #include <algorithm>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "chrome/browser/ui/app_list/search/history_data_observer.h"
12 #include "chrome/browser/ui/app_list/search/history_data_store.h"
13
14 namespace app_list {
15
16 namespace {
17
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,
22                 const base::Time* update_time)
23       : query(query),
24         update_time(update_time) {
25   }
26
27   const std::string* query;
28   const base::Time* update_time;
29 };
30
31 bool EntrySortByTimeAscending(const EntrySortData& entry1,
32                               const EntrySortData& entry2) {
33   return *entry1.update_time < *entry2.update_time;
34 }
35
36 }  // namespace
37
38 HistoryData::Data::Data() {}
39 HistoryData::Data::~Data() {}
40
41 HistoryData::HistoryData(HistoryDataStore* store,
42                          size_t max_primary,
43                          size_t max_secondary)
44     : store_(store),
45       max_primary_(max_primary),
46       max_secondary_(max_secondary) {
47   store_->Load(base::Bind(&HistoryData::OnStoreLoaded, AsWeakPtr()));
48 }
49
50 HistoryData::~HistoryData() {}
51
52 void HistoryData::Add(const std::string& query, const std::string& result_id) {
53   Associations::iterator assoc_it = associations_.find(query);
54
55   // Creates a primary association if query is seen for the first time.
56   if (assoc_it == associations_.end()) {
57     Data& data = associations_[query];
58     data.primary = result_id;
59     data.update_time = base::Time::Now();
60
61     store_->SetPrimary(query, result_id);
62     store_->SetUpdateTime(query, data.update_time);
63
64     TrimEntries();
65     return;
66   }
67
68   Data& data = assoc_it->second;
69   data.update_time = base::Time::Now();
70   store_->SetUpdateTime(query, data.update_time);
71
72   SecondaryDeque& secondary = data.secondary;
73   if (!secondary.empty() && secondary.back() == result_id) {
74     // Nothing to do if the last secondary is the current primary.
75     if (data.primary == result_id)
76       return;
77
78     // If |result_id| is the last (the most recent) secondary and it's not the
79     // current primary, promote it and demote the primary.
80     secondary.pop_back();
81     secondary.push_back(data.primary);
82     data.primary = result_id;
83
84     store_->SetPrimary(query, result_id);
85     store_->SetSecondary(query, secondary);
86     return;
87   }
88
89   // Otherwise, append to secondary list.
90   SecondaryDeque::iterator secondary_it =
91       std::find(secondary.begin(), secondary.end(), result_id);
92   if (secondary_it != secondary.end())
93     secondary.erase(secondary_it);
94
95   secondary.push_back(result_id);
96   if (secondary.size() > max_secondary_)
97     secondary.pop_front();
98   store_->SetSecondary(query, secondary);
99 }
100
101 scoped_ptr<KnownResults> HistoryData::GetKnownResults(
102     const std::string& query) const {
103   scoped_ptr<KnownResults> results(new KnownResults);
104   for (Associations::const_iterator assoc_it =
105            associations_.lower_bound(query);
106        assoc_it != associations_.end();
107        ++assoc_it) {
108     // Break out of the loop if |query| is no longer a prefix.
109     if (assoc_it->first.size() < query.size() ||
110         strncmp(assoc_it->first.c_str(),
111                 query.c_str(),
112                 query.length()) != 0) {
113       break;
114     }
115
116     const bool perfect = assoc_it->first == query;
117     // Primary match should always be returned.
118     (*results)[assoc_it->second.primary] =
119         perfect ? PERFECT_PRIMARY : PREFIX_PRIMARY;
120
121     const KnownResultType secondary_type =
122         perfect ? PERFECT_SECONDARY : PREFIX_SECONDARY;
123     const HistoryData::SecondaryDeque& secondary = assoc_it->second.secondary;
124     for (HistoryData::SecondaryDeque::const_iterator secondary_it =
125              secondary.begin();
126          secondary_it != secondary.end();
127          ++secondary_it) {
128       const std::string& secondary_result_id = (*secondary_it);
129
130       // Secondary match only gets added if there there is no primary match.
131       if (results->find(secondary_result_id) != results->end())
132         continue;
133       (*results)[secondary_result_id] = secondary_type;
134     }
135   }
136
137   return results.Pass();
138 }
139
140 void HistoryData::AddObserver(HistoryDataObserver* observer) {
141   observers_.AddObserver(observer);
142 }
143
144 void HistoryData::RemoveObserver(HistoryDataObserver* observer) {
145   observers_.RemoveObserver(observer);
146 }
147
148 void HistoryData::OnStoreLoaded(scoped_ptr<Associations> loaded_data) {
149   if (loaded_data)
150     loaded_data->swap(associations_);
151
152   FOR_EACH_OBSERVER(HistoryDataObserver, observers_,
153                     OnHistoryDataLoadedFromStore());
154 }
155
156 void HistoryData::TrimEntries() {
157   if (associations_.size() <= max_primary_)
158     return;
159
160   std::vector<EntrySortData> entries;
161   for (Associations::const_iterator it = associations_.begin();
162        it != associations_.end(); ++it) {
163     entries.push_back(EntrySortData(&it->first, &it->second.update_time));
164   }
165
166   const size_t entries_to_remove = associations_.size() - max_primary_;
167   std::partial_sort(entries.begin(),
168                     entries.begin() + entries_to_remove,
169                     entries.end(),
170                     &EntrySortByTimeAscending);
171
172   for (size_t i = 0; i < entries_to_remove; ++i) {
173     const std::string& query = *entries[i].query;
174     store_->Delete(query);
175     associations_.erase(query);
176   }
177 }
178
179 }  // namespace app_list