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#ifndef COMPONENTS_DOM_DISTILLER_CORE_DOM_DISTILLER_STORE_H_
6#define COMPONENTS_DOM_DISTILLER_CORE_DOM_DISTILLER_STORE_H_
7
8#include <string>
9#include <vector>
10
11#include "base/containers/hash_tables.h"
12#include "base/memory/weak_ptr.h"
13#include "base/observer_list.h"
14#include "components/dom_distiller/core/article_entry.h"
15#include "components/dom_distiller/core/dom_distiller_model.h"
16#include "components/dom_distiller/core/dom_distiller_observer.h"
17#include "components/leveldb_proto/proto_database.h"
18#include "sync/api/sync_change.h"
19#include "sync/api/sync_data.h"
20#include "sync/api/sync_error.h"
21#include "sync/api/sync_error_factory.h"
22#include "sync/api/sync_merge_result.h"
23#include "sync/api/syncable_service.h"
24#include "url/gurl.h"
25
26namespace base {
27class FilePath;
28}
29
30namespace dom_distiller {
31
32// Interface for accessing the stored/synced DomDistiller entries.
33class DomDistillerStoreInterface {
34 public:
35  virtual ~DomDistillerStoreInterface() {}
36
37  // Gets the syncable service for this store or null if it is not synced.
38  virtual syncer::SyncableService* GetSyncableService() = 0;
39
40  virtual bool AddEntry(const ArticleEntry& entry) = 0;
41
42  // Returns false if |entry| is not present or |entry| was not updated.
43  virtual bool UpdateEntry(const ArticleEntry& entry) = 0;
44
45  virtual bool RemoveEntry(const ArticleEntry& entry) = 0;
46
47  // Lookup an ArticleEntry by ID or URL. Returns whether a corresponding entry
48  // was found. On success, if |entry| is not null, it will contain the entry.
49  virtual bool GetEntryById(const std::string& entry_id,
50                            ArticleEntry* entry) = 0;
51  virtual bool GetEntryByUrl(const GURL& url, ArticleEntry* entry) = 0;
52
53  // Gets a copy of all the current entries.
54  virtual std::vector<ArticleEntry> GetEntries() const = 0;
55
56  virtual void AddObserver(DomDistillerObserver* observer) = 0;
57
58  virtual void RemoveObserver(DomDistillerObserver* observer) = 0;
59};
60
61// Implements syncing/storing of DomDistiller entries. This keeps three
62// models of the DOM distiller data in sync: the local database, sync, and the
63// user (i.e. of DomDistillerStore). No changes are accepted while the local
64// database is loading. Once the local database has loaded, changes from any of
65// the three sources (technically just two, since changes don't come from the
66// database) are handled similarly:
67// 1. convert the change to a SyncChangeList.
68// 2. apply that change to the in-memory model, calculating what changed
69// (changes_applied) and what is missing--i.e. entries missing for a full merge,
70// conflict resolution for normal changes-- (changes_missing).
71// 3. send a message (possibly handled asynchronously) containing
72// changes_missing to the source of the change.
73// 4. send messages (possibly handled asynchronously) containing changes_applied
74// to the other (i.e. non-source) two models.
75// TODO(cjhopman): Support deleting entries.
76class DomDistillerStore : public syncer::SyncableService,
77                          public DomDistillerStoreInterface {
78 public:
79  typedef std::vector<ArticleEntry> EntryVector;
80
81  // Creates storage using the given database for local storage. Initializes the
82  // database with |database_dir|.
83  DomDistillerStore(
84      scoped_ptr<leveldb_proto::ProtoDatabase<ArticleEntry> > database,
85      const base::FilePath& database_dir);
86
87  // Creates storage using the given database for local storage. Initializes the
88  // database with |database_dir|.  Also initializes the internal model to
89  // |initial_model|.
90  DomDistillerStore(
91      scoped_ptr<leveldb_proto::ProtoDatabase<ArticleEntry> > database,
92      const std::vector<ArticleEntry>& initial_data,
93      const base::FilePath& database_dir);
94
95  virtual ~DomDistillerStore();
96
97  // DomDistillerStoreInterface implementation.
98  virtual syncer::SyncableService* GetSyncableService() OVERRIDE;
99  virtual bool AddEntry(const ArticleEntry& entry) OVERRIDE;
100  virtual bool UpdateEntry(const ArticleEntry& entry) OVERRIDE;
101  virtual bool RemoveEntry(const ArticleEntry& entry) OVERRIDE;
102  virtual bool GetEntryById(const std::string& entry_id,
103                            ArticleEntry* entry) OVERRIDE;
104  virtual bool GetEntryByUrl(const GURL& url, ArticleEntry* entry) OVERRIDE;
105  virtual std::vector<ArticleEntry> GetEntries() const OVERRIDE;
106  virtual void AddObserver(DomDistillerObserver* observer) OVERRIDE;
107  virtual void RemoveObserver(DomDistillerObserver* observer) OVERRIDE;
108
109  // syncer::SyncableService implementation.
110  virtual syncer::SyncMergeResult MergeDataAndStartSyncing(
111      syncer::ModelType type, const syncer::SyncDataList& initial_sync_data,
112      scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
113      scoped_ptr<syncer::SyncErrorFactory> error_handler) OVERRIDE;
114  virtual void StopSyncing(syncer::ModelType type) OVERRIDE;
115  virtual syncer::SyncDataList GetAllSyncData(
116      syncer::ModelType type) const OVERRIDE;
117  virtual syncer::SyncError ProcessSyncChanges(
118      const tracked_objects::Location& from_here,
119      const syncer::SyncChangeList& change_list) OVERRIDE;
120
121 private:
122  void OnDatabaseInit(bool success);
123  void OnDatabaseLoad(bool success, scoped_ptr<EntryVector> entries);
124  void OnDatabaseSave(bool success);
125
126  syncer::SyncMergeResult MergeDataWithModel(
127      const syncer::SyncDataList& data, syncer::SyncChangeList* changes_applied,
128      syncer::SyncChangeList* changes_missing);
129
130  // Convert a SyncDataList to a SyncChangeList of add or update changes based
131  // on the state of the in-memory model. Also calculate the entries missing
132  // from the SyncDataList.
133  void CalculateChangesForMerge(const syncer::SyncDataList& data,
134                                syncer::SyncChangeList* changes_to_apply,
135                                syncer::SyncChangeList* changes_missing);
136
137  bool ApplyChangesToSync(const tracked_objects::Location& from_here,
138                          const syncer::SyncChangeList& change_list);
139  bool ApplyChangesToDatabase(const syncer::SyncChangeList& change_list);
140
141  // Applies the changes to |model_|. If the model returns an error, disables
142  // syncing and database changes and returns false.
143  void ApplyChangesToModel(const syncer::SyncChangeList& change_list,
144                           syncer::SyncChangeList* changes_applied,
145                           syncer::SyncChangeList* changes_missing);
146
147  void NotifyObservers(const syncer::SyncChangeList& changes);
148
149  scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
150  scoped_ptr<syncer::SyncErrorFactory> error_factory_;
151  scoped_ptr<leveldb_proto::ProtoDatabase<ArticleEntry> > database_;
152  bool database_loaded_;
153  ObserverList<DomDistillerObserver> observers_;
154
155  DomDistillerModel model_;
156
157  base::WeakPtrFactory<DomDistillerStore> weak_ptr_factory_;
158
159  DISALLOW_COPY_AND_ASSIGN(DomDistillerStore);
160};
161
162}  // namespace dom_distiller
163
164#endif  // COMPONENTS_DOM_DISTILLER_CORE_DOM_DISTILLER_STORE_H_
165