OLD | NEW |
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 Loading... |
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_|. Null out shared instances and |
| 52 // ScopedVector will take care of the ones that are not shared (if any). |
| 53 if (!model_type_root_ids_[i].IsNull()) { |
| 54 DCHECK_EQ(type_root_child_sets_[i], |
| 55 parent_children_map_.find(model_type_root_ids_[i])->second); |
| 56 type_root_child_sets_[i] = nullptr; |
| 57 } |
| 58 } |
| 59 |
| 60 STLDeleteContainerPairSecondPointers(parent_children_map_.begin(), |
| 61 parent_children_map_.end()); |
48 } | 62 } |
49 | 63 |
50 bool ParentChildIndex::ShouldInclude(const EntryKernel* entry) { | 64 bool ParentChildIndex::ShouldInclude(const EntryKernel* entry) { |
51 // This index excludes deleted items and the root item. The root | 65 // 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. | 66 // 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(); | 67 // The index also excludes items without client data e.g. the items that |
| 68 // don't have client SPECIFICS or PARENT_ID. There is no need to include |
| 69 // server-only entries because they shouldn't show up on the client until |
| 70 // they're applied. |
| 71 return !entry->ref(IS_DEL) && !entry->ref(ID).IsRoot() && |
| 72 (entry->GetModelType() != UNSPECIFIED || |
| 73 !entry->ref(PARENT_ID).IsNull()); |
54 } | 74 } |
55 | 75 |
56 bool ParentChildIndex::Insert(EntryKernel* entry) { | 76 bool ParentChildIndex::Insert(EntryKernel* entry) { |
57 DCHECK(ShouldInclude(entry)); | 77 DCHECK(ShouldInclude(entry)); |
58 | 78 |
59 const Id& parent_id = GetParentId(entry); | 79 OrderedChildSet* siblings = nullptr; |
60 // Store type root ID when inserting a type root entry. | 80 const Id& parent_id = entry->ref(PARENT_ID); |
61 if (parent_id.IsRoot()) { | 81 ModelType model_type = entry->GetModelType(); |
62 ModelType model_type = GetModelType(entry); | 82 |
63 // TODO(stanisc): there are some unit tests that create entries | 83 if (ShouldUseParentId(parent_id, model_type)) { |
64 // at the root and don't bother initializing specifics which | 84 // Hierarchical type, lookup child set in the map. |
65 // produces TOP_LEVEL_FOLDER type here. | 85 DCHECK(!parent_id.IsNull()); |
66 if (syncer::IsRealDataType(model_type)) { | 86 ParentChildrenMap::iterator it = parent_children_map_.find(parent_id); |
67 model_type_root_ids_[model_type] = entry->ref(ID); | 87 if (it != parent_children_map_.end()) { |
| 88 siblings = it->second; |
| 89 } else { |
| 90 siblings = new OrderedChildSet(); |
| 91 parent_children_map_.insert(std::make_pair(parent_id, siblings)); |
68 } | 92 } |
| 93 } else { |
| 94 // Non-hierarchical type, return a pre-defined collection by type. |
| 95 siblings = GetOrCreateModelTypeChildSet(model_type); |
69 } | 96 } |
70 | 97 |
71 OrderedChildSet* children = NULL; | 98 // If this is one of type root folder for a non-hierarchical type, associate |
72 ParentChildrenMap::iterator i = parent_children_map_.find(parent_id); | 99 // its ID with the model type and the type's pre-defined child set with the |
73 if (i != parent_children_map_.end()) { | 100 // type root ID. |
74 children = i->second; | 101 // TODO(stanisc): crbug/438313: Just TypeSupportsHierarchy condition should |
75 } else { | 102 // theoretically be sufficient but in practice many tests don't properly |
76 children = new OrderedChildSet(); | 103 // initialize entries so TypeSupportsHierarchy ends up failing. Consider |
77 parent_children_map_.insert(std::make_pair(parent_id, children)); | 104 // tweaking TypeSupportsHierarchy and fixing all related test code. |
| 105 if (parent_id.IsRoot() && entry->ref(IS_DIR) && |
| 106 syncer::IsRealDataType(model_type) && |
| 107 !TypeSupportsHierarchy(model_type)) { |
| 108 const Id& type_root_id = entry->ref(ID); |
| 109 // Disassociate the type root child set with the previous ID if any. |
| 110 const Id& prev_type_root_id = model_type_root_ids_[model_type]; |
| 111 if (!prev_type_root_id.IsNull()) { |
| 112 ParentChildrenMap::iterator it = |
| 113 parent_children_map_.find(prev_type_root_id); |
| 114 if (it != parent_children_map_.end()) { |
| 115 // If the entry exists in the map it must already have the same |
| 116 // model type specific child set. This child set will be re-inserted |
| 117 // in the map under the new ID below so it is safe to simply erase |
| 118 // the entry here. |
| 119 DCHECK_EQ(it->second, GetModelTypeChildSet(model_type)); |
| 120 parent_children_map_.erase(it); |
| 121 } |
| 122 } |
| 123 // Store the new type root ID and associate the child set. |
| 124 // Note that the child set isn't owned by the map in this case. |
| 125 model_type_root_ids_[model_type] = type_root_id; |
| 126 parent_children_map_.insert( |
| 127 std::make_pair(type_root_id, GetOrCreateModelTypeChildSet(model_type))); |
78 } | 128 } |
79 | 129 |
80 return children->insert(entry).second; | 130 // Finally, insert the entry in the child set. |
| 131 return siblings->insert(entry).second; |
81 } | 132 } |
82 | 133 |
83 // Like the other containers used to help support the syncable::Directory, this | 134 // 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 | 135 // one does not own any EntryKernels. This function removes references to the |
85 // given EntryKernel but does not delete it. | 136 // given EntryKernel but does not delete it. |
86 void ParentChildIndex::Remove(EntryKernel* e) { | 137 void ParentChildIndex::Remove(EntryKernel* e) { |
87 const Id& parent_id = GetParentId(e); | 138 OrderedChildSet* siblings = nullptr; |
| 139 ModelType model_type = e->GetModelType(); |
| 140 const Id& parent_id = e->ref(PARENT_ID); |
| 141 bool should_erase = false; |
| 142 ParentChildrenMap::iterator it; |
88 | 143 |
89 ParentChildrenMap::iterator parent = parent_children_map_.find(parent_id); | 144 if (ShouldUseParentId(parent_id, model_type)) { |
90 DCHECK(parent != parent_children_map_.end()); | 145 // Hierarchical type, lookup child set in the map. |
| 146 DCHECK(!parent_id.IsNull()); |
| 147 it = parent_children_map_.find(parent_id); |
| 148 DCHECK(it != parent_children_map_.end()); |
| 149 siblings = it->second; |
| 150 should_erase = true; |
| 151 } else { |
| 152 // Non-hierarchical type, return a pre-defined child set by type. |
| 153 siblings = type_root_child_sets_[model_type]; |
| 154 } |
91 | 155 |
92 OrderedChildSet* children = parent->second; | 156 OrderedChildSet::iterator j = siblings->find(e); |
93 OrderedChildSet::iterator j = children->find(e); | 157 DCHECK(j != siblings->end()); |
94 DCHECK(j != children->end()); | |
95 | 158 |
96 children->erase(j); | 159 // Erase the entry from the child set. |
97 if (children->empty()) { | 160 siblings->erase(j); |
98 delete children; | 161 // If the set is now empty and isn't shareable with |type_root_child_sets_|, |
99 parent_children_map_.erase(parent); | 162 // erase it from the map. |
| 163 if (siblings->empty() && should_erase) { |
| 164 delete siblings; |
| 165 parent_children_map_.erase(it); |
100 } | 166 } |
101 } | 167 } |
102 | 168 |
103 bool ParentChildIndex::Contains(EntryKernel *e) const { | 169 bool ParentChildIndex::Contains(EntryKernel *e) const { |
104 const Id& parent_id = GetParentId(e); | 170 const OrderedChildSet* siblings = GetChildSet(e); |
105 ParentChildrenMap::const_iterator parent = | 171 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 } | 172 } |
114 | 173 |
115 const OrderedChildSet* ParentChildIndex::GetChildren(const Id& id) const { | 174 const OrderedChildSet* ParentChildIndex::GetChildren(const Id& id) const { |
116 DCHECK(!id.IsNull()); | 175 DCHECK(!id.IsNull()); |
117 | 176 |
118 ParentChildrenMap::const_iterator parent = parent_children_map_.find(id); | 177 ParentChildrenMap::const_iterator parent = parent_children_map_.find(id); |
119 if (parent == parent_children_map_.end()) { | 178 if (parent == parent_children_map_.end()) { |
120 return nullptr; | 179 return nullptr; |
121 } | 180 } |
122 | 181 |
123 // A successful lookup implies at least some children exist. | 182 OrderedChildSet* children = parent->second; |
124 DCHECK(!parent->second->empty()); | 183 // The expectation is that the function returns nullptr instead of an empty |
125 return parent->second; | 184 // child set |
| 185 if (children && children->empty()) |
| 186 children = nullptr; |
| 187 return children; |
126 } | 188 } |
127 | 189 |
128 const OrderedChildSet* ParentChildIndex::GetChildren(EntryKernel* e) const { | 190 const OrderedChildSet* ParentChildIndex::GetChildren(EntryKernel* e) const { |
129 return GetChildren(e->ref(ID)); | 191 return GetChildren(e->ref(ID)); |
130 } | 192 } |
131 | 193 |
132 const OrderedChildSet* ParentChildIndex::GetSiblings(EntryKernel* e) const { | 194 const OrderedChildSet* ParentChildIndex::GetSiblings(EntryKernel* e) const { |
| 195 // This implies the entry is in the index. |
133 DCHECK(Contains(e)); | 196 DCHECK(Contains(e)); |
134 const OrderedChildSet* siblings = GetChildren(GetParentId(e)); | 197 const OrderedChildSet* siblings = GetChildSet(e); |
135 DCHECK(siblings && !siblings->empty()); | 198 DCHECK(siblings && !siblings->empty()); |
136 return siblings; | 199 return siblings; |
137 } | 200 } |
138 | 201 |
139 const Id& ParentChildIndex::GetParentId(EntryKernel* e) const { | 202 /* static */ |
140 const Id& parent_id = e->ref(PARENT_ID); | 203 bool ParentChildIndex::ShouldUseParentId(const Id& parent_id, |
141 if (!parent_id.IsNull()) { | 204 ModelType model_type) { |
142 return parent_id; | 205 // For compatibility with legacy unit tests, in addition to hierarchical |
143 } | 206 // entries, this returns true any entries directly under root and for entries |
144 return GetModelTypeRootId(GetModelType(e)); | 207 // of UNSPECIFIED model type. |
| 208 return parent_id.IsRoot() || TypeSupportsHierarchy(model_type) || |
| 209 !syncer::IsRealDataType(model_type); |
145 } | 210 } |
146 | 211 |
147 ModelType ParentChildIndex::GetModelType(EntryKernel* e) { | 212 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(); | 213 ModelType model_type = e->GetModelType(); |
150 if (!syncer::IsRealDataType(model_type)) { | 214 |
151 model_type = e->GetServerModelType(); | 215 const Id& parent_id = e->ref(PARENT_ID); |
| 216 if (ShouldUseParentId(parent_id, model_type)) { |
| 217 // Hierarchical type, lookup child set in the map. |
| 218 ParentChildrenMap::const_iterator it = parent_children_map_.find(parent_id); |
| 219 if (it == parent_children_map_.end()) |
| 220 return nullptr; |
| 221 return it->second; |
152 } | 222 } |
153 return model_type; | 223 |
| 224 // Non-hierarchical type, return a collection indexed by type. |
| 225 return GetModelTypeChildSet(model_type); |
| 226 } |
| 227 |
| 228 const OrderedChildSet* ParentChildIndex::GetModelTypeChildSet( |
| 229 ModelType model_type) const { |
| 230 return type_root_child_sets_[model_type]; |
| 231 } |
| 232 |
| 233 OrderedChildSet* ParentChildIndex::GetOrCreateModelTypeChildSet( |
| 234 ModelType model_type) { |
| 235 if (!type_root_child_sets_[model_type]) |
| 236 type_root_child_sets_[model_type] = new OrderedChildSet(); |
| 237 return type_root_child_sets_[model_type]; |
154 } | 238 } |
155 | 239 |
156 const Id& ParentChildIndex::GetModelTypeRootId(ModelType model_type) const { | 240 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]; | 241 return model_type_root_ids_[model_type]; |
160 } | 242 } |
161 | 243 |
162 } // namespace syncable | 244 } // namespace syncable |
163 } // namespace syncer | 245 } // namespace syncer |
OLD | NEW |