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 "chrome/browser/ui/app_list/search/history_data.h"
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"
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
)
24 update_time(update_time
) {
27 const std::string
* query
;
28 const base::Time
* update_time
;
31 bool EntrySortByTimeAscending(const EntrySortData
& entry1
,
32 const EntrySortData
& entry2
) {
33 return *entry1
.update_time
< *entry2
.update_time
;
38 HistoryData::Data::Data() {}
39 HistoryData::Data::~Data() {}
41 HistoryData::HistoryData(HistoryDataStore
* store
,
45 max_primary_(max_primary
),
46 max_secondary_(max_secondary
) {
47 store_
->Load(base::Bind(&HistoryData::OnStoreLoaded
, AsWeakPtr()));
50 HistoryData::~HistoryData() {}
52 void HistoryData::Add(const std::string
& query
, const std::string
& result_id
) {
53 Associations::iterator assoc_it
= associations_
.find(query
);
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();
61 store_
->SetPrimary(query
, result_id
);
62 store_
->SetUpdateTime(query
, data
.update_time
);
68 Data
& data
= assoc_it
->second
;
69 data
.update_time
= base::Time::Now();
70 store_
->SetUpdateTime(query
, data
.update_time
);
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
)
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.
81 secondary
.push_back(data
.primary
);
82 data
.primary
= result_id
;
84 store_
->SetPrimary(query
, result_id
);
85 store_
->SetSecondary(query
, secondary
);
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
);
95 secondary
.push_back(result_id
);
96 if (secondary
.size() > max_secondary_
)
97 secondary
.pop_front();
98 store_
->SetSecondary(query
, secondary
);
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();
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(),
112 query
.length()) != 0) {
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
;
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
=
126 secondary_it
!= secondary
.end();
128 const std::string
& secondary_result_id
= (*secondary_it
);
130 // Secondary match only gets added if there there is no primary match.
131 if (results
->find(secondary_result_id
) != results
->end())
133 (*results
)[secondary_result_id
] = secondary_type
;
137 return results
.Pass();
140 void HistoryData::AddObserver(HistoryDataObserver
* observer
) {
141 observers_
.AddObserver(observer
);
144 void HistoryData::RemoveObserver(HistoryDataObserver
* observer
) {
145 observers_
.RemoveObserver(observer
);
148 void HistoryData::OnStoreLoaded(scoped_ptr
<Associations
> loaded_data
) {
150 loaded_data
->swap(associations_
);
152 FOR_EACH_OBSERVER(HistoryDataObserver
, observers_
,
153 OnHistoryDataLoadedFromStore());
156 void HistoryData::TrimEntries() {
157 if (associations_
.size() <= max_primary_
)
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
));
166 const size_t entries_to_remove
= associations_
.size() - max_primary_
;
167 std::partial_sort(entries
.begin(),
168 entries
.begin() + entries_to_remove
,
170 &EntrySortByTimeAscending
);
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
);
179 } // namespace app_list