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"
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"
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
;
35 HistoryData::Data::Data() {
37 HistoryData::Data::~Data() {
40 HistoryData::HistoryData(HistoryDataStore
* store
,
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
);
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
)
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.
79 secondary
.push_back(data
.primary
);
80 data
.primary
= result_id
;
82 store_
->SetPrimary(query
, result_id
);
83 store_
->SetSecondary(query
, secondary
);
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();
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) {
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
=
121 secondary_it
!= secondary
.end();
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())
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
) {
145 loaded_data
->swap(associations_
);
148 HistoryDataObserver
, observers_
, OnHistoryDataLoadedFromStore());
151 void HistoryData::TrimEntries() {
152 if (associations_
.size() <= max_primary_
)
155 std::vector
<EntrySortData
> entries
;
156 for (Associations::const_iterator it
= associations_
.begin();
157 it
!= associations_
.end();
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
,
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