history_data_store.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
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_store.h" 6 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" 16 17using content::BrowserThread; 18 19namespace app_list { 20 21namespace { 22 23const char kKeyVersion[] = "version"; 24const char kCurrentVersion[] = "1"; 25 26const char kKeyAssociations[] = "associations"; 27const char kKeyPrimary[] = "p"; 28const char kKeySecondary[] = "s"; 29const char kKeyUpdateTime[] = "t"; 30 31// Extracts strings from ListValue. 32void 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) { 37 std::string str; 38 if (!(*it)->GetAsString(&str)) 39 return; 40 41 results.push_back(str); 42 } 43 44 secondary->swap(results); 45} 46 47// V1 format json dictionary: 48// { 49// "version": "1", 50// "associations": { 51// "user typed query": { 52// "p" : "result id of primary association", 53// "s" : [ 54// "result id of 1st (oldest) secondary association", 55// ... 56// "result id of the newest secondary association" 57// ], 58// "t" : "last_update_timestamp" 59// }, 60// ... 61// } 62// } 63scoped_ptr<HistoryData::Associations> Parse( 64 scoped_ptr<base::DictionaryValue> dict) { 65 std::string version; 66 if (!dict->GetStringWithoutPathExpansion(kKeyVersion, &version) || 67 version != kCurrentVersion) { 68 return scoped_ptr<HistoryData::Associations>(); 69 } 70 71 const base::DictionaryValue* assoc_dict = NULL; 72 if (!dict->GetDictionaryWithoutPathExpansion(kKeyAssociations, 73 &assoc_dict) || 74 !assoc_dict) { 75 return scoped_ptr<HistoryData::Associations>(); 76 } 77 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)) 83 continue; 84 85 std::string primary; 86 std::string update_time_string; 87 if (!entry_dict->GetStringWithoutPathExpansion(kKeyPrimary, &primary) || 88 !entry_dict->GetStringWithoutPathExpansion(kKeyUpdateTime, 89 &update_time_string)) { 90 continue; 91 } 92 93 const base::ListValue* secondary_list = NULL; 94 HistoryData::SecondaryDeque secondary; 95 if (entry_dict->GetListWithoutPathExpansion(kKeySecondary, &secondary_list)) 96 GetSecondary(secondary_list, &secondary); 97 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); 102 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); 107 } 108 109 return data.Pass(); 110} 111 112} // namespace 113 114HistoryDataStore::HistoryDataStore(const base::FilePath& data_file) 115 : data_store_(new DictionaryDataStore(data_file)) { 116 base::DictionaryValue* dict = data_store_->cached_dict(); 117 DCHECK(dict); 118 dict->SetString(kKeyVersion, kCurrentVersion); 119 dict->Set(kKeyAssociations, new base::DictionaryValue); 120} 121 122HistoryDataStore::~HistoryDataStore() { 123} 124 125void HistoryDataStore::Flush( 126 const DictionaryDataStore::OnFlushedCallback& on_flushed) { 127 data_store_->Flush(on_flushed); 128} 129 130void HistoryDataStore::Load( 131 const HistoryDataStore::OnLoadedCallback& on_loaded) { 132 data_store_->Load(base::Bind(&HistoryDataStore::OnDictionaryLoadedCallback, 133 this, 134 on_loaded)); 135} 136 137void 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(); 143} 144 145void 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]); 151 152 base::DictionaryValue* entry_dict = GetEntryDict(query); 153 entry_dict->SetWithoutPathExpansion(kKeySecondary, results_list.release()); 154 data_store_->ScheduleWrite(); 155} 156 157void 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(); 164} 165 166void HistoryDataStore::Delete(const std::string& query) { 167 base::DictionaryValue* assoc_dict = GetAssociationDict(); 168 assoc_dict->RemoveWithoutPathExpansion(query, NULL); 169 data_store_->ScheduleWrite(); 170} 171 172base::DictionaryValue* HistoryDataStore::GetAssociationDict() { 173 base::DictionaryValue* cached_dict = data_store_->cached_dict(); 174 DCHECK(cached_dict); 175 176 base::DictionaryValue* assoc_dict = NULL; 177 CHECK(cached_dict->GetDictionary(kKeyAssociations, &assoc_dict) && 178 assoc_dict); 179 180 return assoc_dict; 181} 182 183base::DictionaryValue* HistoryDataStore::GetEntryDict( 184 const std::string& query) { 185 base::DictionaryValue* assoc_dict = GetAssociationDict(); 186 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); 192 } 193 194 return entry_dict; 195} 196 197void HistoryDataStore::OnDictionaryLoadedCallback( 198 OnLoadedCallback callback, scoped_ptr<base::DictionaryValue> dict) { 199 if (!dict) { 200 callback.Run(scoped_ptr<HistoryData::Associations>()); 201 } else { 202 callback.Run(Parse(dict.Pass()).Pass()); 203 } 204} 205 206} // namespace app_list 207