| // Copyright (c) 2012 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 "sync/engine/update_applicator.h" |
| |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "sync/engine/syncer_util.h" |
| #include "sync/sessions/session_state.h" |
| #include "sync/syncable/entry.h" |
| #include "sync/syncable/mutable_entry.h" |
| #include "sync/syncable/syncable_id.h" |
| #include "sync/syncable/write_transaction.h" |
| |
| using std::vector; |
| |
| namespace syncer { |
| |
| UpdateApplicator::UpdateApplicator(ConflictResolver* resolver, |
| Cryptographer* cryptographer, |
| const UpdateIterator& begin, |
| const UpdateIterator& end, |
| const ModelSafeRoutingInfo& routes, |
| ModelSafeGroup group_filter) |
| : resolver_(resolver), |
| cryptographer_(cryptographer), |
| begin_(begin), |
| end_(end), |
| pointer_(begin), |
| group_filter_(group_filter), |
| progress_(false), |
| routing_info_(routes), |
| application_results_(end - begin) { |
| size_t item_count = end - begin; |
| DVLOG(1) << "UpdateApplicator created for " << item_count << " items."; |
| } |
| |
| UpdateApplicator::~UpdateApplicator() { |
| } |
| |
| // Returns true if there's more to do. |
| bool UpdateApplicator::AttemptOneApplication( |
| syncable::WriteTransaction* trans) { |
| // If there are no updates left to consider, we're done. |
| if (end_ == begin_) |
| return false; |
| if (pointer_ == end_) { |
| if (!progress_) |
| return false; |
| |
| DVLOG(1) << "UpdateApplicator doing additional pass."; |
| pointer_ = begin_; |
| progress_ = false; |
| |
| // Clear the tracked failures to avoid double-counting. |
| application_results_.ClearConflicts(); |
| } |
| |
| syncable::Entry read_only(trans, syncable::GET_BY_HANDLE, *pointer_); |
| if (SkipUpdate(read_only)) { |
| Advance(); |
| return true; |
| } |
| |
| syncable::MutableEntry entry(trans, syncable::GET_BY_HANDLE, *pointer_); |
| UpdateAttemptResponse updateResponse = AttemptToUpdateEntry( |
| trans, &entry, resolver_, cryptographer_); |
| switch (updateResponse) { |
| case SUCCESS: |
| Advance(); |
| progress_ = true; |
| application_results_.AddSuccess(entry.Get(syncable::ID)); |
| break; |
| case CONFLICT_SIMPLE: |
| pointer_++; |
| application_results_.AddSimpleConflict(entry.Get(syncable::ID)); |
| break; |
| case CONFLICT_ENCRYPTION: |
| pointer_++; |
| application_results_.AddEncryptionConflict(entry.Get(syncable::ID)); |
| break; |
| case CONFLICT_HIERARCHY: |
| pointer_++; |
| application_results_.AddHierarchyConflict(entry.Get(syncable::ID)); |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| DVLOG(1) << "Apply Status for " << entry.Get(syncable::META_HANDLE) |
| << " is " << updateResponse; |
| |
| return true; |
| } |
| |
| void UpdateApplicator::Advance() { |
| --end_; |
| *pointer_ = *end_; |
| } |
| |
| bool UpdateApplicator::SkipUpdate(const syncable::Entry& entry) { |
| ModelType type = entry.GetServerModelType(); |
| ModelSafeGroup g = GetGroupForModelType(type, routing_info_); |
| // The set of updates passed to the UpdateApplicator should already |
| // be group-filtered. |
| if (g != group_filter_) { |
| NOTREACHED(); |
| return true; |
| } |
| if (g == GROUP_PASSIVE && |
| !routing_info_.count(type) && |
| type != UNSPECIFIED && |
| type != TOP_LEVEL_FOLDER) { |
| DVLOG(1) << "Skipping update application, type not permitted."; |
| return true; |
| } |
| return false; |
| } |
| |
| bool UpdateApplicator::AllUpdatesApplied() const { |
| return application_results_.no_conflicts() && begin_ == end_; |
| } |
| |
| void UpdateApplicator::SaveProgressIntoSessionState( |
| sessions::ConflictProgress* conflict_progress, |
| sessions::UpdateProgress* update_progress) { |
| DCHECK(begin_ == end_ || ((pointer_ == end_) && !progress_)) |
| << "SaveProgress called before updates exhausted."; |
| |
| application_results_.SaveProgress(conflict_progress, update_progress); |
| } |
| |
| UpdateApplicator::ResultTracker::ResultTracker(size_t num_results) { |
| successful_ids_.reserve(num_results); |
| } |
| |
| UpdateApplicator::ResultTracker::~ResultTracker() { |
| } |
| |
| void UpdateApplicator::ResultTracker::AddSimpleConflict(syncable::Id id) { |
| conflicting_ids_.push_back(id); |
| } |
| |
| void UpdateApplicator::ResultTracker::AddEncryptionConflict(syncable::Id id) { |
| encryption_conflict_ids_.push_back(id); |
| } |
| |
| void UpdateApplicator::ResultTracker::AddHierarchyConflict(syncable::Id id) { |
| hierarchy_conflict_ids_.push_back(id); |
| } |
| |
| void UpdateApplicator::ResultTracker::AddSuccess(syncable::Id id) { |
| successful_ids_.push_back(id); |
| } |
| |
| void UpdateApplicator::ResultTracker::SaveProgress( |
| sessions::ConflictProgress* conflict_progress, |
| sessions::UpdateProgress* update_progress) { |
| vector<syncable::Id>::const_iterator i; |
| for (i = conflicting_ids_.begin(); i != conflicting_ids_.end(); ++i) { |
| conflict_progress->AddSimpleConflictingItemById(*i); |
| update_progress->AddAppliedUpdate(CONFLICT_SIMPLE, *i); |
| } |
| for (i = encryption_conflict_ids_.begin(); |
| i != encryption_conflict_ids_.end(); ++i) { |
| conflict_progress->AddEncryptionConflictingItemById(*i); |
| update_progress->AddAppliedUpdate(CONFLICT_ENCRYPTION, *i); |
| } |
| for (i = hierarchy_conflict_ids_.begin(); |
| i != hierarchy_conflict_ids_.end(); ++i) { |
| conflict_progress->AddHierarchyConflictingItemById(*i); |
| update_progress->AddAppliedUpdate(CONFLICT_HIERARCHY, *i); |
| } |
| for (i = successful_ids_.begin(); i != successful_ids_.end(); ++i) { |
| conflict_progress->EraseSimpleConflictingItemById(*i); |
| update_progress->AddAppliedUpdate(SUCCESS, *i); |
| } |
| } |
| |
| void UpdateApplicator::ResultTracker::ClearConflicts() { |
| conflicting_ids_.clear(); |
| encryption_conflict_ids_.clear(); |
| hierarchy_conflict_ids_.clear(); |
| } |
| |
| bool UpdateApplicator::ResultTracker::no_conflicts() const { |
| return conflicting_ids_.empty(); |
| } |
| |
| } // namespace syncer |