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"
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),
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() {
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_
) {
70 if (model_
.GetEntryById(entry
.entry_id(), NULL
)) {
71 DVLOG(1) << "Already have entry with id " << entry
.entry_id() << ".";
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
);
93 bool DomDistillerStore::UpdateEntry(const ArticleEntry
& entry
) {
94 if (!database_loaded_
) {
98 if (!model_
.GetEntryById(entry
.entry_id(), NULL
)) {
99 DVLOG(1) << "No entry with id " << entry
.entry_id() << " found.";
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() << ".";
117 ApplyChangesToSync(FROM_HERE
, changes_applied
);
118 ApplyChangesToDatabase(changes_applied
);
123 bool DomDistillerStore::RemoveEntry(const ArticleEntry
& entry
) {
124 if (!database_loaded_
) {
128 if (!model_
.GetEntryById(entry
.entry_id(), NULL
)) {
129 DVLOG(1) << "No entry with id " << entry
.entry_id() << " found.";
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
);
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
);
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());
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
;
215 case SyncChange::ACTION_UPDATE
:
216 article_update
.update_type
=
217 DomDistillerObserver::ArticleUpdate::UPDATE
;
219 case SyncChange::ACTION_DELETE
:
220 article_update
.update_type
=
221 DomDistillerObserver::ArticleUpdate::REMOVE
;
223 case SyncChange::ACTION_INVALID
:
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
) {
245 DVLOG(1) << "DOM Distiller database init failed.";
249 database_
->LoadEntries(base::Bind(&DomDistillerStore::OnDatabaseLoad
,
250 weak_ptr_factory_
.GetWeakPtr()));
253 void DomDistillerStore::OnDatabaseLoad(bool success
,
254 scoped_ptr
<EntryVector
> entries
) {
256 DVLOG(1) << "DOM Distiller database load failed.";
260 database_loaded_
= true;
263 for (EntryVector::iterator it
= entries
->begin(); it
!= entries
->end();
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
) {
275 DVLOG(1) << "DOM Distiller database save failed."
276 << " Disabling modifications and sync.";
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_
) {
289 if (change_list
.empty()) {
293 SyncError error
= sync_processor_
->ProcessSyncChanges(from_here
, change_list
);
295 StopSyncing(syncer::ARTICLES
);
301 bool DomDistillerStore::ApplyChangesToDatabase(
302 const SyncChangeList
& change_list
) {
303 if (!database_loaded_
) {
306 if (change_list
.empty()) {
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());
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()));
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
);
342 ApplyChangesToModel(changes_to_apply
, changes_applied
, changes_missing
);
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
:
353 case SyncChange::ACTION_UPDATE
:
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
);
371 } // namespace dom_distiller