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 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
;
24 namespace dom_distiller
{
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()));
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),
44 weak_ptr_factory_(this) {
45 database_
->Init(database_dir
,
46 base::Bind(&DomDistillerStore::OnDatabaseInit
,
47 weak_ptr_factory_
.GetWeakPtr()));
50 DomDistillerStore::~DomDistillerStore() {}
52 // DomDistillerStoreInterface implementation.
53 syncer::SyncableService
* DomDistillerStore::GetSyncableService() {
57 bool DomDistillerStore::GetEntryById(const std::string
& entry_id
,
58 ArticleEntry
* entry
) {
59 return model_
.GetEntryById(entry_id
, entry
);
62 bool DomDistillerStore::GetEntryByUrl(const GURL
& url
,
63 ArticleEntry
* entry
) {
64 return model_
.GetEntryByUrl(url
, entry
);
68 bool DomDistillerStore::AddEntry(const ArticleEntry
& entry
) {
69 if (!database_loaded_
) {
73 if (model_
.GetEntryById(entry
.entry_id(), NULL
)) {
74 DVLOG(1) << "Already have entry with id " << entry
.entry_id() << ".";
78 SyncChangeList changes_to_apply
;
79 changes_to_apply
.push_back(
80 SyncChange(FROM_HERE
, SyncChange::ACTION_ADD
, CreateLocalData(entry
)));
82 SyncChangeList changes_applied
;
83 SyncChangeList changes_missing
;
85 ApplyChangesToModel(changes_to_apply
, &changes_applied
, &changes_missing
);
87 DCHECK_EQ(size_t(0), changes_missing
.size());
88 DCHECK_EQ(size_t(1), changes_applied
.size());
90 ApplyChangesToSync(FROM_HERE
, changes_applied
);
91 ApplyChangesToDatabase(changes_applied
);
96 bool DomDistillerStore::UpdateEntry(const ArticleEntry
& entry
) {
97 if (!database_loaded_
) {
101 if (!model_
.GetEntryById(entry
.entry_id(), NULL
)) {
102 DVLOG(1) << "No entry with id " << entry
.entry_id() << " found.";
106 SyncChangeList changes_to_apply
;
107 changes_to_apply
.push_back(
108 SyncChange(FROM_HERE
, SyncChange::ACTION_UPDATE
, CreateLocalData(entry
)));
110 SyncChangeList changes_applied
;
111 SyncChangeList changes_missing
;
113 ApplyChangesToModel(changes_to_apply
, &changes_applied
, &changes_missing
);
115 if (changes_applied
.size() != 1) {
116 DVLOG(1) << "Failed to update entry with id " << entry
.entry_id() << ".";
120 ApplyChangesToSync(FROM_HERE
, changes_applied
);
121 ApplyChangesToDatabase(changes_applied
);
126 bool DomDistillerStore::RemoveEntry(const ArticleEntry
& entry
) {
127 if (!database_loaded_
) {
131 if (!model_
.GetEntryById(entry
.entry_id(), NULL
)) {
132 DVLOG(1) << "No entry with id " << entry
.entry_id() << " found.";
136 SyncChangeList changes_to_apply
;
137 changes_to_apply
.push_back(
138 SyncChange(FROM_HERE
, SyncChange::ACTION_DELETE
, CreateLocalData(entry
)));
140 SyncChangeList changes_applied
;
141 SyncChangeList changes_missing
;
143 ApplyChangesToModel(changes_to_apply
, &changes_applied
, &changes_missing
);
145 DCHECK_EQ(size_t(0), changes_missing
.size());
146 DCHECK_EQ(size_t(1), changes_applied
.size());
148 ApplyChangesToSync(FROM_HERE
, changes_applied
);
149 ApplyChangesToDatabase(changes_applied
);
154 void DomDistillerStore::AddObserver(DomDistillerObserver
* observer
) {
155 observers_
.AddObserver(observer
);
158 void DomDistillerStore::RemoveObserver(DomDistillerObserver
* observer
) {
159 observers_
.RemoveObserver(observer
);
162 std::vector
<ArticleEntry
> DomDistillerStore::GetEntries() const {
163 return model_
.GetEntries();
166 // syncer::SyncableService implementation.
167 SyncMergeResult
DomDistillerStore::MergeDataAndStartSyncing(
169 const SyncDataList
& initial_sync_data
,
170 scoped_ptr
<syncer::SyncChangeProcessor
> sync_processor
,
171 scoped_ptr
<syncer::SyncErrorFactory
> error_handler
) {
172 DCHECK_EQ(syncer::ARTICLES
, type
);
173 DCHECK(!sync_processor_
);
174 DCHECK(!error_factory_
);
175 sync_processor_
.reset(sync_processor
.release());
176 error_factory_
.reset(error_handler
.release());
178 SyncChangeList database_changes
;
179 SyncChangeList sync_changes
;
180 SyncMergeResult result
=
181 MergeDataWithModel(initial_sync_data
, &database_changes
, &sync_changes
);
182 ApplyChangesToDatabase(database_changes
);
183 ApplyChangesToSync(FROM_HERE
, sync_changes
);
188 void DomDistillerStore::StopSyncing(ModelType type
) {
189 sync_processor_
.reset();
190 error_factory_
.reset();
193 SyncDataList
DomDistillerStore::GetAllSyncData(ModelType type
) const {
194 return model_
.GetAllSyncData();
197 SyncError
DomDistillerStore::ProcessSyncChanges(
198 const tracked_objects::Location
& from_here
,
199 const SyncChangeList
& change_list
) {
200 DCHECK(database_loaded_
);
201 SyncChangeList database_changes
;
202 SyncChangeList sync_changes
;
203 ApplyChangesToModel(change_list
, &database_changes
, &sync_changes
);
204 ApplyChangesToDatabase(database_changes
);
205 DCHECK_EQ(size_t(0), sync_changes
.size());
209 void DomDistillerStore::NotifyObservers(const syncer::SyncChangeList
& changes
) {
210 if (observers_
.might_have_observers() && changes
.size() > 0) {
211 std::vector
<DomDistillerObserver::ArticleUpdate
> article_changes
;
212 for (SyncChangeList::const_iterator it
= changes
.begin();
215 DomDistillerObserver::ArticleUpdate article_update
;
216 switch (it
->change_type()) {
217 case SyncChange::ACTION_ADD
:
218 article_update
.update_type
= DomDistillerObserver::ArticleUpdate::ADD
;
220 case SyncChange::ACTION_UPDATE
:
221 article_update
.update_type
=
222 DomDistillerObserver::ArticleUpdate::UPDATE
;
224 case SyncChange::ACTION_DELETE
:
225 article_update
.update_type
=
226 DomDistillerObserver::ArticleUpdate::REMOVE
;
228 case SyncChange::ACTION_INVALID
:
232 const ArticleEntry
& entry
= GetEntryFromChange(*it
);
233 article_update
.entry_id
= entry
.entry_id();
234 article_changes
.push_back(article_update
);
236 FOR_EACH_OBSERVER(DomDistillerObserver
,
238 ArticleEntriesUpdated(article_changes
));
242 void DomDistillerStore::ApplyChangesToModel(
243 const SyncChangeList
& changes
,
244 SyncChangeList
* changes_applied
,
245 SyncChangeList
* changes_missing
) {
246 model_
.ApplyChangesToModel(changes
, changes_applied
, changes_missing
);
247 NotifyObservers(*changes_applied
);
250 void DomDistillerStore::OnDatabaseInit(bool success
) {
252 DVLOG(1) << "DOM Distiller database init failed.";
256 database_
->LoadEntries(base::Bind(&DomDistillerStore::OnDatabaseLoad
,
257 weak_ptr_factory_
.GetWeakPtr()));
260 void DomDistillerStore::OnDatabaseLoad(bool success
,
261 scoped_ptr
<EntryVector
> entries
) {
263 DVLOG(1) << "DOM Distiller database load failed.";
267 database_loaded_
= true;
270 for (EntryVector::iterator it
= entries
->begin(); it
!= entries
->end();
272 data
.push_back(CreateLocalData(*it
));
274 SyncChangeList changes_applied
;
275 SyncChangeList database_changes_needed
;
276 MergeDataWithModel(data
, &changes_applied
, &database_changes_needed
);
277 ApplyChangesToDatabase(database_changes_needed
);
280 void DomDistillerStore::OnDatabaseSave(bool success
) {
282 DVLOG(1) << "DOM Distiller database save failed."
283 << " Disabling modifications and sync.";
285 database_loaded_
= false;
286 StopSyncing(syncer::ARTICLES
);
290 bool DomDistillerStore::ApplyChangesToSync(
291 const tracked_objects::Location
& from_here
,
292 const SyncChangeList
& change_list
) {
293 if (!sync_processor_
) {
296 if (change_list
.empty()) {
300 SyncError error
= sync_processor_
->ProcessSyncChanges(from_here
, change_list
);
302 StopSyncing(syncer::ARTICLES
);
308 bool DomDistillerStore::ApplyChangesToDatabase(
309 const SyncChangeList
& change_list
) {
310 if (!database_loaded_
) {
313 if (change_list
.empty()) {
316 scoped_ptr
<EntryVector
> entries_to_save(new EntryVector());
317 scoped_ptr
<EntryVector
> entries_to_remove(new EntryVector());
319 for (SyncChangeList::const_iterator it
= change_list
.begin();
320 it
!= change_list
.end();
322 if (it
->change_type() == SyncChange::ACTION_DELETE
)
323 entries_to_remove
->push_back(GetEntryFromChange(*it
));
325 entries_to_save
->push_back(GetEntryFromChange(*it
));
327 database_
->UpdateEntries(entries_to_save
.Pass(),
328 entries_to_remove
.Pass(),
329 base::Bind(&DomDistillerStore::OnDatabaseSave
,
330 weak_ptr_factory_
.GetWeakPtr()));
334 SyncMergeResult
DomDistillerStore::MergeDataWithModel(
335 const SyncDataList
& data
,
336 SyncChangeList
* changes_applied
,
337 SyncChangeList
* changes_missing
) {
338 DCHECK(changes_applied
);
339 DCHECK(changes_missing
);
341 SyncMergeResult
result(syncer::ARTICLES
);
342 result
.set_num_items_before_association(model_
.GetNumEntries());
344 SyncChangeList changes_to_apply
;
345 model_
.CalculateChangesForMerge(data
, &changes_to_apply
, changes_missing
);
347 ApplyChangesToModel(changes_to_apply
, changes_applied
, changes_missing
);
350 int num_modified
= 0;
351 for (SyncChangeList::const_iterator it
= changes_applied
->begin();
352 it
!= changes_applied
->end();
354 DCHECK(it
->IsValid());
355 switch (it
->change_type()) {
356 case SyncChange::ACTION_ADD
:
359 case SyncChange::ACTION_UPDATE
:
366 result
.set_num_items_added(num_added
);
367 result
.set_num_items_modified(num_modified
);
368 result
.set_num_items_deleted(0);
370 result
.set_pre_association_version(0);
371 result
.set_num_items_after_association(model_
.GetNumEntries());
372 result
.set_error(error
);
377 } // namespace dom_distiller