- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / app_list / search / mixer.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/mixer.h"
6
7 #include <algorithm>
8 #include <set>
9 #include <string>
10 #include <vector>
11
12 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
13 #include "chrome/browser/ui/app_list/search/search_provider.h"
14
15 namespace app_list {
16
17 namespace {
18
19 // Maximum number of results to show.
20 const size_t kMaxResults = 6;
21 const size_t kMaxMainGroupResults = 4;
22 const size_t kMaxWebstoreResults = 2;
23 const size_t kMaxPeopleResults = 2;
24
25 // A value to indicate no max number of results limit.
26 const size_t kNoMaxResultsLimit = 0;
27
28 // Used for sorting and mixing results.
29 struct SortData {
30   SortData()
31       : result(NULL),
32         score(0.0) {
33   }
34   SortData(ChromeSearchResult* result, double score)
35       : result(result),
36         score(score) {
37   }
38
39   bool operator<(const SortData& other) const {
40     // This data precedes (less than) |other| if it has higher score.
41     return score > other.score;
42   }
43
44   ChromeSearchResult* result;  // Not owned.
45   double score;
46 };
47 typedef std::vector<SortData> SortedResults;
48
49 // Removes duplicates from |results|.
50 void RemoveDuplicates(SortedResults* results) {
51   SortedResults final;
52   final.reserve(results->size());
53
54   std::set<std::string> id_set;
55   for (SortedResults::iterator it = results->begin();
56        it != results->end();
57        ++it) {
58     const std::string& id = it->result->id();
59     if (id_set.find(id) != id_set.end())
60       continue;
61
62     id_set.insert(id);
63     final.push_back(*it);
64   }
65
66   results->swap(final);
67 }
68
69 // Publishes the given |results| to |ui_results|. Reuse existing ones to avoid
70 // flickering.
71 void Publish(const SortedResults& results,
72              AppListModel::SearchResults* ui_results) {
73   for (size_t i = 0; i < results.size(); ++i) {
74     ChromeSearchResult* result = results[i].result;
75
76     ChromeSearchResult* ui_result = i < ui_results->item_count() ?
77         static_cast<ChromeSearchResult*>(ui_results->GetItemAt(i)) : NULL;
78     if (ui_result && ui_result->id() == result->id()) {
79       ui_result->set_title(result->title());
80       ui_result->set_title_tags(result->title_tags());
81       ui_result->set_details(result->details());
82       ui_result->set_details_tags(result->details_tags());
83       ui_results->NotifyItemsChanged(i, 1);
84     } else {
85       if (ui_result)
86         ui_results->DeleteAt(i);
87       ui_results->AddAt(i, result->Duplicate().release());
88     }
89   }
90
91   while (ui_results->item_count() > results.size())
92     ui_results->DeleteAt(ui_results->item_count() - 1);
93 }
94
95 }  // namespace
96
97 // Used to group relevant providers together fox mixing their results.
98 class Mixer::Group {
99  public:
100   Group(size_t max_results, double boost)
101       : max_results_(max_results),
102         boost_(boost) {
103   }
104   ~Group() {}
105
106   void AddProvider(SearchProvider* provider) {
107     providers_.push_back(provider);
108   }
109
110   void FetchResults(const KnownResults& known_results) {
111     results_.clear();
112
113     for (Providers::const_iterator provider_it = providers_.begin();
114          provider_it != providers_.end();
115          ++provider_it) {
116       for (SearchProvider::Results::const_iterator
117                result_it = (*provider_it)->results().begin();
118                result_it != (*provider_it)->results().end();
119                ++result_it) {
120         DCHECK_GE((*result_it)->relevance(), 0.0);
121         DCHECK_LE((*result_it)->relevance(), 1.0);
122         DCHECK(!(*result_it)->id().empty());
123
124         double boost = boost_;
125         KnownResults::const_iterator known_it =
126             known_results.find((*result_it)->id());
127         if (known_it != known_results.end()) {
128           switch (known_it->second) {
129             case PERFECT_PRIMARY:
130               boost = 4.0;
131               break;
132             case PREFIX_PRIMARY:
133               boost = 3.75;
134               break;
135             case PERFECT_SECONDARY:
136               boost = 3.25;
137               break;
138             case PREFIX_SECONDARY:
139               boost = 3.0;
140               break;
141             case UNKNOWN_RESULT:
142               NOTREACHED() << "Unknown result in KnownResults?";
143               break;
144           }
145         }
146
147         results_.push_back(
148             SortData(*result_it, (*result_it)->relevance() + boost));
149       }
150     }
151
152     std::sort(results_.begin(), results_.end());
153     if (max_results_ != kNoMaxResultsLimit && results_.size() > max_results_)
154       results_.resize(max_results_);
155   }
156
157   const SortedResults& results() const { return results_; }
158
159  private:
160   typedef std::vector<SearchProvider*> Providers;
161   const size_t max_results_;
162   const double boost_;
163
164   Providers providers_;  // Not owned.
165   SortedResults results_;
166
167   DISALLOW_COPY_AND_ASSIGN(Group);
168 };
169
170 Mixer::Mixer(AppListModel::SearchResults* ui_results)
171     : ui_results_(ui_results) {}
172 Mixer::~Mixer() {}
173
174 void Mixer::Init() {
175   groups_.push_back(new Group(kMaxMainGroupResults, 3.0));
176   groups_.push_back(new Group(kNoMaxResultsLimit, 2.0));
177   groups_.push_back(new Group(kMaxWebstoreResults, 1.0));
178   groups_.push_back(new Group(kMaxPeopleResults, 0.0));
179 }
180
181 void Mixer::AddProviderToGroup(GroupId group, SearchProvider* provider) {
182   size_t group_index = static_cast<size_t>(group);
183   groups_[group_index]->AddProvider(provider);
184 }
185
186 void Mixer::MixAndPublish(const KnownResults& known_results) {
187   FetchResults(known_results);
188
189   SortedResults results;
190   results.reserve(kMaxResults);
191
192   // Adds main group and web store results first.
193   results.insert(results.end(),
194                  groups_[MAIN_GROUP]->results().begin(),
195                  groups_[MAIN_GROUP]->results().end());
196   results.insert(results.end(),
197                  groups_[WEBSTORE_GROUP]->results().begin(),
198                  groups_[WEBSTORE_GROUP]->results().end());
199   results.insert(results.end(),
200                  groups_[PEOPLE_GROUP]->results().begin(),
201                  groups_[PEOPLE_GROUP]->results().end());
202
203   // Collapse duplicate apps from local and web store.
204   RemoveDuplicates(&results);
205
206   DCHECK_GE(kMaxResults, results.size());
207   size_t remaining_slots = kMaxResults - results.size();
208
209   // Reserves at least one slot for the omnibox result. If there is no available
210   // slot for omnibox results, removes the last one from web store.
211   const size_t omnibox_results = groups_[OMNIBOX_GROUP]->results().size();
212   if (!remaining_slots && omnibox_results)
213     results.pop_back();
214
215   remaining_slots = std::min(kMaxResults - results.size(), omnibox_results);
216   results.insert(results.end(),
217                  groups_[OMNIBOX_GROUP]->results().begin(),
218                  groups_[OMNIBOX_GROUP]->results().begin() + remaining_slots);
219
220   std::sort(results.begin(), results.end());
221   RemoveDuplicates(&results);
222   if (results.size() > kMaxResults)
223     results.resize(kMaxResults);
224
225   Publish(results, ui_results_);
226 }
227
228 void Mixer::FetchResults(const KnownResults& known_results) {
229   for (Groups::iterator group_it = groups_.begin();
230        group_it != groups_.end();
231        ++group_it) {
232     (*group_it)->FetchResults(known_results);
233   }
234 }
235
236 }  // namespace app_list