Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(157)

Side by Side Diff: sync/syncable/parent_child_index.cc

Issue 1226213002: Sync: Support nodes with implicit permanent folders: Node Browser and out-of-order loading from DB (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed CR feedback Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sync/syncable/parent_child_index.h ('k') | sync/syncable/parent_child_index_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "sync/syncable/parent_child_index.h" 5 #include "sync/syncable/parent_child_index.h"
6 6
7 #include "base/stl_util.h" 7 #include "base/stl_util.h"
8 8
9 #include "sync/syncable/entry_kernel.h" 9 #include "sync/syncable/entry_kernel.h"
10 #include "sync/syncable/syncable_id.h" 10 #include "sync/syncable/syncable_id.h"
(...skipping 22 matching lines...) Expand all
33 } else { 33 } else {
34 // Position doesn't matter. 34 // Position doesn't matter.
35 DCHECK(!a->ref(UNIQUE_POSITION).IsValid()); 35 DCHECK(!a->ref(UNIQUE_POSITION).IsValid());
36 DCHECK(!b->ref(UNIQUE_POSITION).IsValid()); 36 DCHECK(!b->ref(UNIQUE_POSITION).IsValid());
37 // Sort by META_HANDLE to ensure consistent order for testing. 37 // Sort by META_HANDLE to ensure consistent order for testing.
38 return a->ref(META_HANDLE) < b->ref(META_HANDLE); 38 return a->ref(META_HANDLE) < b->ref(META_HANDLE);
39 } 39 }
40 } 40 }
41 41
42 ParentChildIndex::ParentChildIndex() { 42 ParentChildIndex::ParentChildIndex() {
43 // Pre-allocate these two vectors to the number of model types.
44 model_type_root_ids_.resize(MODEL_TYPE_COUNT);
45 type_root_child_sets_.resize(MODEL_TYPE_COUNT);
43 } 46 }
44 47
45 ParentChildIndex::~ParentChildIndex() { 48 ParentChildIndex::~ParentChildIndex() {
46 STLDeleteContainerPairSecondPointers( 49 for (int i = 0; i < MODEL_TYPE_COUNT; i++) {
47 parent_children_map_.begin(), parent_children_map_.end()); 50 // Normally all OrderedChildSet instances in |type_root_child_sets_|
51 // are shared with |parent_children_map_| and will be destroyed
52 // below. The only ones that haven't been shared are those that
53 // correspond to unset IDs in |model_type_root_ids_|.
54 if (type_root_child_sets_[i]) {
55 if (model_type_root_ids_[i].IsNull()) {
56 delete type_root_child_sets_[i];
Nicolas Zea 2015/07/10 00:12:57 Would a scoped_vector work?
stanisc 2015/07/10 04:58:51 Done.
57 } else {
58 DCHECK(parent_children_map_.find(model_type_root_ids_[i]) !=
59 parent_children_map_.end());
60 }
61 }
62 }
63
64 STLDeleteContainerPairSecondPointers(parent_children_map_.begin(),
65 parent_children_map_.end());
48 } 66 }
49 67
50 bool ParentChildIndex::ShouldInclude(const EntryKernel* entry) { 68 bool ParentChildIndex::ShouldInclude(const EntryKernel* entry) {
51 // This index excludes deleted items and the root item. The root 69 // This index excludes deleted items and the root item. The root
52 // item is excluded so that it doesn't show up as a child of itself. 70 // item is excluded so that it doesn't show up as a child of itself.
53 return !entry->ref(IS_DEL) && !entry->ref(ID).IsRoot(); 71 // The index also excludes items without client data e.g. the items that
72 // don't have client SPECIFICS or PARENT_ID. There is no need to include
73 // server-only entries because they shouldn't show up on the client until
74 // they're applied.
75 return !entry->ref(IS_DEL) && !entry->ref(ID).IsRoot() &&
76 (entry->GetModelType() != UNSPECIFIED ||
77 !entry->ref(PARENT_ID).IsNull());
54 } 78 }
55 79
56 bool ParentChildIndex::Insert(EntryKernel* entry) { 80 bool ParentChildIndex::Insert(EntryKernel* entry) {
57 DCHECK(ShouldInclude(entry)); 81 DCHECK(ShouldInclude(entry));
58 82
59 const Id& parent_id = GetParentId(entry); 83 OrderedChildSet* siblings = nullptr;
60 // Store type root ID when inserting a type root entry. 84 const Id& parent_id = entry->ref(PARENT_ID);
61 if (parent_id.IsRoot()) { 85 ModelType model_type = entry->GetModelType();
62 ModelType model_type = GetModelType(entry); 86
63 // TODO(stanisc): there are some unit tests that create entries 87 if (ShouldUseParentId(parent_id, model_type)) {
64 // at the root and don't bother initializing specifics which 88 // Hierarchical type, lookup child set in the map.
65 // produces TOP_LEVEL_FOLDER type here. 89 DCHECK(!parent_id.IsNull());
66 if (syncer::IsRealDataType(model_type)) { 90 ParentChildrenMap::iterator it = parent_children_map_.find(parent_id);
67 model_type_root_ids_[model_type] = entry->ref(ID); 91 if (it != parent_children_map_.end()) {
92 siblings = it->second;
93 } else {
94 siblings = new OrderedChildSet();
95 parent_children_map_.insert(std::make_pair(parent_id, siblings));
68 } 96 }
97 } else {
98 // Non-hierarchical type, return a pre-defined collection by type.
99 siblings = GetOrCreateModelTypeChildSet(model_type);
69 } 100 }
70 101
71 OrderedChildSet* children = NULL; 102 // If this is one of type root folder for a non-hierarchical type, associate
72 ParentChildrenMap::iterator i = parent_children_map_.find(parent_id); 103 // its ID with the model type and the type's pre-defined child set with the
73 if (i != parent_children_map_.end()) { 104 // type root ID.
74 children = i->second; 105 // TODO(stanisc): crbug/438313: Just TypeSupportsHierarchy condition should
75 } else { 106 // theoretically be sufficient but in practice many tests don't properly
76 children = new OrderedChildSet(); 107 // initialize entries so TypeSupportsHierarchy ends up failing. Consider
77 parent_children_map_.insert(std::make_pair(parent_id, children)); 108 // tweaking TypeSupportsHierarchy and fixing all related test code.
109 if (parent_id.IsRoot() && entry->ref(IS_DIR) &&
110 syncer::IsRealDataType(model_type) &&
111 !TypeSupportsHierarchy(model_type)) {
112 const Id& type_root_id = entry->ref(ID);
113 // Disassociate the type root child set with the previous ID if any.
Nicolas Zea 2015/07/10 00:12:57 When would this happen?
stanisc 2015/07/10 04:58:51 This happens when the type root folder ID is chang
114 const Id& prev_type_root_id = model_type_root_ids_[model_type];
115 if (!prev_type_root_id.IsNull()) {
116 ParentChildrenMap::iterator it =
117 parent_children_map_.find(prev_type_root_id);
118 if (it != parent_children_map_.end()) {
119 // If the entry exists in the map it must already have the same
120 // model type specific child set. This child set will be re-inserted
121 // in the map under the new ID below so it is safe to simply erase
122 // the entry here.
123 DCHECK_EQ(it->second, GetModelTypeChildSet(model_type));
124 parent_children_map_.erase(it);
125 }
126 }
127 // Store the new type root ID and associate the child set.
128 // Note that the child set isn't owned by the map in this case.
129 model_type_root_ids_[model_type] = type_root_id;
130 parent_children_map_.insert(
131 std::make_pair(type_root_id, GetOrCreateModelTypeChildSet(model_type)));
78 } 132 }
79 133
80 return children->insert(entry).second; 134 // Finally, insert the entry in the child set.
135 return siblings->insert(entry).second;
81 } 136 }
82 137
83 // Like the other containers used to help support the syncable::Directory, this 138 // Like the other containers used to help support the syncable::Directory, this
84 // one does not own any EntryKernels. This function removes references to the 139 // one does not own any EntryKernels. This function removes references to the
85 // given EntryKernel but does not delete it. 140 // given EntryKernel but does not delete it.
86 void ParentChildIndex::Remove(EntryKernel* e) { 141 void ParentChildIndex::Remove(EntryKernel* e) {
87 const Id& parent_id = GetParentId(e); 142 OrderedChildSet* siblings = nullptr;
143 ModelType model_type = e->GetModelType();
144 const Id& parent_id = e->ref(PARENT_ID);
145 bool should_erase = false;
146 ParentChildrenMap::iterator it;
88 147
89 ParentChildrenMap::iterator parent = parent_children_map_.find(parent_id); 148 if (ShouldUseParentId(parent_id, model_type)) {
90 DCHECK(parent != parent_children_map_.end()); 149 // Hierarchical type, lookup child set in the map.
150 DCHECK(!parent_id.IsNull());
151 it = parent_children_map_.find(parent_id);
152 DCHECK(it != parent_children_map_.end());
153 siblings = it->second;
154 should_erase = true;
155 } else {
156 // Non-hierarchical type, return a pre-defined child set by type.
157 siblings = type_root_child_sets_[model_type];
158 }
91 159
92 OrderedChildSet* children = parent->second; 160 OrderedChildSet::iterator j = siblings->find(e);
93 OrderedChildSet::iterator j = children->find(e); 161 DCHECK(j != siblings->end());
94 DCHECK(j != children->end());
95 162
96 children->erase(j); 163 // Erase the entry from the child set.
97 if (children->empty()) { 164 siblings->erase(j);
98 delete children; 165 // If the set is now empty and isn't shareable with |type_root_child_sets_|,
99 parent_children_map_.erase(parent); 166 // erase it from the map.
167 if (siblings->empty() && should_erase) {
168 delete siblings;
169 parent_children_map_.erase(it);
100 } 170 }
101 } 171 }
102 172
103 bool ParentChildIndex::Contains(EntryKernel *e) const { 173 bool ParentChildIndex::Contains(EntryKernel *e) const {
104 const Id& parent_id = GetParentId(e); 174 const OrderedChildSet* siblings = GetChildSet(e);
105 ParentChildrenMap::const_iterator parent = 175 return siblings && siblings->count(e) > 0;
106 parent_children_map_.find(parent_id);
107 if (parent == parent_children_map_.end()) {
108 return false;
109 }
110 const OrderedChildSet* children = parent->second;
111 DCHECK(children && !children->empty());
112 return children->count(e) > 0;
113 } 176 }
114 177
115 const OrderedChildSet* ParentChildIndex::GetChildren(const Id& id) const { 178 const OrderedChildSet* ParentChildIndex::GetChildren(const Id& id) const {
116 DCHECK(!id.IsNull()); 179 DCHECK(!id.IsNull());
117 180
118 ParentChildrenMap::const_iterator parent = parent_children_map_.find(id); 181 ParentChildrenMap::const_iterator parent = parent_children_map_.find(id);
119 if (parent == parent_children_map_.end()) { 182 if (parent == parent_children_map_.end()) {
120 return nullptr; 183 return nullptr;
121 } 184 }
122 185
123 // A successful lookup implies at least some children exist. 186 OrderedChildSet* children = parent->second;
124 DCHECK(!parent->second->empty()); 187 // The expectation is that the function returns nullptr instead of an empty
125 return parent->second; 188 // child set
189 if (children && children->empty())
190 children = nullptr;
191 return children;
126 } 192 }
127 193
128 const OrderedChildSet* ParentChildIndex::GetChildren(EntryKernel* e) const { 194 const OrderedChildSet* ParentChildIndex::GetChildren(EntryKernel* e) const {
129 return GetChildren(e->ref(ID)); 195 return GetChildren(e->ref(ID));
130 } 196 }
131 197
132 const OrderedChildSet* ParentChildIndex::GetSiblings(EntryKernel* e) const { 198 const OrderedChildSet* ParentChildIndex::GetSiblings(EntryKernel* e) const {
199 // This implies the entry is in the index.
133 DCHECK(Contains(e)); 200 DCHECK(Contains(e));
134 const OrderedChildSet* siblings = GetChildren(GetParentId(e)); 201 const OrderedChildSet* siblings = GetChildSet(e);
135 DCHECK(siblings && !siblings->empty()); 202 DCHECK(siblings && !siblings->empty());
136 return siblings; 203 return siblings;
137 } 204 }
138 205
139 const Id& ParentChildIndex::GetParentId(EntryKernel* e) const { 206 /* static */
140 const Id& parent_id = e->ref(PARENT_ID); 207 bool ParentChildIndex::ShouldUseParentId(const Id& parent_id,
141 if (!parent_id.IsNull()) { 208 ModelType model_type) {
142 return parent_id; 209 // Returns true for compatibility with legacy unit tests.
Nicolas Zea 2015/07/10 00:12:57 I'm not sure I understand this comment? Returns tr
stanisc 2015/07/10 04:58:50 Didn't finish the comment. Updated.
143 } 210 return parent_id.IsRoot() || TypeSupportsHierarchy(model_type) ||
144 return GetModelTypeRootId(GetModelType(e)); 211 !syncer::IsRealDataType(model_type);
145 } 212 }
146 213
147 ModelType ParentChildIndex::GetModelType(EntryKernel* e) { 214 const OrderedChildSet* ParentChildIndex::GetChildSet(EntryKernel* e) const {
148 // TODO(stanisc): is there a more effective way to find out model type?
149 ModelType model_type = e->GetModelType(); 215 ModelType model_type = e->GetModelType();
150 if (!syncer::IsRealDataType(model_type)) { 216
151 model_type = e->GetServerModelType(); 217 const Id& parent_id = e->ref(PARENT_ID);
218 if (ShouldUseParentId(parent_id, model_type)) {
219 // Hierarchical type, lookup child set in the map.
220 ParentChildrenMap::const_iterator it = parent_children_map_.find(parent_id);
221 if (it != parent_children_map_.end()) {
222 return it->second;
223 }
224 } else {
225 // Non-hierarchical type, return a collection indexed by type.
226 return GetModelTypeChildSet(model_type);
152 } 227 }
153 return model_type; 228
229 return nullptr;
Nicolas Zea 2015/07/10 00:12:57 nit: how about rewriting this as: if (ShouldUsePa
stanisc 2015/07/10 04:58:50 Done.
230 }
231
232 const OrderedChildSet* ParentChildIndex::GetModelTypeChildSet(
233 ModelType model_type) const {
234 return type_root_child_sets_[model_type];
235 }
236
237 OrderedChildSet* ParentChildIndex::GetOrCreateModelTypeChildSet(
238 ModelType model_type) {
239 if (!type_root_child_sets_[model_type])
240 type_root_child_sets_[model_type] = new OrderedChildSet();
241 return type_root_child_sets_[model_type];
154 } 242 }
155 243
156 const Id& ParentChildIndex::GetModelTypeRootId(ModelType model_type) const { 244 const Id& ParentChildIndex::GetModelTypeRootId(ModelType model_type) const {
157 // TODO(stanisc): Review whether this approach is reliable enough.
158 // Should this method simply enumerate children of root node ("r") instead?
159 return model_type_root_ids_[model_type]; 245 return model_type_root_ids_[model_type];
160 } 246 }
161 247
162 } // namespace syncable 248 } // namespace syncable
163 } // namespace syncer 249 } // namespace syncer
OLDNEW
« no previous file with comments | « sync/syncable/parent_child_index.h ('k') | sync/syncable/parent_child_index_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698