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