blob: bf6e03fbd2044f55ea01d7b47ea2aa3b1b552c83 [file] [log] [blame]
// 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/syncable/parent_child_index.h"
#include "base/stl_util.h"
#include "sync/syncable/entry_kernel.h"
#include "sync/syncable/syncable_id.h"
namespace syncer {
namespace syncable {
bool ChildComparator::operator()(const EntryKernel* a,
const EntryKernel* b) const {
const UniquePosition& a_pos = a->ref(UNIQUE_POSITION);
const UniquePosition& b_pos = b->ref(UNIQUE_POSITION);
if (a_pos.IsValid() && b_pos.IsValid()) {
// Position is important to this type.
return a_pos.LessThan(b_pos);
} else if (a_pos.IsValid() && !b_pos.IsValid()) {
// TODO(rlarocque): Remove this case.
// An item with valid position as sibling of one with invalid position.
// We should not support this, but the tests rely on it. For now, just
// move all invalid position items to the right.
return true;
} else if (!a_pos.IsValid() && b_pos.IsValid()) {
// TODO(rlarocque): Remove this case.
// Mirror of the above case.
return false;
} else {
// Position doesn't matter.
DCHECK(!a->ref(UNIQUE_POSITION).IsValid());
DCHECK(!b->ref(UNIQUE_POSITION).IsValid());
// Sort by META_HANDLE to ensure consistent order for testing.
return a->ref(META_HANDLE) < b->ref(META_HANDLE);
}
}
ParentChildIndex::ParentChildIndex() {
// Pre-allocate these two vectors to the number of model types.
model_type_root_ids_.resize(MODEL_TYPE_COUNT);
type_root_child_sets_.resize(MODEL_TYPE_COUNT);
}
ParentChildIndex::~ParentChildIndex() {
for (int i = 0; i < MODEL_TYPE_COUNT; i++) {
// Normally all OrderedChildSet instances in |type_root_child_sets_|
// are shared with |parent_children_map_|. Null out shared instances and
// ScopedVector will take care of the ones that are not shared (if any).
if (!model_type_root_ids_[i].IsNull()) {
DCHECK_EQ(type_root_child_sets_[i],
parent_children_map_.find(model_type_root_ids_[i])->second);
type_root_child_sets_[i] = nullptr;
}
}
STLDeleteContainerPairSecondPointers(parent_children_map_.begin(),
parent_children_map_.end());
}
bool ParentChildIndex::ShouldInclude(const EntryKernel* entry) {
// This index excludes deleted items and the root item. The root
// item is excluded so that it doesn't show up as a child of itself.
return !entry->ref(IS_DEL) && !entry->ref(ID).IsRoot();
}
bool ParentChildIndex::Insert(EntryKernel* entry) {
DCHECK(ShouldInclude(entry));
OrderedChildSet* siblings = nullptr;
const Id& parent_id = entry->ref(PARENT_ID);
ModelType model_type = entry->GetModelType();
if (ShouldUseParentId(parent_id, model_type)) {
// Hierarchical type, lookup child set in the map.
DCHECK(!parent_id.IsNull());
ParentChildrenMap::iterator it = parent_children_map_.find(parent_id);
if (it != parent_children_map_.end()) {
siblings = it->second;
} else {
siblings = new OrderedChildSet();
parent_children_map_.insert(std::make_pair(parent_id, siblings));
}
} else {
// Non-hierarchical type, return a pre-defined collection by type.
siblings = GetOrCreateModelTypeChildSet(model_type);
}
// If this is one of type root folder for a non-hierarchical type, associate
// its ID with the model type and the type's pre-defined child set with the
// type root ID.
// TODO(stanisc): crbug/438313: Just TypeSupportsHierarchy condition should
// theoretically be sufficient but in practice many tests don't properly
// initialize entries so TypeSupportsHierarchy ends up failing. Consider
// tweaking TypeSupportsHierarchy and fixing all related test code.
if (parent_id.IsRoot() && entry->ref(IS_DIR) &&
syncer::IsRealDataType(model_type) &&
!TypeSupportsHierarchy(model_type)) {
const Id& type_root_id = entry->ref(ID);
// Disassociate the type root child set with the previous ID if any.
const Id& prev_type_root_id = model_type_root_ids_[model_type];
if (!prev_type_root_id.IsNull()) {
ParentChildrenMap::iterator it =
parent_children_map_.find(prev_type_root_id);
if (it != parent_children_map_.end()) {
// If the entry exists in the map it must already have the same
// model type specific child set. This child set will be re-inserted
// in the map under the new ID below so it is safe to simply erase
// the entry here.
DCHECK_EQ(it->second, GetModelTypeChildSet(model_type));
parent_children_map_.erase(it);
}
}
// Store the new type root ID and associate the child set.
// Note that the child set isn't owned by the map in this case.
model_type_root_ids_[model_type] = type_root_id;
parent_children_map_.insert(
std::make_pair(type_root_id, GetOrCreateModelTypeChildSet(model_type)));
}
// Finally, insert the entry in the child set.
return siblings->insert(entry).second;
}
// Like the other containers used to help support the syncable::Directory, this
// one does not own any EntryKernels. This function removes references to the
// given EntryKernel but does not delete it.
void ParentChildIndex::Remove(EntryKernel* e) {
OrderedChildSet* siblings = nullptr;
ModelType model_type = e->GetModelType();
const Id& parent_id = e->ref(PARENT_ID);
bool should_erase = false;
ParentChildrenMap::iterator it;
if (ShouldUseParentId(parent_id, model_type)) {
// Hierarchical type, lookup child set in the map.
DCHECK(!parent_id.IsNull());
it = parent_children_map_.find(parent_id);
DCHECK(it != parent_children_map_.end());
siblings = it->second;
should_erase = true;
} else {
// Non-hierarchical type, return a pre-defined child set by type.
siblings = type_root_child_sets_[model_type];
}
OrderedChildSet::iterator j = siblings->find(e);
DCHECK(j != siblings->end());
// Erase the entry from the child set.
siblings->erase(j);
// If the set is now empty and isn't shareable with |type_root_child_sets_|,
// erase it from the map.
if (siblings->empty() && should_erase) {
delete siblings;
parent_children_map_.erase(it);
}
}
bool ParentChildIndex::Contains(EntryKernel *e) const {
const OrderedChildSet* siblings = GetChildSet(e);
return siblings && siblings->count(e) > 0;
}
const OrderedChildSet* ParentChildIndex::GetChildren(const Id& id) const {
DCHECK(!id.IsNull());
ParentChildrenMap::const_iterator parent = parent_children_map_.find(id);
if (parent == parent_children_map_.end()) {
return nullptr;
}
OrderedChildSet* children = parent->second;
// The expectation is that the function returns nullptr instead of an empty
// child set
if (children && children->empty())
children = nullptr;
return children;
}
const OrderedChildSet* ParentChildIndex::GetChildren(EntryKernel* e) const {
return GetChildren(e->ref(ID));
}
const OrderedChildSet* ParentChildIndex::GetSiblings(EntryKernel* e) const {
// This implies the entry is in the index.
DCHECK(Contains(e));
const OrderedChildSet* siblings = GetChildSet(e);
DCHECK(siblings && !siblings->empty());
return siblings;
}
/* static */
bool ParentChildIndex::ShouldUseParentId(const Id& parent_id,
ModelType model_type) {
// For compatibility with legacy unit tests, in addition to hierarchical
// entries, this returns true any entries directly under root and for entries
// of UNSPECIFIED model type.
return parent_id.IsRoot() || TypeSupportsHierarchy(model_type) ||
!syncer::IsRealDataType(model_type);
}
const OrderedChildSet* ParentChildIndex::GetChildSet(EntryKernel* e) const {
ModelType model_type = e->GetModelType();
const Id& parent_id = e->ref(PARENT_ID);
if (ShouldUseParentId(parent_id, model_type)) {
// Hierarchical type, lookup child set in the map.
ParentChildrenMap::const_iterator it = parent_children_map_.find(parent_id);
if (it == parent_children_map_.end())
return nullptr;
return it->second;
}
// Non-hierarchical type, return a collection indexed by type.
return GetModelTypeChildSet(model_type);
}
const OrderedChildSet* ParentChildIndex::GetModelTypeChildSet(
ModelType model_type) const {
return type_root_child_sets_[model_type];
}
OrderedChildSet* ParentChildIndex::GetOrCreateModelTypeChildSet(
ModelType model_type) {
if (!type_root_child_sets_[model_type])
type_root_child_sets_[model_type] = new OrderedChildSet();
return type_root_child_sets_[model_type];
}
const Id& ParentChildIndex::GetModelTypeRootId(ModelType model_type) const {
return model_type_root_ids_[model_type];
}
} // namespace syncable
} // namespace syncer