summaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorcjhopman@chromium.org <cjhopman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-22 21:17:56 +0000
committercjhopman@chromium.org <cjhopman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-22 21:17:56 +0000
commit96a45d875ae0cb3a26c2260c711e651f363985b9 (patch)
tree1b82776755cd216f54eabc07560249fad90de198 /components
parent5fd860aba4fb327e73ce1824a037c83bdb5db1ce (diff)
downloadchromium_src-96a45d875ae0cb3a26c2260c711e651f363985b9.zip
chromium_src-96a45d875ae0cb3a26c2260c711e651f363985b9.tar.gz
chromium_src-96a45d875ae0cb3a26c2260c711e651f363985b9.tar.bz2
Extract the in-memory model from DomDistillerStore
This moves interaction with the in-memory model from the store to its own class: DomDistillerModel. This also adds two ways to lookup entries to both the store and the model, GetEntryById and GetEntryByUrl. BUG=288015 Review URL: https://codereview.chromium.org/26574002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@230207 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components')
-rw-r--r--components/components_tests.gyp1
-rw-r--r--components/dom_distiller.gypi2
-rw-r--r--components/dom_distiller/core/article_entry.cc21
-rw-r--r--components/dom_distiller/core/article_entry.h10
-rw-r--r--components/dom_distiller/core/dom_distiller_model.cc208
-rw-r--r--components/dom_distiller/core/dom_distiller_model.h94
-rw-r--r--components/dom_distiller/core/dom_distiller_model_unittest.cc131
-rw-r--r--components/dom_distiller/core/dom_distiller_store.cc177
-rw-r--r--components/dom_distiller/core/dom_distiller_store.h36
-rw-r--r--components/dom_distiller/core/dom_distiller_store_unittest.cc17
10 files changed, 555 insertions, 142 deletions
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index 98f3901..2b6b22f 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -23,6 +23,7 @@
'browser_context_keyed_service/dependency_graph_unittest.cc',
'dom_distiller/core/distiller_url_fetcher_unittest.cc',
'dom_distiller/core/dom_distiller_database_unittest.cc',
+ 'dom_distiller/core/dom_distiller_model_unittest.cc',
'dom_distiller/core/dom_distiller_store_unittest.cc',
'dom_distiller/core/article_entry_unittest.cc',
'json_schema/json_schema_validator_unittest.cc',
diff --git a/components/dom_distiller.gypi b/components/dom_distiller.gypi
index 4efe807..abece3f 100644
--- a/components/dom_distiller.gypi
+++ b/components/dom_distiller.gypi
@@ -82,6 +82,8 @@
'dom_distiller/core/dom_distiller_constants.h',
'dom_distiller/core/dom_distiller_database.cc',
'dom_distiller/core/dom_distiller_database.h',
+ 'dom_distiller/core/dom_distiller_model.cc',
+ 'dom_distiller/core/dom_distiller_model.h',
'dom_distiller/core/dom_distiller_service.cc',
'dom_distiller/core/dom_distiller_service.h',
'dom_distiller/core/dom_distiller_store.cc',
diff --git a/components/dom_distiller/core/article_entry.cc b/components/dom_distiller/core/article_entry.cc
index 2e370a4..1550caf 100644
--- a/components/dom_distiller/core/article_entry.cc
+++ b/components/dom_distiller/core/article_entry.cc
@@ -5,6 +5,7 @@
#include "components/dom_distiller/core/article_entry.h"
#include "base/logging.h"
+#include "sync/api/sync_change.h"
using sync_pb::EntitySpecifics;
using sync_pb::ArticlePage;
@@ -45,4 +46,24 @@ EntitySpecifics SpecificsFromEntry(const ArticleEntry& entry) {
return specifics;
}
+ArticleEntry GetEntryFromChange(const syncer::SyncChange& change) {
+ DCHECK(change.IsValid());
+ DCHECK(change.sync_data().IsValid());
+ return EntryFromSpecifics(change.sync_data().GetSpecifics());
+}
+
+std::string GetEntryIdFromSyncData(const syncer::SyncData& data) {
+ const EntitySpecifics& entity = data.GetSpecifics();
+ DCHECK(entity.has_article());
+ const ArticleSpecifics& specifics = entity.article();
+ DCHECK(specifics.has_entry_id());
+ return specifics.entry_id();
+}
+
+syncer::SyncData CreateLocalData(const ArticleEntry& entry) {
+ EntitySpecifics specifics = SpecificsFromEntry(entry);
+ const std::string& entry_id = entry.entry_id();
+ return syncer::SyncData::CreateLocalData(entry_id, entry_id, specifics);
+}
+
} // namespace dom_distiller
diff --git a/components/dom_distiller/core/article_entry.h b/components/dom_distiller/core/article_entry.h
index 3811612..388dc51 100644
--- a/components/dom_distiller/core/article_entry.h
+++ b/components/dom_distiller/core/article_entry.h
@@ -5,9 +5,14 @@
#ifndef COMPONENTS_DOM_DISTILLER_CORE_ARTICLE_ENTRY_H_
#define COMPONENTS_DOM_DISTILLER_CORE_ARTICLE_ENTRY_H_
+#include "sync/api/sync_data.h"
#include "sync/protocol/article_specifics.pb.h"
#include "sync/protocol/sync.pb.h"
+namespace syncer {
+class SyncChange;
+}
+
namespace dom_distiller {
typedef sync_pb::ArticleSpecifics ArticleEntry;
@@ -20,6 +25,11 @@ bool AreEntriesEqual(const ArticleEntry& left, const ArticleEntry& right);
sync_pb::EntitySpecifics SpecificsFromEntry(const ArticleEntry& entry);
ArticleEntry EntryFromSpecifics(const sync_pb::EntitySpecifics& specifics);
+ArticleEntry GetEntryFromChange(const syncer::SyncChange& change);
+std::string GetEntryIdFromSyncData(const syncer::SyncData& data);
+syncer::SyncData CreateLocalData(const ArticleEntry& entry);
+
+
} // namespace dom_distiller
#endif
diff --git a/components/dom_distiller/core/dom_distiller_model.cc b/components/dom_distiller/core/dom_distiller_model.cc
new file mode 100644
index 0000000..79a95ab
--- /dev/null
+++ b/components/dom_distiller/core/dom_distiller_model.cc
@@ -0,0 +1,208 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/dom_distiller/core/dom_distiller_model.h"
+
+using syncer::SyncChange;
+using syncer::SyncChangeList;
+using syncer::SyncData;
+using syncer::SyncDataList;
+
+namespace dom_distiller {
+
+DomDistillerModel::DomDistillerModel()
+ : next_key_(1) {}
+
+DomDistillerModel::DomDistillerModel(
+ const std::vector<ArticleEntry>& initial_data)
+ : next_key_(1) {
+ for (size_t i = 0; i < initial_data.size(); ++i) {
+ AddEntry(initial_data[i]);
+ }
+}
+
+DomDistillerModel::~DomDistillerModel() {}
+
+bool DomDistillerModel::GetEntryById(const std::string& entry_id,
+ ArticleEntry* entry) const {
+ KeyType key = 0;
+ if (!GetKeyById(entry_id, &key)) {
+ return false;
+ }
+ GetEntryByKey(key, entry);
+ return true;
+}
+
+bool DomDistillerModel::GetEntryByUrl(const GURL& url,
+ ArticleEntry* entry) const {
+ KeyType key = 0;
+ if (!GetKeyByUrl(url, &key)) {
+ return false;
+ }
+ GetEntryByKey(key, entry);
+ return true;
+}
+
+bool DomDistillerModel::GetKeyById(const std::string& entry_id,
+ KeyType* key) const {
+ StringToKeyMap::const_iterator it = entry_id_to_key_map_.find(entry_id);
+ if (it == entry_id_to_key_map_.end()) {
+ return false;
+ }
+ if (key != NULL) {
+ *key = it->second;
+ }
+ return true;
+}
+
+bool DomDistillerModel::GetKeyByUrl(const GURL& url, KeyType* key) const {
+ StringToKeyMap::const_iterator it = url_to_key_map_.find(url.spec());
+ if (it == url_to_key_map_.end()) {
+ return false;
+ }
+ if (key != NULL) {
+ *key = it->second;
+ }
+ return true;
+}
+
+void DomDistillerModel::GetEntryByKey(KeyType key, ArticleEntry* entry) const {
+ if (entry != NULL) {
+ EntryMap::const_iterator it = entries_.find(key);
+ DCHECK(it != entries_.end());
+ *entry = it->second;
+ }
+}
+
+size_t DomDistillerModel::GetNumEntries() const {
+ return entries_.size();
+}
+
+std::vector<ArticleEntry> DomDistillerModel::GetEntries() const {
+ std::vector<ArticleEntry> entries_list;
+ for (EntryMap::const_iterator it = entries_.begin(); it != entries_.end();
+ ++it) {
+ entries_list.push_back(it->second);
+ }
+ return entries_list;
+}
+
+SyncDataList DomDistillerModel::GetAllSyncData() const {
+ SyncDataList data;
+ for (EntryMap::const_iterator it = entries_.begin(); it != entries_.end();
+ ++it) {
+ data.push_back(CreateLocalData(it->second));
+ }
+ return data;
+}
+
+void DomDistillerModel::CalculateChangesForMerge(
+ const SyncDataList& data,
+ SyncChangeList* changes_to_apply,
+ SyncChangeList* changes_missing) {
+ typedef base::hash_set<std::string> StringSet;
+ StringSet entries_to_change;
+ for (SyncDataList::const_iterator it = data.begin(); it != data.end(); ++it) {
+ std::string entry_id = GetEntryIdFromSyncData(*it);
+ std::pair<StringSet::iterator, bool> insert_result =
+ entries_to_change.insert(entry_id);
+
+ DCHECK(insert_result.second);
+
+ SyncChange::SyncChangeType change_type = SyncChange::ACTION_ADD;
+ if (GetEntryById(entry_id, NULL)) {
+ change_type = SyncChange::ACTION_UPDATE;
+ }
+ changes_to_apply->push_back(SyncChange(FROM_HERE, change_type, *it));
+ }
+
+ for (EntryMap::const_iterator it = entries_.begin(); it != entries_.end();
+ ++it) {
+ if (entries_to_change.find(it->second.entry_id()) ==
+ entries_to_change.end()) {
+ changes_missing->push_back(SyncChange(
+ FROM_HERE, SyncChange::ACTION_ADD, CreateLocalData(it->second)));
+ }
+ }
+}
+
+DomDistillerModel::ChangeResult DomDistillerModel::ApplyChangesToModel(
+ const SyncChangeList& changes,
+ SyncChangeList* changes_applied,
+ SyncChangeList* changes_missing) {
+ DCHECK(changes_applied);
+ DCHECK(changes_missing);
+
+ ChangeResult result = SUCCESS;
+
+ for (SyncChangeList::const_iterator it = changes.begin(); it != changes.end();
+ ++it) {
+ result = ApplyChangeToModel(*it, changes_applied, changes_missing);
+ if (result != SUCCESS) {
+ break;
+ }
+ }
+ return result;
+}
+
+void DomDistillerModel::AddEntry(const ArticleEntry& entry) {
+ const std::string& entry_id = entry.entry_id();
+ KeyType key = next_key_++;
+ DCHECK(!GetKeyById(entry_id, NULL));
+ entries_.insert(std::make_pair(key, entry));
+ entry_id_to_key_map_.insert(std::make_pair(entry_id, key));
+ for (int i = 0; i < entry.pages_size(); ++i) {
+ url_to_key_map_.insert(std::make_pair(entry.pages(i).url(), key));
+ }
+}
+
+void DomDistillerModel::RemoveEntry(const ArticleEntry& entry) {
+ const std::string& entry_id = entry.entry_id();
+ KeyType key = 0;
+ bool success = GetKeyById(entry_id, &key);
+ DCHECK(success);
+
+ entries_.erase(key);
+ entry_id_to_key_map_.erase(entry_id);
+ for (int i = 0; i < entry.pages_size(); ++i) {
+ url_to_key_map_.erase(entry.pages(i).url());
+ }
+}
+
+DomDistillerModel::ChangeResult DomDistillerModel::ApplyChangeToModel(
+ const SyncChange& change,
+ SyncChangeList* changes_applied,
+ SyncChangeList* changes_missing) {
+ DCHECK(change.IsValid());
+ DCHECK(changes_applied);
+ DCHECK(changes_missing);
+
+ const std::string& entry_id = GetEntryIdFromSyncData(change.sync_data());
+
+ if (change.change_type() == SyncChange::ACTION_DELETE) {
+ // TODO(cjhopman): Support delete.
+ NOTIMPLEMENTED();
+ return DELETE_NOT_SUPPORTED;
+ }
+
+ ArticleEntry entry = GetEntryFromChange(change);
+ ArticleEntry current_entry;
+ if (!GetEntryById(entry_id, &current_entry)) {
+ AddEntry(entry);
+ changes_applied->push_back(SyncChange(
+ change.location(), SyncChange::ACTION_ADD, change.sync_data()));
+ } else {
+ if (!AreEntriesEqual(current_entry, entry)) {
+ // Currently, conflicts are simply resolved by accepting the last one to
+ // arrive.
+ RemoveEntry(current_entry);
+ AddEntry(entry);
+ changes_applied->push_back(SyncChange(
+ change.location(), SyncChange::ACTION_UPDATE, change.sync_data()));
+ }
+ }
+ return SUCCESS;
+}
+
+} // namespace dom_distiller
diff --git a/components/dom_distiller/core/dom_distiller_model.h b/components/dom_distiller/core/dom_distiller_model.h
new file mode 100644
index 0000000..1bc2ec2
--- /dev/null
+++ b/components/dom_distiller/core/dom_distiller_model.h
@@ -0,0 +1,94 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_DOM_DISTILLER_CORE_DOM_DISTILLER_MODEL_H_
+#define COMPONENTS_DOM_DISTILLER_CORE_DOM_DISTILLER_MODEL_H_
+
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/id_map.h"
+#include "components/dom_distiller/core/article_entry.h"
+#include "sync/api/sync_change.h"
+#include "sync/api/sync_change_processor.h" // syncer::SyncChangeList
+#include "sync/api/sync_data.h"
+#include "url/gurl.h"
+
+namespace dom_distiller {
+
+// This stores the in-memory model of the DOM distiller list. Entries can be
+// looked up by URL or by entry_id.
+// The model assumes that an URL corresponds to at most a single entry. If this
+// assumption is broken, lookup by URL may return unexpected results.
+class DomDistillerModel {
+ public:
+ enum ChangeResult {
+ SUCCESS = 0,
+ DELETE_NOT_SUPPORTED,
+ };
+
+ DomDistillerModel();
+ explicit DomDistillerModel(const std::vector<ArticleEntry>& initial_data);
+
+ ~DomDistillerModel();
+
+ // Lookup an ArticleEntry by ID or URL. Returns whether a corresponding entry
+ // was found. On success, if |entry| is not null, it will contain the entry.
+ bool GetEntryById(const std::string& entry_id, ArticleEntry* entry) const;
+ bool GetEntryByUrl(const GURL& url, ArticleEntry* entry) const;
+
+ std::vector<ArticleEntry> GetEntries() const;
+ size_t GetNumEntries() const;
+
+ syncer::SyncDataList GetAllSyncData() const;
+
+ // Convert a SyncDataList to a SyncChangeList of add or update changes based
+ // on the state of the model. Also calculate the entries missing from the
+ // SyncDataList.
+ void CalculateChangesForMerge(const syncer::SyncDataList& data,
+ syncer::SyncChangeList* changes_to_apply,
+ syncer::SyncChangeList* changes_missing);
+
+ // Applies the change list to the model, appending the actual changes made to
+ // the model to |changes_applied|. If conflict resolution does not apply the
+ // requested change, then adds the "diff" between what was requested and what
+ // was actually applied to |changes_missing|.
+ // Note: Currently conflicts are resolved by just applying the requested
+ // change. This means nothing will be added to |changes_missing|.
+ ChangeResult ApplyChangesToModel(const syncer::SyncChangeList& change_list,
+ syncer::SyncChangeList* changes_applied,
+ syncer::SyncChangeList* changes_missing);
+
+ private:
+ typedef int32 KeyType;
+ typedef base::hash_map<KeyType, ArticleEntry> EntryMap;
+ typedef base::hash_map<std::string, KeyType> StringToKeyMap;
+
+ void AddEntry(const ArticleEntry& entry);
+ void RemoveEntry(const ArticleEntry& entry);
+
+ // Lookup an entry's key by ID or URL. Returns whether a corresponding key was
+ // found. On success, if |key| is not null, it will contain the entry.
+ bool GetKeyById(const std::string& entry_id, KeyType* key) const;
+ bool GetKeyByUrl(const GURL& url, KeyType* key) const;
+
+ // If |entry| is not null, assigns the entry for |key| to it. |key| must map
+ // to an entry in |entries_|.
+ void GetEntryByKey(KeyType key, ArticleEntry* entry) const;
+
+ ChangeResult ApplyChangeToModel(const syncer::SyncChange& change,
+ syncer::SyncChangeList* changes_applied,
+ syncer::SyncChangeList* changes_missing);
+
+ KeyType next_key_;
+ EntryMap entries_;
+ StringToKeyMap url_to_key_map_;
+ StringToKeyMap entry_id_to_key_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(DomDistillerModel);
+};
+
+} // namespace dom_distiller
+
+#endif
diff --git a/components/dom_distiller/core/dom_distiller_model_unittest.cc b/components/dom_distiller/core/dom_distiller_model_unittest.cc
new file mode 100644
index 0000000..0220439
--- /dev/null
+++ b/components/dom_distiller/core/dom_distiller_model_unittest.cc
@@ -0,0 +1,131 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/dom_distiller/core/dom_distiller_model.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace dom_distiller {
+
+TEST(DomDistillerModelTest, TestGetByEntryId) {
+ ArticleEntry entry1;
+ entry1.set_entry_id("id1");
+ entry1.set_title("title1");
+ ArticleEntry entry2;
+ entry2.set_entry_id("id2");
+ entry2.set_title("title1");
+
+ std::vector<ArticleEntry> initial_model;
+ initial_model.push_back(entry1);
+ initial_model.push_back(entry2);
+
+ DomDistillerModel model(initial_model);
+
+ ArticleEntry found_entry;
+ EXPECT_TRUE(model.GetEntryById(entry1.entry_id(), &found_entry));
+ ASSERT_TRUE(IsEntryValid(found_entry));
+ EXPECT_TRUE(AreEntriesEqual(entry1, found_entry));
+
+ EXPECT_TRUE(model.GetEntryById(entry2.entry_id(), &found_entry));
+ ASSERT_TRUE(IsEntryValid(found_entry));
+ EXPECT_TRUE(AreEntriesEqual(entry2, found_entry));
+
+ EXPECT_FALSE(model.GetEntryById("some_other_id", NULL));
+}
+
+TEST(DomDistillerModelTest, TestGetByUrl) {
+ ArticleEntry entry1;
+ entry1.set_entry_id("id1");
+ entry1.set_title("title1");
+ ArticleEntryPage* page1 = entry1.add_pages();
+ page1->set_url("http://example.com/1");
+ ArticleEntryPage* page2 = entry1.add_pages();
+ page2->set_url("http://example.com/2");
+
+ ArticleEntry entry2;
+ entry2.set_entry_id("id2");
+ entry2.set_title("title1");
+ ArticleEntryPage* page3 = entry2.add_pages();
+ page3->set_url("http://example.com/a1");
+
+ std::vector<ArticleEntry> initial_model;
+ initial_model.push_back(entry1);
+ initial_model.push_back(entry2);
+
+ DomDistillerModel model(initial_model);
+
+ ArticleEntry found_entry;
+ EXPECT_TRUE(model.GetEntryByUrl(GURL(page1->url()), &found_entry));
+ ASSERT_TRUE(IsEntryValid(found_entry));
+ EXPECT_TRUE(AreEntriesEqual(entry1, found_entry));
+
+ EXPECT_TRUE(model.GetEntryByUrl(GURL(page2->url()), &found_entry));
+ ASSERT_TRUE(IsEntryValid(found_entry));
+ EXPECT_TRUE(AreEntriesEqual(entry1, found_entry));
+
+ EXPECT_TRUE(model.GetEntryByUrl(GURL(page3->url()), &found_entry));
+ ASSERT_TRUE(IsEntryValid(found_entry));
+ EXPECT_TRUE(AreEntriesEqual(entry2, found_entry));
+
+ EXPECT_FALSE(model.GetEntryByUrl(GURL("http://example.com/foo"), NULL));
+}
+
+// This test ensures that the model handles the case where an URL maps to
+// multiple entries. In that case, the model is allowed to have an inconsistent
+// url-to-entry mapping, but it should not fail in other ways (i.e. id-to-entry
+// should be correct, shouldn't crash).
+TEST(DomDistillerModelTest, TestUrlToMultipleEntries) {
+ ArticleEntry entry1;
+ entry1.set_entry_id("id1");
+ entry1.set_title("title1");
+ ArticleEntryPage* page1 = entry1.add_pages();
+ page1->set_url("http://example.com/1");
+ ArticleEntryPage* page2 = entry1.add_pages();
+ page2->set_url("http://example.com/2");
+
+ ArticleEntry entry2;
+ entry2.set_entry_id("id2");
+ entry2.set_title("title1");
+ ArticleEntryPage* page3 = entry2.add_pages();
+ page3->set_url("http://example.com/1");
+
+ std::vector<ArticleEntry> initial_model;
+ initial_model.push_back(entry1);
+ initial_model.push_back(entry2);
+
+ DomDistillerModel model(initial_model);
+
+ EXPECT_TRUE(model.GetEntryByUrl(GURL(page1->url()), NULL));
+ EXPECT_TRUE(model.GetEntryByUrl(GURL(page2->url()), NULL));
+ EXPECT_TRUE(model.GetEntryByUrl(GURL(page3->url()), NULL));
+
+ ArticleEntry found_entry;
+ EXPECT_TRUE(model.GetEntryById(entry1.entry_id(), &found_entry));
+ ASSERT_TRUE(IsEntryValid(found_entry));
+ EXPECT_TRUE(AreEntriesEqual(entry1, found_entry));
+
+ EXPECT_TRUE(model.GetEntryById(entry2.entry_id(), &found_entry));
+ ASSERT_TRUE(IsEntryValid(found_entry));
+ EXPECT_TRUE(AreEntriesEqual(entry2, found_entry));
+
+ syncer::SyncChangeList changes_to_apply;
+ syncer::SyncChangeList changes_applied;
+ syncer::SyncChangeList changes_missing;
+
+ entry2.mutable_pages(0)->set_url("http://example.com/foo1");
+ changes_to_apply.push_back(syncer::SyncChange(
+ FROM_HERE, syncer::SyncChange::ACTION_UPDATE, CreateLocalData(entry2)));
+ model.ApplyChangesToModel(
+ changes_to_apply, &changes_applied, &changes_missing);
+
+ EXPECT_TRUE(model.GetEntryById(entry1.entry_id(), &found_entry));
+ ASSERT_TRUE(IsEntryValid(found_entry));
+ EXPECT_TRUE(AreEntriesEqual(entry1, found_entry));
+
+ EXPECT_TRUE(model.GetEntryById(entry2.entry_id(), &found_entry));
+ ASSERT_TRUE(IsEntryValid(found_entry));
+ EXPECT_TRUE(AreEntriesEqual(entry2, found_entry));
+}
+
+} // namespace dom_distiller
diff --git a/components/dom_distiller/core/dom_distiller_store.cc b/components/dom_distiller/core/dom_distiller_store.cc
index f5a7a70..8be2cb6 100644
--- a/components/dom_distiller/core/dom_distiller_store.cc
+++ b/components/dom_distiller/core/dom_distiller_store.cc
@@ -23,24 +23,6 @@ using syncer::SyncMergeResult;
namespace dom_distiller {
-namespace {
-
-std::string GetEntryIdFromSyncData(const SyncData& data) {
- const EntitySpecifics& entity = data.GetSpecifics();
- DCHECK(entity.has_article());
- const ArticleSpecifics& specifics = entity.article();
- DCHECK(specifics.has_entry_id());
- return specifics.entry_id();
-}
-
-SyncData CreateLocalData(const ArticleEntry& entry) {
- EntitySpecifics specifics = SpecificsFromEntry(entry);
- const std::string& entry_id = entry.entry_id();
- return SyncData::CreateLocalData(entry_id, entry_id, specifics);
-}
-
-} // namespace
-
DomDistillerStore::DomDistillerStore(
scoped_ptr<DomDistillerDatabaseInterface> database,
const base::FilePath& database_dir)
@@ -54,11 +36,11 @@ DomDistillerStore::DomDistillerStore(
DomDistillerStore::DomDistillerStore(
scoped_ptr<DomDistillerDatabaseInterface> database,
- const EntryMap& initial_model,
+ const std::vector<ArticleEntry>& initial_data,
const base::FilePath& database_dir)
: database_(database.Pass()),
database_loaded_(false),
- model_(initial_model),
+ model_(initial_data),
weak_ptr_factory_(this) {
database_->Init(database_dir,
base::Bind(&DomDistillerStore::OnDatabaseInit,
@@ -72,11 +54,23 @@ syncer::SyncableService* DomDistillerStore::GetSyncableService() {
return this;
}
+bool DomDistillerStore::GetEntryById(const std::string& entry_id,
+ ArticleEntry* entry) {
+ return model_.GetEntryById(entry_id, entry);
+}
+
+bool DomDistillerStore::GetEntryByUrl(const GURL& url,
+ ArticleEntry* entry) {
+ return model_.GetEntryByUrl(url, entry);
+}
+
+
bool DomDistillerStore::AddEntry(const ArticleEntry& entry) {
if (!database_loaded_) {
return false;
}
- if (model_.find(entry.entry_id()) != model_.end()) {
+
+ if (model_.GetEntryById(entry.entry_id(), NULL)) {
return false;
}
@@ -86,7 +80,11 @@ bool DomDistillerStore::AddEntry(const ArticleEntry& entry) {
SyncChangeList changes_applied;
SyncChangeList changes_missing;
- ApplyChangesToModel(changes_to_apply, &changes_applied, &changes_missing);
+
+ if (!ApplyChangesToModel(
+ changes_to_apply, &changes_applied, &changes_missing)) {
+ return false;
+ }
DCHECK_EQ(size_t(0), changes_missing.size());
DCHECK_EQ(size_t(1), changes_applied.size());
@@ -98,11 +96,7 @@ bool DomDistillerStore::AddEntry(const ArticleEntry& entry) {
}
std::vector<ArticleEntry> DomDistillerStore::GetEntries() const {
- std::vector<ArticleEntry> entries;
- for (EntryMap::const_iterator it = model_.begin(); it != model_.end(); ++it) {
- entries.push_back(it->second);
- }
- return entries;
+ return model_.GetEntries();
}
// syncer::SyncableService implementation.
@@ -133,11 +127,7 @@ void DomDistillerStore::StopSyncing(ModelType type) {
}
SyncDataList DomDistillerStore::GetAllSyncData(ModelType type) const {
- SyncDataList data;
- for (EntryMap::const_iterator it = model_.begin(); it != model_.end(); ++it) {
- data.push_back(CreateLocalData(it->second));
- }
- return data;
+ return model_.GetAllSyncData();
}
SyncError DomDistillerStore::ProcessSyncChanges(
@@ -146,17 +136,34 @@ SyncError DomDistillerStore::ProcessSyncChanges(
DCHECK(database_loaded_);
SyncChangeList database_changes;
SyncChangeList sync_changes;
- SyncError error =
- ApplyChangesToModel(change_list, &database_changes, &sync_changes);
+ if (!ApplyChangesToModel(change_list, &database_changes, &sync_changes)) {
+ return SyncError(FROM_HERE,
+ SyncError::DATATYPE_ERROR,
+ "Applying changes to the DOM distiller model failed",
+ syncer::ARTICLES);
+ }
ApplyChangesToDatabase(database_changes);
DCHECK_EQ(size_t(0), sync_changes.size());
- return error;
+ return SyncError();
}
-ArticleEntry DomDistillerStore::GetEntryFromChange(const SyncChange& change) {
- DCHECK(change.IsValid());
- DCHECK(change.sync_data().IsValid());
- return EntryFromSpecifics(change.sync_data().GetSpecifics());
+bool DomDistillerStore::ApplyChangesToModel(
+ const SyncChangeList& changes,
+ SyncChangeList* changes_applied,
+ SyncChangeList* changes_missing) {
+ DomDistillerModel::ChangeResult change_result =
+ model_.ApplyChangesToModel(changes, changes_applied, changes_missing);
+ if (change_result == DomDistillerModel::SUCCESS) {
+ return true;
+ }
+
+ LOG(WARNING) << "Applying changes to DOM distiller model failed with error "
+ << change_result;
+
+ database_.reset();
+ database_loaded_ = false;
+ StopSyncing(syncer::ARTICLES);
+ return false;
}
void DomDistillerStore::OnDatabaseInit(bool success) {
@@ -238,35 +245,6 @@ bool DomDistillerStore::ApplyChangesToDatabase(
return true;
}
-void DomDistillerStore::CalculateChangesForMerge(
- const SyncDataList& data,
- SyncChangeList* changes_to_apply,
- SyncChangeList* changes_missing) {
- typedef base::hash_set<std::string> StringSet;
- StringSet entries_to_change;
- for (SyncDataList::const_iterator it = data.begin(); it != data.end(); ++it) {
- std::string entry_id = GetEntryIdFromSyncData(*it);
- std::pair<StringSet::iterator, bool> insert_result =
- entries_to_change.insert(entry_id);
-
- DCHECK(insert_result.second);
-
- SyncChange::SyncChangeType change_type = SyncChange::ACTION_ADD;
- EntryMap::const_iterator current = model_.find(entry_id);
- if (current != model_.end()) {
- change_type = SyncChange::ACTION_UPDATE;
- }
- changes_to_apply->push_back(SyncChange(FROM_HERE, change_type, *it));
- }
-
- for (EntryMap::const_iterator it = model_.begin(); it != model_.end(); ++it) {
- if (entries_to_change.find(it->first) == entries_to_change.end()) {
- changes_missing->push_back(SyncChange(
- FROM_HERE, SyncChange::ACTION_ADD, CreateLocalData(it->second)));
- }
- }
-}
-
SyncMergeResult DomDistillerStore::MergeDataWithModel(
const SyncDataList& data,
SyncChangeList* changes_applied,
@@ -275,12 +253,18 @@ SyncMergeResult DomDistillerStore::MergeDataWithModel(
DCHECK(changes_missing);
SyncMergeResult result(syncer::ARTICLES);
- result.set_num_items_before_association(model_.size());
+ result.set_num_items_before_association(model_.GetNumEntries());
SyncChangeList changes_to_apply;
- CalculateChangesForMerge(data, &changes_to_apply, changes_missing);
- SyncError error =
- ApplyChangesToModel(changes_to_apply, changes_applied, changes_missing);
+ model_.CalculateChangesForMerge(data, &changes_to_apply, changes_missing);
+ SyncError error;
+ if (!ApplyChangesToModel(
+ changes_to_apply, changes_applied, changes_missing)) {
+ error = SyncError(FROM_HERE,
+ SyncError::DATATYPE_ERROR,
+ "Applying changes to the DOM distiller model failed",
+ syncer::ARTICLES);
+ }
int num_added = 0;
int num_modified = 0;
@@ -304,57 +288,10 @@ SyncMergeResult DomDistillerStore::MergeDataWithModel(
result.set_num_items_deleted(0);
result.set_pre_association_version(0);
- result.set_num_items_after_association(model_.size());
+ result.set_num_items_after_association(model_.GetNumEntries());
result.set_error(error);
return result;
}
-SyncError DomDistillerStore::ApplyChangesToModel(
- const SyncChangeList& changes,
- SyncChangeList* changes_applied,
- SyncChangeList* changes_missing) {
- DCHECK(changes_applied);
- DCHECK(changes_missing);
-
- for (SyncChangeList::const_iterator it = changes.begin(); it != changes.end();
- ++it) {
- ApplyChangeToModel(*it, changes_applied, changes_missing);
- }
- return SyncError();
-}
-
-void DomDistillerStore::ApplyChangeToModel(const SyncChange& change,
- SyncChangeList* changes_applied,
- SyncChangeList* changes_missing) {
- DCHECK(changes_applied);
- DCHECK(changes_missing);
- DCHECK(change.IsValid());
-
- const std::string& entry_id = GetEntryIdFromSyncData(change.sync_data());
- EntryMap::iterator entry_it = model_.find(entry_id);
-
- if (change.change_type() == SyncChange::ACTION_DELETE) {
- // TODO(cjhopman): Support delete.
- NOTIMPLEMENTED();
- StopSyncing(syncer::ARTICLES);
- return;
- }
-
- ArticleEntry entry = GetEntryFromChange(change);
- if (entry_it == model_.end()) {
- model_.insert(std::make_pair(entry.entry_id(), entry));
- changes_applied->push_back(SyncChange(
- change.location(), SyncChange::ACTION_ADD, change.sync_data()));
- } else {
- if (!AreEntriesEqual(entry_it->second, entry)) {
- // Currently, conflicts are simply resolved by accepting the last one to
- // arrive.
- entry_it->second = entry;
- changes_applied->push_back(SyncChange(
- change.location(), SyncChange::ACTION_UPDATE, change.sync_data()));
- }
- }
-}
-
} // namespace dom_distiller
diff --git a/components/dom_distiller/core/dom_distiller_store.h b/components/dom_distiller/core/dom_distiller_store.h
index fdc1851..18641b7 100644
--- a/components/dom_distiller/core/dom_distiller_store.h
+++ b/components/dom_distiller/core/dom_distiller_store.h
@@ -11,12 +11,14 @@
#include "base/memory/weak_ptr.h"
#include "components/dom_distiller/core/article_entry.h"
#include "components/dom_distiller/core/dom_distiller_database.h"
+#include "components/dom_distiller/core/dom_distiller_model.h"
#include "sync/api/sync_change.h"
#include "sync/api/sync_data.h"
#include "sync/api/sync_error.h"
#include "sync/api/sync_error_factory.h"
#include "sync/api/sync_merge_result.h"
#include "sync/api/syncable_service.h"
+#include "url/gurl.h"
namespace base {
class FilePath;
@@ -34,6 +36,12 @@ class DomDistillerStoreInterface {
virtual bool AddEntry(const ArticleEntry& entry) = 0;
+ // Lookup an ArticleEntry by ID or URL. Returns whether a corresponding entry
+ // was found. On success, if |entry| is not null, it will contain the entry.
+ virtual bool GetEntryById(const std::string& entry_id,
+ ArticleEntry* entry) = 0;
+ virtual bool GetEntryByUrl(const GURL& url, ArticleEntry* entry) = 0;
+
// Gets a copy of all the current entries.
virtual std::vector<ArticleEntry> GetEntries() const = 0;
@@ -59,8 +67,6 @@ class DomDistillerStoreInterface {
class DomDistillerStore : public syncer::SyncableService,
DomDistillerStoreInterface {
public:
- typedef base::hash_map<std::string, ArticleEntry> EntryMap;
-
// Creates storage using the given database for local storage. Initializes the
// database with |database_dir|.
DomDistillerStore(scoped_ptr<DomDistillerDatabaseInterface> database,
@@ -70,7 +76,7 @@ class DomDistillerStore : public syncer::SyncableService,
// database with |database_dir|. Also initializes the internal model to
// |initial_model|.
DomDistillerStore(scoped_ptr<DomDistillerDatabaseInterface> database,
- const EntryMap& initial_model,
+ const std::vector<ArticleEntry>& initial_data,
const base::FilePath& database_dir);
virtual ~DomDistillerStore();
@@ -78,6 +84,9 @@ class DomDistillerStore : public syncer::SyncableService,
// DomDistillerStoreInterface implementation.
virtual syncer::SyncableService* GetSyncableService() OVERRIDE;
virtual bool AddEntry(const ArticleEntry& entry) OVERRIDE;
+ virtual bool GetEntryById(const std::string& entry_id,
+ ArticleEntry* entry) OVERRIDE;
+ virtual bool GetEntryByUrl(const GURL& url, ArticleEntry* entry) OVERRIDE;
virtual std::vector<ArticleEntry> GetEntries() const OVERRIDE;
// syncer::SyncableService implementation.
@@ -92,9 +101,6 @@ class DomDistillerStore : public syncer::SyncableService,
virtual syncer::SyncError ProcessSyncChanges(
const tracked_objects::Location& from_here,
const syncer::SyncChangeList& change_list) OVERRIDE;
-
- static ArticleEntry GetEntryFromChange(const syncer::SyncChange& change);
-
private:
void OnDatabaseInit(bool success);
void OnDatabaseLoad(bool success, scoped_ptr<EntryVector> entries);
@@ -116,24 +122,18 @@ class DomDistillerStore : public syncer::SyncableService,
const syncer::SyncChangeList& change_list);
bool ApplyChangesToDatabase(const syncer::SyncChangeList& change_list);
- // Applies the change list to the in-memory model, appending the actual
- // changes made to the model to changes_applied. If conflict resolution does
- // not apply the requested change, then adds the "diff" to changes_missing.
- syncer::SyncError ApplyChangesToModel(
- const syncer::SyncChangeList& change_list,
- syncer::SyncChangeList* changes_applied,
- syncer::SyncChangeList* changes_missing);
-
- void ApplyChangeToModel(const syncer::SyncChange& change,
- syncer::SyncChangeList* changes_applied,
- syncer::SyncChangeList* changes_missing);
+ // Applies the changes to |model_|. If the model returns an error, disables
+ // syncing and database changes and returns false.
+ bool ApplyChangesToModel(const syncer::SyncChangeList& change_list,
+ syncer::SyncChangeList* changes_applied,
+ syncer::SyncChangeList* changes_missing);
scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
scoped_ptr<syncer::SyncErrorFactory> error_factory_;
scoped_ptr<DomDistillerDatabaseInterface> database_;
bool database_loaded_;
- EntryMap model_;
+ DomDistillerModel model_;
base::WeakPtrFactory<DomDistillerStore> weak_ptr_factory_;
diff --git a/components/dom_distiller/core/dom_distiller_store_unittest.cc b/components/dom_distiller/core/dom_distiller_store_unittest.cc
index 333d72d..9e4cf35 100644
--- a/components/dom_distiller/core/dom_distiller_store_unittest.cc
+++ b/components/dom_distiller/core/dom_distiller_store_unittest.cc
@@ -34,12 +34,21 @@ namespace {
const ModelType kDomDistillerModelType = syncer::ARTICLES;
-typedef DomDistillerStore::EntryMap EntryMap;
+typedef base::hash_map<std::string, ArticleEntry> EntryMap;
void AddEntry(const ArticleEntry& e, EntryMap* map) {
(*map)[e.entry_id()] = e;
}
+std::vector<ArticleEntry> EntryMapToList(const EntryMap& entries) {
+ std::vector<ArticleEntry> entry_list;
+ for (EntryMap::const_iterator it = entries.begin(); it != entries.end();
+ ++it) {
+ entry_list.push_back(it->second);
+ }
+ return entry_list;
+}
+
class FakeDB : public DomDistillerDatabaseInterface {
typedef base::Callback<void(bool)> Callback;
@@ -134,7 +143,7 @@ class FakeSyncChangeProcessor : public syncer::SyncChangeProcessor {
for (SyncChangeList::const_iterator it = changes.begin();
it != changes.end();
++it) {
- AddEntry(DomDistillerStore::GetEntryFromChange(*it), model_);
+ AddEntry(GetEntryFromChange(*it), model_);
}
return SyncError();
}
@@ -203,7 +212,7 @@ class DomDistillerStoreTest : public testing::Test {
fake_db_ = new FakeDB(&db_model_);
store_.reset(new DomDistillerStore(
scoped_ptr<DomDistillerDatabaseInterface>(fake_db_),
- store_model_,
+ EntryMapToList(store_model_),
db_dir_));
}
@@ -448,7 +457,7 @@ TEST_F(DomDistillerStoreTest, TestSyncMergeWithSecondDomDistillerStore) {
FakeDB* other_fake_db = new FakeDB(&other_db_model);
scoped_ptr<DomDistillerStore> owned_other_store(new DomDistillerStore(
scoped_ptr<DomDistillerDatabaseInterface>(other_fake_db),
- EntryMap(),
+ std::vector<ArticleEntry>(),
base::FilePath(FILE_PATH_LITERAL("/fake/other/path"))));
DomDistillerStore* other_store = owned_other_store.get();
other_fake_db->InitCallback(true);