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_store.h"
7 #include "base/callback.h"
8 #include "base/json/json_file_value_serializer.h"
9 #include "base/json/json_string_value_serializer.h"
10 #include "base/logging.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/task_runner_util.h"
13 #include "base/threading/sequenced_worker_pool.h"
14 #include "base/values.h"
15 #include "content/public/browser/browser_thread.h"
17 using content::BrowserThread;
23 const char kKeyVersion[] = "version";
24 const char kCurrentVersion[] = "1";
26 const char kKeyAssociations[] = "associations";
27 const char kKeyPrimary[] = "p";
28 const char kKeySecondary[] = "s";
29 const char kKeyUpdateTime[] = "t";
31 // Extracts strings from ListValue.
32 void GetSecondary(const base::ListValue* list,
33 HistoryData::SecondaryDeque* secondary) {
34 HistoryData::SecondaryDeque results;
35 for (base::ListValue::const_iterator it = list->begin();
36 it != list->end(); ++it) {
38 if (!(*it)->GetAsString(&str))
41 results.push_back(str);
44 secondary->swap(results);
47 // V1 format json dictionary:
51 // "user typed query": {
52 // "p" : "result id of primary association",
54 // "result id of 1st (oldest) secondary association",
56 // "result id of the newest secondary association"
58 // "t" : "last_update_timestamp"
63 scoped_ptr<HistoryData::Associations> Parse(
64 scoped_ptr<base::DictionaryValue> dict) {
66 if (!dict->GetStringWithoutPathExpansion(kKeyVersion, &version) ||
67 version != kCurrentVersion) {
68 return scoped_ptr<HistoryData::Associations>();
71 const base::DictionaryValue* assoc_dict = NULL;
72 if (!dict->GetDictionaryWithoutPathExpansion(kKeyAssociations,
75 return scoped_ptr<HistoryData::Associations>();
78 scoped_ptr<HistoryData::Associations> data(new HistoryData::Associations);
79 for (base::DictionaryValue::Iterator it(*assoc_dict);
80 !it.IsAtEnd(); it.Advance()) {
81 const base::DictionaryValue* entry_dict = NULL;
82 if (!it.value().GetAsDictionary(&entry_dict))
86 std::string update_time_string;
87 if (!entry_dict->GetStringWithoutPathExpansion(kKeyPrimary, &primary) ||
88 !entry_dict->GetStringWithoutPathExpansion(kKeyUpdateTime,
89 &update_time_string)) {
93 const base::ListValue* secondary_list = NULL;
94 HistoryData::SecondaryDeque secondary;
95 if (entry_dict->GetListWithoutPathExpansion(kKeySecondary, &secondary_list))
96 GetSecondary(secondary_list, &secondary);
98 const std::string& query = it.key();
99 HistoryData::Data& association_data = (*data.get())[query];
100 association_data.primary = primary;
101 association_data.secondary.swap(secondary);
103 int64 update_time_val;
104 base::StringToInt64(update_time_string, &update_time_val);
105 association_data.update_time =
106 base::Time::FromInternalValue(update_time_val);
114 HistoryDataStore::HistoryDataStore(const base::FilePath& data_file)
115 : data_store_(new DictionaryDataStore(data_file)) {
116 base::DictionaryValue* dict = data_store_->cached_dict();
118 dict->SetString(kKeyVersion, kCurrentVersion);
119 dict->Set(kKeyAssociations, new base::DictionaryValue);
122 HistoryDataStore::~HistoryDataStore() {
125 void HistoryDataStore::Flush(
126 const DictionaryDataStore::OnFlushedCallback& on_flushed) {
127 data_store_->Flush(on_flushed);
130 void HistoryDataStore::Load(
131 const HistoryDataStore::OnLoadedCallback& on_loaded) {
132 data_store_->Load(base::Bind(&HistoryDataStore::OnDictionaryLoadedCallback,
137 void HistoryDataStore::SetPrimary(const std::string& query,
138 const std::string& result) {
139 base::DictionaryValue* entry_dict = GetEntryDict(query);
140 entry_dict->SetWithoutPathExpansion(kKeyPrimary,
141 new base::StringValue(result));
142 data_store_->ScheduleWrite();
145 void HistoryDataStore::SetSecondary(
146 const std::string& query,
147 const HistoryData::SecondaryDeque& results) {
148 scoped_ptr<base::ListValue> results_list(new base::ListValue);
149 for (size_t i = 0; i< results.size(); ++i)
150 results_list->AppendString(results[i]);
152 base::DictionaryValue* entry_dict = GetEntryDict(query);
153 entry_dict->SetWithoutPathExpansion(kKeySecondary, results_list.release());
154 data_store_->ScheduleWrite();
157 void HistoryDataStore::SetUpdateTime(const std::string& query,
158 const base::Time& update_time) {
159 base::DictionaryValue* entry_dict = GetEntryDict(query);
160 entry_dict->SetWithoutPathExpansion(kKeyUpdateTime,
161 new base::StringValue(base::Int64ToString(
162 update_time.ToInternalValue())));
163 data_store_->ScheduleWrite();
166 void HistoryDataStore::Delete(const std::string& query) {
167 base::DictionaryValue* assoc_dict = GetAssociationDict();
168 assoc_dict->RemoveWithoutPathExpansion(query, NULL);
169 data_store_->ScheduleWrite();
172 base::DictionaryValue* HistoryDataStore::GetAssociationDict() {
173 base::DictionaryValue* cached_dict = data_store_->cached_dict();
176 base::DictionaryValue* assoc_dict = NULL;
177 CHECK(cached_dict->GetDictionary(kKeyAssociations, &assoc_dict) &&
183 base::DictionaryValue* HistoryDataStore::GetEntryDict(
184 const std::string& query) {
185 base::DictionaryValue* assoc_dict = GetAssociationDict();
187 base::DictionaryValue* entry_dict = NULL;
188 if (!assoc_dict->GetDictionaryWithoutPathExpansion(query, &entry_dict)) {
189 // Creates one if none exists. Ownership is taken in the set call after.
190 entry_dict = new base::DictionaryValue;
191 assoc_dict->SetWithoutPathExpansion(query, entry_dict);
197 void HistoryDataStore::OnDictionaryLoadedCallback(
198 OnLoadedCallback callback, scoped_ptr<base::DictionaryValue> dict) {
200 callback.Run(scoped_ptr<HistoryData::Associations>());
202 callback.Run(Parse(dict.Pass()).Pass());
206 } // namespace app_list