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/strings/string_number_conversions.h"
11#include "base/values.h"
12
13namespace app_list {
14
15namespace {
16
17const char kKeyVersion[] = "version";
18const char kCurrentVersion[] = "1";
19
20const char kKeyAssociations[] = "associations";
21const char kKeyPrimary[] = "p";
22const char kKeySecondary[] = "s";
23const char kKeyUpdateTime[] = "t";
24
25// Extracts strings from ListValue.
26void GetSecondary(const base::ListValue* list,
27                  HistoryData::SecondaryDeque* secondary) {
28  HistoryData::SecondaryDeque results;
29  for (base::ListValue::const_iterator it = list->begin();
30       it != list->end(); ++it) {
31    std::string str;
32    if (!(*it)->GetAsString(&str))
33      return;
34
35    results.push_back(str);
36  }
37
38  secondary->swap(results);
39}
40
41// V1 format json dictionary:
42//  {
43//    "version": "1",
44//    "associations": {
45//      "user typed query": {
46//        "p" : "result id of primary association",
47//        "s" : [
48//                "result id of 1st (oldest) secondary association",
49//                ...
50//                "result id of the newest secondary association"
51//              ],
52//        "t" : "last_update_timestamp"
53//      },
54//      ...
55//    }
56//  }
57scoped_ptr<HistoryData::Associations> Parse(
58    scoped_ptr<base::DictionaryValue> dict) {
59  std::string version;
60  if (!dict->GetStringWithoutPathExpansion(kKeyVersion, &version) ||
61      version != kCurrentVersion) {
62    return scoped_ptr<HistoryData::Associations>();
63  }
64
65  const base::DictionaryValue* assoc_dict = NULL;
66  if (!dict->GetDictionaryWithoutPathExpansion(kKeyAssociations,
67                                               &assoc_dict) ||
68      !assoc_dict) {
69    return scoped_ptr<HistoryData::Associations>();
70  }
71
72  scoped_ptr<HistoryData::Associations> data(new HistoryData::Associations);
73  for (base::DictionaryValue::Iterator it(*assoc_dict);
74       !it.IsAtEnd(); it.Advance()) {
75    const base::DictionaryValue* entry_dict = NULL;
76    if (!it.value().GetAsDictionary(&entry_dict))
77      continue;
78
79    std::string primary;
80    std::string update_time_string;
81    if (!entry_dict->GetStringWithoutPathExpansion(kKeyPrimary, &primary) ||
82        !entry_dict->GetStringWithoutPathExpansion(kKeyUpdateTime,
83                                                   &update_time_string)) {
84      continue;
85    }
86
87    const base::ListValue* secondary_list = NULL;
88    HistoryData::SecondaryDeque secondary;
89    if (entry_dict->GetListWithoutPathExpansion(kKeySecondary, &secondary_list))
90      GetSecondary(secondary_list, &secondary);
91
92    const std::string& query = it.key();
93    HistoryData::Data& association_data = (*data.get())[query];
94    association_data.primary = primary;
95    association_data.secondary.swap(secondary);
96
97    int64 update_time_val;
98    base::StringToInt64(update_time_string, &update_time_val);
99    association_data.update_time =
100        base::Time::FromInternalValue(update_time_val);
101  }
102
103  return data.Pass();
104}
105
106}  // namespace
107
108HistoryDataStore::HistoryDataStore()
109    : cached_dict_(new base::DictionaryValue()) {
110  Init(cached_dict_.get());
111}
112
113HistoryDataStore::HistoryDataStore(
114    scoped_refptr<DictionaryDataStore> data_store)
115    : data_store_(data_store) {
116  Init(data_store_->cached_dict());
117}
118
119HistoryDataStore::~HistoryDataStore() {
120}
121
122void HistoryDataStore::Init(base::DictionaryValue* cached_dict) {
123  DCHECK(cached_dict);
124  cached_dict->SetString(kKeyVersion, kCurrentVersion);
125  cached_dict->Set(kKeyAssociations, new base::DictionaryValue);
126}
127
128void HistoryDataStore::Flush(
129    const DictionaryDataStore::OnFlushedCallback& on_flushed) {
130  if (data_store_.get())
131    data_store_->Flush(on_flushed);
132  else
133    on_flushed.Run();
134}
135
136void HistoryDataStore::Load(
137    const HistoryDataStore::OnLoadedCallback& on_loaded) {
138  if (data_store_.get()) {
139    data_store_->Load(base::Bind(&HistoryDataStore::OnDictionaryLoadedCallback,
140                                 this,
141                                 on_loaded));
142  } else {
143    OnDictionaryLoadedCallback(
144        on_loaded, make_scoped_ptr(cached_dict_->DeepCopy()));
145  }
146}
147
148void HistoryDataStore::SetPrimary(const std::string& query,
149                                  const std::string& result) {
150  base::DictionaryValue* entry_dict = GetEntryDict(query);
151  entry_dict->SetWithoutPathExpansion(kKeyPrimary,
152                                      new base::StringValue(result));
153  if (data_store_.get())
154    data_store_->ScheduleWrite();
155}
156
157void HistoryDataStore::SetSecondary(
158    const std::string& query,
159    const HistoryData::SecondaryDeque& results) {
160  scoped_ptr<base::ListValue> results_list(new base::ListValue);
161  for (size_t i = 0; i< results.size(); ++i)
162    results_list->AppendString(results[i]);
163
164  base::DictionaryValue* entry_dict = GetEntryDict(query);
165  entry_dict->SetWithoutPathExpansion(kKeySecondary, results_list.release());
166  if (data_store_.get())
167    data_store_->ScheduleWrite();
168}
169
170void HistoryDataStore::SetUpdateTime(const std::string& query,
171                                     const base::Time& update_time) {
172  base::DictionaryValue* entry_dict = GetEntryDict(query);
173  entry_dict->SetWithoutPathExpansion(kKeyUpdateTime,
174                                      new base::StringValue(base::Int64ToString(
175                                          update_time.ToInternalValue())));
176  if (data_store_.get())
177    data_store_->ScheduleWrite();
178}
179
180void HistoryDataStore::Delete(const std::string& query) {
181  base::DictionaryValue* assoc_dict = GetAssociationDict();
182  assoc_dict->RemoveWithoutPathExpansion(query, NULL);
183  if (data_store_.get())
184    data_store_->ScheduleWrite();
185}
186
187base::DictionaryValue* HistoryDataStore::GetAssociationDict() {
188  base::DictionaryValue* cached_dict =
189      cached_dict_ ? cached_dict_.get() : data_store_->cached_dict();
190  DCHECK(cached_dict);
191
192  base::DictionaryValue* assoc_dict = NULL;
193  CHECK(cached_dict->GetDictionary(kKeyAssociations, &assoc_dict) &&
194        assoc_dict);
195
196  return assoc_dict;
197}
198
199base::DictionaryValue* HistoryDataStore::GetEntryDict(
200    const std::string& query) {
201  base::DictionaryValue* assoc_dict = GetAssociationDict();
202
203  base::DictionaryValue* entry_dict = NULL;
204  if (!assoc_dict->GetDictionaryWithoutPathExpansion(query, &entry_dict)) {
205    // Creates one if none exists. Ownership is taken in the set call after.
206    entry_dict = new base::DictionaryValue;
207    assoc_dict->SetWithoutPathExpansion(query, entry_dict);
208  }
209
210  return entry_dict;
211}
212
213void HistoryDataStore::OnDictionaryLoadedCallback(
214    OnLoadedCallback callback, scoped_ptr<base::DictionaryValue> dict) {
215  if (!dict) {
216    callback.Run(scoped_ptr<HistoryData::Associations>());
217  } else {
218    callback.Run(Parse(dict.Pass()).Pass());
219  }
220}
221
222}  // namespace app_list
223