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