Home | History | Annotate | Download | only in core
      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_model.h"
      6 
      7 #include <utility>
      8 
      9 using syncer::SyncChange;
     10 using syncer::SyncChangeList;
     11 using syncer::SyncData;
     12 using syncer::SyncDataList;
     13 
     14 namespace dom_distiller {
     15 
     16 DomDistillerModel::DomDistillerModel()
     17     : next_key_(1) {}
     18 
     19 DomDistillerModel::DomDistillerModel(
     20     const std::vector<ArticleEntry>& initial_data)
     21     : next_key_(1) {
     22   for (size_t i = 0; i < initial_data.size(); ++i) {
     23     AddEntry(initial_data[i]);
     24   }
     25 }
     26 
     27 DomDistillerModel::~DomDistillerModel() {}
     28 
     29 bool DomDistillerModel::GetEntryById(const std::string& entry_id,
     30                                      ArticleEntry* entry) const {
     31   KeyType key = 0;
     32   if (!GetKeyById(entry_id, &key)) {
     33     return false;
     34   }
     35   GetEntryByKey(key, entry);
     36   return true;
     37 }
     38 
     39 bool DomDistillerModel::GetEntryByUrl(const GURL& url,
     40                                      ArticleEntry* entry) const {
     41   KeyType key = 0;
     42   if (!GetKeyByUrl(url, &key)) {
     43     return false;
     44   }
     45   GetEntryByKey(key, entry);
     46   return true;
     47 }
     48 
     49 bool DomDistillerModel::GetKeyById(const std::string& entry_id,
     50                                    KeyType* key) const {
     51   StringToKeyMap::const_iterator it = entry_id_to_key_map_.find(entry_id);
     52   if (it == entry_id_to_key_map_.end()) {
     53     return false;
     54   }
     55   if (key != NULL) {
     56     *key = it->second;
     57   }
     58   return true;
     59 }
     60 
     61 bool DomDistillerModel::GetKeyByUrl(const GURL& url, KeyType* key) const {
     62   StringToKeyMap::const_iterator it = url_to_key_map_.find(url.spec());
     63   if (it == url_to_key_map_.end()) {
     64     return false;
     65   }
     66   if (key != NULL) {
     67     *key = it->second;
     68   }
     69   return true;
     70 }
     71 
     72 void DomDistillerModel::GetEntryByKey(KeyType key, ArticleEntry* entry) const {
     73   if (entry != NULL) {
     74     EntryMap::const_iterator it = entries_.find(key);
     75     DCHECK(it != entries_.end());
     76     *entry = it->second;
     77   }
     78 }
     79 
     80 size_t DomDistillerModel::GetNumEntries() const {
     81   return entries_.size();
     82 }
     83 
     84 std::vector<ArticleEntry> DomDistillerModel::GetEntries() const {
     85   std::vector<ArticleEntry> entries_list;
     86   for (EntryMap::const_iterator it = entries_.begin(); it != entries_.end();
     87        ++it) {
     88     entries_list.push_back(it->second);
     89   }
     90   return entries_list;
     91 }
     92 
     93 SyncDataList DomDistillerModel::GetAllSyncData() const {
     94   SyncDataList data;
     95   for (EntryMap::const_iterator it = entries_.begin(); it != entries_.end();
     96        ++it) {
     97     data.push_back(CreateLocalData(it->second));
     98   }
     99   return data;
    100 }
    101 
    102 void DomDistillerModel::CalculateChangesForMerge(
    103     const SyncDataList& data,
    104     SyncChangeList* changes_to_apply,
    105     SyncChangeList* changes_missing) {
    106   typedef base::hash_set<std::string> StringSet;
    107   StringSet entries_to_change;
    108   for (SyncDataList::const_iterator it = data.begin(); it != data.end(); ++it) {
    109     std::string entry_id = GetEntryIdFromSyncData(*it);
    110     std::pair<StringSet::iterator, bool> insert_result =
    111         entries_to_change.insert(entry_id);
    112 
    113     DCHECK(insert_result.second);
    114 
    115     SyncChange::SyncChangeType change_type = SyncChange::ACTION_ADD;
    116     if (GetEntryById(entry_id, NULL)) {
    117       change_type = SyncChange::ACTION_UPDATE;
    118     }
    119     changes_to_apply->push_back(SyncChange(FROM_HERE, change_type, *it));
    120   }
    121 
    122   for (EntryMap::const_iterator it = entries_.begin(); it != entries_.end();
    123        ++it) {
    124     if (entries_to_change.find(it->second.entry_id()) ==
    125         entries_to_change.end()) {
    126       changes_missing->push_back(SyncChange(
    127           FROM_HERE, SyncChange::ACTION_ADD, CreateLocalData(it->second)));
    128     }
    129   }
    130 }
    131 
    132 void DomDistillerModel::ApplyChangesToModel(
    133     const SyncChangeList& changes,
    134     SyncChangeList* changes_applied,
    135     SyncChangeList* changes_missing) {
    136   DCHECK(changes_applied);
    137   DCHECK(changes_missing);
    138 
    139   for (SyncChangeList::const_iterator it = changes.begin(); it != changes.end();
    140        ++it) {
    141     ApplyChangeToModel(*it, changes_applied, changes_missing);
    142   }
    143 }
    144 
    145 void DomDistillerModel::AddEntry(const ArticleEntry& entry) {
    146   const std::string& entry_id = entry.entry_id();
    147   KeyType key = next_key_++;
    148   DCHECK(!GetKeyById(entry_id, NULL));
    149   entries_.insert(std::make_pair(key, entry));
    150   entry_id_to_key_map_.insert(std::make_pair(entry_id, key));
    151   for (int i = 0; i < entry.pages_size(); ++i) {
    152     url_to_key_map_.insert(std::make_pair(entry.pages(i).url(), key));
    153   }
    154 }
    155 
    156 void DomDistillerModel::RemoveEntry(const ArticleEntry& entry) {
    157   const std::string& entry_id = entry.entry_id();
    158   KeyType key = 0;
    159   bool success = GetKeyById(entry_id, &key);
    160   DCHECK(success);
    161 
    162   entries_.erase(key);
    163   entry_id_to_key_map_.erase(entry_id);
    164   for (int i = 0; i < entry.pages_size(); ++i) {
    165     url_to_key_map_.erase(entry.pages(i).url());
    166   }
    167 }
    168 
    169 void DomDistillerModel::ApplyChangeToModel(
    170     const SyncChange& change,
    171     SyncChangeList* changes_applied,
    172     SyncChangeList* changes_missing) {
    173   DCHECK(change.IsValid());
    174   DCHECK(changes_applied);
    175   DCHECK(changes_missing);
    176 
    177   const std::string& entry_id = GetEntryIdFromSyncData(change.sync_data());
    178 
    179   if (change.change_type() == SyncChange::ACTION_DELETE) {
    180     ArticleEntry current_entry;
    181     if (GetEntryById(entry_id, &current_entry)) {
    182       RemoveEntry(current_entry);
    183       changes_applied->push_back(SyncChange(
    184           change.location(), SyncChange::ACTION_DELETE, change.sync_data()));
    185     }
    186     // If we couldn't find in sync db, we were deleting anyway so swallow the
    187     // error.
    188     return;
    189   }
    190 
    191   ArticleEntry entry = GetEntryFromChange(change);
    192   ArticleEntry current_entry;
    193   if (!GetEntryById(entry_id, &current_entry)) {
    194     AddEntry(entry);
    195     changes_applied->push_back(SyncChange(
    196         change.location(), SyncChange::ACTION_ADD, change.sync_data()));
    197   } else {
    198     if (!AreEntriesEqual(current_entry, entry)) {
    199       // Currently, conflicts are simply resolved by accepting the last one to
    200       // arrive.
    201       RemoveEntry(current_entry);
    202       AddEntry(entry);
    203       changes_applied->push_back(SyncChange(
    204           change.location(), SyncChange::ACTION_UPDATE, change.sync_data()));
    205     }
    206   }
    207 }
    208 
    209 }  // namespace dom_distiller
    210