1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
|
// 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 "components/sync/syncable/parent_child_index.h"
#include <utility>
#include "base/stl_util.h"
#include "components/sync/syncable/entry_kernel.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() {}
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));
OrderedChildSetRef 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 = OrderedChildSetRef(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) && IsRealDataType(model_type) &&
!TypeSupportsHierarchy(model_type)) {
const Id& type_root_id = entry->ref(ID);
// If the entry exists in the map it must already have the same
// model type specific child set. It's possible another type root exists
// in parent_children_map_, but that's okay as the new type root will
// point to the same OrderedChildSet. As such, we just blindly store the
// new type root ID and associate it to the (possibly existing) child set.
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) {
OrderedChildSetRef siblings = nullptr;
ModelType model_type = e->GetModelType();
const Id& parent_id = e->ref(PARENT_ID);
bool should_erase = false;
ParentChildrenMap::iterator sibling_iterator;
if (ShouldUseParentId(parent_id, model_type)) {
// Hierarchical type, lookup child set in the map.
DCHECK(!parent_id.IsNull());
sibling_iterator = parent_children_map_.find(parent_id);
DCHECK(sibling_iterator != parent_children_map_.end());
siblings = sibling_iterator->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) {
parent_children_map_.erase(sibling_iterator);
}
}
bool ParentChildIndex::Contains(EntryKernel* e) const {
const OrderedChildSetRef 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;
}
OrderedChildSetRef 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.get();
}
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 OrderedChildSetRef siblings = GetChildSet(e);
DCHECK(siblings && !siblings->empty());
return siblings.get();
}
size_t ParentChildIndex::EstimateMemoryUsage() const {
using base::trace_event::EstimateMemoryUsage;
return EstimateMemoryUsage(parent_children_map_) +
EstimateMemoryUsage(model_type_root_ids_) +
EstimateMemoryUsage(type_root_child_sets_);
}
/* 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) ||
!IsRealDataType(model_type);
}
const OrderedChildSetRef 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 OrderedChildSetRef ParentChildIndex::GetModelTypeChildSet(
ModelType model_type) const {
return type_root_child_sets_[model_type];
}
OrderedChildSetRef ParentChildIndex::GetOrCreateModelTypeChildSet(
ModelType model_type) {
if (!type_root_child_sets_[model_type])
type_root_child_sets_[model_type] =
OrderedChildSetRef(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
|