summaryrefslogtreecommitdiffstats
path: root/sync/syncable/parent_child_index.cc
blob: bf6e03fbd2044f55ea01d7b47ea2aa3b1b552c83 (plain)
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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// 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