#include "sync/internal_api/change_reorder_buffer.h"
#include <limits>
#include <queue>
#include <set>
#include <utility>
#include "sync/internal_api/public/base/model_type.h"
#include "sync/internal_api/public/read_node.h"
#include "sync/syncable/base_transaction.h"
#include "sync/syncable/directory.h"
#include "sync/syncable/entry.h"
using std::numeric_limits;
using std::pair;
using std::queue;
using std::set;
namespace syncer {
class ChangeReorderBuffer::Traversal {
public:
typedef pair<int64, int64> ParentChildLink;
typedef set<ParentChildLink> LinkSet;
Traversal() : top_(kInvalidId) { }
void ExpandToInclude(syncable::BaseTransaction* trans,
int64 child_handle) {
if (top_ == kInvalidId) {
top_ = child_handle;
return;
}
int64 node_to_include = child_handle;
while (node_to_include != kInvalidId && node_to_include != top_) {
int64 node_parent = 0;
syncable::Entry node(trans, syncable::GET_BY_HANDLE, node_to_include);
CHECK(node.good());
if (node.Get(syncable::ID).IsRoot()) {
node_to_include = top_;
top_ = node.Get(syncable::META_HANDLE);
} else {
syncable::Entry parent(trans, syncable::GET_BY_ID,
node.Get(syncable::PARENT_ID));
CHECK(parent.good());
node_parent = parent.Get(syncable::META_HANDLE);
ParentChildLink link(node_parent, node_to_include);
if (links_.find(link) != links_.end())
return;
links_.insert(link);
node_to_include = node_parent;
}
}
}
int64 top() const { return top_; }
LinkSet::const_iterator begin_children(int64 parent_id) const {
return links_.upper_bound(
ParentChildLink(parent_id, numeric_limits<int64>::min()));
}
LinkSet::const_iterator end_children(int64 parent_id) const {
return begin_children(parent_id + 1);
}
private:
int64 top_;
LinkSet links_;
DISALLOW_COPY_AND_ASSIGN(Traversal);
};
ChangeReorderBuffer::ChangeReorderBuffer() {
}
ChangeReorderBuffer::~ChangeReorderBuffer() {
}
bool ChangeReorderBuffer::GetAllChangesInTreeOrder(
const BaseTransaction* sync_trans,
ImmutableChangeRecordList* changes) {
syncable::BaseTransaction* trans = sync_trans->GetWrappedTrans();
set<int64> parents_of_position_changes;
Traversal traversal;
ChangeRecordList changelist;
OperationMap::const_iterator i;
for (i = operations_.begin(); i != operations_.end(); ++i) {
if (i->second == OP_DELETE) {
ChangeRecord record;
record.id = i->first;
record.action = ChangeRecord::ACTION_DELETE;
if (specifics_.find(record.id) != specifics_.end())
record.specifics = specifics_[record.id];
if (extra_data_.find(record.id) != extra_data_.end())
record.extra = extra_data_[record.id];
changelist.push_back(record);
} else {
traversal.ExpandToInclude(trans, i->first);
if (i->second == OP_ADD ||
i->second == OP_UPDATE_POSITION_AND_PROPERTIES) {
ReadNode node(sync_trans);
CHECK_EQ(BaseNode::INIT_OK, node.InitByIdLookup(i->first));
if (ShouldMaintainPosition(node.GetEntry()->GetModelType())) {
parents_of_position_changes.insert(node.GetParentId());
}
}
}
}
queue<int64> to_visit;
to_visit.push(traversal.top());
while (!to_visit.empty()) {
int64 next = to_visit.front();
to_visit.pop();
i = operations_.find(next);
if (i != operations_.end()) {
ChangeRecord record;
record.id = next;
if (i->second == OP_ADD)
record.action = ChangeRecord::ACTION_ADD;
else
record.action = ChangeRecord::ACTION_UPDATE;
if (specifics_.find(record.id) != specifics_.end())
record.specifics = specifics_[record.id];
if (extra_data_.find(record.id) != extra_data_.end())
record.extra = extra_data_[record.id];
changelist.push_back(record);
}
if (parents_of_position_changes.find(next) ==
parents_of_position_changes.end()) {
Traversal::LinkSet::const_iterator j = traversal.begin_children(next);
Traversal::LinkSet::const_iterator end = traversal.end_children(next);
for (; j != end; ++j) {
CHECK(j->first == next);
to_visit.push(j->second);
}
} else {
syncable::Entry parent(trans, syncable::GET_BY_HANDLE, next);
syncable::Id id;
if (!trans->directory()->GetFirstChildId(
trans, parent.Get(syncable::ID), &id)) {
*changes = ImmutableChangeRecordList();
return false;
}
while (!id.IsRoot()) {
syncable::Entry child(trans, syncable::GET_BY_ID, id);
CHECK(child.good());
int64 handle = child.Get(syncable::META_HANDLE);
to_visit.push(handle);
if (operations_.find(handle) == operations_.end())
operations_[handle] = OP_UPDATE_POSITION_AND_PROPERTIES;
id = child.Get(syncable::NEXT_ID);
}
}
}
*changes = ImmutableChangeRecordList(&changelist);
return true;
}
}