Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / components / dom_distiller / core / dom_distiller_store.cc
blobc6def8d4e3bb4b2ab965a18cae2805f038924db7
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 "components/dom_distiller/core/dom_distiller_store.h"
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "components/dom_distiller/core/article_entry.h"
10 #include "sync/api/sync_change.h"
11 #include "sync/protocol/article_specifics.pb.h"
12 #include "sync/protocol/sync.pb.h"
14 using leveldb_proto::ProtoDatabase;
15 using sync_pb::ArticleSpecifics;
16 using sync_pb::EntitySpecifics;
17 using syncer::ModelType;
18 using syncer::SyncChange;
19 using syncer::SyncChangeList;
20 using syncer::SyncData;
21 using syncer::SyncDataList;
22 using syncer::SyncError;
23 using syncer::SyncMergeResult;
25 namespace dom_distiller {
27 DomDistillerStore::DomDistillerStore(
28 scoped_ptr<ProtoDatabase<ArticleEntry> > database,
29 const base::FilePath& database_dir)
30 : database_(database.Pass()),
31 database_loaded_(false),
32 weak_ptr_factory_(this) {
33 database_->Init(database_dir, base::Bind(&DomDistillerStore::OnDatabaseInit,
34 weak_ptr_factory_.GetWeakPtr()));
37 DomDistillerStore::DomDistillerStore(
38 scoped_ptr<ProtoDatabase<ArticleEntry> > database,
39 const std::vector<ArticleEntry>& initial_data,
40 const base::FilePath& database_dir)
41 : database_(database.Pass()),
42 database_loaded_(false),
43 model_(initial_data),
44 weak_ptr_factory_(this) {
45 database_->Init(database_dir, base::Bind(&DomDistillerStore::OnDatabaseInit,
46 weak_ptr_factory_.GetWeakPtr()));
49 DomDistillerStore::~DomDistillerStore() {}
51 // DomDistillerStoreInterface implementation.
52 syncer::SyncableService* DomDistillerStore::GetSyncableService() {
53 return this;
56 bool DomDistillerStore::GetEntryById(const std::string& entry_id,
57 ArticleEntry* entry) {
58 return model_.GetEntryById(entry_id, entry);
61 bool DomDistillerStore::GetEntryByUrl(const GURL& url, ArticleEntry* entry) {
62 return model_.GetEntryByUrl(url, entry);
65 bool DomDistillerStore::AddEntry(const ArticleEntry& entry) {
66 if (!database_loaded_) {
67 return false;
70 if (model_.GetEntryById(entry.entry_id(), NULL)) {
71 DVLOG(1) << "Already have entry with id " << entry.entry_id() << ".";
72 return false;
75 SyncChangeList changes_to_apply;
76 changes_to_apply.push_back(
77 SyncChange(FROM_HERE, SyncChange::ACTION_ADD, CreateLocalData(entry)));
79 SyncChangeList changes_applied;
80 SyncChangeList changes_missing;
82 ApplyChangesToModel(changes_to_apply, &changes_applied, &changes_missing);
84 DCHECK_EQ(size_t(0), changes_missing.size());
85 DCHECK_EQ(size_t(1), changes_applied.size());
87 ApplyChangesToSync(FROM_HERE, changes_applied);
88 ApplyChangesToDatabase(changes_applied);
90 return true;
93 bool DomDistillerStore::UpdateEntry(const ArticleEntry& entry) {
94 if (!database_loaded_) {
95 return false;
98 if (!model_.GetEntryById(entry.entry_id(), NULL)) {
99 DVLOG(1) << "No entry with id " << entry.entry_id() << " found.";
100 return false;
103 SyncChangeList changes_to_apply;
104 changes_to_apply.push_back(
105 SyncChange(FROM_HERE, SyncChange::ACTION_UPDATE, CreateLocalData(entry)));
107 SyncChangeList changes_applied;
108 SyncChangeList changes_missing;
110 ApplyChangesToModel(changes_to_apply, &changes_applied, &changes_missing);
112 if (changes_applied.size() != 1) {
113 DVLOG(1) << "Failed to update entry with id " << entry.entry_id() << ".";
114 return false;
117 ApplyChangesToSync(FROM_HERE, changes_applied);
118 ApplyChangesToDatabase(changes_applied);
120 return true;
123 bool DomDistillerStore::RemoveEntry(const ArticleEntry& entry) {
124 if (!database_loaded_) {
125 return false;
128 if (!model_.GetEntryById(entry.entry_id(), NULL)) {
129 DVLOG(1) << "No entry with id " << entry.entry_id() << " found.";
130 return false;
133 SyncChangeList changes_to_apply;
134 changes_to_apply.push_back(
135 SyncChange(FROM_HERE, SyncChange::ACTION_DELETE, CreateLocalData(entry)));
137 SyncChangeList changes_applied;
138 SyncChangeList changes_missing;
140 ApplyChangesToModel(changes_to_apply, &changes_applied, &changes_missing);
142 DCHECK_EQ(size_t(0), changes_missing.size());
143 DCHECK_EQ(size_t(1), changes_applied.size());
145 ApplyChangesToSync(FROM_HERE, changes_applied);
146 ApplyChangesToDatabase(changes_applied);
148 return true;
151 void DomDistillerStore::AddObserver(DomDistillerObserver* observer) {
152 observers_.AddObserver(observer);
155 void DomDistillerStore::RemoveObserver(DomDistillerObserver* observer) {
156 observers_.RemoveObserver(observer);
159 std::vector<ArticleEntry> DomDistillerStore::GetEntries() const {
160 return model_.GetEntries();
163 // syncer::SyncableService implementation.
164 SyncMergeResult DomDistillerStore::MergeDataAndStartSyncing(
165 ModelType type, const SyncDataList& initial_sync_data,
166 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
167 scoped_ptr<syncer::SyncErrorFactory> error_handler) {
168 DCHECK_EQ(syncer::ARTICLES, type);
169 DCHECK(!sync_processor_);
170 DCHECK(!error_factory_);
171 sync_processor_.reset(sync_processor.release());
172 error_factory_.reset(error_handler.release());
174 SyncChangeList database_changes;
175 SyncChangeList sync_changes;
176 SyncMergeResult result =
177 MergeDataWithModel(initial_sync_data, &database_changes, &sync_changes);
178 ApplyChangesToDatabase(database_changes);
179 ApplyChangesToSync(FROM_HERE, sync_changes);
181 return result;
184 void DomDistillerStore::StopSyncing(ModelType type) {
185 sync_processor_.reset();
186 error_factory_.reset();
189 SyncDataList DomDistillerStore::GetAllSyncData(ModelType type) const {
190 return model_.GetAllSyncData();
193 SyncError DomDistillerStore::ProcessSyncChanges(
194 const tracked_objects::Location& from_here,
195 const SyncChangeList& change_list) {
196 DCHECK(database_loaded_);
197 SyncChangeList database_changes;
198 SyncChangeList sync_changes;
199 ApplyChangesToModel(change_list, &database_changes, &sync_changes);
200 ApplyChangesToDatabase(database_changes);
201 DCHECK_EQ(size_t(0), sync_changes.size());
202 return SyncError();
205 void DomDistillerStore::NotifyObservers(const syncer::SyncChangeList& changes) {
206 if (observers_.might_have_observers() && changes.size() > 0) {
207 std::vector<DomDistillerObserver::ArticleUpdate> article_changes;
208 for (SyncChangeList::const_iterator it = changes.begin();
209 it != changes.end(); ++it) {
210 DomDistillerObserver::ArticleUpdate article_update;
211 switch (it->change_type()) {
212 case SyncChange::ACTION_ADD:
213 article_update.update_type = DomDistillerObserver::ArticleUpdate::ADD;
214 break;
215 case SyncChange::ACTION_UPDATE:
216 article_update.update_type =
217 DomDistillerObserver::ArticleUpdate::UPDATE;
218 break;
219 case SyncChange::ACTION_DELETE:
220 article_update.update_type =
221 DomDistillerObserver::ArticleUpdate::REMOVE;
222 break;
223 case SyncChange::ACTION_INVALID:
224 NOTREACHED();
225 break;
227 const ArticleEntry& entry = GetEntryFromChange(*it);
228 article_update.entry_id = entry.entry_id();
229 article_changes.push_back(article_update);
231 FOR_EACH_OBSERVER(DomDistillerObserver, observers_,
232 ArticleEntriesUpdated(article_changes));
236 void DomDistillerStore::ApplyChangesToModel(const SyncChangeList& changes,
237 SyncChangeList* changes_applied,
238 SyncChangeList* changes_missing) {
239 model_.ApplyChangesToModel(changes, changes_applied, changes_missing);
240 NotifyObservers(*changes_applied);
243 void DomDistillerStore::OnDatabaseInit(bool success) {
244 if (!success) {
245 DVLOG(1) << "DOM Distiller database init failed.";
246 database_.reset();
247 return;
249 database_->LoadEntries(base::Bind(&DomDistillerStore::OnDatabaseLoad,
250 weak_ptr_factory_.GetWeakPtr()));
253 void DomDistillerStore::OnDatabaseLoad(bool success,
254 scoped_ptr<EntryVector> entries) {
255 if (!success) {
256 DVLOG(1) << "DOM Distiller database load failed.";
257 database_.reset();
258 return;
260 database_loaded_ = true;
262 SyncDataList data;
263 for (EntryVector::iterator it = entries->begin(); it != entries->end();
264 ++it) {
265 data.push_back(CreateLocalData(*it));
267 SyncChangeList changes_applied;
268 SyncChangeList database_changes_needed;
269 MergeDataWithModel(data, &changes_applied, &database_changes_needed);
270 ApplyChangesToDatabase(database_changes_needed);
273 void DomDistillerStore::OnDatabaseSave(bool success) {
274 if (!success) {
275 DVLOG(1) << "DOM Distiller database save failed."
276 << " Disabling modifications and sync.";
277 database_.reset();
278 database_loaded_ = false;
279 StopSyncing(syncer::ARTICLES);
283 bool DomDistillerStore::ApplyChangesToSync(
284 const tracked_objects::Location& from_here,
285 const SyncChangeList& change_list) {
286 if (!sync_processor_) {
287 return false;
289 if (change_list.empty()) {
290 return true;
293 SyncError error = sync_processor_->ProcessSyncChanges(from_here, change_list);
294 if (error.IsSet()) {
295 StopSyncing(syncer::ARTICLES);
296 return false;
298 return true;
301 bool DomDistillerStore::ApplyChangesToDatabase(
302 const SyncChangeList& change_list) {
303 if (!database_loaded_) {
304 return false;
306 if (change_list.empty()) {
307 return true;
309 scoped_ptr<ProtoDatabase<ArticleEntry>::KeyEntryVector> entries_to_save(
310 new ProtoDatabase<ArticleEntry>::KeyEntryVector());
311 scoped_ptr<std::vector<std::string> > keys_to_remove(
312 new std::vector<std::string>());
314 for (SyncChangeList::const_iterator it = change_list.begin();
315 it != change_list.end(); ++it) {
316 if (it->change_type() == SyncChange::ACTION_DELETE) {
317 ArticleEntry entry = GetEntryFromChange(*it);
318 keys_to_remove->push_back(entry.entry_id());
319 } else {
320 ArticleEntry entry = GetEntryFromChange(*it);
321 entries_to_save->push_back(std::make_pair(entry.entry_id(), entry));
324 database_->UpdateEntries(entries_to_save.Pass(), keys_to_remove.Pass(),
325 base::Bind(&DomDistillerStore::OnDatabaseSave,
326 weak_ptr_factory_.GetWeakPtr()));
327 return true;
330 SyncMergeResult DomDistillerStore::MergeDataWithModel(
331 const SyncDataList& data, SyncChangeList* changes_applied,
332 SyncChangeList* changes_missing) {
333 DCHECK(changes_applied);
334 DCHECK(changes_missing);
336 SyncMergeResult result(syncer::ARTICLES);
337 result.set_num_items_before_association(model_.GetNumEntries());
339 SyncChangeList changes_to_apply;
340 model_.CalculateChangesForMerge(data, &changes_to_apply, changes_missing);
341 SyncError error;
342 ApplyChangesToModel(changes_to_apply, changes_applied, changes_missing);
344 int num_added = 0;
345 int num_modified = 0;
346 for (SyncChangeList::const_iterator it = changes_applied->begin();
347 it != changes_applied->end(); ++it) {
348 DCHECK(it->IsValid());
349 switch (it->change_type()) {
350 case SyncChange::ACTION_ADD:
351 num_added++;
352 break;
353 case SyncChange::ACTION_UPDATE:
354 num_modified++;
355 break;
356 default:
357 NOTREACHED();
360 result.set_num_items_added(num_added);
361 result.set_num_items_modified(num_modified);
362 result.set_num_items_deleted(0);
364 result.set_pre_association_version(0);
365 result.set_num_items_after_association(model_.GetNumEntries());
366 result.set_error(error);
368 return result;
371 } // namespace dom_distiller