OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2006-2009 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 "chrome/browser/sync/engine/change_reorder_buffer.h" |
| 6 |
| 7 #include <limits> |
| 8 #include <queue> |
| 9 #include <set> |
| 10 #include <utility> // for pair<> |
| 11 #include <vector> |
| 12 |
| 13 #include "chrome/browser/sync/syncable/syncable.h" |
| 14 |
| 15 using std::numeric_limits; |
| 16 using std::pair; |
| 17 using std::queue; |
| 18 using std::set; |
| 19 using std::vector; |
| 20 |
| 21 namespace sync_api { |
| 22 |
| 23 // Traversal provides a way to collect a set of nodes from the syncable |
| 24 // directory structure and then traverse them, along with any intermediate |
| 25 // nodes, in a top-down fashion, starting from a single common ancestor. A |
| 26 // Traversal starts out empty and is grown by means of the ExpandToInclude |
| 27 // method. Once constructed, the top(), begin_children(), and end_children() |
| 28 // methods can be used to explore the nodes in root-to-leaf order. |
| 29 class ChangeReorderBuffer::Traversal { |
| 30 public: |
| 31 typedef pair<int64, int64> ParentChildLink; |
| 32 typedef set<ParentChildLink> LinkSet; |
| 33 |
| 34 Traversal() : top_(kInvalidId) { } |
| 35 |
| 36 // Expand the traversal so that it includes the node indicated by |
| 37 // |child_handle|. |
| 38 void ExpandToInclude(syncable::BaseTransaction* trans, |
| 39 int64 child_handle) { |
| 40 // If |top_| is invalid, this is the first insertion -- easy. |
| 41 if (top_ == kInvalidId) { |
| 42 top_ = child_handle; |
| 43 return; |
| 44 } |
| 45 |
| 46 int64 node_to_include = child_handle; |
| 47 while (node_to_include != kInvalidId && node_to_include != top_) { |
| 48 int64 node_parent = 0; |
| 49 |
| 50 syncable::Entry node(trans, syncable::GET_BY_HANDLE, node_to_include); |
| 51 CHECK(node.good()); |
| 52 if (node.Get(syncable::ID).IsRoot()) { |
| 53 // If we've hit the root, and the root isn't already in the tree |
| 54 // (it would have to be |top_| if it were), start a new expansion |
| 55 // upwards from |top_| to unite the original traversal with the |
| 56 // path we just added that goes from |child_handle| to the root. |
| 57 node_to_include = top_; |
| 58 top_ = node.Get(syncable::META_HANDLE); |
| 59 } else { |
| 60 // Otherwise, get the parent ID so that we can add a ParentChildLink. |
| 61 syncable::Entry parent(trans, syncable::GET_BY_ID, |
| 62 node.Get(syncable::PARENT_ID)); |
| 63 CHECK(parent.good()); |
| 64 node_parent = parent.Get(syncable::META_HANDLE); |
| 65 |
| 66 ParentChildLink link(node_parent, node_to_include); |
| 67 |
| 68 // If the link exists in the LinkSet |links_|, we don't need to search |
| 69 // any higher; we are done. |
| 70 if (links_.find(link) != links_.end()) |
| 71 return; |
| 72 |
| 73 // Otherwise, extend |links_|, and repeat on the parent. |
| 74 links_.insert(link); |
| 75 node_to_include = node_parent; |
| 76 } |
| 77 } |
| 78 } |
| 79 |
| 80 // Return the top node of the traversal. Use this as a starting point |
| 81 // for walking the tree. |
| 82 int64 top() const { return top_; } |
| 83 |
| 84 // Return an iterator corresponding to the first child (in the traversal) |
| 85 // of the node specified by |parent_id|. Iterate this return value until |
| 86 // it is equal to the value returned by end_children(parent_id). The |
| 87 // enumeration thus provided is unordered. |
| 88 LinkSet::const_iterator begin_children(int64 parent_id) const { |
| 89 return links_.upper_bound( |
| 90 ParentChildLink(parent_id, numeric_limits<int64>::min())); |
| 91 } |
| 92 |
| 93 // Return an iterator corresponding to the last child in the traversal |
| 94 // of the node specified by |parent_id|. |
| 95 LinkSet::const_iterator end_children(int64 parent_id) const { |
| 96 return begin_children(parent_id + 1); |
| 97 } |
| 98 |
| 99 private: |
| 100 // The topmost point in the directory hierarchy that is in the traversal, |
| 101 // and thus the first node to be traversed. If the traversal is empty, |
| 102 // this is kInvalidId. If the traversal contains exactly one member, |top_| |
| 103 // will be the solitary member, and |links_| will be empty. |
| 104 int64 top_; |
| 105 // A set of single-level links that compose the traversal below |top_|. The |
| 106 // (parent, child) ordering of values enables efficient lookup of children |
| 107 // given the parent handle, which is used for top-down traversal. |links_| |
| 108 // is expected to be connected -- every node that appears as a parent in a |
| 109 // link must either appear as a child of another link, or else be the |
| 110 // topmost node, |top_|. |
| 111 LinkSet links_; |
| 112 |
| 113 DISALLOW_COPY_AND_ASSIGN(Traversal); |
| 114 }; |
| 115 |
| 116 void ChangeReorderBuffer::GetAllChangesInTreeOrder( |
| 117 const BaseTransaction* sync_trans, |
| 118 vector<ChangeRecord>* changelist) { |
| 119 syncable::BaseTransaction* trans = sync_trans->GetWrappedTrans(); |
| 120 |
| 121 // Step 1: Iterate through the operations, doing three things: |
| 122 // (a) Push deleted items straight into the |changelist|. |
| 123 // (b) Construct a traversal spanning all non-deleted items. |
| 124 // (c) Construct a set of all parent nodes of any position changes. |
| 125 set<int64> parents_of_position_changes; |
| 126 Traversal traversal; |
| 127 |
| 128 OperationMap::const_iterator i; |
| 129 for (i = operations_.begin(); i != operations_.end(); ++i) { |
| 130 if (i->second == OP_DELETE) { |
| 131 ChangeRecord record; |
| 132 record.id = i->first; |
| 133 record.action = ChangeRecord::ACTION_DELETE; |
| 134 changelist->push_back(record); |
| 135 } else { |
| 136 traversal.ExpandToInclude(trans, i->first); |
| 137 if (i->second == OP_ADD || |
| 138 i->second == OP_UPDATE_POSITION_AND_PROPERTIES) { |
| 139 ReadNode node(sync_trans); |
| 140 CHECK(node.InitByIdLookup(i->first)); |
| 141 parents_of_position_changes.insert(node.GetParentId()); |
| 142 } |
| 143 } |
| 144 } |
| 145 |
| 146 // Step 2: Breadth-first expansion of the traversal, enumerating children in |
| 147 // the syncable sibling order if there were any position updates. |
| 148 queue<int64> to_visit; |
| 149 to_visit.push(traversal.top()); |
| 150 while (!to_visit.empty()) { |
| 151 int64 next = to_visit.front(); |
| 152 to_visit.pop(); |
| 153 |
| 154 // If the node has an associated action, output a change record. |
| 155 i = operations_.find(next); |
| 156 if (i != operations_.end()) { |
| 157 ChangeRecord record; |
| 158 record.id = next; |
| 159 if (i->second == OP_ADD) |
| 160 record.action = ChangeRecord::ACTION_ADD; |
| 161 else |
| 162 record.action = ChangeRecord::ACTION_UPDATE; |
| 163 changelist->push_back(record); |
| 164 } |
| 165 |
| 166 // Now add the children of |next| to |to_visit|. |
| 167 if (parents_of_position_changes.find(next) == |
| 168 parents_of_position_changes.end()) { |
| 169 // No order changes on this parent -- traverse only the nodes listed |
| 170 // in the traversal (and not in sibling order). |
| 171 Traversal::LinkSet::const_iterator j = traversal.begin_children(next); |
| 172 Traversal::LinkSet::const_iterator end = traversal.end_children(next); |
| 173 for (; j != end; ++j) { |
| 174 CHECK(j->first == next); |
| 175 to_visit.push(j->second); |
| 176 } |
| 177 } else { |
| 178 // There were ordering changes on the children of this parent, so |
| 179 // enumerate all the children in the sibling order. |
| 180 syncable::Entry parent(trans, syncable::GET_BY_HANDLE, next); |
| 181 syncable::Id id = trans->directory()-> |
| 182 GetFirstChildId(trans, parent.Get(syncable::ID)); |
| 183 while (!id.IsRoot()) { |
| 184 syncable::Entry child(trans, syncable::GET_BY_ID, id); |
| 185 CHECK(child.good()); |
| 186 int64 handle = child.Get(syncable::META_HANDLE); |
| 187 to_visit.push(handle); |
| 188 // If there is no operation on this child node, record it as as an |
| 189 // update, so that the listener gets notified of all nodes in the new |
| 190 // ordering. |
| 191 if (operations_.find(handle) == operations_.end()) |
| 192 operations_[handle] = OP_UPDATE_POSITION_AND_PROPERTIES; |
| 193 id = child.Get(syncable::NEXT_ID); |
| 194 } |
| 195 } |
| 196 } |
| 197 } |
| 198 |
| 199 } // namespace sync_api |
OLD | NEW |