- add sources.
[platform/framework/web/crosswalk.git] / src / components / dom_distiller / core / dom_distiller_store.cc
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 "components/dom_distiller/core/dom_distiller_store.h"
6
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"
13
14 using sync_pb::ArticleSpecifics;
15 using sync_pb::EntitySpecifics;
16 using syncer::ModelType;
17 using syncer::SyncChange;
18 using syncer::SyncChangeList;
19 using syncer::SyncData;
20 using syncer::SyncDataList;
21 using syncer::SyncError;
22 using syncer::SyncMergeResult;
23
24 namespace dom_distiller {
25
26 DomDistillerStore::DomDistillerStore(
27     scoped_ptr<DomDistillerDatabaseInterface> database,
28     const base::FilePath& database_dir)
29     : database_(database.Pass()),
30       database_loaded_(false),
31       weak_ptr_factory_(this) {
32   database_->Init(database_dir,
33                   base::Bind(&DomDistillerStore::OnDatabaseInit,
34                              weak_ptr_factory_.GetWeakPtr()));
35 }
36
37 DomDistillerStore::DomDistillerStore(
38     scoped_ptr<DomDistillerDatabaseInterface> 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,
46                   base::Bind(&DomDistillerStore::OnDatabaseInit,
47                              weak_ptr_factory_.GetWeakPtr()));
48 }
49
50 DomDistillerStore::~DomDistillerStore() {}
51
52 // DomDistillerStoreInterface implementation.
53 syncer::SyncableService* DomDistillerStore::GetSyncableService() {
54   return this;
55 }
56
57 bool DomDistillerStore::GetEntryById(const std::string& entry_id,
58                                      ArticleEntry* entry) {
59   return model_.GetEntryById(entry_id, entry);
60 }
61
62 bool DomDistillerStore::GetEntryByUrl(const GURL& url,
63                                      ArticleEntry* entry) {
64   return model_.GetEntryByUrl(url, entry);
65 }
66
67
68 bool DomDistillerStore::AddEntry(const ArticleEntry& entry) {
69   if (!database_loaded_) {
70     return false;
71   }
72
73   if (model_.GetEntryById(entry.entry_id(), NULL)) {
74     return false;
75   }
76
77   SyncChangeList changes_to_apply;
78   changes_to_apply.push_back(
79       SyncChange(FROM_HERE, SyncChange::ACTION_ADD, CreateLocalData(entry)));
80
81   SyncChangeList changes_applied;
82   SyncChangeList changes_missing;
83
84   if (!ApplyChangesToModel(
85            changes_to_apply, &changes_applied, &changes_missing)) {
86     return false;
87   }
88
89   DCHECK_EQ(size_t(0), changes_missing.size());
90   DCHECK_EQ(size_t(1), changes_applied.size());
91
92   ApplyChangesToSync(FROM_HERE, changes_applied);
93   ApplyChangesToDatabase(changes_applied);
94
95   return true;
96 }
97
98 std::vector<ArticleEntry> DomDistillerStore::GetEntries() const {
99   return model_.GetEntries();
100 }
101
102 // syncer::SyncableService implementation.
103 SyncMergeResult DomDistillerStore::MergeDataAndStartSyncing(
104     ModelType type,
105     const SyncDataList& initial_sync_data,
106     scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
107     scoped_ptr<syncer::SyncErrorFactory> error_handler) {
108   DCHECK_EQ(syncer::ARTICLES, type);
109   DCHECK(!sync_processor_);
110   DCHECK(!error_factory_);
111   sync_processor_.reset(sync_processor.release());
112   error_factory_.reset(error_handler.release());
113
114   SyncChangeList database_changes;
115   SyncChangeList sync_changes;
116   SyncMergeResult result =
117       MergeDataWithModel(initial_sync_data, &database_changes, &sync_changes);
118   ApplyChangesToDatabase(database_changes);
119   ApplyChangesToSync(FROM_HERE, sync_changes);
120
121   return result;
122 }
123
124 void DomDistillerStore::StopSyncing(ModelType type) {
125   sync_processor_.reset();
126   error_factory_.reset();
127 }
128
129 SyncDataList DomDistillerStore::GetAllSyncData(ModelType type) const {
130   return model_.GetAllSyncData();
131 }
132
133 SyncError DomDistillerStore::ProcessSyncChanges(
134     const tracked_objects::Location& from_here,
135     const SyncChangeList& change_list) {
136   DCHECK(database_loaded_);
137   SyncChangeList database_changes;
138   SyncChangeList sync_changes;
139   if (!ApplyChangesToModel(change_list, &database_changes, &sync_changes)) {
140     return SyncError(FROM_HERE,
141                      SyncError::DATATYPE_ERROR,
142                      "Applying changes to the DOM distiller model failed",
143                      syncer::ARTICLES);
144   }
145   ApplyChangesToDatabase(database_changes);
146   DCHECK_EQ(size_t(0), sync_changes.size());
147   return SyncError();
148 }
149
150 bool DomDistillerStore::ApplyChangesToModel(
151     const SyncChangeList& changes,
152     SyncChangeList* changes_applied,
153     SyncChangeList* changes_missing) {
154   DomDistillerModel::ChangeResult change_result =
155       model_.ApplyChangesToModel(changes, changes_applied, changes_missing);
156   if (change_result == DomDistillerModel::SUCCESS) {
157     return true;
158   }
159
160   LOG(WARNING) << "Applying changes to DOM distiller model failed with error "
161                << change_result;
162
163   database_.reset();
164   database_loaded_ = false;
165   StopSyncing(syncer::ARTICLES);
166   return false;
167 }
168
169 void DomDistillerStore::OnDatabaseInit(bool success) {
170   if (!success) {
171     LOG(INFO) << "DOM Distiller database init failed.";
172     database_.reset();
173     return;
174   }
175   database_->LoadEntries(base::Bind(&DomDistillerStore::OnDatabaseLoad,
176                                     weak_ptr_factory_.GetWeakPtr()));
177 }
178
179 void DomDistillerStore::OnDatabaseLoad(bool success,
180                                        scoped_ptr<EntryVector> entries) {
181   if (!success) {
182     LOG(INFO) << "DOM Distiller database load failed.";
183     database_.reset();
184     return;
185   }
186   database_loaded_ = true;
187
188   SyncDataList data;
189   for (EntryVector::iterator it = entries->begin(); it != entries->end();
190        ++it) {
191     data.push_back(CreateLocalData(*it));
192   }
193   SyncChangeList changes_applied;
194   SyncChangeList database_changes_needed;
195   MergeDataWithModel(data, &changes_applied, &database_changes_needed);
196   ApplyChangesToDatabase(database_changes_needed);
197 }
198
199 void DomDistillerStore::OnDatabaseSave(bool success) {
200   if (!success) {
201     LOG(INFO) << "DOM Distiller database save failed."
202               << " Disabling modifications and sync.";
203     database_.reset();
204     database_loaded_ = false;
205     StopSyncing(syncer::ARTICLES);
206   }
207 }
208
209 bool DomDistillerStore::ApplyChangesToSync(
210     const tracked_objects::Location& from_here,
211     const SyncChangeList& change_list) {
212   if (!sync_processor_) {
213     return false;
214   }
215   if (change_list.empty()) {
216     return true;
217   }
218
219   SyncError error = sync_processor_->ProcessSyncChanges(from_here, change_list);
220   if (error.IsSet()) {
221     StopSyncing(syncer::ARTICLES);
222     return false;
223   }
224   return true;
225 }
226
227 bool DomDistillerStore::ApplyChangesToDatabase(
228     const SyncChangeList& change_list) {
229   if (!database_loaded_) {
230     return false;
231   }
232   if (change_list.empty()) {
233     return true;
234   }
235   scoped_ptr<EntryVector> entries_to_save(new EntryVector());
236
237   for (SyncChangeList::const_iterator it = change_list.begin();
238        it != change_list.end();
239        ++it) {
240     entries_to_save->push_back(GetEntryFromChange(*it));
241   }
242   database_->SaveEntries(entries_to_save.Pass(),
243                          base::Bind(&DomDistillerStore::OnDatabaseSave,
244                                     weak_ptr_factory_.GetWeakPtr()));
245   return true;
246 }
247
248 SyncMergeResult DomDistillerStore::MergeDataWithModel(
249     const SyncDataList& data,
250     SyncChangeList* changes_applied,
251     SyncChangeList* changes_missing) {
252   DCHECK(changes_applied);
253   DCHECK(changes_missing);
254
255   SyncMergeResult result(syncer::ARTICLES);
256   result.set_num_items_before_association(model_.GetNumEntries());
257
258   SyncChangeList changes_to_apply;
259   model_.CalculateChangesForMerge(data, &changes_to_apply, changes_missing);
260   SyncError error;
261   if (!ApplyChangesToModel(
262            changes_to_apply, changes_applied, changes_missing)) {
263     error = SyncError(FROM_HERE,
264                       SyncError::DATATYPE_ERROR,
265                       "Applying changes to the DOM distiller model failed",
266                       syncer::ARTICLES);
267   }
268
269   int num_added = 0;
270   int num_modified = 0;
271   for (SyncChangeList::const_iterator it = changes_applied->begin();
272        it != changes_applied->end();
273        ++it) {
274     DCHECK(it->IsValid());
275     switch (it->change_type()) {
276       case SyncChange::ACTION_ADD:
277         num_added++;
278         break;
279       case SyncChange::ACTION_UPDATE:
280         num_modified++;
281         break;
282       default:
283         NOTREACHED();
284     }
285   }
286   result.set_num_items_added(num_added);
287   result.set_num_items_modified(num_modified);
288   result.set_num_items_deleted(0);
289
290   result.set_pre_association_version(0);
291   result.set_num_items_after_association(model_.GetNumEntries());
292   result.set_error(error);
293
294   return result;
295 }
296
297 }  // namespace dom_distiller